1 package org.apache.maven.plugins.shade.mojo;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.artifact.Artifact;
23 import org.apache.maven.artifact.repository.ArtifactRepository;
24 import org.apache.maven.execution.MavenSession;
25 import org.apache.maven.model.Dependency;
26 import org.apache.maven.model.Exclusion;
27 import org.apache.maven.model.Model;
28 import org.apache.maven.plugin.AbstractMojo;
29 import org.apache.maven.plugin.MojoExecutionException;
30 import org.apache.maven.plugins.annotations.Component;
31 import org.apache.maven.plugins.annotations.LifecyclePhase;
32 import org.apache.maven.plugins.annotations.Mojo;
33 import org.apache.maven.plugins.annotations.Parameter;
34 import org.apache.maven.plugins.annotations.ResolutionScope;
35 import org.apache.maven.plugins.shade.ShadeRequest;
36 import org.apache.maven.plugins.shade.Shader;
37 import org.apache.maven.plugins.shade.filter.Filter;
38 import org.apache.maven.plugins.shade.filter.MinijarFilter;
39 import org.apache.maven.plugins.shade.filter.SimpleFilter;
40 import org.apache.maven.plugins.shade.pom.PomWriter;
41 import org.apache.maven.plugins.shade.relocation.Relocator;
42 import org.apache.maven.plugins.shade.relocation.SimpleRelocator;
43 import org.apache.maven.plugins.shade.resource.ManifestResourceTransformer;
44 import org.apache.maven.plugins.shade.resource.ResourceTransformer;
45 import org.apache.maven.project.DefaultProjectBuildingRequest;
46 import org.apache.maven.project.MavenProject;
47 import org.apache.maven.project.MavenProjectHelper;
48 import org.apache.maven.project.ProjectBuilder;
49 import org.apache.maven.project.ProjectBuildingException;
50 import org.apache.maven.project.ProjectBuildingRequest;
51 import org.apache.maven.project.ProjectBuildingResult;
52 import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
53 import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
54 import org.apache.maven.shared.dependency.graph.DependencyNode;
55 import org.apache.maven.shared.transfer.artifact.DefaultArtifactCoordinate;
56 import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolver;
57 import org.apache.maven.shared.transfer.artifact.resolve.ArtifactResolverException;
58 import org.codehaus.plexus.util.IOUtil;
59 import org.codehaus.plexus.util.WriterFactory;
60
61 import java.io.File;
62 import java.io.FileInputStream;
63 import java.io.FileOutputStream;
64 import java.io.IOException;
65 import java.io.InputStream;
66 import java.io.OutputStream;
67 import java.io.Writer;
68 import java.util.ArrayList;
69 import java.util.Arrays;
70 import java.util.Collections;
71 import java.util.HashMap;
72 import java.util.HashSet;
73 import java.util.LinkedHashSet;
74 import java.util.List;
75 import java.util.Map;
76 import java.util.Set;
77
78 import javax.inject.Inject;
79 import static org.apache.maven.plugins.shade.resource.UseDependencyReducedPom.createPomReplaceTransformers;
80
81
82
83
84
85
86
87
88
89
90 @Mojo( name = "shade", defaultPhase = LifecyclePhase.PACKAGE, threadSafe = true, requiresDependencyResolution = ResolutionScope.RUNTIME )
91
92 public class ShadeMojo
93 extends AbstractMojo
94 {
95
96
97
98 @Parameter( defaultValue = "${session}", readonly = true, required = true )
99 private MavenSession session;
100
101
102
103
104 @Parameter( defaultValue = "${project}", readonly = true, required = true )
105 private MavenProject project;
106
107 @Component
108 private MavenProjectHelper projectHelper;
109
110 @Component( hint = "default", role = org.apache.maven.plugins.shade.Shader.class )
111 private Shader shader;
112
113
114
115
116 @Component
117 private DependencyGraphBuilder dependencyGraphBuilder;
118
119
120
121
122 @Component
123 private ProjectBuilder projectBuilder;
124
125
126
127
128 @Parameter( readonly = true, required = true, defaultValue = "${project.remoteArtifactRepositories}" )
129 protected List<ArtifactRepository> remoteArtifactRepositories;
130
131
132
133
134 @Parameter( readonly = true, required = true, defaultValue = "${localRepository}" )
135 protected ArtifactRepository localRepository;
136
137
138
139
140 @Component
141 protected ArtifactResolver artifactResolver;
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162 @Parameter
163 private ArtifactSet artifactSet;
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185 @SuppressWarnings( "MismatchedReadAndWriteOfArray" )
186 @Parameter
187 private PackageRelocation[] relocations;
188
189
190
191
192
193 @Parameter
194 private ResourceTransformer[] transformers;
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218 @SuppressWarnings( "MismatchedReadAndWriteOfArray" )
219 @Parameter
220 private ArchiveFilter[] filters;
221
222
223
224
225 @Parameter( defaultValue = "${project.build.directory}" )
226 private File outputDirectory;
227
228
229
230
231
232
233
234
235 @Parameter
236 private String finalName;
237
238
239
240
241
242
243 @Parameter( defaultValue = "${project.artifactId}" )
244 private String shadedArtifactId;
245
246
247
248
249 @Parameter
250 private String shadedGroupFilter;
251
252
253
254
255
256 @Parameter
257 private boolean shadedArtifactAttached;
258
259
260
261
262
263
264
265
266 @Parameter( defaultValue = "true" )
267 private boolean createDependencyReducedPom;
268
269
270
271
272
273
274
275
276 @Parameter( defaultValue = "${basedir}/dependency-reduced-pom.xml" )
277 private File dependencyReducedPomLocation;
278
279
280
281
282
283
284
285
286 @Parameter( defaultValue = "false" )
287 private boolean generateUniqueDependencyReducedPom;
288
289
290
291
292
293
294
295 @Parameter( defaultValue = "false" )
296 private boolean useDependencyReducedPomInJar;
297
298
299
300
301 @Parameter
302 private boolean keepDependenciesWithProvidedScope;
303
304
305
306
307
308 @Parameter
309 private boolean promoteTransitiveDependencies;
310
311
312
313
314 @Parameter( defaultValue = "shaded" )
315 private String shadedClassifierName;
316
317
318
319
320 @Parameter
321 private boolean createSourcesJar;
322
323
324
325
326 @Parameter
327 private boolean createTestSourcesJar;
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344 @Parameter( property = "shadeSourcesContent", defaultValue = "false" )
345 private boolean shadeSourcesContent;
346
347
348
349
350
351
352
353 @Parameter
354 private boolean minimizeJar;
355
356
357
358
359
360
361
362
363
364 @Parameter
365 private File outputFile;
366
367
368
369
370
371
372 @Parameter
373 private String shaderHint;
374
375
376
377
378
379
380
381
382
383 @Parameter( defaultValue = "false" )
384 private boolean useBaseVersion;
385
386
387
388
389 @Parameter( defaultValue = "false" )
390 private boolean shadeTestJar;
391
392
393
394
395 @Inject
396 private Map<String, Shader> shaders;
397
398
399
400
401
402 @Parameter( defaultValue = "false" )
403 private boolean skip;
404
405
406
407
408 public void execute()
409 throws MojoExecutionException
410 {
411 if ( skip )
412 {
413 getLog().info( "Shading has been skipped." );
414 return;
415 }
416
417 setupHintedShader();
418
419 Set<File> artifacts = new LinkedHashSet<>();
420 Set<String> artifactIds = new LinkedHashSet<>();
421 Set<File> sourceArtifacts = new LinkedHashSet<>();
422 Set<File> testArtifacts = new LinkedHashSet<>();
423 Set<File> testSourceArtifacts = new LinkedHashSet<>();
424
425 ArtifactSelector artifactSelector =
426 new ArtifactSelector( project.getArtifact(), artifactSet, shadedGroupFilter );
427
428 if ( artifactSelector.isSelected( project.getArtifact() ) && !"pom".equals( project.getArtifact().getType() ) )
429 {
430 if ( invalidMainArtifact() )
431 {
432 createErrorOutput();
433 throw new MojoExecutionException( "Failed to create shaded artifact, "
434 + "project main artifact does not exist." );
435 }
436
437 artifacts.add( project.getArtifact().getFile() );
438
439 if ( createSourcesJar )
440 {
441 File file = shadedSourcesArtifactFile();
442 if ( file.isFile() )
443 {
444 sourceArtifacts.add( file );
445 }
446 }
447
448 if ( shadeTestJar )
449 {
450 File file = shadedTestArtifactFile();
451 if ( file.isFile() )
452 {
453 testArtifacts.add( file );
454 }
455 }
456
457 if ( createTestSourcesJar )
458 {
459 File file = shadedTestSourcesArtifactFile();
460 if ( file.isFile() )
461 {
462 testSourceArtifacts.add( file );
463 }
464 }
465 }
466
467 processArtifactSelectors( artifacts, artifactIds, sourceArtifacts, testArtifacts, testSourceArtifacts,
468 artifactSelector );
469
470 File outputJar = ( outputFile != null ) ? outputFile : shadedArtifactFileWithClassifier();
471 File sourcesJar = shadedSourceArtifactFileWithClassifier();
472 File testJar = shadedTestArtifactFileWithClassifier();
473 File testSourcesJar = shadedTestSourceArtifactFileWithClassifier();
474
475
476 try
477 {
478 List<Filter> filters = getFilters();
479
480 List<Relocator> relocators = getRelocators();
481
482 List<ResourceTransformer> resourceTransformers = getResourceTransformers();
483
484 if ( createDependencyReducedPom )
485 {
486 createDependencyReducedPom( artifactIds );
487
488 if ( useDependencyReducedPomInJar )
489 {
490
491 resourceTransformers = new ArrayList<>( resourceTransformers );
492 resourceTransformers.addAll(
493 createPomReplaceTransformers( project, dependencyReducedPomLocation ) );
494 }
495 }
496
497 ShadeRequest shadeRequest = shadeRequest( "jar", artifacts, outputJar, filters, relocators,
498 resourceTransformers );
499
500 shader.shade( shadeRequest );
501
502 if ( createSourcesJar )
503 {
504 ShadeRequest shadeSourcesRequest =
505 createShadeSourcesRequest( "sources-jar", sourceArtifacts, sourcesJar, filters, relocators,
506 resourceTransformers );
507
508 shader.shade( shadeSourcesRequest );
509 }
510
511 if ( shadeTestJar )
512 {
513 ShadeRequest shadeTestRequest =
514 shadeRequest( "test-jar", testArtifacts, testJar, filters, relocators, resourceTransformers );
515
516 shader.shade( shadeTestRequest );
517 }
518
519 if ( createTestSourcesJar )
520 {
521 ShadeRequest shadeTestSourcesRequest =
522 createShadeSourcesRequest( "test-sources-jar", testSourceArtifacts, testSourcesJar, filters,
523 relocators, resourceTransformers );
524
525 shader.shade( shadeTestSourcesRequest );
526 }
527
528 if ( outputFile == null )
529 {
530 boolean renamed = false;
531
532
533
534
535 if ( finalName != null && finalName.length() > 0
536 && !finalName.equals( project.getBuild().getFinalName() ) )
537 {
538 String finalFileName = finalName + "." + project.getArtifact().getArtifactHandler().getExtension();
539 File finalFile = new File( outputDirectory, finalFileName );
540 replaceFile( finalFile, outputJar );
541 outputJar = finalFile;
542
543
544 if ( createSourcesJar )
545 {
546 finalFileName = finalName + "-sources.jar";
547 finalFile = new File( outputDirectory, finalFileName );
548 replaceFile( finalFile, sourcesJar );
549 sourcesJar = finalFile;
550 }
551
552
553 if ( shadeTestJar )
554 {
555 finalFileName = finalName + "-tests.jar";
556 finalFile = new File( outputDirectory, finalFileName );
557 replaceFile( finalFile, testJar );
558 testJar = finalFile;
559 }
560
561 if ( createTestSourcesJar )
562 {
563 finalFileName = finalName + "-test-sources.jar";
564 finalFile = new File( outputDirectory, finalFileName );
565 replaceFile( finalFile, testSourcesJar );
566 testSourcesJar = finalFile;
567 }
568
569 renamed = true;
570 }
571
572 if ( shadedArtifactAttached )
573 {
574 getLog().info( "Attaching shaded artifact." );
575 projectHelper.attachArtifact( project, project.getArtifact().getType(), shadedClassifierName,
576 outputJar );
577 if ( createSourcesJar )
578 {
579 projectHelper.attachArtifact( project, "java-source", shadedClassifierName + "-sources",
580 sourcesJar );
581 }
582
583 if ( shadeTestJar )
584 {
585 projectHelper.attachArtifact( project, "test-jar", shadedClassifierName + "-tests",
586 testJar );
587 }
588
589 if ( createTestSourcesJar )
590 {
591 projectHelper.attachArtifact( project, "java-source",
592 shadedClassifierName + "-test-sources", testSourcesJar );
593 }
594 }
595 else if ( !renamed )
596 {
597 getLog().info( "Replacing original artifact with shaded artifact." );
598 File originalArtifact = project.getArtifact().getFile();
599 if ( originalArtifact != null )
600 {
601 replaceFile( originalArtifact, outputJar );
602
603 if ( createSourcesJar )
604 {
605 getLog().info( "Replacing original source artifact with shaded source artifact." );
606 File shadedSources = shadedSourcesArtifactFile();
607
608 replaceFile( shadedSources, sourcesJar );
609
610 projectHelper.attachArtifact( project, "java-source", "sources", shadedSources );
611 }
612
613 if ( shadeTestJar )
614 {
615 getLog().info( "Replacing original test artifact with shaded test artifact." );
616 File shadedTests = shadedTestArtifactFile();
617
618 replaceFile( shadedTests, testJar );
619
620 projectHelper.attachArtifact( project, "test-jar", shadedTests );
621 }
622
623 if ( createTestSourcesJar )
624 {
625 getLog().info( "Replacing original test source artifact "
626 + "with shaded test source artifact." );
627 File shadedTestSources = shadedTestSourcesArtifactFile();
628
629 replaceFile( shadedTestSources, testSourcesJar );
630
631 projectHelper.attachArtifact( project, "java-source", "test-sources",
632 shadedTestSources );
633 }
634 }
635 }
636 }
637 }
638 catch ( Exception e )
639 {
640 throw new MojoExecutionException( "Error creating shaded jar: " + e.getMessage(), e );
641 }
642 }
643
644 private void createErrorOutput()
645 {
646 getLog().error( "The project main artifact does not exist. This could have the following" );
647 getLog().error( "reasons:" );
648 getLog().error( "- You have invoked the goal directly from the command line. This is not" );
649 getLog().error( " supported. Please add the goal to the default lifecycle via an" );
650 getLog().error( " <execution> element in your POM and use \"mvn package\" to have it run." );
651 getLog().error( "- You have bound the goal to a lifecycle phase before \"package\". Please" );
652 getLog().error( " remove this binding from your POM such that the goal will be run in" );
653 getLog().error( " the proper phase." );
654 getLog().error( "- You removed the configuration of the maven-jar-plugin that produces the main artifact." );
655 }
656
657 private ShadeRequest shadeRequest( String shade, Set<File> artifacts, File outputJar, List<Filter> filters,
658 List<Relocator> relocators, List<ResourceTransformer> resourceTransformers )
659 {
660 ShadeRequest shadeRequest = new ShadeRequest();
661 shadeRequest.setJars( artifacts );
662 shadeRequest.setUberJar( outputJar );
663 shadeRequest.setFilters( filters );
664 shadeRequest.setRelocators( relocators );
665 shadeRequest.setResourceTransformers( toResourceTransformers( shade, resourceTransformers ) );
666 return shadeRequest;
667 }
668
669 private ShadeRequest createShadeSourcesRequest( String shade, Set<File> testArtifacts, File testJar,
670 List<Filter> filters, List<Relocator> relocators,
671 List<ResourceTransformer> resourceTransformers )
672 {
673 ShadeRequest shadeSourcesRequest =
674 shadeRequest( shade, testArtifacts, testJar, filters, relocators, resourceTransformers );
675 shadeSourcesRequest.setShadeSourcesContent( shadeSourcesContent );
676 return shadeSourcesRequest;
677 }
678
679 private void setupHintedShader()
680 throws MojoExecutionException
681 {
682 if ( shaderHint != null )
683 {
684 shader = shaders.get( shaderHint );
685
686 if ( shader == null )
687 {
688 throw new MojoExecutionException(
689 "unable to lookup own Shader implementation with hint: '" + shaderHint + "'"
690 );
691 }
692 }
693 }
694
695 private void processArtifactSelectors( Set<File> artifacts, Set<String> artifactIds, Set<File> sourceArtifacts,
696 Set<File> testArtifacts, Set<File> testSourceArtifacts,
697 ArtifactSelector artifactSelector )
698 {
699
700 List<String> excludedArtifacts = new ArrayList<>();
701 List<String> pomArtifacts = new ArrayList<>();
702 List<String> emptySourceArtifacts = new ArrayList<>();
703 List<String> emptyTestArtifacts = new ArrayList<>();
704 List<String> emptyTestSourceArtifacts = new ArrayList<>();
705
706 for ( Artifact artifact : project.getArtifacts() )
707 {
708 if ( !artifactSelector.isSelected( artifact ) )
709 {
710 excludedArtifacts.add( artifact.getId() );
711
712 continue;
713 }
714
715 if ( "pom".equals( artifact.getType() ) )
716 {
717 pomArtifacts.add( artifact.getId() );
718 continue;
719 }
720
721 getLog().info( "Including " + artifact.getId() + " in the shaded jar." );
722
723 artifacts.add( artifact.getFile() );
724 artifactIds.add( getId( artifact ) );
725
726 if ( createSourcesJar )
727 {
728 File file = resolveArtifactForClassifier( artifact, "sources" );
729 if ( file != null )
730 {
731 if ( file.length() > 0 )
732 {
733 sourceArtifacts.add( file );
734 }
735 else
736 {
737 emptySourceArtifacts.add( artifact.getArtifactId() );
738 }
739 }
740 }
741
742 if ( shadeTestJar )
743 {
744 File file = resolveArtifactForClassifier( artifact, "tests" );
745 if ( file != null )
746 {
747 if ( file.length() > 0 )
748 {
749 testArtifacts.add( file );
750 }
751 else
752 {
753 emptyTestArtifacts.add( artifact.getId() );
754 }
755 }
756 }
757
758 if ( createTestSourcesJar )
759 {
760 File file = resolveArtifactForClassifier( artifact, "test-sources" );
761 if ( file != null )
762 {
763 testSourceArtifacts.add( file );
764 }
765 else
766 {
767 emptyTestSourceArtifacts.add( artifact.getId() );
768 }
769 }
770 }
771
772 for ( String artifactId : excludedArtifacts )
773 {
774 getLog().info( "Excluding " + artifactId + " from the shaded jar." );
775 }
776 for ( String artifactId : pomArtifacts )
777 {
778 getLog().info( "Skipping pom dependency " + artifactId + " in the shaded jar." );
779 }
780 for ( String artifactId : emptySourceArtifacts )
781 {
782 getLog().warn( "Skipping empty source jar " + artifactId + "." );
783 }
784 for ( String artifactId : emptyTestArtifacts )
785 {
786 getLog().warn( "Skipping empty test jar " + artifactId + "." );
787 }
788 for ( String artifactId : emptyTestArtifacts )
789 {
790 getLog().warn( "Skipping empty test source jar " + artifactId + "." );
791 }
792 }
793
794 private boolean invalidMainArtifact()
795 {
796 return project.getArtifact().getFile() == null || !project.getArtifact().getFile().isFile();
797 }
798
799 private void replaceFile( File oldFile, File newFile )
800 throws MojoExecutionException
801 {
802 getLog().info( "Replacing " + oldFile + " with " + newFile );
803
804 File origFile = new File( outputDirectory, "original-" + oldFile.getName() );
805 if ( oldFile.exists() && !oldFile.renameTo( origFile ) )
806 {
807
808 System.gc();
809 System.gc();
810
811 if ( !oldFile.renameTo( origFile ) )
812 {
813
814 try
815 {
816 copyFiles( oldFile, origFile );
817 }
818 catch ( IOException ex )
819 {
820
821 getLog().warn( ex );
822 }
823 }
824 }
825 if ( !newFile.renameTo( oldFile ) )
826 {
827
828 System.gc();
829 System.gc();
830
831 if ( !newFile.renameTo( oldFile ) )
832 {
833
834 try
835 {
836 copyFiles( newFile, oldFile );
837 }
838 catch ( IOException ex )
839 {
840 throw new MojoExecutionException( "Could not replace original artifact with shaded artifact!", ex );
841 }
842 }
843 }
844 }
845
846 private void copyFiles( File source, File target )
847 throws IOException
848 {
849 try ( InputStream in = new FileInputStream( source );
850 OutputStream out = new FileOutputStream( target ) )
851 {
852 IOUtil.copy( in, out );
853 }
854 }
855
856 private File resolveArtifactForClassifier( Artifact artifact, String classifier )
857 {
858 DefaultArtifactCoordinate coordinate = new DefaultArtifactCoordinate();
859 coordinate.setGroupId( artifact.getGroupId() );
860 coordinate.setArtifactId( artifact.getArtifactId() );
861 coordinate.setVersion( artifact.getVersion() );
862 coordinate.setExtension( "jar" );
863 coordinate.setClassifier( classifier );
864
865 Artifact resolvedArtifact;
866 try
867 {
868 resolvedArtifact =
869 artifactResolver.resolveArtifact( session.getProjectBuildingRequest(), coordinate ).getArtifact();
870 }
871 catch ( ArtifactResolverException e )
872 {
873 getLog().warn( "Could not get " + classifier + " for " + artifact );
874 return null;
875 }
876
877 if ( resolvedArtifact.isResolved() )
878 {
879 return resolvedArtifact.getFile();
880 }
881 return null;
882 }
883
884 private List<Relocator> getRelocators()
885 {
886 List<Relocator> relocators = new ArrayList<>();
887
888 if ( relocations == null )
889 {
890 return relocators;
891 }
892
893 for ( PackageRelocation r : relocations )
894 {
895 relocators.add( new SimpleRelocator( r.getPattern(), r.getShadedPattern(), r.getIncludes(), r.getExcludes(),
896 r.isRawString() ) );
897 }
898
899 return relocators;
900 }
901
902 private List<ResourceTransformer> getResourceTransformers()
903 {
904 if ( transformers == null )
905 {
906 return Collections.emptyList();
907 }
908
909 return Arrays.asList( transformers );
910 }
911
912 private List<Filter> getFilters()
913 throws MojoExecutionException
914 {
915 List<Filter> filters = new ArrayList<>();
916 List<SimpleFilter> simpleFilters = new ArrayList<>();
917
918 if ( this.filters != null && this.filters.length > 0 )
919 {
920 Map<Artifact, ArtifactId> artifacts = new HashMap<>();
921
922 artifacts.put( project.getArtifact(), new ArtifactId( project.getArtifact() ) );
923
924 for ( Artifact artifact : project.getArtifacts() )
925 {
926 artifacts.put( artifact, new ArtifactId( artifact ) );
927 }
928
929 for ( ArchiveFilter filter : this.filters )
930 {
931 ArtifactId pattern = new ArtifactId( filter.getArtifact() );
932
933 Set<File> jars = new HashSet<>();
934
935 for ( Map.Entry<Artifact, ArtifactId> entry : artifacts.entrySet() )
936 {
937 if ( entry.getValue().matches( pattern ) )
938 {
939 Artifact artifact = entry.getKey();
940
941 jars.add( artifact.getFile() );
942
943 if ( createSourcesJar )
944 {
945 File file = resolveArtifactForClassifier( artifact, "sources" );
946 if ( file != null )
947 {
948 jars.add( file );
949 }
950 }
951
952 if ( shadeTestJar )
953 {
954 File file = resolveArtifactForClassifier( artifact, "tests" );
955 if ( file != null )
956 {
957 jars.add( file );
958 }
959 }
960 }
961 }
962
963 if ( jars.isEmpty() )
964 {
965 getLog().info( "No artifact matching filter " + filter.getArtifact() );
966
967 continue;
968 }
969
970 simpleFilters.add( new SimpleFilter( jars, filter ) );
971 }
972 }
973
974 filters.addAll( simpleFilters );
975
976 if ( minimizeJar )
977 {
978 getLog().info( "Minimizing jar " + project.getArtifact() );
979
980 try
981 {
982 filters.add( new MinijarFilter( project, getLog(), simpleFilters ) );
983 }
984 catch ( IOException e )
985 {
986 throw new MojoExecutionException( "Failed to analyze class dependencies", e );
987 }
988 }
989
990 return filters;
991 }
992
993 private File shadedArtifactFileWithClassifier()
994 {
995 Artifact artifact = project.getArtifact();
996 final String shadedName = shadedArtifactId + "-" + artifact.getVersion() + "-" + shadedClassifierName + "."
997 + artifact.getArtifactHandler().getExtension();
998 return new File( outputDirectory, shadedName );
999 }
1000
1001 private File shadedSourceArtifactFileWithClassifier()
1002 {
1003 return shadedArtifactFileWithClassifier( "sources" );
1004 }
1005
1006 private File shadedTestSourceArtifactFileWithClassifier()
1007 {
1008 return shadedArtifactFileWithClassifier( "test-sources" );
1009 }
1010
1011 private File shadedArtifactFileWithClassifier( String classifier )
1012 {
1013 Artifact artifact = project.getArtifact();
1014 final String shadedName = shadedArtifactId + "-" + artifact.getVersion() + "-" + shadedClassifierName
1015 + "-" + classifier + "." + artifact.getArtifactHandler().getExtension();
1016 return new File( outputDirectory, shadedName );
1017 }
1018
1019 private File shadedTestArtifactFileWithClassifier()
1020 {
1021 return shadedArtifactFileWithClassifier( "tests" );
1022 }
1023
1024 private File shadedSourcesArtifactFile()
1025 {
1026 return shadedArtifactFile( "sources" );
1027 }
1028
1029 private File shadedTestSourcesArtifactFile()
1030 {
1031 return shadedArtifactFile( "test-sources" );
1032 }
1033
1034 private File shadedArtifactFile( String classifier )
1035 {
1036 Artifact artifact = project.getArtifact();
1037
1038 String shadedName;
1039
1040 if ( project.getBuild().getFinalName() != null )
1041 {
1042 shadedName = project.getBuild().getFinalName() + "-" + classifier + "."
1043 + artifact.getArtifactHandler().getExtension();
1044 }
1045 else
1046 {
1047 shadedName = shadedArtifactId + "-" + artifact.getVersion() + "-" + classifier + "."
1048 + artifact.getArtifactHandler().getExtension();
1049 }
1050
1051 return new File( outputDirectory, shadedName );
1052 }
1053
1054 private File shadedTestArtifactFile()
1055 {
1056 return shadedArtifactFile( "tests" );
1057 }
1058
1059
1060
1061 private void createDependencyReducedPom( Set<String> artifactsToRemove )
1062 throws IOException, DependencyGraphBuilderException, ProjectBuildingException
1063 {
1064 List<Dependency> transitiveDeps = new ArrayList<>();
1065
1066
1067
1068 for ( Artifact artifact : project.getArtifacts() )
1069 {
1070 if ( "pom".equals( artifact.getType() ) )
1071 {
1072
1073 continue;
1074 }
1075
1076
1077 Dependency dep = createDependency( artifact );
1078
1079
1080 transitiveDeps.add( dep );
1081 }
1082
1083 Model model = project.getOriginalModel();
1084
1085
1086
1087
1088 List<Dependency> origDeps = new ArrayList<>();
1089 List<Dependency> source = promoteTransitiveDependencies ? transitiveDeps : project.getDependencies();
1090 for ( Dependency d : source )
1091 {
1092 origDeps.add( d.clone() );
1093 }
1094 model = model.clone();
1095
1096
1097
1098
1099 List<Dependency> originalDependencies = model.getDependencies();
1100 removeSystemScopedDependencies( artifactsToRemove, originalDependencies );
1101
1102 List<Dependency> dependencies = new ArrayList<>();
1103 boolean modified = false;
1104 for ( Dependency d : origDeps )
1105 {
1106 if ( artifactsToRemove.contains( getId( d ) ) )
1107 {
1108 if ( keepDependenciesWithProvidedScope )
1109 {
1110 if ( !"provided".equals( d.getScope() ) )
1111 {
1112 modified = true;
1113 d.setScope( "provided" );
1114 }
1115 }
1116 else
1117 {
1118 modified = true;
1119 continue;
1120 }
1121 }
1122
1123 dependencies.add( d );
1124 }
1125
1126
1127 model.setArtifactId( shadedArtifactId );
1128
1129
1130
1131
1132 addSystemScopedDependencyFromNonInterpolatedPom( dependencies, originalDependencies );
1133
1134
1135 rewriteDependencyReducedPomIfWeHaveReduction( dependencies, modified, transitiveDeps, model );
1136 }
1137
1138 private void rewriteDependencyReducedPomIfWeHaveReduction( List<Dependency> dependencies, boolean modified,
1139 List<Dependency> transitiveDeps, Model model )
1140 throws IOException, ProjectBuildingException,
1141 DependencyGraphBuilderException
1142 {
1143 if ( modified )
1144 {
1145 for ( int loopCounter = 0; modified; loopCounter++ )
1146 {
1147
1148 model.setDependencies( dependencies );
1149
1150 if ( generateUniqueDependencyReducedPom )
1151 {
1152 dependencyReducedPomLocation =
1153 File.createTempFile( "dependency-reduced-pom-", ".xml", project.getBasedir() );
1154 project.getProperties().setProperty( "maven.shade.dependency-reduced-pom",
1155 dependencyReducedPomLocation.getAbsolutePath() );
1156 }
1157 else
1158 {
1159 if ( dependencyReducedPomLocation == null )
1160 {
1161
1162 dependencyReducedPomLocation = new File( project.getBasedir(), "dependency-reduced-pom.xml" );
1163 }
1164 }
1165
1166 File f = dependencyReducedPomLocation;
1167
1168
1169 if ( loopCounter == 0 )
1170 {
1171 getLog().info( "Dependency-reduced POM written at: " + f.getAbsolutePath() );
1172 }
1173
1174 if ( f.exists() )
1175 {
1176
1177 f.delete();
1178 }
1179
1180 Writer w = WriterFactory.newXmlWriter( f );
1181
1182 String replaceRelativePath = null;
1183 if ( model.getParent() != null )
1184 {
1185 replaceRelativePath = model.getParent().getRelativePath();
1186
1187 }
1188
1189 if ( model.getParent() != null )
1190 {
1191 File parentFile =
1192 new File( project.getBasedir(), model.getParent().getRelativePath() ).getCanonicalFile();
1193 if ( !parentFile.isFile() )
1194 {
1195 parentFile = new File( parentFile, "pom.xml" );
1196 }
1197
1198 parentFile = parentFile.getCanonicalFile();
1199
1200 String relPath = RelativizePath.convertToRelativePath( parentFile, f );
1201 model.getParent().setRelativePath( relPath );
1202 }
1203
1204 try
1205 {
1206 PomWriter.write( w, model, true );
1207 }
1208 finally
1209 {
1210 if ( model.getParent() != null )
1211 {
1212 model.getParent().setRelativePath( replaceRelativePath );
1213 }
1214 w.close();
1215 }
1216
1217 ProjectBuildingRequest projectBuildingRequest =
1218 new DefaultProjectBuildingRequest( session.getProjectBuildingRequest() );
1219 projectBuildingRequest.setLocalRepository( localRepository );
1220 projectBuildingRequest.setRemoteRepositories( remoteArtifactRepositories );
1221
1222 ProjectBuildingResult result = projectBuilder.build( f, projectBuildingRequest );
1223
1224 getLog().debug( "updateExcludesInDeps()" );
1225 modified = updateExcludesInDeps( result.getProject(), dependencies, transitiveDeps );
1226 }
1227
1228 project.setFile( dependencyReducedPomLocation );
1229 }
1230 }
1231
1232 private void removeSystemScopedDependencies( Set<String> artifactsToRemove, List<Dependency> originalDependencies )
1233 {
1234 for ( Dependency dependency : originalDependencies )
1235 {
1236 if ( dependency.getScope() != null && dependency.getScope().equalsIgnoreCase( "system" ) )
1237 {
1238 artifactsToRemove.add( getId( dependency ) );
1239 }
1240 }
1241 }
1242
1243 private void addSystemScopedDependencyFromNonInterpolatedPom( List<Dependency> dependencies,
1244 List<Dependency> originalDependencies )
1245 {
1246 for ( Dependency dependency : originalDependencies )
1247 {
1248 if ( dependency.getScope() != null && dependency.getScope().equalsIgnoreCase( "system" ) )
1249 {
1250 dependencies.add( dependency );
1251 }
1252 }
1253 }
1254
1255 private Dependency createDependency( Artifact artifact )
1256 {
1257 Dependency dep = new Dependency();
1258 dep.setArtifactId( artifact.getArtifactId() );
1259 if ( artifact.hasClassifier() )
1260 {
1261 dep.setClassifier( artifact.getClassifier() );
1262 }
1263 dep.setGroupId( artifact.getGroupId() );
1264 dep.setOptional( artifact.isOptional() );
1265 dep.setScope( artifact.getScope() );
1266 dep.setType( artifact.getType() );
1267 if ( useBaseVersion )
1268 {
1269 dep.setVersion( artifact.getBaseVersion() );
1270 }
1271 else
1272 {
1273 dep.setVersion( artifact.getVersion() );
1274 }
1275 return dep;
1276 }
1277
1278 private String getId( Artifact artifact )
1279 {
1280 return getId( artifact.getGroupId(), artifact.getArtifactId(), artifact.getType(), artifact.getClassifier() );
1281 }
1282
1283 private String getId( Dependency dependency )
1284 {
1285 return getId( dependency.getGroupId(), dependency.getArtifactId(), dependency.getType(),
1286 dependency.getClassifier() );
1287 }
1288
1289 private String getId( String groupId, String artifactId, String type, String classifier )
1290 {
1291 return groupId + ":" + artifactId + ":" + type + ":" + ( ( classifier != null ) ? classifier : "" );
1292 }
1293
1294 public boolean updateExcludesInDeps( MavenProject project, List<Dependency> dependencies,
1295 List<Dependency> transitiveDeps )
1296 throws DependencyGraphBuilderException
1297 {
1298 MavenProject original = session.getProjectBuildingRequest().getProject();
1299 try
1300 {
1301 session.getProjectBuildingRequest().setProject( project );
1302 DependencyNode node = dependencyGraphBuilder
1303 .buildDependencyGraph( session.getProjectBuildingRequest(), null );
1304 boolean modified = false;
1305 for ( DependencyNode n2 : node.getChildren() )
1306 {
1307 String artifactId2 = getId( n2.getArtifact() );
1308
1309 for ( DependencyNode n3 : n2.getChildren() )
1310 {
1311 Artifact artifact3 = n3.getArtifact();
1312 String artifactId3 = getId( artifact3 );
1313
1314
1315
1316
1317
1318
1319
1320 boolean found = false;
1321 for ( Dependency dep : transitiveDeps )
1322 {
1323 if ( getId( dep ).equals( artifactId3 ) )
1324 {
1325 found = true;
1326 break;
1327 }
1328 }
1329
1330
1331
1332
1333
1334 if ( !found && !"provided".equals( artifact3.getScope() ) )
1335 {
1336 getLog().debug( String.format( "dependency %s (scope %s) not found in transitive dependencies",
1337 artifactId3, artifact3.getScope() ) );
1338 for ( Dependency dep : dependencies )
1339 {
1340 if ( getId( dep ).equals( artifactId2 ) )
1341 {
1342
1343
1344
1345
1346 if ( !dependencyHasExclusion( dep, artifact3 ) )
1347 {
1348 getLog().debug( String.format( "Adding exclusion for dependency %s (scope %s) "
1349 + "to %s (scope %s)",
1350 artifactId3, artifact3.getScope(),
1351 getId( dep ), dep.getScope() ) );
1352 Exclusion exclusion = new Exclusion();
1353 exclusion.setArtifactId( artifact3.getArtifactId() );
1354 exclusion.setGroupId( artifact3.getGroupId() );
1355 dep.addExclusion( exclusion );
1356 modified = true;
1357 break;
1358 }
1359 }
1360 }
1361 }
1362 }
1363 }
1364 return modified;
1365 }
1366 finally
1367 {
1368
1369 session.getProjectBuildingRequest().setProject( original );
1370 }
1371 }
1372
1373 private boolean dependencyHasExclusion( Dependency dep, Artifact exclusionToCheck )
1374 {
1375 boolean containsExclusion = false;
1376 for ( Exclusion existingExclusion : dep.getExclusions() )
1377 {
1378 if ( existingExclusion.getGroupId().equals( exclusionToCheck.getGroupId() )
1379 && existingExclusion.getArtifactId().equals( exclusionToCheck.getArtifactId() ) )
1380 {
1381 containsExclusion = true;
1382 break;
1383 }
1384 }
1385 return containsExclusion;
1386 }
1387
1388 private List<ResourceTransformer> toResourceTransformers(
1389 String shade, List<ResourceTransformer> resourceTransformers )
1390 {
1391 List<ResourceTransformer> forShade = new ArrayList<>();
1392 ManifestResourceTransformer lastMt = null;
1393 for ( ResourceTransformer transformer : resourceTransformers )
1394 {
1395 if ( !( transformer instanceof ManifestResourceTransformer ) )
1396 {
1397 forShade.add( transformer );
1398 }
1399 else if ( ( ( ManifestResourceTransformer ) transformer ) .isForShade( shade ) )
1400 {
1401 final ManifestResourceTransformer mt = (ManifestResourceTransformer) transformer;
1402 if ( mt.isUsedForDefaultShading() && lastMt != null && !lastMt.isUsedForDefaultShading() )
1403 {
1404 continue;
1405 }
1406 if ( !mt.isUsedForDefaultShading() && lastMt != null && lastMt.isUsedForDefaultShading() )
1407 {
1408 forShade.remove( lastMt );
1409 }
1410 else if ( !mt.isUsedForDefaultShading() && lastMt != null )
1411 {
1412 getLog().warn( "Ambiguous manifest transformer definition for '" + shade + "': "
1413 + mt + " / " + lastMt );
1414 }
1415 if ( lastMt == null || !mt.isUsedForDefaultShading() )
1416 {
1417 lastMt = mt;
1418 }
1419 forShade.add( transformer );
1420 }
1421 }
1422 return forShade;
1423 }
1424 }