This is an automated email from the ASF dual-hosted git repository. kwin pushed a commit to branch bugfix/prevent-infinite-loop-in-ManifestResourceTransformer in repository https://gitbox.apache.org/repos/asf/maven-shade-plugin.git
commit c10aa07d013e9e1a678146ab7b151ee620274d54 Author: Konrad Windszus <[email protected]> AuthorDate: Wed Mar 4 20:14:46 2026 +0100 Prevent infinite loop in ManifestResourceTransformer.relocate Introduce Relocator.relocateAllClasses() to replace all patterns in a given string instead of only the first one and use that. Otherwise recursively using Relocator.relocateClass() on already relocated string over and over again leads to endless loop when pattern is a prefix of the shaded pattern. This closes #478 --- .../maven/plugins/shade/relocation/Relocator.java | 12 +++- .../plugins/shade/relocation/SimpleRelocator.java | 9 ++- .../resource/ManifestResourceTransformer.java | 6 +- .../shade/relocation/SimpleRelocatorTest.java | 15 +++++ .../resource/ManifestResourceTransformerTest.java | 77 +++++++++++++++------- 5 files changed, 88 insertions(+), 31 deletions(-) diff --git a/src/main/java/org/apache/maven/plugins/shade/relocation/Relocator.java b/src/main/java/org/apache/maven/plugins/shade/relocation/Relocator.java index d3027c4..be9ac6c 100644 --- a/src/main/java/org/apache/maven/plugins/shade/relocation/Relocator.java +++ b/src/main/java/org/apache/maven/plugins/shade/relocation/Relocator.java @@ -28,7 +28,17 @@ public interface Relocator { boolean canRelocateClass(String clazz); - String relocateClass(String clazz); + /** + * Replace the class first pattern match in the given string + * @see #relocateAllClasses(String) + */ + String relocateClass(String input); String applyToSourceContent(String sourceContent); + + /** + * Replace all class pattern matches in the given input. + * @see #relocateClass(String) + */ + String relocateAllClasses(String input); } diff --git a/src/main/java/org/apache/maven/plugins/shade/relocation/SimpleRelocator.java b/src/main/java/org/apache/maven/plugins/shade/relocation/SimpleRelocator.java index 9d49f77..b801fd0 100644 --- a/src/main/java/org/apache/maven/plugins/shade/relocation/SimpleRelocator.java +++ b/src/main/java/org/apache/maven/plugins/shade/relocation/SimpleRelocator.java @@ -208,8 +208,13 @@ public class SimpleRelocator implements Relocator { } @Override - public String relocateClass(String clazz) { - return rawString ? clazz : clazz.replaceFirst(pattern, shadedPattern); + public String relocateClass(String input) { + return rawString ? input : input.replaceFirst(pattern, shadedPattern); + } + + @Override + public String relocateAllClasses(String input) { + return rawString ? input : input.replaceAll(pattern, shadedPattern); } @Override diff --git a/src/main/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformer.java b/src/main/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformer.java index 9ef598b..b9d63b3 100644 --- a/src/main/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformer.java +++ b/src/main/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformer.java @@ -152,11 +152,7 @@ public class ManifestResourceTransformer extends AbstractCompatibilityTransforme private String relocate(String originalValue, List<Relocator> relocators) { String newValue = originalValue; for (Relocator relocator : relocators) { - String value; - do { - value = newValue; - newValue = relocator.relocateClass(value); - } while (!value.equals(newValue)); + newValue = relocator.relocateAllClasses(newValue); } return newValue; } diff --git a/src/test/java/org/apache/maven/plugins/shade/relocation/SimpleRelocatorTest.java b/src/test/java/org/apache/maven/plugins/shade/relocation/SimpleRelocatorTest.java index 9f7c29d..44afee3 100644 --- a/src/test/java/org/apache/maven/plugins/shade/relocation/SimpleRelocatorTest.java +++ b/src/test/java/org/apache/maven/plugins/shade/relocation/SimpleRelocatorTest.java @@ -157,6 +157,21 @@ public class SimpleRelocatorTest { assertEquals("private.stuff.bar.Class", relocator.relocateClass("org.foo.bar.Class")); } + @Test + public void testRelocateAllClasses() { + SimpleRelocator relocator; + + relocator = new SimpleRelocator("org.foo", null, null, null); + assertEquals( + "hidden.org.foo.bar.Class, hidden.hidden.org.foo.bar.Class and hidden.org.foo.bar.Class", + relocator.relocateAllClasses("org.foo.bar.Class, hidden.org.foo.bar.Class and org.foo.bar.Class")); + + relocator = new SimpleRelocator("org.foo", "private.stuff", null, null); + assertEquals( + "private.stuff.bar.Class, private.stuff.bar.Class and private.stuff.bar.Class", + relocator.relocateAllClasses("org.foo.bar.Class, private.stuff.bar.Class and org.foo.bar.Class")); + } + @Test public void testRelocateRawString() { SimpleRelocator relocator; diff --git a/src/test/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformerTest.java b/src/test/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformerTest.java index ed14ed2..59bb33f 100644 --- a/src/test/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformerTest.java +++ b/src/test/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformerTest.java @@ -48,6 +48,47 @@ public class ManifestResourceTransformerTest { @Test public void rewriteDefaultAttributes() throws Exception { + final Manifest manifest = createTestManifest(); + + List<Relocator> relocators = Collections.<Relocator>singletonList(new SimpleRelocator( + "javax", "jakarta", Collections.<String>emptyList(), Collections.<String>emptyList())); + + final ByteArrayOutputStream out = transform(manifest, relocators); + + try (JarInputStream jis = new JarInputStream(new ByteArrayInputStream(out.toByteArray()))) { + Attributes attrs = jis.getManifest().getMainAttributes(); + assertEquals( + "jakarta.decorator;version=\"2.0\";uses:=\"jakarta.enterprise.inject\"," + + "jakarta.enterprise.context;version=\"2.0\";uses:=\"jakarta.enterprise.util," + + "jakarta.inject\"", + attrs.getValue("Export-Package")); + assertEquals("jakarta.el,jakarta.enterprise.context;version=\"[2.0,3)\"", attrs.getValue("Import-Package")); + assertEquals( + "osgi.contract;osgi.contract=JavaCDI;" + "uses:=\"jakarta.enterprise.context," + + "jakarta.enterprise.context.spi,jakarta.enterprise.context.control," + + "jakarta.enterprise.util,jakarta.enterprise.inject,jakarta.enterprise.inject.spi," + + "jakarta.enterprise.inject.spi.configurator,jakarta.enterprise.inject.literal," + + "jakarta.enterprise.inject.se,jakarta.enterprise.event," + + "jakarta.decorator\";version:List<Version>=\"2.0,1.2,1.1,1.0\"", + attrs.getValue("Provide-Capability")); + assertEquals( + "osgi.serviceloader;" + + "filter:=\"(osgi.serviceloader=jakarta.enterprise.inject.se.SeContainerInitializer)\";" + + "cardinality:=multiple,osgi.serviceloader;" + + "filter:=\"(osgi.serviceloader=jakarta.enterprise.inject.spi.CDIProvider)\";" + + "cardinality:=multiple,osgi.extender;" + + "filter:=\"(osgi.extender=osgi.serviceloader.processor)\"," + + "osgi.contract;osgi.contract=JavaEL;filter:=\"(&(osgi.contract=JavaEL)(version=2.2.0))\"," + + "osgi.contract;osgi.contract=JavaInterceptor;" + + "filter:=\"(&(osgi.contract=JavaInterceptor)(version=1.2.0))\"," + + "osgi.contract;osgi.contract=JavaInject;" + + "filter:=\"(&(osgi.contract=JavaInject)(version=1.0.0))\"," + + "osgi.ee;filter:=\"(&(osgi.ee=JavaSE)(version=1.8))\"", + attrs.getValue("Require-Capability")); + } + } + + protected Manifest createTestManifest() { final Manifest manifest = new Manifest(); final Attributes attributes = manifest.getMainAttributes(); attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0"); @@ -79,41 +120,31 @@ public class ManifestResourceTransformerTest { + "osgi.contract;osgi.contract=JavaInject;" + "filter:=\"(&(osgi.contract=JavaInject)(version=1.0.0))\"," + "osgi.ee;filter:=\"(&(osgi.ee=JavaSE)(version=1.8))\""); + return manifest; + } + + @Test() + public void rewriteDefaultAttributesWithSameSuffix() throws Exception { + final Manifest manifest = createTestManifest(); List<Relocator> relocators = Collections.<Relocator>singletonList(new SimpleRelocator( - "javax", "jakarta", Collections.<String>emptyList(), Collections.<String>emptyList())); + "javax", "shaded.javax", Collections.<String>emptyList(), Collections.<String>emptyList())); final ByteArrayOutputStream out = transform(manifest, relocators); try (JarInputStream jis = new JarInputStream(new ByteArrayInputStream(out.toByteArray()))) { Attributes attrs = jis.getManifest().getMainAttributes(); assertEquals( - "jakarta.decorator;version=\"2.0\";uses:=\"jakarta.enterprise.inject\"," - + "jakarta.enterprise.context;version=\"2.0\";uses:=\"jakarta.enterprise.util," - + "jakarta.inject\"", + "shaded.javax.decorator;version=\"2.0\";uses:=\"shaded.javax.enterprise.inject\",shaded.javax.enterprise.context;version=\"2.0\";uses:=\"shaded.javax.enterprise.util,shaded.javax.inject\"", attrs.getValue("Export-Package")); - assertEquals("jakarta.el,jakarta.enterprise.context;version=\"[2.0,3)\"", attrs.getValue("Import-Package")); assertEquals( - "osgi.contract;osgi.contract=JavaCDI;" + "uses:=\"jakarta.enterprise.context," - + "jakarta.enterprise.context.spi,jakarta.enterprise.context.control," - + "jakarta.enterprise.util,jakarta.enterprise.inject,jakarta.enterprise.inject.spi," - + "jakarta.enterprise.inject.spi.configurator,jakarta.enterprise.inject.literal," - + "jakarta.enterprise.inject.se,jakarta.enterprise.event," - + "jakarta.decorator\";version:List<Version>=\"2.0,1.2,1.1,1.0\"", + "shaded.javax.el,shaded.javax.enterprise.context;version=\"[2.0,3)\"", + attrs.getValue("Import-Package")); + assertEquals( + "osgi.contract;osgi.contract=JavaCDI;uses:=\"shaded.javax.enterprise.context,shaded.javax.enterprise.context.spi,shaded.javax.enterprise.context.control,shaded.javax.enterprise.util,shaded.javax.enterprise.inject,shaded.javax.enterprise.inject.spi,shaded.javax.enterprise.inject.spi.configurator,shaded.javax.enterprise.inject.literal,shaded.javax.enterprise.inject.se,shaded.javax.enterprise.event,shaded.javax.decorator\";version:List<Version>=\"2.0,1.2,1.1,1.0\"", attrs.getValue("Provide-Capability")); assertEquals( - "osgi.serviceloader;" - + "filter:=\"(osgi.serviceloader=jakarta.enterprise.inject.se.SeContainerInitializer)\";" - + "cardinality:=multiple,osgi.serviceloader;" - + "filter:=\"(osgi.serviceloader=jakarta.enterprise.inject.spi.CDIProvider)\";" - + "cardinality:=multiple,osgi.extender;" - + "filter:=\"(osgi.extender=osgi.serviceloader.processor)\"," - + "osgi.contract;osgi.contract=JavaEL;filter:=\"(&(osgi.contract=JavaEL)(version=2.2.0))\"," - + "osgi.contract;osgi.contract=JavaInterceptor;" - + "filter:=\"(&(osgi.contract=JavaInterceptor)(version=1.2.0))\"," - + "osgi.contract;osgi.contract=JavaInject;" - + "filter:=\"(&(osgi.contract=JavaInject)(version=1.0.0))\"," - + "osgi.ee;filter:=\"(&(osgi.ee=JavaSE)(version=1.8))\"", + "osgi.serviceloader;filter:=\"(osgi.serviceloader=shaded.javax.enterprise.inject.se.SeContainerInitializer)\";cardinality:=multiple,osgi.serviceloader;filter:=\"(osgi.serviceloader=shaded.javax.enterprise.inject.spi.CDIProvider)\";cardinality:=multiple,osgi.extender;filter:=\"(osgi.extender=osgi.serviceloader.processor)\",osgi.contract;osgi.contract=JavaEL;filter:=\"(&(osgi.contract=JavaEL)(version=2.2.0))\",osgi.contract;osgi.contract=JavaInterceptor;filter:=\"(&(osg [...] attrs.getValue("Require-Capability")); } }
