View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.shared.test.plugin;
20  
21  import java.io.File;
22  import java.io.FileReader;
23  import java.io.FileWriter;
24  import java.io.IOException;
25  import java.util.ArrayList;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Properties;
29  
30  import org.apache.maven.artifact.Artifact;
31  import org.apache.maven.artifact.factory.ArtifactFactory;
32  import org.apache.maven.artifact.handler.ArtifactHandler;
33  import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
34  import org.apache.maven.artifact.repository.ArtifactRepository;
35  import org.apache.maven.model.Build;
36  import org.apache.maven.model.Model;
37  import org.apache.maven.model.Plugin;
38  import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
39  import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
40  import org.apache.maven.project.MavenProject;
41  import org.apache.maven.project.MavenProjectBuilder;
42  import org.apache.maven.project.ProjectBuildingException;
43  import org.apache.maven.project.artifact.ProjectArtifactMetadata;
44  import org.codehaus.plexus.util.IOUtil;
45  import org.codehaus.plexus.util.xml.Xpp3Dom;
46  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
47  
48  /**
49   * Testing tool used to read MavenProject instances from pom.xml files, and to create plugin jar
50   * files (package phase of the normal build process) for distribution to a test local repository
51   * directory.
52   * 
53   * @plexus.component role="org.apache.maven.shared.test.plugin.ProjectTool" role-hint="default"
54   * @author jdcasey
55   */
56  public class ProjectTool
57  {
58      public static final String ROLE = ProjectTool.class.getName();
59  
60      /**
61       * @plexus.requirement role-hint="default"
62       */
63      private BuildTool buildTool;
64  
65      /**
66       * @plexus.requirement role-hint="default"
67       */
68      private RepositoryTool repositoryTool;
69  
70      /**
71       * @plexus.requirement
72       */
73      private MavenProjectBuilder projectBuilder;
74  
75      /**
76       * @plexus.requirement
77       */
78      private ArtifactHandlerManager artifactHandlerManager;
79  
80      /**
81       * @plexus.requirement
82       */
83      private ArtifactFactory artifactFactory;
84  
85      /**
86       * Construct a MavenProject instance from the specified POM file.
87       */
88      public MavenProject readProject( File pomFile )
89          throws TestToolsException
90      {
91          return readProject( pomFile, repositoryTool.findLocalRepositoryDirectory() );
92      }
93  
94      /**
95       * Construct a MavenProject instance from the specified POM file, using the specified local
96       * repository directory to resolve ancestor POMs as needed.
97       */
98      public MavenProject readProject( File pomFile, File localRepositoryBasedir )
99          throws TestToolsException
100     {
101         try
102         {
103             ArtifactRepository localRepository = repositoryTool
104                 .createLocalArtifactRepositoryInstance( localRepositoryBasedir );
105 
106             return projectBuilder.build( pomFile, localRepository, null );
107         }
108         catch ( ProjectBuildingException e )
109         {
110             throw new TestToolsException( "Error building MavenProject instance from test pom: " + pomFile, e );
111         }
112     }
113 
114     /**
115      * Run the plugin's Maven build up to the package phase, in order to produce a jar file for 
116      * distribution to a test-time local repository. The testVersion parameter specifies the version
117      * to be used in the <version/> element of the plugin configuration, and also in fully
118      * qualified, unambiguous goal invocations (as in 
119      * org.apache.maven.plugins:maven-eclipse-plugin:test:eclipse).
120      * 
121      * @param pomFile The plugin's POM
122      * @param testVersion The version to use for testing this plugin. To promote test resiliency, 
123      *   this version should remain unchanged, regardless of what plugin version is under 
124      *   development.
125      * @param skipUnitTests In cases where test builds occur during the unit-testing phase (usually
126      *   a bad testing smell), the plugin jar must be produced <b>without</b> running unit tests.
127      *   Otherwise, the testing process will result in a recursive loop of building a plugin jar and
128      *   trying to unit test it during the build. In these cases, set this flag to <code>true</code>.
129      * @return The resulting MavenProject, after the test version and skip flag (for unit tests) 
130      *   have been appropriately configured.
131      */
132     public MavenProject packageProjectArtifact( File pomFile, String testVersion, boolean skipUnitTests )
133         throws TestToolsException
134     {
135         return packageProjectArtifact( pomFile, testVersion, skipUnitTests, null );
136     }
137 
138     /**
139      * Run the plugin's Maven build up to the package phase, in order to produce a jar file for 
140      * distribution to a test-time local repository. The testVersion parameter specifies the version
141      * to be used in the &lt;version/&gt; element of the plugin configuration, and also in fully
142      * qualified, unambiguous goal invocations (as in 
143      * org.apache.maven.plugins:maven-eclipse-plugin:test:eclipse).
144      * 
145      * @param pomFile The plugin's POM
146      * @param testVersion The version to use for testing this plugin. To promote test resiliency, 
147      *   this version should remain unchanged, regardless of what plugin version is under 
148      *   development.
149      * @param skipUnitTests In cases where test builds occur during the unit-testing phase (usually
150      *   a bad testing smell), the plugin jar must be produced <b>without</b> running unit tests.
151      *   Otherwise, the testing process will result in a recursive loop of building a plugin jar and
152      *   trying to unit test it during the build. In these cases, set this flag to <code>true</code>.
153      * @param logFile The file to which build output should be logged, in order to allow later 
154      *   inspection in case this build fails.
155      * @return The resulting MavenProject, after the test version and skip flag (for unit tests) 
156      *   have been appropriately configured.
157      */
158     public MavenProject packageProjectArtifact( File pomFile, String testVersion, boolean skipUnitTests, File logFile )
159         throws TestToolsException
160     {
161         PomInfo pomInfo = manglePomForTesting( pomFile, testVersion, skipUnitTests );
162 
163         Properties properties = new Properties();
164 
165         List goals = new ArrayList();
166         goals.add( "package" );
167 
168         File buildLog = logFile == null ? pomInfo.getBuildLogFile() : logFile;
169 
170         buildTool.executeMaven( pomInfo.getPomFile(), properties, goals, buildLog );
171 
172         File artifactFile = new File( pomInfo.getBuildOutputDirectory() + "/" + pomInfo.getFinalName() );
173 
174         try
175         {
176             MavenProject project = projectBuilder.build( pomInfo.getPomFile(), repositoryTool
177                 .createLocalArtifactRepositoryInstance(), null );
178 
179             Artifact artifact = artifactFactory.createArtifact( project.getGroupId(), project.getArtifactId(), project
180                 .getVersion(), null, project.getPackaging() );
181 
182             artifact.setFile( artifactFile );
183             artifact.addMetadata( new ProjectArtifactMetadata( artifact, project.getFile() ) );
184 
185             project.setArtifact( artifact );
186 
187             return project;
188         }
189         catch ( ProjectBuildingException e )
190         {
191             throw new TestToolsException(
192                                           "Error building MavenProject instance from test pom: " + pomInfo.getPomFile(),
193                                           e );
194         }
195     }
196 
197     /**
198      * Inject a special version for testing, to allow tests to unambiguously reference the plugin
199      * currently under test. If test builds will be executed from the unit-testing phase, also inject
200      * &lt;skip&gt;true&lt;/skip&gt; into the configuration of the <code>maven-surefire-plugin</code>
201      * to allow production of a test-only version of the plugin jar without running unit tests.
202      * 
203      * @param pomFile The plugin POM
204      * @param testVersion The version that allows test builds to reference the plugin under test
205      * @param skipUnitTests If true, configure the surefire plugin to skip unit tests
206      * @return Information about mangled POM, including the temporary file to which it was written.
207      */
208     protected PomInfo manglePomForTesting( File pomFile, String testVersion, boolean skipUnitTests )
209         throws TestToolsException
210     {
211         File input = new File( "pom.xml" );
212 
213         File output = new File( "pom-test.xml" );
214         output.deleteOnExit();
215 
216         FileReader reader = null;
217         FileWriter writer = null;
218 
219         Model model = null;
220         String finalName = null;
221         String buildOutputDirectory = null;
222 
223         try
224         {
225             reader = new FileReader( input );
226             writer = new FileWriter( output );
227 
228             model = new MavenXpp3Reader().read( reader );
229 
230             model.setVersion( testVersion );
231 
232             Build build = model.getBuild();
233             if ( build == null )
234             {
235                 build = new Build();
236                 model.setBuild( build );
237             }
238 
239             finalName = build.getFinalName();
240 
241             if ( finalName == null )
242             {
243                 ArtifactHandler handler = artifactHandlerManager.getArtifactHandler( model.getPackaging() );
244 
245                 String ext = handler.getExtension();
246 
247                 finalName = model.getArtifactId() + "-" + model.getVersion() + "." + ext;
248             }
249 
250             buildOutputDirectory = build.getOutputDirectory();
251 
252             if ( buildOutputDirectory == null )
253             {
254                 buildOutputDirectory = "target";
255             }
256 
257             if ( skipUnitTests )
258             {
259                 List plugins = build.getPlugins();
260                 Plugin plugin = null;
261                 for ( Iterator iter = plugins.iterator(); iter.hasNext(); )
262                 {
263                     Plugin plug = (Plugin) iter.next();
264 
265                     if ( "maven-surefire-plugin".equals( plug.getArtifactId() ) )
266                     {
267                         plugin = plug;
268                         break;
269                     }
270                 }
271 
272                 if ( plugin == null )
273                 {
274                     plugin = new Plugin();
275                     plugin.setArtifactId( "maven-surefire-plugin" );
276                     build.addPlugin( plugin );
277                 }
278 
279                 Xpp3Dom configDom = (Xpp3Dom) plugin.getConfiguration();
280                 if ( configDom == null )
281                 {
282                     configDom = new Xpp3Dom( "configuration" );
283                     plugin.setConfiguration( configDom );
284                 }
285 
286                 Xpp3Dom skipDom = new Xpp3Dom( "skip" );
287                 skipDom.setValue( "true" );
288 
289                 configDom.addChild( skipDom );
290             }
291 
292             new MavenXpp3Writer().write( writer, model );
293         }
294         catch ( IOException e )
295         {
296             throw new TestToolsException( "Error creating test-time version of POM for: " + input, e );
297         }
298         catch ( XmlPullParserException e )
299         {
300             throw new TestToolsException( "Error creating test-time version of POM for: " + input, e );
301         }
302         finally
303         {
304             IOUtil.close( reader );
305             IOUtil.close( writer );
306         }
307 
308         return new PomInfo( output, model.getGroupId(), model.getArtifactId(), model.getVersion(),
309                             buildOutputDirectory, finalName );
310     }
311 
312     static final class PomInfo
313     {
314         private final File pomFile;
315 
316         private final String groupId;
317 
318         private final String artifactId;
319 
320         private final String version;
321 
322         private final String finalName;
323 
324         private final String buildOutputDirectory;
325 
326         PomInfo( File pomFile, String groupId, String artifactId, String version, String buildOutputDirectory,
327                  String finalName )
328         {
329             this.pomFile = pomFile;
330             this.groupId = groupId;
331             this.artifactId = artifactId;
332             this.version = version;
333             this.buildOutputDirectory = buildOutputDirectory;
334             this.finalName = finalName;
335         }
336 
337         File getPomFile()
338         {
339             return pomFile;
340         }
341 
342         String getBuildOutputDirectory()
343         {
344             return buildOutputDirectory;
345         }
346 
347         String getFinalName()
348         {
349             return finalName;
350         }
351 
352         File getBuildLogFile()
353         {
354             return new File( buildOutputDirectory + "/test-build-logs/" + groupId + "_" + artifactId + "_" + version
355                 + ".build.log" );
356         }
357 
358     }
359 
360 }