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