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 }