1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
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 <relativePath/> 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 <relativePath/> 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 }