From 422a14e4d8498a25bf9c947f85afed4c484a2236 Mon Sep 17 00:00:00 2001 From: Robert Stupp Date: Mon, 21 Mar 2022 15:38:03 +0100 Subject: [PATCH] [MSHADE-413] Fix endless loop caused by manipulating shared objects Maven objects (like `Dependency` or `Model`) returned by Maven must not be modified. Doing so will result in endless loops or incorrect builds. --- .../maven/plugins/shade/mojo/ShadeMojo.java | 64 +++++++++++++++---- 1 file changed, 51 insertions(+), 13 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 d717253f..deeadd13 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 @@ -1083,14 +1083,20 @@ private void createDependencyReducedPom( Set artifactsToRemove ) // we'll figure out the exclusions in a bit. transitiveDeps.add( dep ); } - List origDeps = project.getDependencies(); - if ( promoteTransitiveDependencies ) + Model model = project.getOriginalModel(); + + // 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 = transitiveDeps; + origDeps.add( d.clone() ); } + model = model.clone(); - Model model = project.getOriginalModel(); // MSHADE-185: We will remove all system scoped dependencies which usually // have some kind of property usage. At this time the properties within // such things are already evaluated. @@ -1299,8 +1305,13 @@ public boolean updateExcludesInDeps( MavenProject project, List depe boolean modified = false; for ( DependencyNode n2 : node.getChildren() ) { + String artifactId2 = getId( n2.getArtifact() ); + for ( DependencyNode n3 : n2.getChildren() ) { + Artifact artifact3 = n3.getArtifact(); + 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 @@ -1310,7 +1321,7 @@ public boolean updateExcludesInDeps( MavenProject project, List depe boolean found = false; for ( Dependency dep : transitiveDeps ) { - if ( getId( dep ).equals( getId( n3.getArtifact() ) ) ) + if ( getId( dep ).equals( artifactId3 ) ) { found = true; break; @@ -1321,18 +1332,31 @@ public boolean updateExcludesInDeps( MavenProject project, List depe // 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( n3.getArtifact().getScope() ) ) + 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( getId( n2.getArtifact() ) ) ) + if ( getId( dep ).equals( artifactId2 ) ) { - Exclusion exclusion = new Exclusion(); - exclusion.setArtifactId( n3.getArtifact().getArtifactId() ); - exclusion.setGroupId( n3.getArtifact().getGroupId() ); - dep.addExclusion( exclusion ); - modified = true; - break; + // 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; + } } } } @@ -1347,6 +1371,20 @@ public boolean updateExcludesInDeps( MavenProject project, List depe } } + 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 ) {