From de668074f73650593807af3dc4f953499747cb53 Mon Sep 17 00:00:00 2001 From: Peter Zmilczak Date: Fri, 7 Nov 2025 18:04:30 +0100 Subject: [PATCH 1/2] [MSHADE-573] Fix missing exclusions - direct and transitive Refactor shade plugin to streamline dependency exclusion handling and remove VerboseJavaScopeSelector implementation Refactor dependency reduced POM handling to improve exclusion resolution logic and streamline transitive dependency processing Update integration test to reflect transitive dependency handling in dependency reduced POM with exclusions Remove test files for dependency reduced POM validation in `shadeMT2` and `shadeMT3` integration tests --- ...itive-dependencies-with-exclusions-1.0.pom | 75 +++ .../expected-dependency-reduced-pom.xml | 600 ++++++++++++++++++ .../verify.groovy | 2 +- .../pom.xml | 76 +++ .../verify.groovy | 32 + .../mojo/DependenciesExclusionResolver.java | 371 +++++++++++ .../plugins/shade/mojo/DependencyList.java | 103 +++ .../maven/plugins/shade/mojo/ShadeMojo.java | 34 +- 8 files changed, 1267 insertions(+), 26 deletions(-) create mode 100644 src/it/mrm/repository/dep-reduced-pom-with-transitive-dependencies-with-exclusions/dep-reduced-pom-with-transitive-dependencies-with-exclusions-1.0.pom create mode 100644 src/it/projects/MSHADE-467_parallel-dependency-reduced-pom/shadeMT1/expected-dependency-reduced-pom.xml create mode 100644 src/it/projects/dep-reduced-pom-with-transitive-dependencies-with-exclusions/pom.xml create mode 100644 src/it/projects/dep-reduced-pom-with-transitive-dependencies-with-exclusions/verify.groovy create mode 100644 src/main/java/org/apache/maven/plugins/shade/mojo/DependenciesExclusionResolver.java create mode 100644 src/main/java/org/apache/maven/plugins/shade/mojo/DependencyList.java 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 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..e1fe9c21 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,16 +1221,16 @@ 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 : ""); } From a1af4ac53c986f65b49858e21e9aa50d976322d1 Mon Sep 17 00:00:00 2001 From: marwin1991 Date: Sun, 9 Nov 2025 12:35:32 +0100 Subject: [PATCH 2/2] Remove unused `updateExcludesInDeps` method from ShadeMojo class --- .../maven/plugins/shade/mojo/ShadeMojo.java | 88 ------------------- 1 file changed, 88 deletions(-) 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 e1fe9c21..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 @@ -1234,94 +1234,6 @@ static String getId(String groupId, String artifactId, String type, String class 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<>();