Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
InstallMojo |
|
| 5.2727272727272725;5.273 |
1 | package org.apache.maven.plugin.invoker; | |
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.util.Collection; | |
25 | import java.util.HashMap; | |
26 | import java.util.HashSet; | |
27 | import java.util.Iterator; | |
28 | import java.util.LinkedHashSet; | |
29 | import java.util.Map; | |
30 | ||
31 | import org.apache.maven.artifact.Artifact; | |
32 | import org.apache.maven.artifact.factory.ArtifactFactory; | |
33 | import org.apache.maven.artifact.installer.ArtifactInstaller; | |
34 | import org.apache.maven.artifact.repository.ArtifactRepository; | |
35 | import org.apache.maven.artifact.repository.ArtifactRepositoryFactory; | |
36 | import org.apache.maven.model.Model; | |
37 | import org.apache.maven.model.Parent; | |
38 | import org.apache.maven.plugin.AbstractMojo; | |
39 | import org.apache.maven.plugin.MojoExecutionException; | |
40 | import org.apache.maven.project.MavenProject; | |
41 | import org.codehaus.plexus.util.FileUtils; | |
42 | ||
43 | /** | |
44 | * Installs the project artifacts of the main build into the local repository as a preparation to run the sub projects. | |
45 | * More precisely, all artifacts of the project itself, all its locally reachable parent POMs and all its dependencies | |
46 | * from the reactor will be installed to the local repository. | |
47 | * | |
48 | * @goal install | |
49 | * @phase pre-integration-test | |
50 | * @requiresDependencyResolution runtime | |
51 | * @since 1.2 | |
52 | * @author Paul Gier | |
53 | * @author Benjamin Bentmann | |
54 | * @version $Id: InstallMojo.java 814226 2009-09-12 20:01:59Z bentmann $ | |
55 | */ | |
56 | 0 | public class InstallMojo |
57 | extends AbstractMojo | |
58 | { | |
59 | ||
60 | /** | |
61 | * Maven artifact install component to copy artifacts to the local repository. | |
62 | * | |
63 | * @component | |
64 | */ | |
65 | private ArtifactInstaller installer; | |
66 | ||
67 | /** | |
68 | * The component used to create artifacts. | |
69 | * | |
70 | * @component | |
71 | */ | |
72 | private ArtifactFactory artifactFactory; | |
73 | ||
74 | /** | |
75 | * The component used to create artifacts. | |
76 | * | |
77 | * @component | |
78 | */ | |
79 | private ArtifactRepositoryFactory repositoryFactory; | |
80 | ||
81 | /** | |
82 | * @parameter expression="${localRepository}" | |
83 | * @required | |
84 | * @readonly | |
85 | */ | |
86 | private ArtifactRepository localRepository; | |
87 | ||
88 | /** | |
89 | * The path to the local repository into which the project artifacts should be installed for the integration tests. | |
90 | * If not set, the regular local repository will be used. To prevent soiling of your regular local repository with | |
91 | * possibly broken artifacts, it is strongly recommended to use an isolated repository for the integration tests | |
92 | * (e.g. <code>${project.build.directory}/it-repo</code>). | |
93 | * | |
94 | * @parameter expression="${invoker.localRepositoryPath}" | |
95 | */ | |
96 | private File localRepositoryPath; | |
97 | ||
98 | /** | |
99 | * The current Maven project. | |
100 | * | |
101 | * @parameter expression="${project}" | |
102 | * @required | |
103 | * @readonly | |
104 | */ | |
105 | private MavenProject project; | |
106 | ||
107 | /** | |
108 | * The set of Maven projects in the reactor build. | |
109 | * | |
110 | * @parameter default-value="${reactorProjects}" | |
111 | * @readonly | |
112 | */ | |
113 | private Collection reactorProjects; | |
114 | ||
115 | /** | |
116 | * A flag used to disable the installation procedure. This is primarily intended for usage from the command line to | |
117 | * occasionally adjust the build. | |
118 | * | |
119 | * @parameter expression="${invoker.skip}" default-value="false" | |
120 | * @since 1.4 | |
121 | */ | |
122 | private boolean skipInstallation; | |
123 | ||
124 | /** | |
125 | * The identifiers of already installed artifacts, used to avoid multiple installation of the same artifact. | |
126 | */ | |
127 | private Collection installedArtifacts; | |
128 | ||
129 | /** | |
130 | * The identifiers of already copied artifacts, used to avoid multiple installation of the same artifact. | |
131 | */ | |
132 | private Collection copiedArtifacts; | |
133 | ||
134 | /** | |
135 | * Performs this mojo's tasks. | |
136 | * | |
137 | * @throws MojoExecutionException If the artifacts could not be installed. | |
138 | */ | |
139 | public void execute() | |
140 | throws MojoExecutionException | |
141 | { | |
142 | 0 | if ( skipInstallation ) |
143 | { | |
144 | 0 | getLog().info( "Skipping artifact installation per configuration." ); |
145 | 0 | return; |
146 | } | |
147 | ||
148 | 0 | ArtifactRepository testRepository = createTestRepository(); |
149 | ||
150 | 0 | installedArtifacts = new HashSet(); |
151 | 0 | copiedArtifacts = new HashSet(); |
152 | ||
153 | 0 | installProjectDependencies( project, reactorProjects, testRepository ); |
154 | 0 | installProjectParents( project, testRepository ); |
155 | 0 | installProjectArtifacts( project, testRepository ); |
156 | 0 | } |
157 | ||
158 | /** | |
159 | * Creates the local repository for the integration tests. If the user specified a custom repository location, the | |
160 | * custom repository will have the same identifier, layout and policies as the real local repository. That means | |
161 | * apart from the location, the custom repository will be indistinguishable from the real repository such that its | |
162 | * usage is transparent to the integration tests. | |
163 | * | |
164 | * @return The local repository for the integration tests, never <code>null</code>. | |
165 | * @throws MojoExecutionException If the repository could not be created. | |
166 | */ | |
167 | private ArtifactRepository createTestRepository() | |
168 | throws MojoExecutionException | |
169 | { | |
170 | 0 | ArtifactRepository testRepository = localRepository; |
171 | ||
172 | 0 | if ( localRepositoryPath != null ) |
173 | { | |
174 | try | |
175 | { | |
176 | 0 | if ( !localRepositoryPath.exists() && !localRepositoryPath.mkdirs() ) |
177 | { | |
178 | 0 | throw new IOException( "Failed to create directory: " + localRepositoryPath ); |
179 | } | |
180 | ||
181 | 0 | testRepository = |
182 | repositoryFactory.createArtifactRepository( localRepository.getId(), | |
183 | localRepositoryPath.toURL().toExternalForm(), | |
184 | localRepository.getLayout(), | |
185 | localRepository.getSnapshots(), | |
186 | localRepository.getReleases() ); | |
187 | } | |
188 | 0 | catch ( Exception e ) |
189 | { | |
190 | 0 | throw new MojoExecutionException( "Failed to create local repository: " + localRepositoryPath, e ); |
191 | 0 | } |
192 | } | |
193 | ||
194 | 0 | return testRepository; |
195 | } | |
196 | ||
197 | /** | |
198 | * Installs the specified artifact to the local repository. Note: This method should only be used for artifacts that | |
199 | * originate from the current (reactor) build. Artifacts that have been grabbed from the user's local repository | |
200 | * should be installed to the test repository via {@link #copyArtifact(File, Artifact, ArtifactRepository)}. | |
201 | * | |
202 | * @param file The file associated with the artifact, must not be <code>null</code>. This is in most cases the value | |
203 | * of <code>artifact.getFile()</code> with the exception of the main artifact from a project with | |
204 | * packaging "pom". Projects with packaging "pom" have no main artifact file. They have however artifact | |
205 | * metadata (e.g. site descriptors) which needs to be installed. | |
206 | * @param artifact The artifact to install, must not be <code>null</code>. | |
207 | * @param testRepository The local repository to install the artifact to, must not be <code>null</code>. | |
208 | * @throws MojoExecutionException If the artifact could not be installed (e.g. has no associated file). | |
209 | */ | |
210 | private void installArtifact( File file, Artifact artifact, ArtifactRepository testRepository ) | |
211 | throws MojoExecutionException | |
212 | { | |
213 | try | |
214 | { | |
215 | 0 | if ( file == null ) |
216 | { | |
217 | 0 | throw new IllegalStateException( "Artifact has no associated file: " + file ); |
218 | } | |
219 | 0 | if ( !file.isFile() ) |
220 | { | |
221 | 0 | throw new IllegalStateException( "Artifact is not fully assembled: " + file ); |
222 | } | |
223 | ||
224 | 0 | if ( installedArtifacts.add( artifact.getId() ) ) |
225 | { | |
226 | 0 | installer.install( file, artifact, testRepository ); |
227 | } | |
228 | else | |
229 | { | |
230 | 0 | getLog().debug( "Not re-installing " + artifact + ", " + file ); |
231 | } | |
232 | } | |
233 | 0 | catch ( Exception e ) |
234 | { | |
235 | 0 | throw new MojoExecutionException( "Failed to install artifact: " + artifact, e ); |
236 | 0 | } |
237 | 0 | } |
238 | ||
239 | /** | |
240 | * Installs the specified artifact to the local repository. This method serves basically the same purpose as | |
241 | * {@link #installArtifact(File, Artifact, ArtifactRepository)} but is meant for artifacts that have been resolved | |
242 | * from the user's local repository (and not the current build outputs). The subtle difference here is that | |
243 | * artifacts from the repository have already undergone transformations and these manipulations should not be redone | |
244 | * by the artifact installer. For this reason, this method performs plain copy operations to install the artifacts. | |
245 | * | |
246 | * @param file The file associated with the artifact, must not be <code>null</code>. | |
247 | * @param artifact The artifact to install, must not be <code>null</code>. | |
248 | * @param testRepository The local repository to install the artifact to, must not be <code>null</code>. | |
249 | * @throws MojoExecutionException If the artifact could not be installed (e.g. has no associated file). | |
250 | */ | |
251 | private void copyArtifact( File file, Artifact artifact, ArtifactRepository testRepository ) | |
252 | throws MojoExecutionException | |
253 | { | |
254 | try | |
255 | { | |
256 | 0 | if ( file == null ) |
257 | { | |
258 | 0 | throw new IllegalStateException( "Artifact has no associated file: " + file ); |
259 | } | |
260 | 0 | if ( !file.isFile() ) |
261 | { | |
262 | 0 | throw new IllegalStateException( "Artifact is not fully assembled: " + file ); |
263 | } | |
264 | ||
265 | 0 | if ( copiedArtifacts.add( artifact.getId() ) ) |
266 | { | |
267 | 0 | File destination = new File( testRepository.getBasedir(), testRepository.pathOf( artifact ) ); |
268 | ||
269 | 0 | getLog().debug( "Installing " + file + " to " + destination ); |
270 | ||
271 | 0 | copyFileIfDifferent( file, destination ); |
272 | ||
273 | 0 | MetadataUtils.createMetadata( destination, artifact ); |
274 | } | |
275 | else | |
276 | { | |
277 | 0 | getLog().debug( "Not re-installing " + artifact + ", " + file ); |
278 | } | |
279 | } | |
280 | 0 | catch ( Exception e ) |
281 | { | |
282 | 0 | throw new MojoExecutionException( "Failed to stage artifact: " + artifact, e ); |
283 | 0 | } |
284 | 0 | } |
285 | ||
286 | private void copyFileIfDifferent( File src, File dst ) | |
287 | throws IOException | |
288 | { | |
289 | 0 | if ( src.lastModified() != dst.lastModified() || src.length() != dst.length() ) |
290 | { | |
291 | 0 | FileUtils.copyFile( src, dst ); |
292 | 0 | dst.setLastModified( src.lastModified() ); |
293 | } | |
294 | 0 | } |
295 | ||
296 | /** | |
297 | * Installs the main artifact and any attached artifacts of the specified project to the local repository. | |
298 | * | |
299 | * @param mvnProject The project whose artifacts should be installed, must not be <code>null</code>. | |
300 | * @param testRepository The local repository to install the artifacts to, must not be <code>null</code>. | |
301 | * @throws MojoExecutionException If any artifact could not be installed. | |
302 | */ | |
303 | private void installProjectArtifacts( MavenProject mvnProject, ArtifactRepository testRepository ) | |
304 | throws MojoExecutionException | |
305 | { | |
306 | try | |
307 | { | |
308 | // Install POM (usually attached as metadata but that happens only as a side effect of the Install Plugin) | |
309 | 0 | installProjectPom( mvnProject, testRepository ); |
310 | ||
311 | // Install the main project artifact (if the project has one, e.g. has no "pom" packaging) | |
312 | 0 | Artifact mainArtifact = mvnProject.getArtifact(); |
313 | 0 | if ( mainArtifact.getFile() != null ) |
314 | { | |
315 | 0 | installArtifact( mainArtifact.getFile(), mainArtifact, testRepository ); |
316 | } | |
317 | ||
318 | // Install any attached project artifacts | |
319 | 0 | Collection attachedArtifacts = mvnProject.getAttachedArtifacts(); |
320 | 0 | for ( Iterator artifactIter = attachedArtifacts.iterator(); artifactIter.hasNext(); ) |
321 | { | |
322 | 0 | Artifact attachedArtifact = (Artifact) artifactIter.next(); |
323 | 0 | installArtifact( attachedArtifact.getFile(), attachedArtifact, testRepository ); |
324 | } | |
325 | } | |
326 | 0 | catch ( Exception e ) |
327 | { | |
328 | 0 | throw new MojoExecutionException( "Failed to install project artifacts: " + mvnProject, e ); |
329 | 0 | } |
330 | 0 | } |
331 | ||
332 | /** | |
333 | * Installs the (locally reachable) parent POMs of the specified project to the local repository. The parent POMs | |
334 | * from the reactor must be installed or the forked IT builds will fail when using a clean repository. | |
335 | * | |
336 | * @param mvnProject The project whose parent POMs should be installed, must not be <code>null</code>. | |
337 | * @param testRepository The local repository to install the POMs to, must not be <code>null</code>. | |
338 | * @throws MojoExecutionException If any POM could not be installed. | |
339 | */ | |
340 | private void installProjectParents( MavenProject mvnProject, ArtifactRepository testRepository ) | |
341 | throws MojoExecutionException | |
342 | { | |
343 | try | |
344 | { | |
345 | 0 | for ( MavenProject parent = mvnProject.getParent(); parent != null; parent = parent.getParent() ) |
346 | { | |
347 | 0 | if ( parent.getFile() == null ) |
348 | { | |
349 | 0 | copyParentPoms( parent.getGroupId(), parent.getArtifactId(), parent.getVersion(), testRepository ); |
350 | 0 | break; |
351 | } | |
352 | 0 | installProjectPom( parent, testRepository ); |
353 | } | |
354 | } | |
355 | 0 | catch ( Exception e ) |
356 | { | |
357 | 0 | throw new MojoExecutionException( "Failed to install project parents: " + mvnProject, e ); |
358 | 0 | } |
359 | 0 | } |
360 | ||
361 | /** | |
362 | * Installs the POM of the specified project to the local repository. | |
363 | * | |
364 | * @param mvnProject The project whose POM should be installed, must not be <code>null</code>. | |
365 | * @param testRepository The local repository to install the POM to, must not be <code>null</code>. | |
366 | * @throws MojoExecutionException If the POM could not be installed. | |
367 | */ | |
368 | private void installProjectPom( MavenProject mvnProject, ArtifactRepository testRepository ) | |
369 | throws MojoExecutionException | |
370 | { | |
371 | try | |
372 | { | |
373 | 0 | Artifact pomArtifact = null; |
374 | 0 | if ( "pom".equals( mvnProject.getPackaging() ) ) |
375 | { | |
376 | 0 | pomArtifact = mvnProject.getArtifact(); |
377 | } | |
378 | 0 | if ( pomArtifact == null ) |
379 | { | |
380 | 0 | pomArtifact = |
381 | artifactFactory.createProjectArtifact( mvnProject.getGroupId(), mvnProject.getArtifactId(), | |
382 | mvnProject.getVersion() ); | |
383 | } | |
384 | 0 | installArtifact( mvnProject.getFile(), pomArtifact, testRepository ); |
385 | } | |
386 | 0 | catch ( Exception e ) |
387 | { | |
388 | 0 | throw new MojoExecutionException( "Failed to install POM: " + mvnProject, e ); |
389 | 0 | } |
390 | 0 | } |
391 | ||
392 | /** | |
393 | * Installs the dependent projects from the reactor to the local repository. The dependencies on other modules from | |
394 | * the reactor must be installed or the forked IT builds will fail when using a clean repository. | |
395 | * | |
396 | * @param mvnProject The project whose dependent projects should be installed, must not be <code>null</code>. | |
397 | * @param reactorProjects The set of projects in the reactor build, must not be <code>null</code>. | |
398 | * @param testRepository The local repository to install the POMs to, must not be <code>null</code>. | |
399 | * @throws MojoExecutionException If any dependency could not be installed. | |
400 | */ | |
401 | private void installProjectDependencies( MavenProject mvnProject, Collection reactorProjects, | |
402 | ArtifactRepository testRepository ) | |
403 | throws MojoExecutionException | |
404 | { | |
405 | // index available reactor projects | |
406 | 0 | Map projects = new HashMap(); |
407 | 0 | for ( Iterator it = reactorProjects.iterator(); it.hasNext(); ) |
408 | { | |
409 | 0 | MavenProject reactorProject = (MavenProject) it.next(); |
410 | ||
411 | 0 | String projectId = |
412 | reactorProject.getGroupId() + ':' + reactorProject.getArtifactId() + ':' + reactorProject.getVersion(); | |
413 | ||
414 | 0 | projects.put( projectId, reactorProject ); |
415 | } | |
416 | ||
417 | // group transitive dependencies (even those that don't contribute to the class path like POMs) ... | |
418 | 0 | Collection artifacts = mvnProject.getArtifacts(); |
419 | // ... into dependencies that were resolved from reactor projects ... | |
420 | 0 | Collection dependencyProjects = new LinkedHashSet(); |
421 | // ... and those that were resolved from the (local) repo | |
422 | 0 | Collection dependencyArtifacts = new LinkedHashSet(); |
423 | 0 | for ( Iterator it = artifacts.iterator(); it.hasNext(); ) |
424 | { | |
425 | 0 | Artifact artifact = (Artifact) it.next(); |
426 | ||
427 | // workaround for MNG-2961 to ensure the base version does not contain a timestamp | |
428 | 0 | artifact.isSnapshot(); |
429 | ||
430 | 0 | String projectId = artifact.getGroupId() + ':' + artifact.getArtifactId() + ':' + artifact.getBaseVersion(); |
431 | ||
432 | 0 | if ( projects.containsKey( projectId ) ) |
433 | { | |
434 | 0 | dependencyProjects.add( projectId ); |
435 | } | |
436 | else | |
437 | { | |
438 | 0 | dependencyArtifacts.add( artifact ); |
439 | } | |
440 | } | |
441 | ||
442 | // install dependencies | |
443 | try | |
444 | { | |
445 | // copy dependencies that where resolved from the local repo | |
446 | 0 | for ( Iterator it = dependencyArtifacts.iterator(); it.hasNext(); ) |
447 | { | |
448 | 0 | Artifact artifact = (Artifact) it.next(); |
449 | ||
450 | 0 | Artifact depArtifact = |
451 | artifactFactory.createArtifactWithClassifier( artifact.getGroupId(), artifact.getArtifactId(), | |
452 | artifact.getBaseVersion(), artifact.getType(), | |
453 | artifact.getClassifier() ); | |
454 | ||
455 | 0 | File artifactFile = artifact.getFile(); |
456 | ||
457 | 0 | Artifact pomArtifact = |
458 | artifactFactory.createProjectArtifact( depArtifact.getGroupId(), depArtifact.getArtifactId(), | |
459 | depArtifact.getBaseVersion() ); | |
460 | ||
461 | 0 | File pomFile = new File( localRepository.getBasedir(), localRepository.pathOf( pomArtifact ) ); |
462 | ||
463 | 0 | if ( pomFile.isFile() ) |
464 | { | |
465 | 0 | if ( !pomArtifact.getId().equals( depArtifact.getId() ) ) |
466 | { | |
467 | 0 | copyArtifact( pomFile, pomArtifact, testRepository ); |
468 | } | |
469 | 0 | copyParentPoms( pomFile, testRepository ); |
470 | } | |
471 | ||
472 | 0 | copyArtifact( artifactFile, depArtifact, testRepository ); |
473 | } | |
474 | ||
475 | // install dependencies that were resolved from the reactor | |
476 | 0 | for ( Iterator it = dependencyProjects.iterator(); it.hasNext(); ) |
477 | { | |
478 | 0 | String projectId = (String) it.next(); |
479 | ||
480 | 0 | MavenProject dependencyProject = (MavenProject) projects.get( projectId ); |
481 | ||
482 | 0 | installProjectArtifacts( dependencyProject, testRepository ); |
483 | 0 | installProjectParents( dependencyProject, testRepository ); |
484 | } | |
485 | } | |
486 | 0 | catch ( Exception e ) |
487 | { | |
488 | 0 | throw new MojoExecutionException( "Failed to install project dependencies: " + mvnProject, e ); |
489 | 0 | } |
490 | 0 | } |
491 | ||
492 | /** | |
493 | * Installs all parent POMs of the specified POM file that are available in the local repository. | |
494 | * | |
495 | * @param pomFile The path to the POM file whose parents should be installed, must not be <code>null</code>. | |
496 | * @param testRepository The local repository to install the POMs to, must not be <code>null</code>. | |
497 | * @throws MojoExecutionException If any (existing) parent POM could not be installed. | |
498 | */ | |
499 | private void copyParentPoms( File pomFile, ArtifactRepository testRepository ) | |
500 | throws MojoExecutionException | |
501 | { | |
502 | 0 | Model model = PomUtils.loadPom( pomFile ); |
503 | 0 | Parent parent = model.getParent(); |
504 | 0 | if ( parent != null ) |
505 | { | |
506 | 0 | copyParentPoms( parent.getGroupId(), parent.getArtifactId(), parent.getVersion(), testRepository ); |
507 | } | |
508 | 0 | } |
509 | ||
510 | /** | |
511 | * Installs the specified POM and all its parent POMs to the local repository. | |
512 | * | |
513 | * @param groupId The group id of the POM which should be installed, must not be <code>null</code>. | |
514 | * @param artifactId The artifact id of the POM which should be installed, must not be <code>null</code>. | |
515 | * @param version The version of the POM which should be installed, must not be <code>null</code>. | |
516 | * @param testRepository The local repository to install the POMs to, must not be <code>null</code>. | |
517 | * @throws MojoExecutionException If any (existing) parent POM could not be installed. | |
518 | */ | |
519 | private void copyParentPoms( String groupId, String artifactId, String version, ArtifactRepository testRepository ) | |
520 | throws MojoExecutionException | |
521 | { | |
522 | 0 | Artifact pomArtifact = artifactFactory.createProjectArtifact( groupId, artifactId, version ); |
523 | ||
524 | 0 | if ( installedArtifacts.contains( pomArtifact.getId() ) || copiedArtifacts.contains( pomArtifact.getId() ) ) |
525 | { | |
526 | 0 | getLog().debug( "Not re-installing " + pomArtifact ); |
527 | 0 | return; |
528 | } | |
529 | ||
530 | 0 | File pomFile = new File( localRepository.getBasedir(), localRepository.pathOf( pomArtifact ) ); |
531 | 0 | if ( pomFile.isFile() ) |
532 | { | |
533 | 0 | copyArtifact( pomFile, pomArtifact, testRepository ); |
534 | 0 | copyParentPoms( pomFile, testRepository ); |
535 | } | |
536 | 0 | } |
537 | ||
538 | } |