diff --git a/src/it/mrm/repository/dep-reduced-pom-with-transitive-dependencies-with-exclusions/dep-reduced-pom-with-transitive-dependencies-with-exclusions-1.0.pom b/src/it/mrm/repository/dep-reduced-pom-with-transitive-dependencies-with-exclusions/dep-reduced-pom-with-transitive-dependencies-with-exclusions-1.0.pom
new file mode 100644
index 00000000..874c7b5a
--- /dev/null
+++ b/src/it/mrm/repository/dep-reduced-pom-with-transitive-dependencies-with-exclusions/dep-reduced-pom-with-transitive-dependencies-with-exclusions-1.0.pom
@@ -0,0 +1,75 @@
+
+
+
+
+
+ 4.0.0
+
+ org.apache.maven.its.shade.drpwtdwe
+ dep-reduced-pom-with-transitive-dependencies-with-exclusions
+ 1.0
+
+
+ Test that creation of the dependency reduced POM properly handles transitive dependencies with exclusions.
+
+
+
+
+ org.apache.maven.its.shade.drp
+ a
+ 0.1
+
+
+ com.opencsv
+ opencsv
+ 5.9
+
+
+ commons-beanutils
+ commons-beanutils
+
+
+ org.apache.commons
+ commons-text
+
+
+ org.apache.commons
+ commons-collections4
+
+
+
+
+ org.apache.commons
+ commons-collections4
+ 4.5.0
+
+
+ junit
+ junit
+
+
+
+
+ org.apache.commons
+ commons-text
+ 1.14.0
+
+
+
diff --git a/src/it/projects/MSHADE-467_parallel-dependency-reduced-pom/shadeMT1/expected-dependency-reduced-pom.xml b/src/it/projects/MSHADE-467_parallel-dependency-reduced-pom/shadeMT1/expected-dependency-reduced-pom.xml
new file mode 100644
index 00000000..384acab2
--- /dev/null
+++ b/src/it/projects/MSHADE-467_parallel-dependency-reduced-pom/shadeMT1/expected-dependency-reduced-pom.xml
@@ -0,0 +1,600 @@
+
+
+
+ MSHADE-467
+ org.apache.maven.its.shade.parallel
+ 1.0.0-SNAPSHOT
+
+ 4.0.0
+ shadeMT1
+
+
+
+ maven-shade-plugin
+
+
+
+
+
+ org.apache.commons
+ commons-vfs2
+ 2.9.0
+ compile
+
+
+ hadoop-hdfs-client
+ org.apache.hadoop
+
+
+
+
+ org.apache.commons
+ commons-lang3
+ 3.12.0
+ compile
+
+
+ com.vividsolutions
+ jts
+ 1.13
+ compile
+
+
+ com.itextpdf
+ itextpdf
+ 5.5.13.3
+ compile
+
+
+ org.apache.xmlgraphics
+ batik-swing
+ 1.17
+ compile
+
+
+ org.apache.xmlgraphics
+ batik-anim
+ 1.17
+ compile
+
+
+ org.apache.xmlgraphics
+ batik-parser
+ 1.17
+ compile
+
+
+ org.apache.xmlgraphics
+ batik-svg-dom
+ 1.17
+ compile
+
+
+ org.apache.xmlgraphics
+ batik-awt-util
+ 1.17
+ compile
+
+
+ org.apache.xmlgraphics
+ xmlgraphics-commons
+ 2.9
+ compile
+
+
+ commons-io
+ commons-io
+ 2.11.0
+ compile
+
+
+ org.apache.xmlgraphics
+ batik-bridge
+ 1.17
+ compile
+
+
+ org.apache.xmlgraphics
+ batik-css
+ 1.17
+ compile
+
+
+ org.apache.xmlgraphics
+ batik-gui-util
+ 1.17
+ compile
+
+
+ org.apache.xmlgraphics
+ batik-gvt
+ 1.17
+ compile
+
+
+ org.apache.xmlgraphics
+ batik-script
+ 1.17
+ compile
+
+
+ org.apache.xmlgraphics
+ batik-shared-resources
+ 1.17
+ compile
+
+
+ org.apache.xmlgraphics
+ batik-util
+ 1.17
+ compile
+
+
+ org.apache.xmlgraphics
+ batik-constants
+ 1.17
+ compile
+
+
+ org.apache.xmlgraphics
+ batik-i18n
+ 1.17
+ compile
+
+
+ xml-apis
+ xml-apis-ext
+ 1.3.04
+ compile
+
+
+ org.apache.xmlgraphics
+ batik-dom
+ 1.17
+ compile
+
+
+ org.apache.xmlgraphics
+ batik-ext
+ 1.17
+ compile
+
+
+ org.apache.xmlgraphics
+ batik-xml
+ 1.17
+ compile
+
+
+ xml-apis
+ xml-apis
+ 1.4.01
+ compile
+
+
+ org.apache.xmlgraphics
+ batik-transcoder
+ 1.17
+ compile
+
+
+ org.apache.xmlgraphics
+ batik-svggen
+ 1.17
+ compile
+
+
+ org.springframework
+ spring-core
+ 5.3.31
+ compile
+
+
+ org.springframework
+ spring-jcl
+ 5.3.31
+ compile
+
+
+ io.grpc
+ grpc-core
+ 1.58.0
+ compile
+
+
+ io.grpc
+ grpc-api
+ 1.58.0
+ compile
+
+
+ com.google.code.findbugs
+ jsr305
+ 3.0.2
+ compile
+
+
+ com.google.code.gson
+ gson
+ 2.10.1
+ compile
+
+
+ com.google.android
+ annotations
+ 4.1.1.4
+ runtime
+
+
+ org.codehaus.mojo
+ animal-sniffer-annotations
+ 1.23
+ runtime
+
+
+ com.google.errorprone
+ error_prone_annotations
+ 2.20.0
+ compile
+
+
+ com.google.guava
+ guava
+ 32.0.1-android
+ compile
+
+
+ com.google.guava
+ failureaccess
+ 1.0.1
+ compile
+
+
+ com.google.guava
+ listenablefuture
+ 9999.0-empty-to-avoid-conflict-with-guava
+ compile
+
+
+ org.checkerframework
+ checker-qual
+ 3.33.0
+ compile
+
+
+ com.google.j2objc
+ j2objc-annotations
+ 2.8
+ compile
+
+
+ io.perfmark
+ perfmark-api
+ 0.26.0
+ runtime
+
+
+ io.grpc
+ grpc-context
+ 1.58.0
+ compile
+
+
+ io.grpc
+ grpc-util
+ 1.58.0
+ runtime
+
+
+ io.micrometer
+ micrometer-registry-stackdriver
+ 1.9.16
+ compile
+
+
+ io.micrometer
+ micrometer-core
+ 1.9.16
+ compile
+
+
+ org.hdrhistogram
+ HdrHistogram
+ 2.1.12
+ compile
+
+
+ org.latencyutils
+ LatencyUtils
+ 2.0.3
+ runtime
+
+
+ com.google.cloud
+ google-cloud-monitoring
+ 3.2.10
+ compile
+
+
+ javax.annotation-api
+ javax.annotation
+
+
+
+
+ io.grpc
+ grpc-stub
+ 1.45.2
+ compile
+
+
+ io.grpc
+ grpc-protobuf
+ 1.45.2
+ compile
+
+
+ io.grpc
+ grpc-protobuf-lite
+ 1.45.2
+ compile
+
+
+ protobuf-javalite
+ com.google.protobuf
+
+
+
+
+ com.google.api
+ api-common
+ 2.1.5
+ compile
+
+
+ javax.annotation-api
+ javax.annotation
+
+
+
+
+ com.google.protobuf
+ protobuf-java
+ 3.19.6
+ compile
+
+
+ com.google.api.grpc
+ proto-google-common-protos
+ 2.8.4
+ compile
+
+
+ com.google.api.grpc
+ proto-google-cloud-monitoring-v3
+ 3.2.10
+ compile
+
+
+ javax.annotation-api
+ javax.annotation
+
+
+
+
+ com.google.api
+ gax
+ 2.16.0
+ compile
+
+
+ com.google.auth
+ google-auth-library-credentials
+ 1.6.0
+ compile
+
+
+ com.google.api
+ gax-grpc
+ 2.16.0
+ compile
+
+
+ io.grpc
+ grpc-alts
+ 1.45.2
+ runtime
+
+
+ io.grpc
+ grpc-grpclb
+ 1.45.2
+ runtime
+
+
+ com.google.protobuf
+ protobuf-java-util
+ 3.19.6
+ runtime
+
+
+ org.conscrypt
+ conscrypt-openjdk-uber
+ 2.5.1
+ runtime
+
+
+ io.grpc
+ grpc-auth
+ 1.45.2
+ runtime
+
+
+ io.grpc
+ grpc-netty-shaded
+ 1.45.2
+ runtime
+
+
+ io.grpc
+ grpc-googleapis
+ 1.45.2
+ runtime
+
+
+ io.grpc
+ grpc-xds
+ 1.45.2
+ runtime
+
+
+ io.grpc
+ grpc-services
+ 1.45.2
+ runtime
+
+
+ com.google.re2j
+ re2j
+ 1.5
+ runtime
+
+
+ org.bouncycastle
+ bcpkix-jdk15on
+ 1.67
+ runtime
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+ 1.67
+ runtime
+
+
+ io.opencensus
+ opencensus-proto
+ 0.2.0
+ runtime
+
+
+ org.threeten
+ threetenbp
+ 1.6.0
+ compile
+
+
+ com.google.auth
+ google-auth-library-oauth2-http
+ 1.6.0
+ compile
+
+
+ com.google.auto.value
+ auto-value-annotations
+ 1.9
+ compile
+
+
+ com.google.http-client
+ google-http-client
+ 1.41.7
+ compile
+
+
+ org.apache.httpcomponents
+ httpclient
+ 4.5.13
+ compile
+
+
+ commons-logging
+ commons-logging
+ 1.2
+ compile
+
+
+ commons-codec
+ commons-codec
+ 1.15
+ compile
+
+
+ org.apache.httpcomponents
+ httpcore
+ 4.4.15
+ compile
+
+
+ io.opencensus
+ opencensus-api
+ 0.31.0
+ compile
+
+
+ io.opencensus
+ opencensus-contrib-http-util
+ 0.31.0
+ compile
+
+
+ com.google.http-client
+ google-http-client-gson
+ 1.41.7
+ compile
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.36
+ compile
+
+
+ org.springframework
+ spring-context
+ 5.3.31
+ compile
+
+
+ org.springframework
+ spring-aop
+ 5.3.31
+ compile
+
+
+ org.springframework
+ spring-beans
+ 5.3.31
+ compile
+
+
+ org.springframework
+ spring-expression
+ 5.3.31
+ compile
+
+
+ org.slf4j
+ slf4j-simple
+ 1.7.36
+ compile
+
+
+
diff --git a/src/it/projects/MSHADE-467_parallel-dependency-reduced-pom/verify.groovy b/src/it/projects/MSHADE-467_parallel-dependency-reduced-pom/verify.groovy
index 71ba38cb..cea4acab 100644
--- a/src/it/projects/MSHADE-467_parallel-dependency-reduced-pom/verify.groovy
+++ b/src/it/projects/MSHADE-467_parallel-dependency-reduced-pom/verify.groovy
@@ -21,5 +21,5 @@
.readLines()
.findAll { it.contains '' }
.size()
- assert exclusionCount == 5
+ assert exclusionCount == 15
} || true
diff --git a/src/it/projects/dep-reduced-pom-with-transitive-dependencies-with-exclusions/pom.xml b/src/it/projects/dep-reduced-pom-with-transitive-dependencies-with-exclusions/pom.xml
new file mode 100644
index 00000000..3b85e69d
--- /dev/null
+++ b/src/it/projects/dep-reduced-pom-with-transitive-dependencies-with-exclusions/pom.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+ 4.0.0
+
+ org.apache.maven.its.shade.drpwtdwe
+ test
+ 1.0
+
+
+ Test that creation of the dependency reduced POM properly handles transitive dependencies with exclusions.
+
+
+
+
+ org.apache.maven.its.shade.drpwtdwe
+ dep-reduced-pom-with-transitive-dependencies-with-exclusions
+ 1.0
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ @project.version@
+
+
+
+ org.apache.maven.its.shade.drpwtdwe:dep-reduced-pom-with-transitive-dependencies-with-exclusions
+
+
+
+
+ *:*
+
+ META-INF/maven/**
+
+
+
+ target/dependency-reduced-pom.xml
+ true
+ true
+
+
+
+ package
+
+ shade
+
+
+
+
+
+
+
diff --git a/src/it/projects/dep-reduced-pom-with-transitive-dependencies-with-exclusions/verify.groovy b/src/it/projects/dep-reduced-pom-with-transitive-dependencies-with-exclusions/verify.groovy
new file mode 100644
index 00000000..fd761e8f
--- /dev/null
+++ b/src/it/projects/dep-reduced-pom-with-transitive-dependencies-with-exclusions/verify.groovy
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import groovy.xml.XmlParser
+
+File pomFile = new File( basedir, "target/dependency-reduced-pom.xml" );
+assert pomFile.isFile()
+
+def ns = new groovy.xml.Namespace("http://maven.apache.org/POM/4.0.0")
+def pom = new XmlParser().parse( pomFile )
+
+assert pom[ns.modelVersion].size() == 1
+assert pom[ns.dependencies][ns.dependency].size() == 5
+assert pom[ns.dependencies][ns.dependency][1][ns.exclusions][ns.exclusion].size() == 3
+assert pom[ns.dependencies][ns.dependency][3][ns.exclusions][ns.exclusion].size() == 1
+
+
diff --git a/src/main/java/org/apache/maven/plugins/shade/mojo/DependenciesExclusionResolver.java b/src/main/java/org/apache/maven/plugins/shade/mojo/DependenciesExclusionResolver.java
new file mode 100644
index 00000000..a73c00c5
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugins/shade/mojo/DependenciesExclusionResolver.java
@@ -0,0 +1,371 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.plugins.shade.mojo;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.apache.maven.RepositoryUtils;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.model.Exclusion;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.project.MavenProject;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.artifact.Artifact;
+import org.eclipse.aether.collection.CollectRequest;
+import org.eclipse.aether.collection.CollectResult;
+import org.eclipse.aether.collection.DependencyCollectionException;
+import org.eclipse.aether.graph.DependencyNode;
+import org.eclipse.aether.resolution.ArtifactDescriptorException;
+import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
+import org.eclipse.aether.resolution.ArtifactDescriptorResult;
+
+/**
+ * 1 CASE - direct exclusion with promotion
+ * If our pom.xml has a dependency, called A (and A is defined to include in artifactSet).
+ * And this dependency has defined exclusions (in our pom.xml),
+ * So in reduced pom, we need to add exclusions for these transitive dependencies.
+ *
+ * 2 CASE - direct exclusion without promotion
+ * If our pom.xml has a dependency called A (and A is NOT defined to include in artifactSet).
+ * And this dependency has defined exclusions (in out pom.xml),
+ * So in reduced pom, we need to add original exclusions to A dependency
+ *
+ * 3 CASE - transitive exclusion
+ * If our pom.xml has a dependency, called A (and A is defined to include in artifactSet).
+ * Inside A pom.xml there can be a dependency (B) with exclusions, and we promote this transitive dependency (B),
+ * So in reduced pom, if we have B dependency, we need to keep original exclusions for B.
+ */
+public class DependenciesExclusionResolver {
+
+ private final MavenSession session;
+ private final MavenProject originalProject;
+
+ private final MavenProject shadedProject;
+ private final RepositorySystem repositorySystem;
+ private final Log log;
+ private final CollectResult shadedProjectStructure;
+
+ private boolean pomModified;
+
+ public DependenciesExclusionResolver(
+ MavenSession session,
+ MavenProject originalProject,
+ MavenProject shadedProject,
+ RepositorySystem repositorySystem,
+ Log log)
+ throws DependencyCollectionException {
+ this.session = session;
+ this.originalProject = originalProject;
+ this.shadedProject = shadedProject;
+ this.repositorySystem = repositorySystem;
+ this.log = log;
+ this.shadedProjectStructure = getCollectResult();
+ this.pomModified = false;
+ }
+
+ private CollectResult getCollectResult() throws DependencyCollectionException {
+ CollectRequest collectRequest = new CollectRequest();
+ collectRequest.setRootArtifact(RepositoryUtils.toArtifact(shadedProject.getArtifact()));
+ collectRequest.setRepositories(shadedProject.getRemoteProjectRepositories());
+ collectRequest.setDependencies(shadedProject.getDependencies().stream()
+ .map(d -> RepositoryUtils.toDependency(
+ d, session.getRepositorySession().getArtifactTypeRegistry()))
+ .collect(Collectors.toList()));
+ if (shadedProject.getDependencyManagement() != null) {
+ collectRequest.setManagedDependencies(shadedProject.getDependencyManagement().getDependencies().stream()
+ .map(d -> RepositoryUtils.toDependency(
+ d, session.getRepositorySession().getArtifactTypeRegistry()))
+ .collect(Collectors.toList()));
+ }
+
+ return repositorySystem.collectDependencies(session.getRepositorySession(), collectRequest);
+ }
+
+ public boolean resolve(DependencyList transitiveDependencies, List finalDependencies)
+ throws ArtifactDescriptorException, DependencyCollectionException {
+
+ calculateExclusionsBasedOnMissingDependenciesInFinal(transitiveDependencies, finalDependencies);
+ addDirectAndTransitiveExclusions(finalDependencies);
+
+ return pomModified;
+ }
+
+ private void calculateExclusionsBasedOnMissingDependenciesInFinal(
+ DependencyList transitiveDependencies, List finalDependencies) {
+ if (shadedProjectStructure.getRoot() == null) {
+ return;
+ }
+
+ for (DependencyNode n2 : shadedProjectStructure.getRoot().getChildren()) {
+ String artifactId2 = ShadeMojo.getId(RepositoryUtils.toArtifact(n2.getArtifact()));
+
+ for (DependencyNode n3 : n2.getChildren()) {
+ // stupid m-a Artifact that has no idea what it is: dependency or artifact?
+ org.apache.maven.artifact.Artifact artifact3 = RepositoryUtils.toArtifact(n3.getArtifact());
+ artifact3.setScope(n3.getDependency().getScope());
+ String artifactId3 = ShadeMojo.getId(artifact3);
+
+ // check if it really isn't in the list of original dependencies. Maven
+ // prior to 2.0.8 may grab versions from transients instead of
+ // from the direct deps in which case they would be marked included
+ // instead of OMITTED_FOR_DUPLICATE
+
+ // also, if not promoting the transitives, level 2's would be included
+ boolean found = false;
+ for (Dependency dep : transitiveDependencies) {
+ if (ShadeMojo.getId(dep).equals(artifactId3)) {
+ found = true;
+ break;
+ }
+ }
+
+ // MSHADE-311: do not add exclusion for provided transitive dep
+ // note: MSHADE-31 introduced the exclusion logic for promoteTransitiveDependencies=true,
+ // but as of 3.2.1 promoteTransitiveDependencies has no effect for provided deps,
+ // which makes this fix even possible (see also MSHADE-181)
+ if (!found && !"provided".equals(artifact3.getScope())) {
+ log.debug(String.format(
+ "dependency %s (scope %s) not found in transitive dependencies",
+ artifactId3, artifact3.getScope()));
+ for (Dependency dep : finalDependencies) {
+ if (ShadeMojo.getId(dep).equals(artifactId2)) {
+ // MSHADE-413: First check whether the exclusion has already been added,
+ // because it's meaningless to add it more than once. Certain cases
+ // can end up adding the exclusion "forever" and cause an endless loop
+ // rewriting the whole dependency-reduced-pom.xml file.
+ if (!hasExclusion(dep, artifact3)) {
+ log.debug(String.format(
+ "Adding exclusion for dependency %s (scope %s) " + "to %s (scope %s)",
+ artifactId3, artifact3.getScope(), ShadeMojo.getId(dep), dep.getScope()));
+ dep.addExclusion(toExclusion(artifact3));
+ pomModified = true;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void addDirectAndTransitiveExclusions(List finalDependencies)
+ throws ArtifactDescriptorException {
+ for (Dependency originalDirectDep : originalProject.getDependencies()) {
+ List directExclusions = getDirectExclusions(originalDirectDep);
+ log.debug(
+ "Found " + directExclusions.size() + " direct exclusions for " + originalDirectDep.getArtifactId());
+
+ RepositorySystemSession repoSession = session.getRepositorySession();
+
+ ArtifactDescriptorRequest descriptorRequest = new ArtifactDescriptorRequest();
+ descriptorRequest.setArtifact(toArtifact(originalDirectDep));
+ descriptorRequest.setRepositories(originalProject.getRemoteProjectRepositories());
+
+ ArtifactDescriptorResult descriptorResult =
+ repositorySystem.readArtifactDescriptor(repoSession, descriptorRequest);
+
+ List dependenciesWithExclusions =
+ getDependenciesWithExclusions(descriptorResult);
+
+ for (Dependency dependencyWithMissingExclusions : finalDependencies) {
+ // MSHADE-311: do not add exclusion for provided transitive dep
+ // note: MSHADE-31 introduced the exclusion logic for promoteTransitiveDependencies=true,
+ // but as of 3.2.1 promoteTransitiveDependencies has no effect for provided deps,
+ // which makes this fix even possible (see also MSHADE-181)
+ if (!dependencyWithMissingExclusions.getScope().equals("provided")) {
+ // move original exclusions from pom.xml to reduced pom.xml
+ if (isEqual(dependencyWithMissingExclusions, originalDirectDep)) {
+ addDirectExclusions(dependencyWithMissingExclusions, directExclusions);
+ }
+
+ // move exclusions defined inside dependencies
+ // Our pom.xml has dependency A.
+ // Dependency A has defined dependency B with exclusions
+ // If we add B to reduced pom, we need to keep original exclusions for B
+ addMissingTransitiveExclusions(dependencyWithMissingExclusions, dependenciesWithExclusions);
+ }
+ }
+ }
+ }
+
+ private void addDirectExclusions(Dependency dependencyWithMissingExclusions, List directExclusions) {
+ for (Exclusion directExclusion : directExclusions) {
+ if (!hasExclusion(dependencyWithMissingExclusions, directExclusion)) {
+ String msg = String.format(
+ "Adding direct exclusion for %s:%s to dependency %s (scope %s)",
+ directExclusion.getGroupId(),
+ directExclusion.getArtifactId(),
+ dependencyWithMissingExclusions.getArtifactId(),
+ dependencyWithMissingExclusions.getScope());
+ log.debug(msg);
+ pomModified = true;
+ dependencyWithMissingExclusions.addExclusion(clone(directExclusion));
+ }
+ }
+ }
+
+ private List getDependenciesWithExclusions(
+ ArtifactDescriptorResult descriptorResult) {
+
+ List toCopyList = descriptorResult.getDependencies();
+
+ ArrayList copy = new ArrayList<>(toCopyList.size());
+
+ for (org.eclipse.aether.graph.Dependency toCopy : toCopyList) {
+ if (toCopy.getScope() != null && toCopy.getScope().equals("test")) {
+ // test scope dependencies are not included in the resolved descriptor
+ continue;
+ }
+ copy.add(new org.eclipse.aether.graph.Dependency(
+ toCopy.getArtifact(), toCopy.getScope(), toCopy.isOptional(), toCopy.getExclusions()));
+ }
+ return copy;
+ }
+
+ private void addMissingTransitiveExclusions(
+ Dependency depWithMissingExclusions, List dependenciesWithExclusions) {
+
+ for (org.eclipse.aether.graph.Dependency depWithExclusions : dependenciesWithExclusions) {
+ if (isEqual(depWithMissingExclusions, depWithExclusions)) {
+ List exclusions = getExclusions(depWithExclusions);
+ for (Exclusion exclusion : exclusions) {
+ if (!hasExclusion(depWithMissingExclusions, exclusion)) {
+ String msg = String.format(
+ "Adding exclusion for %s:%s to dependency %s (scope %s)",
+ exclusion.getGroupId(),
+ exclusion.getArtifactId(),
+ depWithMissingExclusions.getArtifactId(),
+ depWithMissingExclusions.getScope());
+ log.debug(msg);
+ pomModified = true;
+ depWithMissingExclusions.addExclusion(clone(exclusion));
+ }
+ }
+ }
+ }
+ }
+
+ private List getExclusions(org.eclipse.aether.graph.Dependency depWithExclusions) {
+ if (depWithExclusions.getExclusions() == null
+ || depWithExclusions.getExclusions().isEmpty()) {
+ return Collections.emptyList();
+ }
+
+ List exclusions =
+ new ArrayList<>(depWithExclusions.getExclusions().size());
+
+ log.debug("Found " + depWithExclusions.getExclusions().size() + " transitive exclusions for "
+ + depWithExclusions.getArtifact().getArtifactId());
+
+ for (org.eclipse.aether.graph.Exclusion aetherExclusion : depWithExclusions.getExclusions()) {
+ exclusions.add(toExclusion(aetherExclusion));
+ }
+
+ return exclusions;
+ }
+
+ private List getDirectExclusions(Dependency dependency) {
+ for (Dependency originalDependency : originalProject.getDependencies()) {
+ if (isEqual(dependency, originalDependency)) {
+ return copy(originalDependency.getExclusions());
+ }
+ }
+
+ return Collections.emptyList();
+ }
+
+ private List copy(List toCopyList) {
+ ArrayList copy = new ArrayList<>(toCopyList.size());
+ for (Exclusion toCopy : toCopyList) {
+ Exclusion exclusion = new Exclusion();
+ exclusion.setArtifactId(toCopy.getArtifactId());
+ exclusion.setGroupId(toCopy.getGroupId());
+ copy.add(exclusion);
+ }
+
+ return copy;
+ }
+
+ private boolean isEqual(Dependency d1, Dependency d2) {
+ return d1.getGroupId().equals(d2.getGroupId()) && d1.getArtifactId().equals(d2.getArtifactId());
+ }
+
+ private boolean isEqual(Dependency d1, org.eclipse.aether.graph.Dependency d2) {
+ return d1.getGroupId().equals(d2.getArtifact().getGroupId())
+ && d1.getArtifactId().equals(d2.getArtifact().getArtifactId());
+ }
+
+ private boolean isEqual(Exclusion e1, Exclusion e2) {
+ return e1.getGroupId().equals(e2.getGroupId()) && e1.getArtifactId().equals(e2.getArtifactId());
+ }
+
+ private Artifact toArtifact(Dependency dependency) {
+ org.eclipse.aether.graph.Dependency aetherDep = RepositoryUtils.toDependency(
+ dependency, session.getRepositorySession().getArtifactTypeRegistry());
+
+ return aetherDep.getArtifact();
+ }
+
+ private boolean hasExclusion(Dependency dep, Exclusion exclusion) {
+ for (Exclusion existingExclusion : dep.getExclusions()) {
+ if (isEqual(existingExclusion, exclusion)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private boolean hasExclusion(Dependency dep, org.apache.maven.artifact.Artifact exclusionToCheck) {
+ for (Exclusion existingExclusion : dep.getExclusions()) {
+ if (existingExclusion.getGroupId().equals(exclusionToCheck.getGroupId())
+ && existingExclusion.getArtifactId().equals(exclusionToCheck.getArtifactId())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private Exclusion toExclusion(org.apache.maven.artifact.Artifact artifact) {
+ Exclusion exclusion = new Exclusion();
+ exclusion.setArtifactId(artifact.getArtifactId());
+ exclusion.setGroupId(artifact.getGroupId());
+ return exclusion;
+ }
+
+ private Exclusion toExclusion(org.eclipse.aether.graph.Exclusion aetherExclusion) {
+ Exclusion exclusion = new Exclusion();
+ exclusion.setGroupId(aetherExclusion.getGroupId());
+ exclusion.setArtifactId(aetherExclusion.getArtifactId());
+ return exclusion;
+ }
+
+ private Exclusion clone(Exclusion toClone) {
+ Exclusion exclusion = new Exclusion();
+ exclusion.setGroupId(toClone.getGroupId());
+ exclusion.setArtifactId(toClone.getArtifactId());
+ return exclusion;
+ }
+}
diff --git a/src/main/java/org/apache/maven/plugins/shade/mojo/DependencyList.java b/src/main/java/org/apache/maven/plugins/shade/mojo/DependencyList.java
new file mode 100644
index 00000000..ad59e856
--- /dev/null
+++ b/src/main/java/org/apache/maven/plugins/shade/mojo/DependencyList.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.plugins.shade.mojo;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.model.Dependency;
+import org.apache.maven.plugin.logging.Log;
+import org.apache.maven.project.DefaultProjectBuildingRequest;
+import org.apache.maven.project.MavenProject;
+import org.apache.maven.project.ProjectBuilder;
+import org.apache.maven.project.ProjectBuildingException;
+import org.apache.maven.project.ProjectBuildingRequest;
+import org.apache.maven.project.ProjectBuildingResult;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.collection.DependencyCollectionException;
+import org.eclipse.aether.resolution.ArtifactDescriptorException;
+
+public class DependencyList implements Iterable {
+
+ private final List dependencies;
+
+ public DependencyList(List dependencies) {
+ this.dependencies = new ArrayList<>();
+ copyDependencies(dependencies);
+ }
+
+ public boolean resolveTransitiveDependenciesExclusions(
+ MavenSession session,
+ MavenProject originalProject,
+ ProjectBuilder projectBuilder,
+ File reducedPomFile,
+ RepositorySystem repositorySystem,
+ List finalDependencies,
+ Log log) {
+ try {
+
+ synchronized (session.getProjectBuildingRequest()) { // Lock critical section to fix MSHADE-467
+ ProjectBuildingRequest request = new DefaultProjectBuildingRequest(session.getProjectBuildingRequest());
+ request.setLocalRepository(session.getLocalRepository());
+ request.setRemoteRepositories(originalProject.getRemoteArtifactRepositories());
+
+ ProjectBuildingResult shaded = projectBuilder.build(reducedPomFile, request);
+
+ DependenciesExclusionResolver resolver = new DependenciesExclusionResolver(
+ session, originalProject, shaded.getProject(), repositorySystem, log);
+
+ return resolver.resolve(this, finalDependencies);
+ }
+ } catch (ArtifactDescriptorException | DependencyCollectionException | ProjectBuildingException e) {
+ log.error("Failed to resolve exclusions for " + originalProject.getArtifact(), e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ // MSHADE-413: Must not use objects (for example `Model` or `Dependency`) that are "owned
+ // by Maven" and being used by other projects/plugins. Modifying those will break the
+ // correctness of the build - or cause an endless loop.
+ private void copyDependencies(List dependencies) {
+ for (Dependency d : dependencies) {
+ Dependency cloned = d.clone();
+ this.dependencies.add(cloned);
+ }
+ }
+
+ @Override
+ public Iterator iterator() {
+ return Collections.unmodifiableList(dependencies).iterator();
+ }
+
+ @Override
+ public void forEach(Consumer super Dependency> action) {
+ Iterable.super.forEach(action);
+ }
+
+ @Override
+ public Spliterator spliterator() {
+ return Iterable.super.spliterator();
+ }
+}
diff --git a/src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java b/src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java
index 312c76ae..e5b89a41 100644
--- a/src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java
+++ b/src/main/java/org/apache/maven/plugins/shade/mojo/ShadeMojo.java
@@ -61,13 +61,10 @@
import org.apache.maven.plugins.shade.relocation.SimpleRelocator;
import org.apache.maven.plugins.shade.resource.ManifestResourceTransformer;
import org.apache.maven.plugins.shade.resource.ResourceTransformer;
-import org.apache.maven.project.DefaultProjectBuildingRequest;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.apache.maven.project.ProjectBuilder;
import org.apache.maven.project.ProjectBuildingException;
-import org.apache.maven.project.ProjectBuildingRequest;
-import org.apache.maven.project.ProjectBuildingResult;
import org.codehaus.plexus.util.IOUtil;
import org.codehaus.plexus.util.WriterFactory;
import org.eclipse.aether.RepositorySystem;
@@ -1078,11 +1075,8 @@ private void createDependencyReducedPom(Set artifactsToRemove)
// MSHADE-413: Must not use objects (for example `Model` or `Dependency`) that are "owned
// by Maven" and being used by other projects/plugins. Modifying those will break the
// correctness of the build - or cause an endless loop.
- List origDeps = new ArrayList<>();
- List source = promoteTransitiveDependencies ? transitiveDeps : project.getDependencies();
- for (Dependency d : source) {
- origDeps.add(d.clone());
- }
+ DependencyList origDeps =
+ new DependencyList(promoteTransitiveDependencies ? transitiveDeps : project.getDependencies());
model = model.clone();
// MSHADE-185: We will remove all system scoped dependencies which usually
@@ -1118,12 +1112,11 @@ private void createDependencyReducedPom(Set artifactsToRemove)
addSystemScopedDependencyFromNonInterpolatedPom(dependencies, originalDependencies);
// Check to see if we have a reduction and if so rewrite the POM.
- rewriteDependencyReducedPomIfWeHaveReduction(dependencies, modified, transitiveDeps, model);
+ rewriteDependencyReducedPomIfWeHaveReduction(dependencies, modified, model, origDeps);
}
private void rewriteDependencyReducedPomIfWeHaveReduction(
- List dependencies, boolean modified, List transitiveDeps, Model model)
- throws IOException, ProjectBuildingException, DependencyCollectionException {
+ List dependencies, boolean modified, Model model, DependencyList origDeps) throws IOException {
if (modified) {
for (int loopCounter = 0; modified; loopCounter++) {
@@ -1185,17 +1178,8 @@ private void rewriteDependencyReducedPomIfWeHaveReduction(
w.close();
}
- synchronized (session.getProjectBuildingRequest()) { // Lock critical section to fix MSHADE-467
- ProjectBuildingRequest projectBuildingRequest =
- new DefaultProjectBuildingRequest(session.getProjectBuildingRequest());
- projectBuildingRequest.setLocalRepository(session.getLocalRepository());
- projectBuildingRequest.setRemoteRepositories(project.getRemoteArtifactRepositories());
-
- ProjectBuildingResult result = projectBuilder.build(f, projectBuildingRequest);
-
- getLog().debug("updateExcludesInDeps()");
- modified = updateExcludesInDeps(result.getProject(), dependencies, transitiveDeps);
- }
+ modified = origDeps.resolveTransitiveDependenciesExclusions(
+ session, project, projectBuilder, f, repositorySystem, dependencies, getLog());
}
project.setFile(dependencyReducedPomLocation);
@@ -1237,107 +1221,19 @@ private Dependency createDependency(Artifact artifact) {
return dep;
}
- private String getId(Artifact artifact) {
+ static String getId(Artifact artifact) {
return getId(artifact.getGroupId(), artifact.getArtifactId(), artifact.getType(), artifact.getClassifier());
}
- private String getId(Dependency dependency) {
+ static String getId(Dependency dependency) {
return getId(
dependency.getGroupId(), dependency.getArtifactId(), dependency.getType(), dependency.getClassifier());
}
- private String getId(String groupId, String artifactId, String type, String classifier) {
+ static String getId(String groupId, String artifactId, String type, String classifier) {
return groupId + ":" + artifactId + ":" + type + ":" + ((classifier != null) ? classifier : "");
}
- public boolean updateExcludesInDeps(
- MavenProject project, List dependencies, List transitiveDeps)
- throws DependencyCollectionException {
- CollectRequest collectRequest = new CollectRequest();
- collectRequest.setRootArtifact(RepositoryUtils.toArtifact(project.getArtifact()));
- collectRequest.setRepositories(project.getRemoteProjectRepositories());
- collectRequest.setDependencies(project.getDependencies().stream()
- .map(d -> RepositoryUtils.toDependency(
- d, session.getRepositorySession().getArtifactTypeRegistry()))
- .collect(Collectors.toList()));
- if (project.getDependencyManagement() != null) {
- collectRequest.setManagedDependencies(project.getDependencyManagement().getDependencies().stream()
- .map(d -> RepositoryUtils.toDependency(
- d, session.getRepositorySession().getArtifactTypeRegistry()))
- .collect(Collectors.toList()));
- }
- CollectResult result = repositorySystem.collectDependencies(session.getRepositorySession(), collectRequest);
- boolean modified = false;
- if (result.getRoot() != null) {
- for (DependencyNode n2 : result.getRoot().getChildren()) {
- String artifactId2 = getId(RepositoryUtils.toArtifact(n2.getArtifact()));
-
- for (DependencyNode n3 : n2.getChildren()) {
- // stupid m-a Artifact that has no idea what it is: dependency or artifact?
- Artifact artifact3 = RepositoryUtils.toArtifact(n3.getArtifact());
- artifact3.setScope(n3.getDependency().getScope());
- String artifactId3 = getId(artifact3);
-
- // check if it really isn't in the list of original dependencies. Maven
- // prior to 2.0.8 may grab versions from transients instead of
- // from the direct deps in which case they would be marked included
- // instead of OMITTED_FOR_DUPLICATE
-
- // also, if not promoting the transitives, level 2's would be included
- boolean found = false;
- for (Dependency dep : transitiveDeps) {
- if (getId(dep).equals(artifactId3)) {
- found = true;
- break;
- }
- }
-
- // MSHADE-311: do not add exclusion for provided transitive dep
- // note: MSHADE-31 introduced the exclusion logic for promoteTransitiveDependencies=true,
- // but as of 3.2.1 promoteTransitiveDependencies has no effect for provided deps,
- // which makes this fix even possible (see also MSHADE-181)
- if (!found && !"provided".equals(artifact3.getScope())) {
- getLog().debug(String.format(
- "dependency %s (scope %s) not found in transitive dependencies",
- artifactId3, artifact3.getScope()));
- for (Dependency dep : dependencies) {
- if (getId(dep).equals(artifactId2)) {
- // MSHADE-413: First check whether the exclusion has already been added,
- // because it's meaningless to add it more than once. Certain cases
- // can end up adding the exclusion "forever" and cause an endless loop
- // rewriting the whole dependency-reduced-pom.xml file.
- if (!dependencyHasExclusion(dep, artifact3)) {
- getLog().debug(String.format(
- "Adding exclusion for dependency %s (scope %s) " + "to %s (scope %s)",
- artifactId3, artifact3.getScope(), getId(dep), dep.getScope()));
- Exclusion exclusion = new Exclusion();
- exclusion.setArtifactId(artifact3.getArtifactId());
- exclusion.setGroupId(artifact3.getGroupId());
- dep.addExclusion(exclusion);
- modified = true;
- break;
- }
- }
- }
- }
- }
- }
- }
- return modified;
- }
-
- private boolean dependencyHasExclusion(Dependency dep, Artifact exclusionToCheck) {
- boolean containsExclusion = false;
- for (Exclusion existingExclusion : dep.getExclusions()) {
- if (existingExclusion.getGroupId().equals(exclusionToCheck.getGroupId())
- && existingExclusion.getArtifactId().equals(exclusionToCheck.getArtifactId())) {
- containsExclusion = true;
- break;
- }
- }
- return containsExclusion;
- }
-
private List toResourceTransformers(
String shade, List resourceTransformers) {
List forShade = new ArrayList<>();