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