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.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 }