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