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 protected Logger logger;
205
206
207
208
209
210
211 public ArtifactMetadataSource getArtifactMetadataSource()
212 {
213 return 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 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 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 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 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 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 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 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 downloadJavadocs;
374 }
375
376
377
378
379
380
381 public void setDownloadJavadocs( boolean downloadJavadoc )
382 {
383 downloadJavadocs = downloadJavadoc;
384 }
385
386
387
388
389
390
391 public boolean getDownloadSources()
392 {
393 return 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
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[] {
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[] {
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
614
615
616
617
618
619
620
621
622
623
624
625
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
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
676 if ( !dependencies.contains( dep ) )
677 {
678 dependencies.add( dep );
679 }
680 }
681
682 }
683
684
685
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
702
703
704
705
706 abstract public String getProjectNameForArifact( Artifact artifact );
707
708
709
710
711
712
713
714
715 private Set getProjectArtifacts()
716 throws MojoExecutionException
717 {
718
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[] {
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() );
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
784
785
786
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
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[] {
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
864
865
866
867
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
883
884
885
886
887
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
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
950
951
952
953
954
955
956
957
958 private List resolveDependenciesWithClassifier( IdeDependency[] deps, String inClassifier,
959 boolean includeRemoteRepositories,
960 Properties unavailableArtifactsCache )
961 {
962 List missingClassifierDependencies = new ArrayList();
963
964
965
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
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
1011
1012
1013 missingClassifierDependencies.add( dependency );
1014 }
1015 }
1016 }
1017
1018
1019
1020 return missingClassifierDependencies;
1021
1022 }
1023
1024
1025
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" ) );
1036 }
1037 else
1038 {
1039 msg.append( Messages.getString( "sourcesnotdownloaded" ) );
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() ) );
1046 }
1047 msg.append( "\n" );
1048 }
1049
1050 if ( !missingJavadocDependencies.isEmpty() )
1051 {
1052 if ( getDownloadJavadocs() )
1053 {
1054 msg.append( Messages.getString( "javadocnotavailable" ) );
1055 }
1056 else
1057 {
1058 msg.append( Messages.getString( "javadocnotdownloaded" ) );
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() ) );
1065 }
1066 msg.append( "\n" );
1067 }
1068 getLog().info( msg );
1069 }
1070
1071
1072
1073
1074
1075 public abstract List getExcludes();
1076
1077
1078
1079
1080
1081
1082
1083 protected boolean hasToResolveJar( Artifact art )
1084 {
1085 return !( getUseProjectReferences() && isAvailableAsAReactorProject( art ) );
1086 }
1087
1088
1089
1090
1091
1092
1093
1094 protected boolean useProjectReference( Artifact art )
1095 {
1096 return getUseProjectReferences() && isAvailableAsAReactorProject( art );
1097 }
1098 }