1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86 public abstract class AbstractIdeSupportMojo
87 extends AbstractMojo
88 implements LogEnabled
89 {
90
91
92
93
94
95
96
97
98 protected MavenProject project;
99
100
101
102
103
104
105
106 protected MavenProject executedProject;
107
108
109
110
111
112
113 protected String packaging;
114
115
116
117
118
119
120
121
122 protected ArtifactFactory artifactFactory;
123
124
125
126
127
128
129
130
131 protected ArtifactResolver artifactResolver;
132
133
134
135
136
137
138
139
140 protected ArtifactCollector artifactCollector;
141
142
143
144
145 protected ArtifactMetadataSource artifactMetadataSource;
146
147
148
149
150
151
152
153
154 protected List remoteArtifactRepositories;
155
156
157
158
159
160
161
162
163 protected ArtifactRepository localRepository;
164
165
166
167
168
169
170
171
172 protected List reactorProjects;
173
174
175
176
177
178
179 private boolean skip;
180
181
182
183
184
185
186
187
188
189 protected boolean downloadSources;
190
191
192
193
194
195
196
197
198
199 protected boolean downloadJavadocs;
200
201
202
203
204 private Logger logger;
205
206
207
208
209
210
211 public ArtifactMetadataSource getArtifactMetadataSource()
212 {
213 return this.artifactMetadataSource;
214 }
215
216
217
218
219
220
221 public void setArtifactMetadataSource( ArtifactMetadataSource artifactMetadataSource )
222 {
223 this.artifactMetadataSource = artifactMetadataSource;
224 }
225
226
227
228
229
230
231 public MavenProject getProject()
232 {
233 return this.project;
234 }
235
236
237
238
239
240
241 public void setProject( MavenProject project )
242 {
243 this.project = project;
244 }
245
246
247
248
249
250
251 public List getReactorProjects()
252 {
253 return this.reactorProjects;
254 }
255
256
257
258
259
260
261 public void setReactorProjects( List reactorProjects )
262 {
263 this.reactorProjects = reactorProjects;
264 }
265
266
267
268
269
270
271 public List getRemoteArtifactRepositories()
272 {
273 return this.remoteArtifactRepositories;
274 }
275
276
277
278
279
280
281 public void setRemoteArtifactRepositories( List remoteArtifactRepositories )
282 {
283 this.remoteArtifactRepositories = remoteArtifactRepositories;
284 }
285
286
287
288
289
290
291 public ArtifactFactory getArtifactFactory()
292 {
293 return this.artifactFactory;
294 }
295
296
297
298
299
300
301 public void setArtifactFactory( ArtifactFactory artifactFactory )
302 {
303 this.artifactFactory = artifactFactory;
304 }
305
306
307
308
309
310
311 public ArtifactResolver getArtifactResolver()
312 {
313 return this.artifactResolver;
314 }
315
316
317
318
319
320
321 public void setArtifactResolver( ArtifactResolver artifactResolver )
322 {
323 this.artifactResolver = artifactResolver;
324 }
325
326
327
328
329
330
331 public MavenProject getExecutedProject()
332 {
333 return this.executedProject;
334 }
335
336
337
338
339
340
341 public void setExecutedProject( MavenProject executedProject )
342 {
343 this.executedProject = executedProject;
344 }
345
346
347
348
349
350
351 public ArtifactRepository getLocalRepository()
352 {
353 return this.localRepository;
354 }
355
356
357
358
359
360
361 public void setLocalRepository( ArtifactRepository localRepository )
362 {
363 this.localRepository = localRepository;
364 }
365
366
367
368
369
370
371 public boolean getDownloadJavadocs()
372 {
373 return this.downloadJavadocs;
374 }
375
376
377
378
379
380
381 public void setDownloadJavadocs( boolean downloadJavadoc )
382 {
383 this.downloadJavadocs = downloadJavadoc;
384 }
385
386
387
388
389
390
391 public boolean getDownloadSources()
392 {
393 return this.downloadSources;
394 }
395
396
397
398
399
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
418
419
420
421
422 protected abstract boolean getUseProjectReferences();
423
424
425
426
427
428
429
430 protected abstract boolean setup()
431 throws MojoExecutionException;
432
433
434
435
436
437
438
439 protected abstract void writeConfiguration( IdeDependency[] deps )
440 throws MojoExecutionException;
441
442
443
444
445 private List missingSourceDependencies = new ArrayList();
446
447
448
449
450
451 private List missingJavadocDependencies = new ArrayList();
452
453
454
455
456 private IdeDependency[] ideDeps;
457
458
459
460
461
462 private boolean resolveDependencies = true;
463
464
465
466
467 public void enableLogging( Logger logger )
468 {
469 this.logger = logger;
470 }
471
472
473
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
490 IdeDependency[] deps = doDependencyResolution();
491
492 resolveSourceAndJavadocArtifacts( deps );
493
494 writeConfiguration( deps );
495
496 reportMissingArtifacts();
497
498 }
499
500
501
502
503
504
505
506
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
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[] {
553 e.getGroupId(), e.getArtifactId(), e.getVersion(),
554 e.getMessage() } ) );
555
556
557
558
559
560 return new IdeDependency[0];
561 }
562
563
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 boolean isReactorProject = getUseProjectReferences() && isAvailableAsAReactorProject( art );
573
574
575 if ( !isReactorProject )
576 {
577 try
578 {
579 artifactResolver.resolve( art, node.getRemoteRepositories(), localRepository );
580 }
581 catch ( ArtifactNotFoundException e )
582 {
583 getLog().debug( e.getMessage(), e );
584 getLog().warn(
585 Messages.getString( "artifactdownload", new Object[] {
586 e.getGroupId(), e.getArtifactId(), e.getVersion(),
587 e.getMessage() } ) );
588 }
589 catch ( ArtifactResolutionException e )
590 {
591 getLog().debug( e.getMessage(), e );
592 getLog().warn(
593 Messages.getString( "artifactresolution", new Object[] {
594 e.getGroupId(), e.getArtifactId(), e.getVersion(),
595 e.getMessage() } ) );
596 }
597 }
598
599 if ( !isReactorProject ||
600 emittedReactorProjectId.add( art.getGroupId() + '-' + art.getArtifactId() ) )
601 {
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616 boolean isOsgiBundle = false;
617 String osgiSymbolicName = null;
618 if ( art.getFile() != null )
619 {
620 JarFile jarFile = null;
621 try
622 {
623 jarFile = new JarFile( art.getFile(), false, ZipFile.OPEN_READ );
624
625 Manifest manifest = jarFile.getManifest();
626 if ( manifest != null )
627 {
628 osgiSymbolicName =
629 manifest.getMainAttributes().getValue(
630 new Attributes.Name(
631 "Bundle-SymbolicName" ) );
632 }
633 }
634 catch ( IOException e )
635 {
636 getLog().info( "Unable to read jar manifest from " + art.getFile() );
637 }
638 finally
639 {
640 if ( jarFile != null )
641 {
642 try
643 {
644 jarFile.close();
645 }
646 catch ( IOException e )
647 {
648
649 }
650 }
651 }
652 }
653
654 isOsgiBundle = osgiSymbolicName != null;
655
656 IdeDependency dep =
657 new IdeDependency( art.getGroupId(), art.getArtifactId(), art.getVersion(),
658 art.getClassifier(), isReactorProject,
659 Artifact.SCOPE_TEST.equals( art.getScope() ),
660 Artifact.SCOPE_SYSTEM.equals( art.getScope() ),
661 Artifact.SCOPE_PROVIDED.equals( art.getScope() ),
662 art.getArtifactHandler().isAddedToClasspath(), art.getFile(),
663 art.getType(), isOsgiBundle, osgiSymbolicName, dependencyDepth,
664 getProjectNameForArifact( art ) );
665
666 if ( !dependencies.contains( dep ) )
667 {
668 dependencies.add( dep );
669 }
670 }
671
672 }
673
674
675
676
677 }
678
679 ideDeps = (IdeDependency[]) dependencies.toArray( new IdeDependency[dependencies.size()] );
680 }
681 else
682 {
683 ideDeps = new IdeDependency[0];
684 }
685 }
686
687 return ideDeps;
688 }
689
690
691
692
693
694
695
696 abstract public String getProjectNameForArifact( Artifact artifact );
697
698
699
700
701
702
703
704
705 private Set getProjectArtifacts()
706 throws MojoExecutionException
707 {
708
709 Set artifacts = new TreeSet();
710
711 for ( Iterator dependencies = getProject().getDependencies().iterator(); dependencies.hasNext(); )
712 {
713 Dependency dependency = (Dependency) dependencies.next();
714
715 String groupId = dependency.getGroupId();
716 String artifactId = dependency.getArtifactId();
717 VersionRange versionRange;
718 try
719 {
720 versionRange = VersionRange.createFromVersionSpec( dependency.getVersion() );
721 }
722 catch ( InvalidVersionSpecificationException e )
723 {
724 throw new MojoExecutionException(
725 Messages.getString(
726 "unabletoparseversion", new Object[] {
727 dependency.getArtifactId(),
728 dependency.getVersion(),
729 dependency.getManagementKey(), e.getMessage() } ),
730 e );
731 }
732
733 String type = dependency.getType();
734 if ( type == null )
735 {
736 type = Constants.PROJECT_PACKAGING_JAR;
737 }
738 String classifier = dependency.getClassifier();
739 boolean optional = dependency.isOptional();
740 String scope = dependency.getScope();
741 if ( scope == null )
742 {
743 scope = Artifact.SCOPE_COMPILE;
744 }
745
746 Artifact art =
747 getArtifactFactory().createDependencyArtifact( groupId, artifactId, versionRange, type, classifier,
748 scope, optional );
749
750 if ( scope.equalsIgnoreCase( Artifact.SCOPE_SYSTEM ) )
751 {
752 art.setFile( new File( dependency.getSystemPath() ) );
753 }
754
755 List exclusions = new ArrayList();
756 for ( Iterator j = dependency.getExclusions().iterator(); j.hasNext(); )
757 {
758 Exclusion e = (Exclusion) j.next();
759 exclusions.add( e.getGroupId() + ":" + e.getArtifactId() );
760 }
761
762 ArtifactFilter newFilter = new ExcludesArtifactFilter( exclusions );
763
764 art.setDependencyFilter( newFilter );
765
766 artifacts.add( art );
767 }
768
769 return artifacts;
770 }
771
772
773
774
775
776
777
778 private boolean isAvailableAsAReactorProject( Artifact artifact )
779 {
780 if ( reactorProjects != null )
781 {
782 for ( Iterator iter = reactorProjects.iterator(); iter.hasNext(); )
783 {
784 MavenProject reactorProject = (MavenProject) iter.next();
785
786 if ( reactorProject.getGroupId().equals( artifact.getGroupId() ) &&
787 reactorProject.getArtifactId().equals( artifact.getArtifactId() ) )
788 {
789 if ( reactorProject.getVersion().equals( artifact.getVersion() ) )
790 {
791 return true;
792 }
793 else
794 {
795 getLog().info(
796 "Artifact " +
797 artifact.getId() +
798 " already available as a reactor project, but with different version. Expected: " +
799 artifact.getVersion() + ", found: " + reactorProject.getVersion() );
800 }
801 }
802 }
803 }
804 return false;
805 }
806
807 private Map createManagedVersionMap( ArtifactFactory artifactFactory, String projectId,
808 DependencyManagement dependencyManagement )
809 throws MojoExecutionException
810 {
811 Map map;
812 if ( dependencyManagement != null && dependencyManagement.getDependencies() != null )
813 {
814 map = new HashMap();
815 for ( Iterator i = dependencyManagement.getDependencies().iterator(); i.hasNext(); )
816 {
817 Dependency d = (Dependency) i.next();
818
819 try
820 {
821 VersionRange versionRange = VersionRange.createFromVersionSpec( d.getVersion() );
822 Artifact artifact =
823 artifactFactory.createDependencyArtifact( d.getGroupId(), d.getArtifactId(), versionRange,
824 d.getType(), d.getClassifier(), d.getScope(),
825 d.isOptional() );
826 map.put( d.getManagementKey(), artifact );
827 }
828 catch ( InvalidVersionSpecificationException e )
829 {
830 throw new MojoExecutionException( Messages.getString( "unabletoparseversion", new Object[] {
831 projectId, d.getVersion(),
832 d.getManagementKey(), e.getMessage() } ),
833 e );
834 }
835 }
836 }
837 else
838 {
839 map = Collections.EMPTY_MAP;
840 }
841 return map;
842 }
843
844
845
846
847
848
849
850
851 private File getReactorTargetDir( MavenProject prj )
852 {
853 if ( prj.getParent() != null )
854 {
855 if ( prj.getParent().getBasedir() != null && prj.getParent().getBasedir().exists() )
856 {
857 return getReactorTargetDir( prj.getParent() );
858 }
859 }
860 return new File( prj.getBuild().getDirectory() );
861 }
862
863
864
865
866
867
868
869
870
871 private void resolveSourceAndJavadocArtifacts( IdeDependency[] deps )
872 {
873
874 File reactorTargetDir = getReactorTargetDir( project );
875 File unavailableArtifactsTmpFile = new File( reactorTargetDir, "mvn-eclipse-cache.properties" );
876
877 getLog().info( "Using source status cache: " + unavailableArtifactsTmpFile.getAbsolutePath() );
878
879
880 if ( !unavailableArtifactsTmpFile.getParentFile().exists() )
881 {
882 unavailableArtifactsTmpFile.getParentFile().mkdirs();
883 }
884
885 Properties unavailableArtifactsCache = new Properties();
886 if ( unavailableArtifactsTmpFile.exists() )
887 {
888 InputStream is = null;
889 try
890 {
891 is = new FileInputStream( unavailableArtifactsTmpFile );
892 unavailableArtifactsCache.load( is );
893 }
894 catch ( IOException e )
895 {
896 getLog().warn( "Unable to read source status for reactor projects" );
897 }
898 finally
899 {
900 IOUtil.close( is );
901 }
902
903 }
904
905 final List missingSources =
906 resolveDependenciesWithClassifier( deps, "sources", getDownloadSources(), unavailableArtifactsCache );
907 missingSourceDependencies.addAll( missingSources );
908
909 final List missingJavadocs =
910 resolveDependenciesWithClassifier( deps, "javadoc", getDownloadJavadocs(), unavailableArtifactsCache );
911 missingJavadocDependencies.addAll( missingJavadocs );
912
913 FileOutputStream fos = null;
914 try
915 {
916 fos = new FileOutputStream( unavailableArtifactsTmpFile );
917 unavailableArtifactsCache.store( fos, "Temporary index for unavailable sources and javadocs" );
918 }
919 catch ( IOException e )
920 {
921 getLog().warn( "Unable to cache source status for reactor projects" );
922 }
923 finally
924 {
925 IOUtil.close( fos );
926 }
927
928 }
929
930
931
932
933
934
935
936
937
938
939
940 private List resolveDependenciesWithClassifier( IdeDependency[] deps, String inClassifier,
941 boolean includeRemoteRepositories,
942 Properties unavailableArtifactsCache )
943 {
944 List missingClassifierDependencies = new ArrayList();
945
946
947
948 List remoteRepos = includeRemoteRepositories ? getRemoteArtifactRepositories() : Collections.EMPTY_LIST;
949
950 for ( int j = 0; j < deps.length; j++ )
951 {
952 IdeDependency dependency = deps[j];
953
954 if ( dependency.isReferencedProject() || dependency.isSystemScoped() )
955 {
956
957 continue;
958 }
959
960
961 String classifier = inClassifier;
962 if ( "sources".equals( classifier ) && "tests".equals( dependency.getClassifier() ) )
963 {
964 classifier = "test-sources";
965 }
966
967 if ( getLog().isDebugEnabled() )
968 {
969 getLog().debug(
970 "Searching for sources for " + dependency.getId() + ":" + dependency.getClassifier() +
971 " at " + dependency.getId() + ":" + classifier );
972 }
973
974 if ( !unavailableArtifactsCache.containsKey( dependency.getId() + ":" + classifier ) )
975 {
976 Artifact artifact =
977 IdeUtils.resolveArtifactWithClassifier( dependency.getGroupId(), dependency.getArtifactId(),
978 dependency.getVersion(), classifier, localRepository,
979 artifactResolver,
980 artifactFactory, remoteRepos, getLog() );
981 if ( artifact.isResolved() )
982 {
983 if ( "sources".equals( classifier ) )
984 {
985 dependency.setSourceAttachment( artifact.getFile() );
986 }
987 else if ( "javadoc".equals( classifier ) )
988 {
989 dependency.setJavadocAttachment( artifact.getFile() );
990 }
991 }
992 else
993 {
994 unavailableArtifactsCache.put( dependency.getId() + ":" + classifier, Boolean.TRUE.toString() );
995
996
997
998 missingClassifierDependencies.add( dependency );
999 }
1000 }
1001 }
1002
1003
1004
1005 return missingClassifierDependencies;
1006
1007 }
1008
1009
1010
1011
1012 private void reportMissingArtifacts()
1013 {
1014 StringBuffer msg = new StringBuffer();
1015
1016 if ( !missingSourceDependencies.isEmpty() )
1017 {
1018 if ( getDownloadSources() )
1019 {
1020 msg.append( Messages.getString( "sourcesnotavailable" ) );
1021 }
1022 else
1023 {
1024 msg.append( Messages.getString( "sourcesnotdownloaded" ) );
1025 }
1026
1027 for ( Iterator it = missingSourceDependencies.iterator(); it.hasNext(); )
1028 {
1029 IdeDependency art = (IdeDependency) it.next();
1030 msg.append( Messages.getString( "sourcesmissingitem", art.getId() ) );
1031 }
1032 msg.append( "\n" );
1033 }
1034
1035 if ( !missingJavadocDependencies.isEmpty() )
1036 {
1037 if ( getDownloadJavadocs() )
1038 {
1039 msg.append( Messages.getString( "javadocnotavailable" ) );
1040 }
1041 else
1042 {
1043 msg.append( Messages.getString( "javadocnotdownloaded" ) );
1044 }
1045
1046 for ( Iterator it = missingJavadocDependencies.iterator(); it.hasNext(); )
1047 {
1048 IdeDependency art = (IdeDependency) it.next();
1049 msg.append( Messages.getString( "javadocmissingitem", art.getId() ) );
1050 }
1051 msg.append( "\n" );
1052 }
1053 getLog().info( msg );
1054 }
1055 }