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