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