View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
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   * Mojo that performs shading delegating to the Shader component.
85   *
86   * @author Jason van Zyl
87   * @author Mauro Talevi
88   * @author David Blevins
89   * @author Hiram Chirino
90   */
91  // CHECKSTYLE_OFF: LineLength
92  @Mojo(
93          name = "shade",
94          defaultPhase = LifecyclePhase.PACKAGE,
95          threadSafe = true,
96          requiresDependencyResolution = ResolutionScope.RUNTIME)
97  // CHECKSTYLE_ON: LineLength
98  public class ShadeMojo extends AbstractMojo {
99      /**
100      * The current Maven session.
101      */
102     @Parameter(defaultValue = "${session}", readonly = true, required = true)
103     private MavenSession session;
104 
105     /**
106      * The current Maven project.
107      */
108     @Parameter(defaultValue = "${project}", readonly = true, required = true)
109     private MavenProject project;
110 
111     /**
112      * Artifacts to include/exclude from the final artifact. Artifacts are denoted by composite identifiers of the
113      * general form <code>groupId:artifactId:type:classifier</code>. Since version 1.3, the wildcard characters '*' and
114      * '?' can be used within the sub parts of those composite identifiers to do pattern matching. For convenience, the
115      * syntax <code>groupId</code> is equivalent to <code>groupId:*:*:*</code>, <code>groupId:artifactId</code> is
116      * equivalent to <code>groupId:artifactId:*:*</code> and <code>groupId:artifactId:classifier</code> is equivalent to
117      * <code>groupId:artifactId:*:classifier</code>. For example:
118      *
119      * <pre>
120      * &lt;artifactSet&gt;
121      *   &lt;includes&gt;
122      *     &lt;include&gt;org.apache.maven:*&lt;/include&gt;
123      *   &lt;/includes&gt;
124      *   &lt;excludes&gt;
125      *     &lt;exclude&gt;*:maven-core&lt;/exclude&gt;
126      *   &lt;/excludes&gt;
127      * &lt;/artifactSet&gt;
128      * </pre>
129      */
130     @Parameter
131     private ArtifactSet artifactSet;
132 
133     /**
134      * Packages to be relocated. For example:
135      *
136      * <pre>
137      * &lt;relocations&gt;
138      *   &lt;relocation&gt;
139      *     &lt;pattern&gt;org.apache&lt;/pattern&gt;
140      *     &lt;shadedPattern&gt;hidden.org.apache&lt;/shadedPattern&gt;
141      *     &lt;includes&gt;
142      *       &lt;include&gt;org.apache.maven.*&lt;/include&gt;
143      *     &lt;/includes&gt;
144      *     &lt;excludes&gt;
145      *       &lt;exclude&gt;org.apache.maven.Public*&lt;/exclude&gt;
146      *     &lt;/excludes&gt;
147      *   &lt;/relocation&gt;
148      * &lt;/relocations&gt;
149      * </pre>
150      *
151      * <em>Note:</em> Support for includes exists only since version 1.4.
152      */
153     @SuppressWarnings("MismatchedReadAndWriteOfArray")
154     @Parameter
155     private PackageRelocation[] relocations;
156 
157     /**
158      * Resource transformers to be used. Please see the "Examples" section for more information on available
159      * transformers and their configuration.
160      */
161     @Parameter
162     private ResourceTransformer[] transformers;
163 
164     /**
165      * Archive Filters to be used. Allows you to specify an artifact in the form of a composite identifier as used by
166      * {@link #artifactSet} and a set of include/exclude file patterns for filtering which contents of the archive are
167      * added to the shaded jar. From a logical perspective, includes are processed before excludes, thus it's possible
168      * to use an include to collect a set of files from the archive then use excludes to further reduce the set. By
169      * default, all files are included and no files are excluded. If multiple filters apply to an artifact, the
170      * intersection of the matched files will be included in the final JAR. For example:
171      *
172      * <pre>
173      * &lt;filters&gt;
174      *   &lt;filter&gt;
175      *     &lt;artifact&gt;junit:junit&lt;/artifact&gt;
176      *     &lt;includes&gt;
177      *       &lt;include&gt;org/junit/**&lt;/include&gt;
178      *     &lt;/includes&gt;
179      *     &lt;excludes&gt;
180      *       &lt;exclude&gt;org/junit/experimental/**&lt;/exclude&gt;
181      *     &lt;/excludes&gt;
182      *   &lt;/filter&gt;
183      * &lt;/filters&gt;
184      * </pre>
185      */
186     @SuppressWarnings("MismatchedReadAndWriteOfArray")
187     @Parameter
188     private ArchiveFilter[] filters;
189 
190     /**
191      * The destination directory for the shaded artifact.
192      */
193     @Parameter(defaultValue = "${project.build.directory}")
194     private File outputDirectory;
195 
196     /**
197      * The name of the shaded artifactId.
198      * <p/>
199      * If you like to change the name of the native artifact, you may use the &lt;build>&lt;finalName> setting. If this
200      * is set to something different than &lt;build>&lt;finalName>, no file replacement will be performed, even if
201      * shadedArtifactAttached is being used.
202      */
203     @Parameter
204     private String finalName;
205 
206     /**
207      * The name of the shaded artifactId. So you may want to use a different artifactId and keep the standard version.
208      * If the original artifactId was "foo" then the final artifact would be something like foo-1.0.jar. So if you
209      * change the artifactId you might have something like foo-special-1.0.jar.
210      */
211     @Parameter(defaultValue = "${project.artifactId}")
212     private String shadedArtifactId;
213 
214     /**
215      * If specified, this will include only artifacts which have groupIds which start with this.
216      */
217     @Parameter
218     private String shadedGroupFilter;
219 
220     /**
221      * Defines whether the shaded artifact should be attached as classifier to the original artifact. If false, the
222      * shaded jar will be the main artifact of the project
223      */
224     @Parameter
225     private boolean shadedArtifactAttached;
226 
227     /**
228      * Flag whether to generate a simplified POM for the shaded artifact. If set to <code>true</code>, dependencies that
229      * have been included into the uber JAR will be removed from the <code>&lt;dependencies&gt;</code> section of the
230      * generated POM. The reduced POM will be named <code>dependency-reduced-pom.xml</code> and is stored into the same
231      * directory as the shaded artifact. Unless you also specify dependencyReducedPomLocation, the plugin will create a
232      * temporary file named <code>dependency-reduced-pom.xml</code> in the project basedir.
233      */
234     @Parameter(defaultValue = "true")
235     private boolean createDependencyReducedPom;
236 
237     /**
238      * Where to put the dependency reduced pom. Note: setting a value for this parameter with a directory other than
239      * ${basedir} will change the value of ${basedir} for all executions that come after the shade execution. This is
240      * often not what you want. This is considered an open issue with this plugin.
241      *
242      * @since 1.7
243      */
244     @Parameter(defaultValue = "${basedir}/dependency-reduced-pom.xml")
245     private File dependencyReducedPomLocation;
246 
247     /**
248      * Create a dependency-reduced POM in ${basedir}/drp-UNIQUE.pom. This avoids build collisions of parallel builds
249      * without moving the dependency-reduced POM to a different directory. The property
250      * maven.shade.dependency-reduced-pom is set to the generated filename.
251      *
252      * @since 1.7.2
253      */
254     @Parameter(defaultValue = "false")
255     private boolean generateUniqueDependencyReducedPom;
256 
257     /**
258      * Add dependency reduced POM to the JAR instead of the original one provided by the project.
259      * If {@code createDependencyReducedPom} is {@code false} this parameter will be ignored.
260      *
261      * @since 3.3.0
262      */
263     @Parameter(defaultValue = "false")
264     private boolean useDependencyReducedPomInJar;
265 
266     /**
267      * When true, dependencies are kept in the pom but with scope 'provided'; when false, the dependency is removed.
268      */
269     @Parameter
270     private boolean keepDependenciesWithProvidedScope;
271 
272     /**
273      * When true, transitive deps of removed dependencies are promoted to direct dependencies. This should allow the
274      * drop in replacement of the removed deps with the new shaded jar and everything should still work.
275      */
276     @Parameter
277     private boolean promoteTransitiveDependencies;
278 
279     /**
280      * The name of the classifier used in case the shaded artifact is attached.
281      */
282     @Parameter(defaultValue = "shaded")
283     private String shadedClassifierName;
284 
285     /**
286      * When true, it will attempt to create a sources jar as well
287      */
288     @Parameter
289     private boolean createSourcesJar;
290 
291     /**
292      * When true, it will attempt to create a test sources jar.
293      */
294     @Parameter
295     private boolean createTestSourcesJar;
296 
297     /**
298      * When true, it will attempt to shade the contents of Java source files when creating the sources JAR. When false,
299      * it will just relocate the Java source files to the shaded paths, but will not modify the actual source file
300      * contents.
301      * <p>
302      * <b>Please note:</b> This feature uses a heuristic search & replace approach which covers many, but definitely not
303      * all possible cases of source code shading and its excludes. There is no full Java parser behind this
304      * functionality, which would be the only way to get this right for Java language elements. As for matching within
305      * Java string constants, this is next to impossible to get 100% right, trying to guess if they are used in
306      * reflection or not.
307      * <p>
308      * Please understand that the source shading feature is not meant as a source code generator anyway, merely as a
309      * tool creating reasonably plausible source code when navigating to a relocated library class from an IDE,
310      * hopefully displaying source code which makes 95% sense - no more, no less.
311      */
312     @Parameter(property = "shadeSourcesContent", defaultValue = "false")
313     private boolean shadeSourcesContent;
314 
315     /**
316      * When true, dependencies will be stripped down on the class level to only the transitive hull required for the
317      * artifact. See also {@link #entryPoints}, if you wish to further optimize JAR minimization.
318      * <p>
319      * <em>Note:</em> This feature uses
320      * <a href="https://github.com/tcurdt/jdependency">jdependency</a>. Its accuracy therefore depends on
321      * jdependency's limitations.
322      *
323      * @since 1.4
324      */
325     @Parameter
326     private boolean minimizeJar;
327 
328     /**
329      * Use this option in order to fine-tune {@link #minimizeJar}: By default, all of the target module's classes are
330      * kept and used as entry points for JAR minimization. By explicitly limiting the set of entry points, you can
331      * further minimize the set of classes kept in the shaded JAR. This affects both classes in the module itself and
332      * dependency classes. If {@link #minimizeJar} is inactive, this option has no effect either.
333      * <p>
334      * <em>Note:</em> This feature requires Java 1.8 or higher due to its use of
335      * <a href="https://github.com/tcurdt/jdependency">jdependency</a>. Its accuracy therefore also depends on
336      * jdependency's limitations.
337      * <p>
338      * Configuration example:
339      * <pre>{@code
340      * <minimizeJar>true</minimizeJar>
341      * <entryPoints>
342      *   <entryPoint>org.acme.Application</entryPoint>
343      *   <entryPoint>org.acme.OtherEntryPoint</entryPoint>
344      * </entryPoints>
345      * }</pre>
346      *
347      * @since 3.5.0
348      */
349     @Parameter
350     private Set<String> entryPoints;
351 
352     /**
353      * The path to the output file for the shaded artifact. When this parameter is set, the created archive will neither
354      * replace the project's main artifact nor will it be attached. Hence, this parameter causes the parameters
355      * {@link #finalName}, {@link #shadedArtifactAttached}, {@link #shadedClassifierName} and
356      * {@link #createDependencyReducedPom} to be ignored when used.
357      *
358      * @since 1.3
359      */
360     @Parameter
361     private File outputFile;
362 
363     /**
364      * You can pass here the roleHint about your own Shader implementation plexus component.
365      *
366      * @since 1.6
367      */
368     @Parameter
369     private String shaderHint;
370 
371     /**
372      * When true, the version of each dependency of the reduced pom will be based on the baseVersion of the original
373      * dependency instead of its resolved version. For example, if the original pom (transitively) depends on
374      * a:a:2.7-SNAPSHOT, if useBaseVersion is set to false, the reduced pom will depend on a:a:2.7-20130312.222222-12
375      * whereas if useBaseVersion is set to true, the reduced pom will depend on a:a:2.7-SNAPSHOT
376      *
377      * @since 3.0
378      */
379     @Parameter(defaultValue = "false")
380     private boolean useBaseVersion;
381 
382     /**
383      * When true, creates a shaded test-jar artifact as well.
384      */
385     @Parameter(defaultValue = "false")
386     private boolean shadeTestJar;
387 
388     /**
389      * When true, skips the execution of this MOJO.
390      * @since 3.3.0
391      */
392     @Parameter(defaultValue = "false")
393     private boolean skip;
394 
395     /**
396      * Extra JAR files to infuse into shaded result. Accepts list of files that must exists. If any of specified
397      * files does not exist (or is not a file), Mojo will fail.
398      * <p>
399      * Extra JARs will be processed in same way as main JAR (if any) is: applied relocation, resource transformers
400      * but <em>not filtering</em>.
401      * <p>
402      * Note: this feature should be used lightly, is not meant as ability to replace dependency hull! It is more
403      * just a feature to be able to slightly "differentiate" shaded JAR from main only.
404      *
405      * @since 3.6.0
406      */
407     @Parameter
408     private List<File> extraJars;
409 
410     /**
411      * Extra Artifacts to infuse into shaded result. Accepts list of GAVs in form of
412      * {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>} that will be resolved. If any of them
413      * cannot be resolved, Mojo will fail.
414      * <p>
415      * The artifacts will be resolved (not transitively), and will be processed in same way as dependency JARs
416      * are (if any): applied relocation, resource transformers and filtering.
417      * <p>
418      * Note: this feature should be used lightly, is not meant as ability to replace dependency hull! It is more
419      * just a feature to be able to slightly "differentiate" shaded JAR from main only.
420      *
421      * @since 3.6.0
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      * ProjectBuilder, needed to create projects from the artifacts.
437      */
438     @Inject
439     private ProjectBuilder projectBuilder;
440 
441     /**
442      * All the present Shaders.
443      */
444     @Inject
445     private Map<String, Shader> shaders;
446 
447     /**
448      * @throws MojoExecutionException in case of an error.
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         // Now add our extra resources
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                     // In some cases the used implementation of the resourceTransformers is immutable.
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                 // rename the output file if a specific finalName is set
572                 // but don't rename if the finalName is the <build><finalName>
573                 // because this will be handled implicitly later
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                     // Also support the sources JAR
584                     if (createSourcesJar) {
585                         finalFileName = finalName + "-sources.jar";
586                         finalFile = new File(outputDirectory, finalFileName);
587                         replaceFile(finalFile, sourcesJar);
588                         sourcesJar = finalFile;
589                     }
590 
591                     // Also support the test JAR
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             // try a gc to see if an unclosed stream needs garbage collecting
835             System.gc();
836             System.gc();
837 
838             if (!oldFile.renameTo(origFile)) {
839                 // Still didn't work. We'll do a copy
840                 try {
841                     copyFiles(oldFile, origFile);
842                 } catch (IOException ex) {
843                     // kind of ignorable here. We're just trying to save the original
844                     getLog().warn(ex);
845                 }
846             }
847         }
848         if (!newFile.renameTo(oldFile)) {
849             // try a gc to see if an unclosed stream needs garbage collecting
850             System.gc();
851             System.gc();
852 
853             if (!newFile.renameTo(oldFile)) {
854                 // Still didn't work. We'll do a copy
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     // We need to find the direct dependencies that have been included in the uber JAR so that we can modify the
1056     // POM accordingly.
1057     private void createDependencyReducedPom(Set<String> artifactsToRemove)
1058             throws IOException, ProjectBuildingException, DependencyCollectionException {
1059         List<Dependency> transitiveDeps = new ArrayList<>();
1060 
1061         // NOTE: By using the getArtifacts() we get the completely evaluated artifacts
1062         // including the system scoped artifacts with expanded values of properties used.
1063         for (Artifact artifact : project.getArtifacts()) {
1064             if ("pom".equals(artifact.getType())) {
1065                 // don't include pom type dependencies in dependency reduced pom
1066                 continue;
1067             }
1068 
1069             // promote
1070             Dependency dep = createDependency(artifact);
1071 
1072             // we'll figure out the exclusions in a bit.
1073             transitiveDeps.add(dep);
1074         }
1075 
1076         Model model = project.getOriginalModel();
1077 
1078         // MSHADE-413: Must not use objects (for example `Model` or `Dependency`) that are "owned
1079         // by Maven" and being used by other projects/plugins. Modifying those will break the
1080         // correctness of the build - or cause an endless loop.
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         // MSHADE-185: We will remove all system scoped dependencies which usually
1089         // have some kind of property usage. At this time the properties within
1090         // such things are already evaluated.
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         // MSHADE-155
1113         model.setArtifactId(shadedArtifactId);
1114 
1115         // MSHADE-185: We will add those system scoped dependencies
1116         // from the non interpolated original pom file. So we keep
1117         // things like this: <systemPath>${tools.jar}</systemPath> intact.
1118         addSystemScopedDependencyFromNonInterpolatedPom(dependencies, originalDependencies);
1119 
1120         // Check to see if we have a reduction and if so rewrite the POM.
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                         // MSHADE-123: We can't default to 'target' because it messes up uses of ${project.basedir}
1143                         dependencyReducedPomLocation = new File(project.getBasedir(), "dependency-reduced-pom.xml");
1144                     }
1145                 }
1146 
1147                 File f = dependencyReducedPomLocation;
1148                 // MSHADE-225
1149                 // Works for now, maybe there's a better algorithm where no for-loop is required
1150                 if (loopCounter == 0) {
1151                     getLog().info("Dependency-reduced POM written at: " + f.getAbsolutePath());
1152                 }
1153 
1154                 if (f.exists()) {
1155                     // noinspection ResultOfMethodCallIgnored
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()) { // Lock critical section to fix MSHADE-467
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                     // stupid m-a Artifact that has no idea what it is: dependency or artifact?
1277                     Artifact artifact3 = RepositoryUtils.toArtifact(n3.getArtifact());
1278                     artifact3.setScope(n3.getDependency().getScope());
1279                     String artifactId3 = getId(artifact3);
1280 
1281                     // check if it really isn't in the list of original dependencies. Maven
1282                     // prior to 2.0.8 may grab versions from transients instead of
1283                     // from the direct deps in which case they would be marked included
1284                     // instead of OMITTED_FOR_DUPLICATE
1285 
1286                     // also, if not promoting the transitives, level 2's would be included
1287                     boolean found = false;
1288                     for (Dependency dep : transitiveDeps) {
1289                         if (getId(dep).equals(artifactId3)) {
1290                             found = true;
1291                             break;
1292                         }
1293                     }
1294 
1295                     // MSHADE-311: do not add exclusion for provided transitive dep
1296                     //       note: MSHADE-31 introduced the exclusion logic for promoteTransitiveDependencies=true,
1297                     //             but as of 3.2.1 promoteTransitiveDependencies has no effect for provided deps,
1298                     //             which makes this fix even possible (see also MSHADE-181)
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                                 // MSHADE-413: First check whether the exclusion has already been added,
1306                                 // because it's meaningless to add it more than once. Certain cases
1307                                 // can end up adding the exclusion "forever" and cause an endless loop
1308                                 // rewriting the whole dependency-reduced-pom.xml file.
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; // skip, we already have a specific transformer
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 }