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         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();
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 void 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<String> excludedArtifacts = new ArrayList<>();
729         List<String> pomArtifacts = new ArrayList<>();
730         List<String> emptySourceArtifacts = new ArrayList<>();
731         List<String> emptyTestArtifacts = new ArrayList<>();
732         List<String> 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         for (Artifact artifact : processedArtifacts) {
759             if (!artifactSelector.isSelected(artifact)) {
760                 excludedArtifacts.add(artifact.getId());
761 
762                 continue;
763             }
764 
765             if ("pom".equals(artifact.getType())) {
766                 pomArtifacts.add(artifact.getId());
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                 File file = resolveArtifactForClassifier(artifact, "sources");
777                 if (file != null) {
778                     if (file.length() > 0) {
779                         sourceArtifacts.add(file);
780                     } else {
781                         emptySourceArtifacts.add(artifact.getArtifactId());
782                     }
783                 }
784             }
785 
786             if (shadeTestJar) {
787                 File file = resolveArtifactForClassifier(artifact, "tests");
788                 if (file != null) {
789                     if (file.length() > 0) {
790                         testArtifacts.add(file);
791                     } else {
792                         emptyTestArtifacts.add(artifact.getId());
793                     }
794                 }
795             }
796 
797             if (createTestSourcesJar) {
798                 File file = resolveArtifactForClassifier(artifact, "test-sources");
799                 if (file != null) {
800                     testSourceArtifacts.add(file);
801                 } else {
802                     emptyTestSourceArtifacts.add(artifact.getId());
803                 }
804             }
805         }
806 
807         for (String artifactId : excludedArtifacts) {
808             getLog().debug("Excluding " + artifactId + " from the shaded jar.");
809         }
810         for (String artifactId : pomArtifacts) {
811             getLog().debug("Skipping pom dependency " + artifactId + " in the shaded jar.");
812         }
813         for (String artifactId : emptySourceArtifacts) {
814             getLog().warn("Skipping empty source jar " + artifactId + ".");
815         }
816         for (String artifactId : emptyTestArtifacts) {
817             getLog().warn("Skipping empty test jar " + artifactId + ".");
818         }
819         for (String artifactId : emptyTestSourceArtifacts) {
820             getLog().warn("Skipping empty test source jar " + artifactId + ".");
821         }
822     }
823 
824     private boolean invalidMainArtifact() {
825         return project.getArtifact().getFile() == null
826                 || !project.getArtifact().getFile().isFile();
827     }
828 
829     private void replaceFile(File oldFile, File newFile) throws MojoExecutionException {
830         getLog().debug("Replacing " + oldFile + " with " + newFile);
831 
832         File origFile = new File(outputDirectory, "original-" + oldFile.getName());
833         if (oldFile.exists() && !oldFile.renameTo(origFile)) {
834             
835             System.gc();
836             System.gc();
837 
838             if (!oldFile.renameTo(origFile)) {
839                 
840                 try {
841                     copyFiles(oldFile, origFile);
842                 } catch (IOException ex) {
843                     
844                     getLog().warn(ex);
845                 }
846             }
847         }
848         if (!newFile.renameTo(oldFile)) {
849             
850             System.gc();
851             System.gc();
852 
853             if (!newFile.renameTo(oldFile)) {
854                 
855                 try {
856                     copyFiles(newFile, oldFile);
857                 } catch (IOException ex) {
858                     throw new MojoExecutionException("Could not replace original artifact with shaded artifact!", ex);
859                 }
860             }
861         }
862     }
863 
864     private void copyFiles(File source, File target) throws IOException {
865         try (InputStream in = Files.newInputStream(source.toPath());
866                 OutputStream out = Files.newOutputStream(target.toPath())) {
867             IOUtil.copy(in, out);
868         }
869     }
870 
871     private File resolveArtifactForClassifier(Artifact artifact, String classifier) {
872         Artifact toResolve = new DefaultArtifact(
873                 artifact.getGroupId(),
874                 artifact.getArtifactId(),
875                 artifact.getVersionRange() == null
876                         ? VersionRange.createFromVersion(artifact.getVersion())
877                         : artifact.getVersionRange(),
878                 artifact.getScope(),
879                 artifact.getType(),
880                 classifier,
881                 artifact.getArtifactHandler(),
882                 artifact.isOptional());
883         try {
884             org.eclipse.aether.artifact.Artifact resolved = resolveArtifact(RepositoryUtils.toArtifact(toResolve));
885             if (resolved.getFile() != null) {
886                 return resolved.getFile();
887             }
888             return null;
889         } catch (ArtifactResolutionException e) {
890             getLog().warn("Could not get " + classifier + " for " + artifact);
891             return null;
892         }
893     }
894 
895     private org.eclipse.aether.artifact.Artifact resolveArtifact(org.eclipse.aether.artifact.Artifact artifact)
896             throws ArtifactResolutionException {
897         return repositorySystem
898                 .resolveArtifact(
899                         session.getRepositorySession(),
900                         new ArtifactRequest(artifact, project.getRemoteProjectRepositories(), "shade"))
901                 .getArtifact();
902     }
903 
904     private List<Relocator> getRelocators() {
905         List<Relocator> relocators = new ArrayList<>();
906 
907         if (relocations == null) {
908             return relocators;
909         }
910 
911         for (PackageRelocation r : relocations) {
912             relocators.add(new SimpleRelocator(
913                     r.getPattern(), r.getShadedPattern(), r.getIncludes(), r.getExcludes(), r.isRawString()));
914         }
915 
916         return relocators;
917     }
918 
919     private List<ResourceTransformer> getResourceTransformers() throws MojoExecutionException {
920         if (transformers == null) {
921             return Collections.emptyList();
922         }
923         for (ResourceTransformer transformer : transformers) {
924             if (transformer == null) {
925                 throw new MojoExecutionException(
926                         "Failed to create shaded artifact: parameter transformers contains null (double-check XML attribute)");
927             }
928         }
929         return Arrays.asList(transformers);
930     }
931 
932     private List<Filter> getFilters() throws MojoExecutionException {
933         List<Filter> filters = new ArrayList<>();
934         List<SimpleFilter> simpleFilters = new ArrayList<>();
935 
936         if (this.filters != null && this.filters.length > 0) {
937             Map<Artifact, ArtifactId> artifacts = new HashMap<>();
938 
939             artifacts.put(project.getArtifact(), new ArtifactId(project.getArtifact()));
940 
941             for (Artifact artifact : project.getArtifacts()) {
942                 artifacts.put(artifact, new ArtifactId(artifact));
943             }
944 
945             for (ArchiveFilter filter : this.filters) {
946                 ArtifactId pattern = new ArtifactId(filter.getArtifact());
947 
948                 Set<File> jars = new HashSet<>();
949 
950                 for (Map.Entry<Artifact, ArtifactId> entry : artifacts.entrySet()) {
951                     if (entry.getValue().matches(pattern)) {
952                         Artifact artifact = entry.getKey();
953 
954                         jars.add(artifact.getFile());
955 
956                         if (createSourcesJar) {
957                             File file = resolveArtifactForClassifier(artifact, "sources");
958                             if (file != null) {
959                                 jars.add(file);
960                             }
961                         }
962 
963                         if (shadeTestJar) {
964                             File file = resolveArtifactForClassifier(artifact, "tests");
965                             if (file != null) {
966                                 jars.add(file);
967                             }
968                         }
969                     }
970                 }
971 
972                 if (jars.isEmpty()) {
973                     getLog().debug("No artifact matching filter " + filter.getArtifact());
974 
975                     continue;
976                 }
977 
978                 simpleFilters.add(new SimpleFilter(jars, filter));
979             }
980         }
981 
982         filters.addAll(simpleFilters);
983 
984         if (minimizeJar) {
985             if (entryPoints == null) {
986                 entryPoints = new HashSet<>();
987             }
988             getLog().info("Minimizing jar " + project.getArtifact()
989                     + (entryPoints.isEmpty() ? "" : " with entry points"));
990 
991             try {
992                 filters.add(new MinijarFilter(project, getLog(), simpleFilters, entryPoints));
993             } catch (IOException e) {
994                 throw new MojoExecutionException("Failed to analyze class dependencies", e);
995             }
996         }
997 
998         return filters;
999     }
1000 
1001     private File shadedArtifactFileWithClassifier() {
1002         Artifact artifact = project.getArtifact();
1003         final String shadedName = shadedArtifactId + "-" + artifact.getVersion() + "-" + shadedClassifierName + "."
1004                 + artifact.getArtifactHandler().getExtension();
1005         return new File(outputDirectory, shadedName);
1006     }
1007 
1008     private File shadedSourceArtifactFileWithClassifier() {
1009         return shadedArtifactFileWithClassifier("sources");
1010     }
1011 
1012     private File shadedTestSourceArtifactFileWithClassifier() {
1013         return shadedArtifactFileWithClassifier("test-sources");
1014     }
1015 
1016     private File shadedArtifactFileWithClassifier(String classifier) {
1017         Artifact artifact = project.getArtifact();
1018         final String shadedName = shadedArtifactId + "-" + artifact.getVersion() + "-" + shadedClassifierName + "-"
1019                 + classifier + "." + artifact.getArtifactHandler().getExtension();
1020         return new File(outputDirectory, shadedName);
1021     }
1022 
1023     private File shadedTestArtifactFileWithClassifier() {
1024         return shadedArtifactFileWithClassifier("tests");
1025     }
1026 
1027     private File shadedSourcesArtifactFile() {
1028         return shadedArtifactFile("sources");
1029     }
1030 
1031     private File shadedTestSourcesArtifactFile() {
1032         return shadedArtifactFile("test-sources");
1033     }
1034 
1035     private File shadedArtifactFile(String classifier) {
1036         Artifact artifact = project.getArtifact();
1037 
1038         String shadedName;
1039 
1040         if (project.getBuild().getFinalName() != null) {
1041             shadedName = project.getBuild().getFinalName() + "-" + classifier + "."
1042                     + artifact.getArtifactHandler().getExtension();
1043         } else {
1044             shadedName = shadedArtifactId + "-" + artifact.getVersion() + "-" + classifier + "."
1045                     + artifact.getArtifactHandler().getExtension();
1046         }
1047 
1048         return new File(outputDirectory, shadedName);
1049     }
1050 
1051     private File shadedTestArtifactFile() {
1052         return shadedArtifactFile("tests");
1053     }
1054 
1055     
1056     
1057     private void createDependencyReducedPom(Set<String> artifactsToRemove)
1058             throws IOException, ProjectBuildingException, DependencyCollectionException {
1059         List<Dependency> transitiveDeps = new ArrayList<>();
1060 
1061         
1062         
1063         for (Artifact artifact : project.getArtifacts()) {
1064             if ("pom".equals(artifact.getType())) {
1065                 
1066                 continue;
1067             }
1068 
1069             
1070             Dependency dep = createDependency(artifact);
1071 
1072             
1073             transitiveDeps.add(dep);
1074         }
1075 
1076         Model model = project.getOriginalModel();
1077 
1078         
1079         
1080         
1081         List<Dependency> origDeps = new ArrayList<>();
1082         List<Dependency> source = promoteTransitiveDependencies ? transitiveDeps : project.getDependencies();
1083         for (Dependency d : source) {
1084             origDeps.add(d.clone());
1085         }
1086         model = model.clone();
1087 
1088         
1089         
1090         
1091         List<Dependency> originalDependencies = model.getDependencies();
1092         removeSystemScopedDependencies(artifactsToRemove, originalDependencies);
1093 
1094         List<Dependency> dependencies = new ArrayList<>();
1095         boolean modified = false;
1096         for (Dependency d : origDeps) {
1097             if (artifactsToRemove.contains(getId(d))) {
1098                 if (keepDependenciesWithProvidedScope) {
1099                     if (!"provided".equals(d.getScope())) {
1100                         modified = true;
1101                         d.setScope("provided");
1102                     }
1103                 } else {
1104                     modified = true;
1105                     continue;
1106                 }
1107             }
1108 
1109             dependencies.add(d);
1110         }
1111 
1112         
1113         model.setArtifactId(shadedArtifactId);
1114 
1115         
1116         
1117         
1118         addSystemScopedDependencyFromNonInterpolatedPom(dependencies, originalDependencies);
1119 
1120         
1121         rewriteDependencyReducedPomIfWeHaveReduction(dependencies, modified, transitiveDeps, model);
1122     }
1123 
1124     private void rewriteDependencyReducedPomIfWeHaveReduction(
1125             List<Dependency> dependencies, boolean modified, List<Dependency> transitiveDeps, Model model)
1126             throws IOException, ProjectBuildingException, DependencyCollectionException {
1127         if (modified) {
1128             for (int loopCounter = 0; modified; loopCounter++) {
1129 
1130                 model.setDependencies(dependencies);
1131 
1132                 if (generateUniqueDependencyReducedPom) {
1133                     dependencyReducedPomLocation = Files.createTempFile(
1134                                     project.getBasedir().toPath(), "dependency-reduced-pom-", ".xml")
1135                             .toFile();
1136                     project.getProperties()
1137                             .setProperty(
1138                                     "maven.shade.dependency-reduced-pom",
1139                                     dependencyReducedPomLocation.getAbsolutePath());
1140                 } else {
1141                     if (dependencyReducedPomLocation == null) {
1142                         
1143                         dependencyReducedPomLocation = new File(project.getBasedir(), "dependency-reduced-pom.xml");
1144                     }
1145                 }
1146 
1147                 File f = dependencyReducedPomLocation;
1148                 
1149                 
1150                 if (loopCounter == 0) {
1151                     getLog().info("Dependency-reduced POM written at: " + f.getAbsolutePath());
1152                 }
1153 
1154                 if (f.exists()) {
1155                     
1156                     f.delete();
1157                 }
1158 
1159                 Writer w = WriterFactory.newXmlWriter(f);
1160 
1161                 String replaceRelativePath = null;
1162                 if (model.getParent() != null) {
1163                     replaceRelativePath = model.getParent().getRelativePath();
1164                 }
1165 
1166                 if (model.getParent() != null) {
1167                     File parentFile =
1168                             new File(project.getBasedir(), model.getParent().getRelativePath()).getCanonicalFile();
1169                     if (!parentFile.isFile()) {
1170                         parentFile = new File(parentFile, "pom.xml");
1171                     }
1172 
1173                     parentFile = parentFile.getCanonicalFile();
1174 
1175                     String relPath = RelativizePath.convertToRelativePath(parentFile, f);
1176                     model.getParent().setRelativePath(relPath);
1177                 }
1178 
1179                 try {
1180                     PomWriter.write(w, model, true);
1181                 } finally {
1182                     if (model.getParent() != null) {
1183                         model.getParent().setRelativePath(replaceRelativePath);
1184                     }
1185                     w.close();
1186                 }
1187 
1188                 synchronized (session.getProjectBuildingRequest()) { 
1189                     ProjectBuildingRequest projectBuildingRequest =
1190                             new DefaultProjectBuildingRequest(session.getProjectBuildingRequest());
1191                     projectBuildingRequest.setLocalRepository(session.getLocalRepository());
1192                     projectBuildingRequest.setRemoteRepositories(project.getRemoteArtifactRepositories());
1193 
1194                     ProjectBuildingResult result = projectBuilder.build(f, projectBuildingRequest);
1195 
1196                     getLog().debug("updateExcludesInDeps()");
1197                     modified = updateExcludesInDeps(result.getProject(), dependencies, transitiveDeps);
1198                 }
1199             }
1200 
1201             project.setFile(dependencyReducedPomLocation);
1202         }
1203     }
1204 
1205     private void removeSystemScopedDependencies(Set<String> artifactsToRemove, List<Dependency> originalDependencies) {
1206         for (Dependency dependency : originalDependencies) {
1207             if (dependency.getScope() != null && dependency.getScope().equalsIgnoreCase("system")) {
1208                 artifactsToRemove.add(getId(dependency));
1209             }
1210         }
1211     }
1212 
1213     private void addSystemScopedDependencyFromNonInterpolatedPom(
1214             List<Dependency> dependencies, List<Dependency> originalDependencies) {
1215         for (Dependency dependency : originalDependencies) {
1216             if (dependency.getScope() != null && dependency.getScope().equalsIgnoreCase("system")) {
1217                 dependencies.add(dependency);
1218             }
1219         }
1220     }
1221 
1222     private Dependency createDependency(Artifact artifact) {
1223         Dependency dep = new Dependency();
1224         dep.setArtifactId(artifact.getArtifactId());
1225         if (artifact.hasClassifier()) {
1226             dep.setClassifier(artifact.getClassifier());
1227         }
1228         dep.setGroupId(artifact.getGroupId());
1229         dep.setOptional(artifact.isOptional());
1230         dep.setScope(artifact.getScope());
1231         dep.setType(artifact.getType());
1232         if (useBaseVersion) {
1233             dep.setVersion(artifact.getBaseVersion());
1234         } else {
1235             dep.setVersion(artifact.getVersion());
1236         }
1237         return dep;
1238     }
1239 
1240     private String getId(Artifact artifact) {
1241         return getId(artifact.getGroupId(), artifact.getArtifactId(), artifact.getType(), artifact.getClassifier());
1242     }
1243 
1244     private String getId(Dependency dependency) {
1245         return getId(
1246                 dependency.getGroupId(), dependency.getArtifactId(), dependency.getType(), dependency.getClassifier());
1247     }
1248 
1249     private String getId(String groupId, String artifactId, String type, String classifier) {
1250         return groupId + ":" + artifactId + ":" + type + ":" + ((classifier != null) ? classifier : "");
1251     }
1252 
1253     public boolean updateExcludesInDeps(
1254             MavenProject project, List<Dependency> dependencies, List<Dependency> transitiveDeps)
1255             throws DependencyCollectionException {
1256         CollectRequest collectRequest = new CollectRequest();
1257         collectRequest.setRootArtifact(RepositoryUtils.toArtifact(project.getArtifact()));
1258         collectRequest.setRepositories(project.getRemoteProjectRepositories());
1259         collectRequest.setDependencies(project.getDependencies().stream()
1260                 .map(d -> RepositoryUtils.toDependency(
1261                         d, session.getRepositorySession().getArtifactTypeRegistry()))
1262                 .collect(Collectors.toList()));
1263         if (project.getDependencyManagement() != null) {
1264             collectRequest.setManagedDependencies(project.getDependencyManagement().getDependencies().stream()
1265                     .map(d -> RepositoryUtils.toDependency(
1266                             d, session.getRepositorySession().getArtifactTypeRegistry()))
1267                     .collect(Collectors.toList()));
1268         }
1269         CollectResult result = repositorySystem.collectDependencies(session.getRepositorySession(), collectRequest);
1270         boolean modified = false;
1271         if (result.getRoot() != null) {
1272             for (DependencyNode n2 : result.getRoot().getChildren()) {
1273                 String artifactId2 = getId(RepositoryUtils.toArtifact(n2.getArtifact()));
1274 
1275                 for (DependencyNode n3 : n2.getChildren()) {
1276                     
1277                     Artifact artifact3 = RepositoryUtils.toArtifact(n3.getArtifact());
1278                     artifact3.setScope(n3.getDependency().getScope());
1279                     String artifactId3 = getId(artifact3);
1280 
1281                     
1282                     
1283                     
1284                     
1285 
1286                     
1287                     boolean found = false;
1288                     for (Dependency dep : transitiveDeps) {
1289                         if (getId(dep).equals(artifactId3)) {
1290                             found = true;
1291                             break;
1292                         }
1293                     }
1294 
1295                     
1296                     
1297                     
1298                     
1299                     if (!found && !"provided".equals(artifact3.getScope())) {
1300                         getLog().debug(String.format(
1301                                 "dependency %s (scope %s) not found in transitive dependencies",
1302                                 artifactId3, artifact3.getScope()));
1303                         for (Dependency dep : dependencies) {
1304                             if (getId(dep).equals(artifactId2)) {
1305                                 
1306                                 
1307                                 
1308                                 
1309                                 if (!dependencyHasExclusion(dep, artifact3)) {
1310                                     getLog().debug(String.format(
1311                                             "Adding exclusion for dependency %s (scope %s) " + "to %s (scope %s)",
1312                                             artifactId3, artifact3.getScope(), getId(dep), dep.getScope()));
1313                                     Exclusion exclusion = new Exclusion();
1314                                     exclusion.setArtifactId(artifact3.getArtifactId());
1315                                     exclusion.setGroupId(artifact3.getGroupId());
1316                                     dep.addExclusion(exclusion);
1317                                     modified = true;
1318                                     break;
1319                                 }
1320                             }
1321                         }
1322                     }
1323                 }
1324             }
1325         }
1326         return modified;
1327     }
1328 
1329     private boolean dependencyHasExclusion(Dependency dep, Artifact exclusionToCheck) {
1330         boolean containsExclusion = false;
1331         for (Exclusion existingExclusion : dep.getExclusions()) {
1332             if (existingExclusion.getGroupId().equals(exclusionToCheck.getGroupId())
1333                     && existingExclusion.getArtifactId().equals(exclusionToCheck.getArtifactId())) {
1334                 containsExclusion = true;
1335                 break;
1336             }
1337         }
1338         return containsExclusion;
1339     }
1340 
1341     private List<ResourceTransformer> toResourceTransformers(
1342             String shade, List<ResourceTransformer> resourceTransformers) {
1343         List<ResourceTransformer> forShade = new ArrayList<>();
1344         ManifestResourceTransformer lastMt = null;
1345         for (ResourceTransformer transformer : resourceTransformers) {
1346             if (!(transformer instanceof ManifestResourceTransformer)) {
1347                 forShade.add(transformer);
1348             } else if (((ManifestResourceTransformer) transformer).isForShade(shade)) {
1349                 final ManifestResourceTransformer mt = (ManifestResourceTransformer) transformer;
1350                 if (mt.isUsedForDefaultShading() && lastMt != null && !lastMt.isUsedForDefaultShading()) {
1351                     continue; 
1352                 }
1353                 if (!mt.isUsedForDefaultShading() && lastMt != null && lastMt.isUsedForDefaultShading()) {
1354                     forShade.remove(lastMt);
1355                 } else if (!mt.isUsedForDefaultShading() && lastMt != null) {
1356                     getLog().warn("Ambiguous manifest transformer definition for '" + shade + "': " + mt + " / "
1357                             + lastMt);
1358                 }
1359                 if (lastMt == null || !mt.isUsedForDefaultShading()) {
1360                     lastMt = mt;
1361                 }
1362                 forShade.add(transformer);
1363             }
1364         }
1365         return forShade;
1366     }
1367 }