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