View Javadoc
1   package org.apache.maven.shared.test.plugin;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.io.IOException;
24  import java.io.Reader;
25  import java.io.Writer;
26  import java.util.ArrayList;
27  import java.util.List;
28  import java.util.Properties;
29  
30  import org.apache.maven.artifact.Artifact;
31  import org.apache.maven.artifact.UnknownRepositoryLayoutException;
32  import org.apache.maven.artifact.factory.ArtifactFactory;
33  import org.apache.maven.artifact.handler.ArtifactHandler;
34  import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
35  import org.apache.maven.artifact.repository.ArtifactRepository;
36  import org.apache.maven.artifact.repository.ArtifactRepositoryFactory;
37  import org.apache.maven.model.Build;
38  import org.apache.maven.model.DeploymentRepository;
39  import org.apache.maven.model.DistributionManagement;
40  import org.apache.maven.model.Model;
41  import org.apache.maven.model.Plugin;
42  import org.apache.maven.model.Repository;
43  import org.apache.maven.model.Site;
44  import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
45  import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
46  import org.apache.maven.project.DefaultProjectBuildingRequest;
47  import org.apache.maven.project.MavenProject;
48  import org.apache.maven.project.ProjectBuilder;
49  import org.apache.maven.project.ProjectBuildingException;
50  import org.apache.maven.project.ProjectBuildingRequest;
51  import org.apache.maven.project.artifact.ProjectArtifactMetadata;
52  import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
53  import org.codehaus.plexus.component.annotations.Component;
54  import org.codehaus.plexus.component.annotations.Requirement;
55  import org.codehaus.plexus.util.FileUtils;
56  import org.codehaus.plexus.util.IOUtil;
57  import org.codehaus.plexus.util.ReaderFactory;
58  import org.codehaus.plexus.util.WriterFactory;
59  import org.codehaus.plexus.util.xml.Xpp3Dom;
60  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
61  
62  /**
63   * Testing tool used to read MavenProject instances from pom.xml files, and to create plugin jar
64   * files (package phase of the normal build process) for distribution to a test local repository
65   * directory.
66   *
67   * @author jdcasey
68   * @version $Id$
69   */
70  @Deprecated
71  @Component( role = ProjectTool.class )
72  public class ProjectTool
73  {
74      /** Plexus role */
75      public static final String ROLE = ProjectTool.class.getName();
76  
77      public static final String INTEGRATION_TEST_DEPLOYMENT_REPO_URL = "integration-test.deployment.repo.url";
78  
79      @Requirement
80      private BuildTool buildTool;
81  
82      @Requirement
83      private RepositoryTool repositoryTool;
84  
85      @Requirement
86      private ProjectBuilder projectBuilder;
87  
88      @Requirement
89      private ArtifactHandlerManager artifactHandlerManager;
90  
91      @Requirement
92      private ArtifactFactory artifactFactory;
93  
94      @Requirement
95      private ArtifactRepositoryFactory artifactRepositoryFactory;
96  
97      /**
98       * Construct a MavenProject instance from the specified POM file.
99       *
100      * @param pomFile current POM file
101      * @return the Maven project from a file
102      * @throws TestToolsException if any
103      */
104     public MavenProject readProject( File pomFile )
105         throws TestToolsException
106     {
107         return readProject( pomFile, repositoryTool.findLocalRepositoryDirectory() );
108     }
109 
110     /**
111      * Construct a MavenProject instance from the specified POM file, using the specified local
112      * repository directory to resolve ancestor POMs as needed.
113      *
114      * @param pomFile current POM file
115      * @param localRepositoryBasedir
116      * @return the Maven project from a file and a local repo
117      * @throws TestToolsException if any
118      */
119     public MavenProject readProject( File pomFile, File localRepositoryBasedir )
120         throws TestToolsException
121     {
122         try
123         {
124             ArtifactRepository localRepository = repositoryTool
125                 .createLocalArtifactRepositoryInstance( localRepositoryBasedir );
126 
127             ProjectBuildingRequest request = new DefaultProjectBuildingRequest();
128             return projectBuilder.build( pomFile, request ).getProject();
129         }
130         catch ( ProjectBuildingException e )
131         {
132             throw new TestToolsException( "Error building MavenProject instance from test pom: " + pomFile, e );
133         }
134     }
135 
136     /**
137      * Construct a MavenProject instance from the specified POM file with dependencies.
138      *
139      * @param pomFile current POM file
140      * @return the Maven project with dependencies from a file
141      * @throws TestToolsException if any
142      */
143     public MavenProject readProjectWithDependencies( File pomFile )
144         throws TestToolsException
145     {
146         return readProjectWithDependencies( pomFile, repositoryTool.findLocalRepositoryDirectory() );
147     }
148 
149     /**
150      * Construct a MavenProject instance from the specified POM file with dependencies, using the specified local
151      * repository directory to resolve ancestor POMs as needed.
152      *
153      * @param pomFile current POM file
154      * @param localRepositoryBasedir
155      * @return the Maven project with dependencies from a file and a local repo
156      * @throws TestToolsException if any
157      */
158     public MavenProject readProjectWithDependencies( File pomFile, File localRepositoryBasedir )
159         throws TestToolsException
160     {
161         try
162         {
163             ArtifactRepository localRepository = repositoryTool
164                 .createLocalArtifactRepositoryInstance( localRepositoryBasedir );
165 
166             ProjectBuildingRequest request = new DefaultProjectBuildingRequest();
167             return projectBuilder.build( pomFile, request ).getProject();
168         }
169         catch ( ProjectBuildingException e )
170         {
171             throw new TestToolsException( "Error building MavenProject instance from test pom: " + pomFile, e );
172         }
173     }
174 
175     /**
176      * Run the plugin's Maven build up to the package phase, in order to produce a jar file for
177      * distribution to a test-time local repository. The testVersion parameter specifies the version
178      * to be used in the <version/> element of the plugin configuration, and also in fully
179      * qualified, unambiguous goal invocations (as in
180      * org.apache.maven.plugins:maven-eclipse-plugin:test:eclipse).
181      *
182      * @param pomFile The plugin's POM
183      * @param testVersion The version to use for testing this plugin. To promote test resiliency,
184      *   this version should remain unchanged, regardless of what plugin version is under
185      *   development.
186      * @param skipUnitTests In cases where test builds occur during the unit-testing phase (usually
187      *   a bad testing smell), the plugin jar must be produced <b>without</b> running unit tests.
188      *   Otherwise, the testing process will result in a recursive loop of building a plugin jar and
189      *   trying to unit test it during the build. In these cases, set this flag to <code>true</code>.
190      * @return The resulting MavenProject, after the test version and skip flag (for unit tests)
191      *   have been appropriately configured.
192      * @throws TestToolsException if any
193      */
194     public MavenProject packageProjectArtifact( File pomFile, String testVersion, boolean skipUnitTests )
195         throws TestToolsException
196     {
197         return packageProjectArtifact( pomFile, testVersion, skipUnitTests, null );
198     }
199 
200     /**
201      * Run the plugin's Maven build up to the package phase, in order to produce a jar file for
202      * distribution to a test-time local repository. The testVersion parameter specifies the version
203      * to be used in the &lt;version/&gt; element of the plugin configuration, and also in fully
204      * qualified, unambiguous goal invocations (as in
205      * org.apache.maven.plugins:maven-eclipse-plugin:test:eclipse).
206      *
207      * @param pomFile The plugin's POM
208      * @param testVersion The version to use for testing this plugin. To promote test resiliency,
209      *   this version should remain unchanged, regardless of what plugin version is under
210      *   development.
211      * @param skipUnitTests In cases where test builds occur during the unit-testing phase (usually
212      *   a bad testing smell), the plugin jar must be produced <b>without</b> running unit tests.
213      *   Otherwise, the testing process will result in a recursive loop of building a plugin jar and
214      *   trying to unit test it during the build. In these cases, set this flag to <code>true</code>.
215      * @param logFile The file to which build output should be logged, in order to allow later
216      *   inspection in case this build fails.
217      * @return The resulting MavenProject, after the test version and skip flag (for unit tests)
218      *   have been appropriately configured.
219      * @throws TestToolsException if any
220      */
221     public MavenProject packageProjectArtifact( File pomFile, String testVersion, boolean skipUnitTests, File logFile )
222         throws TestToolsException
223     {
224         PomInfo pomInfo = manglePomForTesting( pomFile, testVersion, skipUnitTests );
225 
226         Properties properties = new Properties();
227 
228         List<String> goals = new ArrayList<String>();
229         goals.add( "package" );
230 
231         File buildLog = logFile == null ? pomInfo.getBuildLogFile() : logFile;
232         System.out.println( "Now Building test version of the plugin...\nUsing staged plugin-pom: "
233             + pomInfo.getPomFile().getAbsolutePath() );
234 
235         buildTool.executeMaven( pomInfo.getPomFile(), properties, goals, buildLog );
236 
237         File artifactFile = new File( pomInfo.getPomFile().getParentFile(),
238                                       pomInfo.getBuildDirectory() + "/" + pomInfo.getFinalName() );
239         System.out.println( "Using IT Plugin Jar: " + artifactFile.getAbsolutePath() );
240         try
241         {
242             ProjectBuildingRequest request = new DefaultProjectBuildingRequest();
243             request.setLocalRepository( artifactRepositoryFactory.createArtifactRepository( "local", new File( "target/localrepo" ).getCanonicalFile().toURL().toExternalForm(), "default", null, null ) );
244             request.setRepositorySession( MavenRepositorySystemUtils.newSession() );
245             MavenProject project = projectBuilder.build( pomInfo.getPomFile(), request ).getProject();
246 
247             Artifact artifact = artifactFactory.createArtifact( project.getGroupId(), project.getArtifactId(), project
248                 .getVersion(), null, project.getPackaging() );
249 
250             artifact.setFile( artifactFile );
251             artifact.addMetadata( new ProjectArtifactMetadata( artifact, project.getFile() ) );
252 
253             project.setArtifact( artifact );
254 
255             return project;
256         }
257         catch ( ProjectBuildingException e )
258         {
259             throw new TestToolsException(
260                                           "Error building MavenProject instance from test pom: " + pomInfo.getPomFile(),
261                                           e );
262         }
263         catch ( UnknownRepositoryLayoutException e )
264         {
265             throw new TestToolsException(
266                                          "Error building ArtifactRepository instance from test pom: " + pomInfo.getPomFile(),
267                                          e );
268         }
269         catch ( IOException e )
270         {
271             throw new TestToolsException(
272                                          "Error building ArtifactRepository instance from test pom: " + pomInfo.getPomFile(),
273                                          e );
274         }
275     }
276 
277     /**
278      * Inject a special version for testing, to allow tests to unambiguously reference the plugin
279      * currently under test. If test builds will be executed from the unit-testing phase, also inject
280      * &lt;skip&gt;true&lt;/skip&gt; into the configuration of the <code>maven-surefire-plugin</code>
281      * to allow production of a test-only version of the plugin jar without running unit tests.
282      *
283      * @param pomFile The plugin POM
284      * @param testVersion The version that allows test builds to reference the plugin under test
285      * @param skipUnitTests If true, configure the surefire plugin to skip unit tests
286      * @return Information about mangled POM, including the temporary file to which it was written.
287      * @throws TestToolsException if any
288      */
289     protected PomInfo manglePomForTesting( File pomFile, String testVersion, boolean skipUnitTests )
290         throws TestToolsException
291     {
292         File input = pomFile;
293 
294         File output = new File( pomFile.getParentFile(), "pom-" + testVersion + ".xml" );
295         output.deleteOnExit();
296 
297         Reader reader = null;
298         Writer writer = null;
299 
300         Model model = null;
301         String finalName = null;
302         String buildDirectory = null;
303 
304         try
305         {
306             reader = ReaderFactory.newXmlReader( input );
307 
308             model = new MavenXpp3Reader().read( reader );
309         }
310         catch ( IOException e )
311         {
312             throw new TestToolsException( "Error creating test-time version of POM for: " + input, e );
313         }
314         catch ( XmlPullParserException e )
315         {
316             throw new TestToolsException( "Error creating test-time version of POM for: " + input, e );
317         }
318         finally
319         {
320             IOUtil.close( reader );
321         }
322 
323         try
324         {
325             model.setVersion( testVersion );
326 
327             Build build = model.getBuild();
328             if ( build == null )
329             {
330                 build = new Build();
331             }
332             buildDirectory = build.getDirectory();
333 
334             if ( buildDirectory == null )
335             {
336                 buildDirectory = "target";
337             }
338 
339             buildDirectory = ( buildDirectory + File.separatorChar + "it-build-target" );
340             build.setDirectory( buildDirectory );
341             build.setOutputDirectory( buildDirectory + File.separatorChar + "classes" );
342             System.out.println( "Using " + build.getDirectory() + " and " + build.getOutputDirectory()
343                 + " to build IT version of plugin" );
344             model.setBuild( build );
345 
346             finalName = build.getFinalName();
347 
348             if ( finalName == null )
349             {
350                 ArtifactHandler handler = artifactHandlerManager.getArtifactHandler( model.getPackaging() );
351 
352                 String ext = handler.getExtension();
353 
354                 finalName = model.getArtifactId() + "-" + model.getVersion() + "." + ext;
355             }
356 
357             DistributionManagement distMgmt = new DistributionManagement();
358 
359             DeploymentRepository deployRepo = new DeploymentRepository();
360 
361             deployRepo.setId( "integration-test.output" );
362 
363             File tmpDir = FileUtils.createTempFile( "integration-test-repo", "", null );
364             String tmpUrl = tmpDir.toURL().toExternalForm();
365 
366             deployRepo.setUrl( tmpUrl );
367 
368             distMgmt.setRepository( deployRepo );
369             distMgmt.setSnapshotRepository( deployRepo );
370 
371             Repository localAsRemote = new Repository();
372             localAsRemote.setId( "testing.mainLocalAsRemote" );
373 
374             File localRepoDir = repositoryTool.findLocalRepositoryDirectory();
375             localAsRemote.setUrl( localRepoDir.toURL().toExternalForm() );
376 
377             model.addRepository( localAsRemote );
378             model.addPluginRepository( localAsRemote );
379 
380             Site site = new Site();
381 
382             site.setId( "integration-test.output" );
383             site.setUrl( tmpUrl );
384 
385             distMgmt.setSite( site );
386 
387             model.setDistributionManagement( distMgmt );
388 
389             model.addProperty( INTEGRATION_TEST_DEPLOYMENT_REPO_URL, tmpUrl );
390 
391             if ( skipUnitTests )
392             {
393                 List<Plugin> plugins = build.getPlugins();
394                 Plugin plugin = null;
395                 for ( Plugin plug : plugins )
396                 {
397                     if ( "maven-surefire-plugin".equals( plug.getArtifactId() ) )
398                     {
399                         plugin = plug;
400                         break;
401                     }
402                 }
403 
404                 if ( plugin == null )
405                 {
406                     plugin = new Plugin();
407                     plugin.setArtifactId( "maven-surefire-plugin" );
408                     build.addPlugin( plugin );
409                 }
410 
411                 Xpp3Dom configDom = (Xpp3Dom) plugin.getConfiguration();
412                 if ( configDom == null )
413                 {
414                     configDom = new Xpp3Dom( "configuration" );
415                     plugin.setConfiguration( configDom );
416                 }
417 
418                 Xpp3Dom skipDom = new Xpp3Dom( "skip" );
419                 skipDom.setValue( "true" );
420 
421                 configDom.addChild( skipDom );
422             }
423 
424             writer = WriterFactory.newXmlWriter( output );
425 
426             new MavenXpp3Writer().write( writer, model );
427         }
428         catch ( IOException e )
429         {
430             throw new TestToolsException( "Error creating test-time version of POM for: " + input, e );
431         }
432         finally
433         {
434             IOUtil.close( writer );
435         }
436 
437         return new PomInfo( output, model.getGroupId(), model.getArtifactId(), model.getVersion(),
438                             model.getBuild().getDirectory(), model.getBuild().getOutputDirectory(), finalName );
439     }
440 
441     static final class PomInfo
442     {
443         private final File pomFile;
444 
445         private final String groupId;
446 
447         private final String artifactId;
448 
449         private final String version;
450 
451         private final String finalName;
452 
453         private final String buildDirectory;
454 
455         private final String buildOutputDirectory;
456 
457         PomInfo( File pomFile, String groupId, String artifactId, String version, String buildDirectory,
458                  String buildOutputDirectory, String finalName )
459         {
460             this.pomFile = pomFile;
461             this.groupId = groupId;
462             this.artifactId = artifactId;
463             this.version = version;
464             this.buildDirectory = buildDirectory;
465             this.buildOutputDirectory = buildOutputDirectory;
466             this.finalName = finalName;
467         }
468 
469         File getPomFile()
470         {
471             return pomFile;
472         }
473 
474         String getBuildDirectory()
475         {
476             return buildDirectory;
477         }
478 
479         String getBuildOutputDirectory()
480         {
481             return buildOutputDirectory;
482         }
483 
484         String getFinalName()
485         {
486             return finalName;
487         }
488 
489         File getBuildLogFile()
490         {
491             return new File( buildDirectory + "/test-build-logs/" + groupId + "_" + artifactId + "_" + version
492                 + ".build.log" );
493         }
494     }
495 }