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.net.MalformedURLException;
26  
27  import org.apache.maven.artifact.Artifact;
28  import org.apache.maven.artifact.factory.ArtifactFactory;
29  import org.apache.maven.artifact.installer.ArtifactInstallationException;
30  import org.apache.maven.artifact.installer.ArtifactInstaller;
31  import org.apache.maven.artifact.repository.ArtifactRepository;
32  import org.apache.maven.artifact.repository.ArtifactRepositoryFactory;
33  import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
34  import org.apache.maven.execution.DefaultMavenExecutionRequest;
35  import org.apache.maven.execution.DefaultMavenExecutionResult;
36  import org.apache.maven.execution.MavenSession;
37  import org.apache.maven.model.Model;
38  import org.apache.maven.model.Parent;
39  import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
40  import org.apache.maven.plugin.LegacySupport;
41  import org.apache.maven.project.MavenProject;
42  import org.apache.maven.project.artifact.ProjectArtifactMetadata;
43  import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
44  import org.apache.maven.settings.MavenSettingsBuilder;
45  import org.apache.maven.settings.Settings;
46  import org.codehaus.plexus.PlexusConstants;
47  import org.codehaus.plexus.PlexusContainer;
48  import org.codehaus.plexus.component.annotations.Component;
49  import org.codehaus.plexus.component.annotations.Requirement;
50  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
51  import org.codehaus.plexus.context.Context;
52  import org.codehaus.plexus.context.ContextException;
53  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
54  import org.codehaus.plexus.util.IOUtil;
55  import org.codehaus.plexus.util.ReaderFactory;
56  import org.codehaus.plexus.util.StringUtils;
57  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
58  
59  /**
60   * Tools to access and manage Maven repositories for test builds, including construction of a local
61   * repository directory structure.
62   *
63   * <p>
64   * <b>WARNING:</b> Currently, the <code>createLocalRepositoryFromPlugin</code> method will not
65   * resolve parent POMs that exist <b>only</b> in your normal local repository, and are not reachable
66   * using the relativePath element. This may result in failed test builds, as one or more of the
67   * plugin's ancestor POMs cannot be resolved.
68   * </p>
69   *
70   * @author jdcasey
71   * @version $Id$
72   */
73  @Component( role = RepositoryTool.class )
74  public class RepositoryTool
75      implements Contextualizable
76  {
77      /** Plexus role */
78      public static final String ROLE = RepositoryTool.class.getName();
79  
80      @Requirement
81      private ArtifactRepositoryFactory repositoryFactory;
82  
83      @Requirement
84      private MavenSettingsBuilder settingsBuilder;
85  
86      @Requirement
87      private ArtifactFactory artifactFactory;
88  
89      @Requirement
90      private ArtifactInstaller artifactInstaller;
91  
92      @Requirement
93      private LegacySupport legacySupport;
94  
95      // contextualized.
96      private PlexusContainer container;
97  
98      /**
99       * Lookup and return the location of the normal Maven local repository.
100      *
101      * @return the location of the normal Maven local repository.
102      * @throws TestToolsException if any
103      */
104     public File findLocalRepositoryDirectory()
105         throws TestToolsException
106     {
107         String localRepo = System.getProperty( "maven.local.repo" );
108         if ( StringUtils.isNotEmpty( localRepo ) )
109         {
110             return new File( localRepo );
111         }
112 
113         Settings settings;
114         try
115         {
116             DefaultMavenExecutionRequest request = new DefaultMavenExecutionRequest();
117             request.setUserSettingsFile( new File( System.getProperty( "user.home" ), ".m2/settings.xml" ) );
118             request.setGlobalSettingsFile( new File( System.getProperty( "maven.home" ), "conf/settings.xml" ) );
119             settings = settingsBuilder.buildSettings( request );
120         }
121         catch ( IOException e )
122         {
123             throw new TestToolsException( "Error building Maven settings.", e );
124         }
125         catch ( XmlPullParserException e )
126         {
127             throw new TestToolsException( "Error building Maven settings.", e );
128         }
129 
130         if ( settings == null || settings.getLocalRepository() == null
131             || settings.getLocalRepository().trim().length() < 1 )
132         {
133             return new File( System.getProperty( "user.home" ), ".m2/repository" );
134         }
135 
136         return new File( settings.getLocalRepository() );
137     }
138 
139     /**
140      * Construct an ArtifactRepository instance that refers to the normal Maven local repository.
141      *
142      * @return an ArtifactRepository instance
143      * @throws TestToolsException if any
144      */
145     public ArtifactRepository createLocalArtifactRepositoryInstance()
146         throws TestToolsException
147     {
148         File localRepoDir = findLocalRepositoryDirectory();
149 
150         return createLocalArtifactRepositoryInstance( localRepoDir );
151     }
152 
153     /**
154      * Construct an ArtifactRepository instance that refers to the test-time Maven local repository.
155      *
156      * @param localRepositoryDirectory The location of the local repository to be used for test builds.
157      * @return an ArtifactRepository instance
158      * @throws TestToolsException if any
159      */
160     public ArtifactRepository createLocalArtifactRepositoryInstance( File localRepositoryDirectory )
161         throws TestToolsException
162     {
163         ArtifactRepositoryLayout defaultLayout;
164         try
165         {
166             defaultLayout = (ArtifactRepositoryLayout) container.lookup( ArtifactRepositoryLayout.ROLE, "default" );
167         }
168         catch ( ComponentLookupException e )
169         {
170             throw new TestToolsException( "Error retrieving default repository layout.", e );
171         }
172 
173         try
174         {
175             return repositoryFactory.createArtifactRepository( "local", localRepositoryDirectory.toURL()
176                 .toExternalForm(), defaultLayout, null, null );
177         }
178         catch ( MalformedURLException e )
179         {
180             throw new TestToolsException( "Error converting local repo directory to a URL.", e );
181         }
182     }
183 
184     /**
185      * Install a test version of a plugin - along with its POM, and as many ancestor POMs as can be
186      * reached using the &lt;relativePath/&gt; element - to a clean local repository directory for
187      * use in test builds.
188      *
189      * <p>
190      * <b>WARNING:</b> Currently, this method will not resolve parent POMs that exist <b>only</b> in
191      * your normal local repository, and are not reachable using the relativePath element. This may
192      * result in failed test builds, as one or more of the plugin's ancestor POMs cannot be resolved.
193      * </p>
194      *
195      * @param project
196      * @param realPomFile
197      * @param targetLocalRepoBasedir
198      * @throws TestToolsException if any
199      */
200     public void createLocalRepositoryFromComponentProject( MavenProject project, File realPomFile,
201                                                            File targetLocalRepoBasedir )
202         throws TestToolsException
203     {
204         Artifact artifact = project.getArtifact();
205 
206         if ( "pom".equals( project.getPackaging() ) )
207         {
208             artifact.setFile( project.getFile() );
209         }
210 
211         ArtifactRepository localRepository = createLocalArtifactRepositoryInstance( targetLocalRepoBasedir );
212 
213         String localPath = localRepository.pathOf( artifact );
214 
215         File destination = new File( localRepository.getBasedir(), localPath );
216         if ( !destination.getParentFile().exists() )
217         {
218             destination.getParentFile().mkdirs();
219         }
220 
221         legacySupport.setSession( new MavenSession( container, MavenRepositorySystemUtils.newSession(),
222                                                     new DefaultMavenExecutionRequest(),
223                                                     new DefaultMavenExecutionResult() ) );
224         try
225         {
226             artifactInstaller.install( artifact.getFile(), artifact, localRepository );
227         }
228         catch ( ArtifactInstallationException e )
229         {
230             throw new TestToolsException( "Error installing plugin artifact to target local repository: "
231                 + targetLocalRepoBasedir, e );
232         }
233         finally
234         {
235             legacySupport.setSession( null );
236         }
237 
238         installLocallyReachableAncestorPoms( realPomFile, localRepository );
239     }
240 
241     /**
242      * Traverse &lt;relativePath/&gt; links for successive POMs in the plugin's ancestry, installing
243      * each one into the test-time local repository.
244      *
245      * @param realPomFile The real plugin POM; a starting point, but the POM is already installed,
246      *   so we won't actually install this file, only use it to locate parents.
247      * @param localRepo The test-time local repository instance
248      * @throws TestToolsException if any
249      */
250     private void installLocallyReachableAncestorPoms( File realPomFile, ArtifactRepository localRepo )
251         throws TestToolsException
252     {
253         MavenXpp3Reader pomReader = new MavenXpp3Reader();
254 
255         File pom = realPomFile;
256 
257         boolean firstPass = true;
258 
259         while ( pom != null )
260         {
261 
262             if ( !pom.exists() )
263             {
264                 pom = null;
265                 break;
266             }
267 
268             String pomGroupId = null;
269             String pomArtifactId = null;
270             String pomVersion = null;
271 
272             Reader reader = null;
273 
274             File currentPom = pom;
275 
276             try
277             {
278                 reader = ReaderFactory.newXmlReader( pom );
279 
280                 Model model = pomReader.read( reader );
281 
282                 pomGroupId = model.getGroupId();
283                 pomArtifactId = model.getArtifactId();
284                 pomVersion = model.getVersion();
285 
286                 Parent parent = model.getParent();
287                 if ( parent != null )
288                 {
289                     pom = new File( pom.getParentFile(), parent.getRelativePath() );
290 
291                     if ( pomGroupId == null )
292                     {
293                         pomGroupId = parent.getGroupId();
294                     }
295 
296                     if ( pomVersion == null )
297                     {
298                         pomVersion = parent.getVersion();
299                     }
300                 }
301                 else
302                 {
303                     pom = null;
304                 }
305             }
306             catch ( IOException e )
307             {
308                 throw new TestToolsException( "Error reading ancestor POM: " + currentPom, e );
309             }
310             catch ( XmlPullParserException e )
311             {
312                 throw new TestToolsException( "Error reading ancestor POM: " + currentPom, e );
313             }
314             finally
315             {
316                 IOUtil.close( reader );
317             }
318 
319             if ( !firstPass )
320             {
321                 Artifact pomArtifact = artifactFactory.createProjectArtifact( pomGroupId, pomArtifactId, pomVersion );
322                 pomArtifact.addMetadata( new ProjectArtifactMetadata( pomArtifact, currentPom ) );
323 
324                 try
325                 {
326                     artifactInstaller.install( currentPom, pomArtifact, localRepo );
327                 }
328                 catch ( ArtifactInstallationException e )
329                 {
330                     throw new TestToolsException( "Error installing ancestor POM: " + currentPom
331                         + " to target local repository: " + localRepo.getBasedir(), e );
332                 }
333             }
334             else
335             {
336                 firstPass = false;
337             }
338         }
339     }
340 
341     /**
342      * Retrieve the PlexusContainer instance used to instantiate this component. The container is
343      * used to retrieve the default ArtifactRepositoryLayout component, for use in constructing
344      * instances of ArtifactRepository that can be used to access local repositories.
345      *
346      * @see org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable#contextualize(org.codehaus.plexus.context.Context)
347      */
348     public void contextualize( Context context )
349         throws ContextException
350     {
351         this.container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
352     }
353 }