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.LinkedHashSet;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Set;
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 1173952 2011-09-22 05:53:22Z 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. 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 // [MECLIPSE-388] Don't sort this, the order should be identical to getProject.getDependencies()
738 Set artifacts = new LinkedHashSet();
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 ( getDownloadSources() && !missingSourceDependencies.isEmpty() )
1044 {
1045 msg.append( Messages.getString( "AbstractIdeSupportMojo.sourcesnotavailable" ) ); //$NON-NLS-1$
1046
1047 for ( Iterator it = missingSourceDependencies.iterator(); it.hasNext(); )
1048 {
1049 IdeDependency art = (IdeDependency) it.next();
1050 msg.append( Messages.getString( "AbstractIdeSupportMojo.sourcesmissingitem", art.getId() ) ); //$NON-NLS-1$
1051 }
1052 msg.append( "\n" ); //$NON-NLS-1$
1053 }
1054
1055 if ( getDownloadJavadocs() && !missingJavadocDependencies.isEmpty() )
1056 {
1057 msg.append( Messages.getString( "AbstractIdeSupportMojo.javadocnotavailable" ) ); //$NON-NLS-1$
1058
1059 for ( Iterator it = missingJavadocDependencies.iterator(); it.hasNext(); )
1060 {
1061 IdeDependency art = (IdeDependency) it.next();
1062 msg.append( Messages.getString( "AbstractIdeSupportMojo.javadocmissingitem", art.getId() ) ); //$NON-NLS-1$
1063 }
1064 msg.append( "\n" ); //$NON-NLS-1$
1065 }
1066 getLog().info( msg );
1067 }
1068
1069 /**
1070 * @return List of dependencies to exclude from eclipse classpath.
1071 * @since 2.5
1072 */
1073 public abstract List getExcludes();
1074
1075 /**
1076 * Checks if jar has to be resolved for the given artifact
1077 *
1078 * @param art the artifact to check
1079 * @return true if resolution should happen
1080 */
1081 protected boolean hasToResolveJar( Artifact art )
1082 {
1083 return !( getUseProjectReferences() && isAvailableAsAReactorProject( art ) );
1084 }
1085
1086 /**
1087 * Checks if a projects reference has to be used for the given artifact
1088 *
1089 * @param art the artifact to check
1090 * @return true if a project reference has to be used.
1091 */
1092 protected boolean useProjectReference( Artifact art )
1093 {
1094 return getUseProjectReferences() && isAvailableAsAReactorProject( art );
1095 }
1096
1097 /**
1098 * Checks whether the currently running Maven satisfies the specified version (range).
1099 *
1100 * @param version The version range to test for, must not be <code>null</code>.
1101 * @return <code>true</code> if the current Maven version matches the specified version range, <code>false</code>
1102 * otherwise.
1103 */
1104 protected boolean isMavenVersion( String version )
1105 {
1106 try
1107 {
1108 VersionRange versionRange = VersionRange.createFromVersionSpec( version );
1109 ArtifactVersion mavenVersion = runtimeInformation.getApplicationVersion();
1110 return versionRange.containsVersion( mavenVersion );
1111 }
1112 catch ( InvalidVersionSpecificationException e )
1113 {
1114 throw new IllegalArgumentException( e.getMessage() );
1115 }
1116 }
1117
1118 }