From 0aa56da230bd0c86b8ffe7a8e6405b11dce4309a Mon Sep 17 00:00:00 2001 From: rfscholte Date: Sat, 8 Feb 2020 12:18:11 +0100 Subject: [PATCH] [MSHADE-350] Enable ManifestResourceTransformer to rewrite the manifest with relocations --- .../resource/ManifestResourceTransformer.java | 77 +++++++- .../apt/examples/resource-transformers.apt.vm | 12 ++ .../ManifestResourceTransformerTest.java | 170 ++++++++++++++++++ 3 files changed, 256 insertions(+), 3 deletions(-) create mode 100644 src/test/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformerTest.java 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 fd0bf48a..688078cf 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 @@ -19,10 +19,9 @@ * under the License. */ -import org.apache.maven.plugins.shade.relocation.Relocator; - import java.io.IOException; import java.io.InputStream; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.jar.Attributes; @@ -31,6 +30,8 @@ import java.util.jar.JarOutputStream; import java.util.jar.Manifest; +import org.apache.maven.plugins.shade.relocation.Relocator; + /** * A resource processor that allows the arbitrary addition of attributes to * the first MANIFEST.MF that is found in the set of JARs being processed, or @@ -42,17 +43,39 @@ public class ManifestResourceTransformer implements ResourceTransformer { - + private final List defaultAttributes = Arrays.asList( "Export-Package", + "Import-Package", + "Provide-Capability", + "Require-Capability" ); + // Configuration private String mainClass; private Map manifestEntries; + private List additionalAttributes; + // Fields private boolean manifestDiscovered; private Manifest manifest; + + public void setMainClass( String mainClass ) + { + this.mainClass = mainClass; + } + + public void setManifestEntries( Map manifestEntries ) + { + this.manifestEntries = manifestEntries; + } + + public void setAdditionalAttributes( List additionalAttributes ) + { + this.additionalAttributes = additionalAttributes; + } + @Override public boolean canTransformResource( String resource ) { if ( JarFile.MANIFEST_NAME.equalsIgnoreCase( resource ) ) @@ -63,6 +86,7 @@ public boolean canTransformResource( String resource ) return false; } + @Override public void processResource( String resource, InputStream is, List relocators ) throws IOException { @@ -72,15 +96,46 @@ public void processResource( String resource, InputStream is, List re if ( !manifestDiscovered ) { manifest = new Manifest( is ); + + if ( relocators != null && !relocators.isEmpty() ) + { + final Attributes attributes = manifest.getMainAttributes(); + + for ( final String attribute : defaultAttributes ) + { + final String attributeValue = attributes.getValue( attribute ); + if ( attributeValue != null ) + { + String newValue = relocate( attributeValue, relocators ); + attributes.putValue( attribute, newValue ); + } + } + + if ( additionalAttributes != null ) + { + for ( final String attribute : additionalAttributes ) + { + final String attributeValue = attributes.getValue( attribute ); + if ( attributeValue != null ) + { + String newValue = relocate( attributeValue, relocators ); + attributes.putValue( attribute, newValue ); + } + } + } + } + manifestDiscovered = true; } } + @Override public boolean hasTransformedResource() { return true; } + @Override public void modifyOutputStream( JarOutputStream jos ) throws IOException { @@ -108,4 +163,20 @@ public void modifyOutputStream( JarOutputStream jos ) jos.putNextEntry( new JarEntry( JarFile.MANIFEST_NAME ) ); manifest.write( jos ); } + + private String relocate( String originalValue, List relocators ) + { + String newValue = originalValue; + for ( Relocator relocator : relocators ) + { + String value; + do + { + value = newValue; + newValue = relocator.relocateClass( value ); + } + while ( !value.equals( newValue ) ); + } + return newValue; + } } diff --git a/src/site/apt/examples/resource-transformers.apt.vm b/src/site/apt/examples/resource-transformers.apt.vm index 7248027a..1482baa4 100644 --- a/src/site/apt/examples/resource-transformers.apt.vm +++ b/src/site/apt/examples/resource-transformers.apt.vm @@ -387,6 +387,18 @@ Transformers in <<>> * the <<>> entry to the value of the <<>> property. + By default the <<>> will relocate the following attributes: + + * Export-Package + + * Import-Package + + * Provide-Capability + + * Require-Capability + + With <<>> you can specify the attributes that need to be relocated too. + +----- 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 new file mode 100644 index 00000000..c1c7ff2a --- /dev/null +++ b/src/test/java/org/apache/maven/plugins/shade/resource/ManifestResourceTransformerTest.java @@ -0,0 +1,170 @@ +package org.apache.maven.plugins.shade.resource; + +/* + * 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 static org.junit.Assert.assertEquals; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.jar.Attributes; +import java.util.jar.JarFile; +import java.util.jar.JarInputStream; +import java.util.jar.JarOutputStream; +import java.util.jar.Manifest; + +import org.apache.maven.plugins.shade.relocation.Relocator; +import org.apache.maven.plugins.shade.relocation.SimpleRelocator; +import org.junit.Before; +import org.junit.Test; + +public class ManifestResourceTransformerTest +{ + private ManifestResourceTransformer transformer; + + @Before + public void setUp() + { + transformer = new ManifestResourceTransformer(); + } + + @Test + public void rewriteDefaultAttributes() throws Exception + { + final Manifest manifest = new Manifest(); + final Attributes attributes = manifest.getMainAttributes(); + attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0"); + attributes.putValue("Export-Package", + "javax.decorator;version=\"2.0\";uses:=\"javax.enterprise.inject\"," + + "javax.enterprise.context;version=\"2.0\";uses:=\"javax.enterprise.util,javax.inject\""); + attributes.putValue("Import-Package", + "javax.el,javax.enterprise.context;version=\"[2.0,3)\""); + attributes.putValue("Provide-Capability", + "osgi.contract;osgi.contract=JavaCDI;uses:=\"" + + "javax.enterprise.context,javax.enterprise.context.spi,javax.enterprise.context.control," + + "javax.enterprise.util,javax.enterprise.inject,javax.enterprise.inject.spi," + + "javax.enterprise.inject.spi.configurator,javax.enterprise.inject.literal," + + "javax.enterprise.inject.se,javax.enterprise.event,javax.decorator\";" + + "version:List=\"2.0,1.2,1.1,1.0\""); + attributes.putValue("Require-Capability", + "osgi.serviceloader;" + + "filter:=\"(osgi.serviceloader=javax.enterprise.inject.se.SeContainerInitializer)\";" + + "cardinality:=multiple," + + "osgi.serviceloader;" + + "filter:=\"(osgi.serviceloader=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:=\"(&(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))\""); + + List relocators = + Collections.singletonList( new SimpleRelocator( "javax", "jakarta", + Collections.emptyList(), + Collections.emptyList() ) ); + + final ByteArrayOutputStream out = transform( manifest, relocators ); + + try ( final JarInputStream jis = new JarInputStream( new ByteArrayInputStream( out.toByteArray() ) ) ) + { + final 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=\"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")); + } + } + + @Test + public void rewriteAdditionalAttributes() throws Exception + { + final Manifest manifest = new Manifest(); + final Attributes attributes = manifest.getMainAttributes(); + attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0"); + attributes.putValue("description-custom", + "This jar uses javax packages"); + + List relocators = + Collections.singletonList( new SimpleRelocator( "javax", "jakarta", + Collections.emptyList(), + Collections.emptyList() ) ); + + transformer.setAdditionalAttributes( Arrays.asList("description-custom", "attribute-unknown") ); + final ByteArrayOutputStream out = transform( manifest, relocators ); + + try ( final JarInputStream jis = new JarInputStream( new ByteArrayInputStream( out.toByteArray() ) ) ) + { + final Attributes attrs = jis.getManifest().getMainAttributes(); + assertEquals( "This jar uses jakarta packages", attrs.getValue( "description-custom" ) ); + } + } + + private ByteArrayOutputStream transform( final Manifest manifest, List relocators ) + throws IOException + { + final ByteArrayOutputStream mboas = new ByteArrayOutputStream(); + try ( final OutputStream mos = mboas ) + { + manifest.write( mos ); + } + transformer.processResource( JarFile.MANIFEST_NAME, new ByteArrayInputStream( mboas.toByteArray() ), + relocators ); + + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + try ( final JarOutputStream jarOutputStream = new JarOutputStream( out ) ) + { + transformer.modifyOutputStream( jarOutputStream ); + } + return out; + } +} \ No newline at end of file