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.plugin.ide;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.LinkedHashSet;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.jar.Attributes;
32 import java.util.jar.JarFile;
33 import java.util.jar.Manifest;
34 import java.util.zip.ZipFile;
35
36 import org.apache.maven.artifact.Artifact;
37 import org.apache.maven.artifact.factory.ArtifactFactory;
38 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
39 import org.apache.maven.artifact.repository.ArtifactRepository;
40 import org.apache.maven.artifact.resolver.ArtifactCollector;
41 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
42 import org.apache.maven.artifact.resolver.ArtifactResolutionException;
43 import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
44 import org.apache.maven.artifact.resolver.ArtifactResolver;
45 import org.apache.maven.artifact.resolver.DebugResolutionListener;
46 import org.apache.maven.artifact.resolver.ResolutionNode;
47 import org.apache.maven.artifact.resolver.WarningResolutionListener;
48 import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
49 import org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter;
50 import org.apache.maven.artifact.versioning.ArtifactVersion;
51 import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
52 import org.apache.maven.artifact.versioning.VersionRange;
53 import org.apache.maven.execution.RuntimeInformation;
54 import org.apache.maven.model.Dependency;
55 import org.apache.maven.model.DependencyManagement;
56 import org.apache.maven.model.Exclusion;
57 import org.apache.maven.plugin.AbstractMojo;
58 import org.apache.maven.plugin.MojoExecutionException;
59 import org.apache.maven.plugin.MojoFailureException;
60 import org.apache.maven.plugin.eclipse.Constants;
61 import org.apache.maven.plugin.eclipse.Messages;
62 import org.apache.maven.plugins.annotations.Component;
63 import org.apache.maven.plugins.annotations.Parameter;
64 import org.apache.maven.project.MavenProject;
65 import org.codehaus.plexus.logging.LogEnabled;
66 import org.codehaus.plexus.logging.Logger;
67
68 /**
69 * Abstract base plugin which takes care of the common stuff usually needed by maven IDE plugins. A plugin extending
70 * AbstractIdeSupportMojo should implement the <code>setup()</code> and <code>writeConfiguration()</code> methods, plus
71 * the getters needed to get the various configuration flags and required components. The lifecycle:
72 *
73 * <pre>
74 * *** calls setup() where you can configure your specific stuff and stop the mojo from execute if appropriate ***
75 * - manually resolve project dependencies, NOT failing if a dependency is missing
76 * - compute project references (reactor projects) if the getUseProjectReferences() flag is set
77 * - download sources/javadocs if the getDownloadSources() flag is set
78 * *** calls writeConfiguration(), passing the list of resolved referenced dependencies ***
79 * - report the list of missing sources or just tell how to turn this feature on if the flag was disabled
80 * </pre>
81 *
82 * @author Fabrizio Giustina
83 * @version $Id: AbstractIdeSupportMojo.java 1672305 2015-04-09 12:10:45Z khmarbaise $
84 */
85 public abstract class AbstractIdeSupportMojo
86 extends AbstractMojo
87 implements LogEnabled
88 {
89
90 /**
91 * The project whose project files to create.
92 */
93 @Parameter( property = "project", required = true, readonly = true )
94 protected MavenProject project;
95
96 /**
97 * The currently executed project (can be a reactor project).
98 */
99 @Parameter( property = "executedProject", readonly = true )
100 protected MavenProject executedProject;
101
102 /**
103 * The project packaging.
104 */
105 @Parameter( property = "project.packaging" )
106 protected String packaging;
107
108 /**
109 * Artifact factory, needed to download source jars for inclusion in classpath.
110 */
111 @Component( role = ArtifactFactory.class )
112 protected ArtifactFactory artifactFactory;
113
114 /**
115 * Artifact resolver, needed to download source jars for inclusion in classpath.
116 */
117 @Component( role = ArtifactResolver.class )
118 protected ArtifactResolver artifactResolver;
119
120 /**
121 * Artifact collector, needed to resolve dependencies.
122 */
123 @Component( role = ArtifactCollector.class )
124 protected ArtifactCollector artifactCollector;
125
126 @Component( role = ArtifactMetadataSource.class, hint = "maven" )
127 protected ArtifactMetadataSource artifactMetadataSource;
128
129 /**
130 * The runtime information for Maven, used to retrieve Maven's version number.
131 */
132 @Component
133 private RuntimeInformation runtimeInformation;
134
135 /**
136 * Remote repositories which will be searched for source attachments.
137 */
138 @Parameter( property = "project.remoteArtifactRepositories", required = true, readonly = true )
139 protected List remoteArtifactRepositories;
140
141 /**
142 * Local maven repository.
143 */
144 @Parameter( property = "localRepository", required = true, readonly = true )
145 protected ArtifactRepository localRepository;
146
147 /**
148 * If the executed project is a reactor project, this will contains the full list of projects in the reactor.
149 */
150 @Parameter( property = "reactorProjects", required = true, readonly = true )
151 protected List reactorProjects;
152
153 /**
154 * Skip the operation when true.
155 */
156 @Parameter( property = "eclipse.skip", defaultValue = "false" )
157 private boolean skip;
158
159 /**
160 * Enables/disables the downloading of source attachments. Defaults to false. When this flag is <code>true</code>
161 * remote repositories are checked for sources: in order to avoid repeated check for unavailable source archives, a
162 * status cache is mantained. With versions 2.6+ of the plugin to reset this cache run
163 * <code>mvn eclipse:remove-cache</code>, or use the <code>forceRecheck</code> option with versions. With older
164 * versions delete the file <code>mvn-eclipse-cache.properties</code> in the target directory.
165 */
166 @Parameter( property = "downloadSources" )
167 protected boolean downloadSources;
168
169 /**
170 * Enables/disables the downloading of javadoc attachments. Defaults to false. When this flag is <code>true</code>
171 * remote repositories are checked for javadocs: in order to avoid repeated check for unavailable javadoc archives,
172 * a status cache is mantained. With versions 2.6+ of the plugin to reset this cache run
173 * <code>mvn eclipse:remove-cache</code>, or use the <code>forceRecheck</code> option with versions. With older
174 * versions delete the file <code>mvn-eclipse-cache.properties</code> in the target directory.
175 */
176 @Parameter( property = "downloadJavadocs" )
177 protected boolean downloadJavadocs;
178
179 /**
180 * Enables/disables the rechecking of the remote repository for downloading source/javadoc attachments. Defaults to
181 * false. When this flag is <code>true</code> and the source or javadoc attachment has a status cache to indicate
182 * that it is not available, then the remote repository will be rechecked for a source or javadoc attachment and the
183 * status cache updated to reflect the new state.
184 */
185 @Parameter( property = "forceRecheck" )
186 protected boolean forceRecheck;
187
188 /**
189 * Plexus logger needed for debugging manual artifact resolution.
190 */
191 protected Logger logger;
192
193 /**
194 * Getter for <code>artifactMetadataSource</code>.
195 *
196 * @return Returns the artifactMetadataSource.
197 */
198 public ArtifactMetadataSource getArtifactMetadataSource()
199 {
200 return artifactMetadataSource;
201 }
202
203 /**
204 * Setter for <code>artifactMetadataSource</code>.
205 *
206 * @param artifactMetadataSource The artifactMetadataSource to set.
207 */
208 public void setArtifactMetadataSource( ArtifactMetadataSource artifactMetadataSource )
209 {
210 this.artifactMetadataSource = artifactMetadataSource;
211 }
212
213 /**
214 * Getter for <code>project</code>.
215 *
216 * @return Returns the project.
217 */
218 public MavenProject getProject()
219 {
220 return project;
221 }
222
223 /**
224 * Setter for <code>project</code>.
225 *
226 * @param project The project to set.
227 */
228 public void setProject( MavenProject project )
229 {
230 this.project = project;
231 }
232
233 /**
234 * Getter for <code>reactorProjects</code>.
235 *
236 * @return Returns the reactorProjects.
237 */
238 public List getReactorProjects()
239 {
240 return reactorProjects;
241 }
242
243 /**
244 * Setter for <code>reactorProjects</code>.
245 *
246 * @param reactorProjects The reactorProjects to set.
247 */
248 public void setReactorProjects( List reactorProjects )
249 {
250 this.reactorProjects = reactorProjects;
251 }
252
253 /**
254 * Getter for <code>remoteArtifactRepositories</code>.
255 *
256 * @return Returns the remoteArtifactRepositories.
257 */
258 public List getRemoteArtifactRepositories()
259 {
260 return remoteArtifactRepositories;
261 }
262
263 /**
264 * Setter for <code>remoteArtifactRepositories</code>.
265 *
266 * @param remoteArtifactRepositories The remoteArtifactRepositories to set.
267 */
268 public void setRemoteArtifactRepositories( List remoteArtifactRepositories )
269 {
270 this.remoteArtifactRepositories = remoteArtifactRepositories;
271 }
272
273 /**
274 * Getter for <code>artifactFactory</code>.
275 *
276 * @return Returns the artifactFactory.
277 */
278 public ArtifactFactory getArtifactFactory()
279 {
280 return artifactFactory;
281 }
282
283 /**
284 * Setter for <code>artifactFactory</code>.
285 *
286 * @param artifactFactory The artifactFactory to set.
287 */
288 public void setArtifactFactory( ArtifactFactory artifactFactory )
289 {
290 this.artifactFactory = artifactFactory;
291 }
292
293 /**
294 * Getter for <code>artifactResolver</code>.
295 *
296 * @return Returns the artifactResolver.
297 */
298 public ArtifactResolver getArtifactResolver()
299 {
300 return artifactResolver;
301 }
302
303 /**
304 * Setter for <code>artifactResolver</code>.
305 *
306 * @param artifactResolver The artifactResolver to set.
307 */
308 public void setArtifactResolver( ArtifactResolver artifactResolver )
309 {
310 this.artifactResolver = artifactResolver;
311 }
312
313 /**
314 * Getter for <code>executedProject</code>.
315 *
316 * @return Returns the executedProject.
317 */
318 public MavenProject getExecutedProject()
319 {
320 return executedProject;
321 }
322
323 /**
324 * Setter for <code>executedProject</code>.
325 *
326 * @param executedProject The executedProject to set.
327 */
328 public void setExecutedProject( MavenProject executedProject )
329 {
330 this.executedProject = executedProject;
331 }
332
333 /**
334 * Getter for <code>localRepository</code>.
335 *
336 * @return Returns the localRepository.
337 */
338 public ArtifactRepository getLocalRepository()
339 {
340 return localRepository;
341 }
342
343 /**
344 * Setter for <code>localRepository</code>.
345 *
346 * @param localRepository The localRepository to set.
347 */
348 public void setLocalRepository( ArtifactRepository localRepository )
349 {
350 this.localRepository = localRepository;
351 }
352
353 /**
354 * Getter for <code>downloadJavadocs</code>.
355 *
356 * @return Returns the downloadJavadocs.
357 */
358 public boolean getDownloadJavadocs()
359 {
360 return downloadJavadocs;
361 }
362
363 /**
364 * Setter for <code>downloadJavadocs</code>.
365 *
366 * @param downloadJavadoc The downloadJavadocs to set.
367 */
368 public void setDownloadJavadocs( boolean downloadJavadoc )
369 {
370 downloadJavadocs = downloadJavadoc;
371 }
372
373 /**
374 * Getter for <code>downloadSources</code>.
375 *
376 * @return Returns the downloadSources.
377 */
378 public boolean getDownloadSources()
379 {
380 return downloadSources;
381 }
382
383 /**
384 * Setter for <code>downloadSources</code>.
385 *
386 * @param downloadSources The downloadSources to set.
387 */
388 public void setDownloadSources( boolean downloadSources )
389 {
390 this.downloadSources = downloadSources;
391 }
392
393 protected void setResolveDependencies( boolean resolveDependencies )
394 {
395 this.resolveDependencies = resolveDependencies;
396 }
397
398 protected boolean isResolveDependencies()
399 {
400 return resolveDependencies;
401 }
402
403 /**
404 * return <code>false</code> if projects available in a reactor build should be considered normal dependencies,
405 * <code>true</code> if referenced project will be linked and not need artifact resolution.
406 *
407 * @return <code>true</code> if referenced project will be linked and not need artifact resolution
408 */
409 protected abstract boolean getUseProjectReferences();
410
411 /**
412 * Hook for preparation steps before the actual plugin execution.
413 *
414 * @return <code>true</code> if execution should continue or <code>false</code> if not.
415 * @throws MojoExecutionException generic mojo exception
416 */
417 protected abstract boolean setup()
418 throws MojoExecutionException;
419
420 /**
421 * Main plugin method where dependencies should be processed in order to generate IDE configuration files.
422 *
423 * @param deps list of <code>IdeDependency</code> objects, with artifacts, sources and javadocs already resolved
424 * @throws MojoExecutionException generic mojo exception
425 */
426 protected abstract void writeConfiguration( IdeDependency[] deps )
427 throws MojoExecutionException;
428
429 /**
430 * Not a plugin parameter. Collect the list of dependencies with a missing source artifact for the final report.
431 */
432 private List missingSourceDependencies = new ArrayList();
433
434 /**
435 * Not a plugin parameter. Collect the list of dependencies with a missing javadoc artifact for the final report.
436 */
437 // TODO merge this with the missingSourceDependencies in a classifier based map?
438 private List missingJavadocDependencies = new ArrayList();
439
440 /**
441 * Cached array of resolved dependencies.
442 */
443 private IdeDependency[] ideDeps;
444
445 /**
446 * Flag for mojo implementations to control whether normal maven dependencies should be resolved. Default value is
447 * true.
448 */
449 private boolean resolveDependencies = true;
450
451 /**
452 * @see org.codehaus.plexus.logging.LogEnabled#enableLogging(org.codehaus.plexus.logging.Logger)
453 */
454 public void enableLogging( Logger logger )
455 {
456 this.logger = logger;
457 }
458
459 /**
460 * @see org.apache.maven.plugin.Mojo#execute()
461 */
462 public final void execute()
463 throws MojoExecutionException, MojoFailureException
464 {
465 if ( skip )
466 {
467 return;
468 }
469
470 boolean processProject = setup();
471 if ( !processProject )
472 {
473 return;
474 }
475
476 // resolve artifacts
477 IdeDependency[] deps = doDependencyResolution();
478
479 resolveSourceAndJavadocArtifacts( deps );
480
481 writeConfiguration( deps );
482
483 reportMissingArtifacts();
484
485 }
486
487 /**
488 * Resolve project dependencies. Manual resolution is needed in order to avoid resolution of multiproject artifacts
489 * (if projects will be linked each other an installed jar is not needed) and to avoid a failure when a jar is
490 * missing.
491 *
492 * @throws MojoExecutionException if dependencies can't be resolved
493 * @return resolved IDE dependencies, with attached jars for non-reactor dependencies
494 */
495 protected IdeDependency[] doDependencyResolution()
496 throws MojoExecutionException
497 {
498 if ( ideDeps == null )
499 {
500 if ( resolveDependencies )
501 {
502 MavenProject project = getProject();
503 ArtifactRepository localRepo = getLocalRepository();
504
505 List deps = getProject().getDependencies();
506
507 // Collect the list of resolved IdeDependencies.
508 List dependencies = new ArrayList();
509
510 if ( deps != null )
511 {
512 Map managedVersions =
513 createManagedVersionMap( getArtifactFactory(), project.getId(),
514 project.getDependencyManagement() );
515
516 ArtifactResolutionResult artifactResolutionResult;
517
518 try
519 {
520
521 List listeners = new ArrayList();
522
523 if ( logger.isDebugEnabled() )
524 {
525 listeners.add( new DebugResolutionListener( logger ) );
526 }
527
528 listeners.add( new WarningResolutionListener( logger ) );
529
530 artifactResolutionResult =
531 artifactCollector.collect( getProjectArtifacts(), project.getArtifact(), managedVersions,
532 localRepo, project.getRemoteArtifactRepositories(),
533 getArtifactMetadataSource(), null, listeners );
534 }
535 catch ( ArtifactResolutionException e )
536 {
537 getLog().debug( e.getMessage(), e );
538 getLog().error(
539 Messages.getString( "AbstractIdeSupportMojo.artifactresolution", new Object[] { //$NON-NLS-1$
540 e.getGroupId(), e.getArtifactId(), e.getVersion(),
541 e.getMessage() } ) );
542
543 // if we are here artifactResolutionResult is null, create a project without dependencies but
544 // don't fail
545 // (this could be a reactor projects, we don't want to fail everything)
546 // Causes MECLIPSE-185. Not sure if it should be handled this way??
547 return new IdeDependency[0];
548 }
549
550 // keep track of added reactor projects in order to avoid duplicates
551 Set emittedReactorProjectId = new HashSet();
552
553 for (Object o : artifactResolutionResult.getArtifactResolutionNodes()) {
554
555 ResolutionNode node = (ResolutionNode) o;
556 int dependencyDepth = node.getDepth();
557 Artifact art = node.getArtifact();
558 // don't resolve jars for reactor projects
559 if (hasToResolveJar(art)) {
560 try {
561 artifactResolver.resolve(art, node.getRemoteRepositories(), localRepository);
562 } catch (ArtifactNotFoundException e) {
563 getLog().debug(e.getMessage(), e);
564 getLog().warn(
565 Messages.getString(
566 "AbstractIdeSupportMojo.artifactdownload", new Object[]{ //$NON-NLS-1$
567 e.getGroupId(), e.getArtifactId(), e.getVersion(),
568 e.getMessage()}));
569 } catch (ArtifactResolutionException e) {
570 getLog().debug(e.getMessage(), e);
571 getLog().warn(
572 Messages.getString(
573 "AbstractIdeSupportMojo.artifactresolution", new Object[]{ //$NON-NLS-1$
574 e.getGroupId(), e.getArtifactId(), e.getVersion(),
575 e.getMessage()}));
576 }
577 }
578
579 boolean includeArtifact = true;
580 if (getExcludes() != null) {
581 String artifactFullId = art.getGroupId() + ":" + art.getArtifactId();
582 if (getExcludes().contains(artifactFullId)) {
583 getLog().info("excluded: " + artifactFullId);
584 includeArtifact = false;
585 }
586 }
587
588 if (includeArtifact
589 && (!(getUseProjectReferences() && isAvailableAsAReactorProject(art)) || emittedReactorProjectId.add(art.getGroupId()
590 + '-' + art.getArtifactId()))) {
591
592 // the following doesn't work: art.getArtifactHandler().getPackaging() always returns "jar"
593 // also
594 // if the packaging specified in pom.xml is different.
595
596 // osgi-bundle packaging is provided by the felix osgi plugin
597 // eclipse-plugin packaging is provided by this eclipse plugin
598 // String packaging = art.getArtifactHandler().getPackaging();
599 // boolean isOsgiBundle = "osgi-bundle".equals( packaging ) || "eclipse-plugin".equals(
600 // packaging );
601
602 // we need to check the manifest, if "Bundle-SymbolicName" is there the artifact can be
603 // considered
604 // an osgi bundle
605 boolean isOsgiBundle;
606 String osgiSymbolicName = null;
607 if (art.getFile() != null) {
608 JarFile jarFile = null;
609 try {
610 jarFile = new JarFile(art.getFile(), false, ZipFile.OPEN_READ);
611
612 Manifest manifest = jarFile.getManifest();
613 if (manifest != null) {
614 osgiSymbolicName =
615 manifest.getMainAttributes().getValue(
616 new Attributes.Name(
617 "Bundle-SymbolicName"));
618 }
619 } catch (IOException e) {
620 getLog().info("Unable to read jar manifest from " + art.getFile());
621 } finally {
622 if (jarFile != null) {
623 try {
624 jarFile.close();
625 } catch (IOException e) {
626 // ignore
627 }
628 }
629 }
630 }
631
632 isOsgiBundle = osgiSymbolicName != null;
633
634 IdeDependency dep =
635 new IdeDependency(art.getGroupId(), art.getArtifactId(), art.getVersion(),
636 art.getClassifier(), useProjectReference(art),
637 Artifact.SCOPE_TEST.equals(art.getScope()),
638 Artifact.SCOPE_SYSTEM.equals(art.getScope()),
639 Artifact.SCOPE_PROVIDED.equals(art.getScope()),
640 art.getArtifactHandler().isAddedToClasspath(), art.getFile(),
641 art.getType(), isOsgiBundle, osgiSymbolicName, dependencyDepth,
642 getProjectNameForArifact(art));
643 // no duplicate entries allowed. System paths can cause this problem.
644 if (!dependencies.contains(dep)) {
645 dependencies.add(dep);
646 }
647 }
648
649 }
650
651 // @todo a final report with the list of
652 // missingArtifacts?
653
654 }
655
656 ideDeps = (IdeDependency[]) dependencies.toArray( new IdeDependency[dependencies.size()] );
657 }
658 else
659 {
660 ideDeps = new IdeDependency[0];
661 }
662 }
663
664 return ideDeps;
665 }
666
667 /**
668 * Find the name of the project as used in eclipse.
669 *
670 * @param artifact The artifact to find the eclipse name for.
671 * @return The name os the eclipse project.
672 */
673 abstract public String getProjectNameForArifact( Artifact artifact );
674
675 /**
676 * Returns the list of project artifacts. Also artifacts generated from referenced projects will be added, but with
677 * the <code>resolved</code> property set to true.
678 *
679 * @return list of projects artifacts
680 * @throws MojoExecutionException if unable to parse dependency versions
681 */
682 private Set getProjectArtifacts()
683 throws MojoExecutionException
684 {
685 // [MECLIPSE-388] Don't sort this, the order should be identical to getProject.getDependencies()
686 Set artifacts = new LinkedHashSet();
687
688 for (Object o : getProject().getDependencies()) {
689 Dependency dependency = (Dependency) o;
690
691 String groupId = dependency.getGroupId();
692 String artifactId = dependency.getArtifactId();
693 VersionRange versionRange;
694 try {
695 versionRange = VersionRange.createFromVersionSpec(dependency.getVersion());
696 } catch (InvalidVersionSpecificationException e) {
697 throw new MojoExecutionException(
698 Messages.getString(
699 "AbstractIdeSupportMojo.unabletoparseversion", new Object[]{ //$NON-NLS-1$
700 dependency.getArtifactId(),
701 dependency.getVersion(),
702 dependency.getManagementKey(), e.getMessage()}),
703 e);
704 }
705
706 String type = dependency.getType();
707 if (type == null) {
708 type = Constants.PROJECT_PACKAGING_JAR;
709 }
710 String classifier = dependency.getClassifier();
711 boolean optional = dependency.isOptional();
712 String scope = dependency.getScope();
713 if (scope == null) {
714 scope = Artifact.SCOPE_COMPILE;
715 }
716
717 Artifact art =
718 getArtifactFactory().createDependencyArtifact(groupId, artifactId, versionRange, type, classifier,
719 scope, optional);
720
721 if (scope.equalsIgnoreCase(Artifact.SCOPE_SYSTEM)) {
722 art.setFile(new File(dependency.getSystemPath()));
723 }
724
725 handleExclusions(art, dependency);
726
727 artifacts.add(art);
728 }
729
730 return artifacts;
731 }
732
733 /**
734 * Apply exclusion filters to direct AND transitive dependencies.
735 *
736 * @param artifact
737 * @param dependency
738 */
739 private void handleExclusions( Artifact artifact, Dependency dependency )
740 {
741
742 List exclusions = new ArrayList();
743 for (Exclusion e : dependency.getExclusions()) {
744 exclusions.add(e.getGroupId() + ":" + e.getArtifactId()); //$NON-NLS-1$
745 }
746
747 ArtifactFilter newFilter = new ExcludesArtifactFilter( exclusions );
748
749 artifact.setDependencyFilter( newFilter );
750 }
751
752 /**
753 * Utility method that locates a project producing the given artifact.
754 *
755 * @param artifact the artifact a project should produce.
756 * @return <code>true</code> if the artifact is produced by a reactor projectart.
757 */
758 protected boolean isAvailableAsAReactorProject( Artifact artifact )
759 {
760 return getReactorProject( artifact ) != null;
761 }
762
763 /**
764 * Checks the list of reactor projects to see if the artifact is included.
765 *
766 * @param artifact the artifact to check if it is in the reactor
767 * @return the reactor project or null if it is not in the reactor
768 */
769 protected MavenProject getReactorProject( Artifact artifact )
770 {
771 if ( reactorProjects != null )
772 {
773 for (Object reactorProject1 : reactorProjects) {
774 MavenProject reactorProject = (MavenProject) reactorProject1;
775
776 if (reactorProject.getGroupId().equals(artifact.getGroupId())
777 && reactorProject.getArtifactId().equals(artifact.getArtifactId())) {
778 if (reactorProject.getVersion().equals(artifact.getVersion())) {
779 return reactorProject;
780 } else {
781 getLog().info(
782 "Artifact "
783 + artifact.getId()
784 + " already available as a reactor project, but with different version. Expected: "
785 + artifact.getVersion() + ", found: " + reactorProject.getVersion());
786 }
787 }
788 }
789 }
790 return null;
791 }
792
793 /**
794 * @return an array with all dependencies available in the workspace, to be implemented by the subclasses.
795 */
796 protected IdeDependency[] getWorkspaceArtefacts()
797 {
798 return new IdeDependency[0];
799 }
800
801 private Map createManagedVersionMap( ArtifactFactory artifactFactory, String projectId,
802 DependencyManagement dependencyManagement )
803 throws MojoExecutionException
804 {
805 Map map;
806 if ( dependencyManagement != null && dependencyManagement.getDependencies() != null )
807 {
808 map = new HashMap();
809 for (Dependency d : dependencyManagement.getDependencies()) {
810 try {
811 VersionRange versionRange = VersionRange.createFromVersionSpec(d.getVersion());
812 Artifact artifact =
813 artifactFactory.createDependencyArtifact(d.getGroupId(), d.getArtifactId(), versionRange,
814 d.getType(), d.getClassifier(), d.getScope(),
815 d.isOptional());
816
817 handleExclusions(artifact, d);
818 map.put(d.getManagementKey(), artifact);
819 } catch (InvalidVersionSpecificationException e) {
820 throw new MojoExecutionException(
821 Messages.getString(
822 "AbstractIdeSupportMojo.unabletoparseversion", new Object[]{ //$NON-NLS-1$
823 projectId, d.getVersion(),
824 d.getManagementKey(), e.getMessage()}),
825 e);
826 }
827 }
828 }
829 else
830 {
831 map = Collections.EMPTY_MAP;
832 }
833 return map;
834 }
835
836 /**
837 * Resolve source artifacts and download them if <code>downloadSources</code> is <code>true</code>. Source and
838 * javadocs artifacts will be attached to the <code>IdeDependency</code> Resolve source and javadoc artifacts. The
839 * resolved artifacts will be downloaded based on the <code>downloadSources</code> and <code>downloadJavadocs</code>
840 * attributes. Source and
841 *
842 * @param deps resolved dependencies
843 */
844 private void resolveSourceAndJavadocArtifacts( IdeDependency[] deps )
845 {
846 final List missingSources = resolveDependenciesWithClassifier( deps, "sources", getDownloadSources() );
847 missingSourceDependencies.addAll( missingSources );
848
849 final List missingJavadocs = resolveDependenciesWithClassifier( deps, "javadoc", getDownloadJavadocs() );
850 missingJavadocDependencies.addAll( missingJavadocs );
851 }
852
853 /**
854 * Resolve the required artifacts for each of the dependency. <code>sources</code> or <code>javadoc</code> artifacts
855 * (depending on the <code>classifier</code>) are attached to the dependency.
856 *
857 * @param deps resolved dependencies
858 * @param inClassifier the classifier we are looking for (either <code>sources</code> or <code>javadoc</code>)
859 * @param includeRemoteRepositories flag whether we should search remote repositories for the artifacts or not
860 * @return the list of dependencies for which the required artifact was not found
861 */
862 private List resolveDependenciesWithClassifier( IdeDependency[] deps, String inClassifier,
863 boolean includeRemoteRepositories )
864 {
865 List missingClassifierDependencies = new ArrayList();
866
867 // if downloadSources is off, just check
868 // local repository for reporting missing source jars
869 List remoteRepos = includeRemoteRepositories ? getRemoteArtifactRepositories() : Collections.EMPTY_LIST;
870
871 for (IdeDependency dependency : deps) {
872 if (dependency.isReferencedProject() || dependency.isSystemScoped()) {
873 // artifact not needed
874 continue;
875 }
876
877 if (getLog().isDebugEnabled()) {
878 getLog().debug(
879 "Searching for sources for " + dependency.getId() + ":" + dependency.getClassifier()
880 + " at " + dependency.getId() + ":" + inClassifier);
881 }
882
883 Artifact baseArtifact =
884 artifactFactory.createArtifactWithClassifier(dependency.getGroupId(), dependency.getArtifactId(),
885 dependency.getVersion(), dependency.getType(),
886 dependency.getClassifier());
887 baseArtifact =
888 IdeUtils.resolveArtifact(artifactResolver, baseArtifact, remoteRepos, localRepository, getLog());
889 if (!baseArtifact.isResolved()) {
890 // base artifact does not exist - no point checking for javadoc/sources
891 continue;
892 }
893
894 Artifact artifact =
895 IdeUtils.createArtifactWithClassifier(dependency.getGroupId(), dependency.getArtifactId(),
896 dependency.getVersion(), dependency.getClassifier(),
897 inClassifier, artifactFactory);
898 File notAvailableMarkerFile = IdeUtils.getNotAvailableMarkerFile(localRepository, artifact);
899
900 if (forceRecheck && notAvailableMarkerFile.exists()) {
901 if (!notAvailableMarkerFile.delete()) {
902 getLog().warn(
903 Messages.getString("AbstractIdeSupportMojo.unabletodeletenotavailablemarkerfile",
904 notAvailableMarkerFile));
905 }
906 }
907
908 if (!notAvailableMarkerFile.exists()) {
909 artifact =
910 IdeUtils.resolveArtifact(artifactResolver, artifact, remoteRepos, localRepository, getLog());
911 if (artifact.isResolved()) {
912 if ("sources".equals(inClassifier)) {
913 dependency.setSourceAttachment(artifact.getFile());
914 } else if ("javadoc".equals(inClassifier) && includeRemoteRepositories ) {
915 dependency.setJavadocAttachment(artifact.getFile());
916 }
917 } else {
918 if (includeRemoteRepositories) {
919 try {
920 notAvailableMarkerFile.createNewFile();
921 getLog().debug(
922 Messages.getString("AbstractIdeSupportMojo.creatednotavailablemarkerfile",
923 notAvailableMarkerFile));
924 } catch (IOException e) {
925 getLog().warn(
926 Messages.getString(
927 "AbstractIdeSupportMojo.failedtocreatenotavailablemarkerfile",
928 notAvailableMarkerFile));
929 }
930 }
931 // add the dependencies to the list
932 // of those lacking the required
933 // artifact
934 missingClassifierDependencies.add(dependency);
935 }
936 }
937 }
938
939 // return the list of dependencies missing the
940 // required artifact
941 return missingClassifierDependencies;
942
943 }
944
945 /**
946 * Output a message with the list of missing dependencies and info on how turn download on if it was disabled.
947 */
948 private void reportMissingArtifacts()
949 {
950 StringBuilder msg = new StringBuilder();
951
952 if ( getDownloadSources() && !missingSourceDependencies.isEmpty() )
953 {
954 msg.append( Messages.getString( "AbstractIdeSupportMojo.sourcesnotavailable" ) ); //$NON-NLS-1$
955
956 for (Object missingSourceDependency : missingSourceDependencies) {
957 IdeDependency art = (IdeDependency) missingSourceDependency;
958 msg.append(Messages.getString("AbstractIdeSupportMojo.sourcesmissingitem", art.getId())); //$NON-NLS-1$
959 }
960 msg.append( "\n" ); //$NON-NLS-1$
961 }
962
963 if ( getDownloadJavadocs() && !missingJavadocDependencies.isEmpty() )
964 {
965 msg.append( Messages.getString( "AbstractIdeSupportMojo.javadocnotavailable" ) ); //$NON-NLS-1$
966
967 for (Object missingJavadocDependency : missingJavadocDependencies) {
968 IdeDependency art = (IdeDependency) missingJavadocDependency;
969 msg.append(Messages.getString("AbstractIdeSupportMojo.javadocmissingitem", art.getId())); //$NON-NLS-1$
970 }
971 msg.append( "\n" ); //$NON-NLS-1$
972 }
973 getLog().info( msg );
974 }
975
976 /**
977 * @return List of dependencies to exclude from eclipse classpath.
978 * @since 2.5
979 */
980 public abstract List getExcludes();
981
982 /**
983 * Checks if jar has to be resolved for the given artifact
984 *
985 * @param art the artifact to check
986 * @return true if resolution should happen
987 */
988 protected boolean hasToResolveJar( Artifact art )
989 {
990 return !( getUseProjectReferences() && isAvailableAsAReactorProject( art ) );
991 }
992
993 /**
994 * Checks if a projects reference has to be used for the given artifact
995 *
996 * @param art the artifact to check
997 * @return true if a project reference has to be used.
998 */
999 protected boolean useProjectReference( Artifact art )
1000 {
1001 return getUseProjectReferences() && isAvailableAsAReactorProject( art );
1002 }
1003
1004 /**
1005 * Checks whether the currently running Maven satisfies the specified version (range).
1006 *
1007 * @param version The version range to test for, must not be <code>null</code>.
1008 * @return <code>true</code> if the current Maven version matches the specified version range, <code>false</code>
1009 * otherwise.
1010 */
1011 protected boolean isMavenVersion( String version )
1012 {
1013 try
1014 {
1015 VersionRange versionRange = VersionRange.createFromVersionSpec( version );
1016 ArtifactVersion mavenVersion = runtimeInformation.getApplicationVersion();
1017 return versionRange.containsVersion( mavenVersion );
1018 }
1019 catch ( InvalidVersionSpecificationException e )
1020 {
1021 throw new IllegalArgumentException( e.getMessage() );
1022 }
1023 }
1024
1025 }