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