This is an automated email from the ASF dual-hosted git repository.

rmannibucau pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-shade-plugin.git


The following commit(s) were added to refs/heads/master by this push:
     new c798d01  [MSHADE-378] Shade plugin changes the compression level of 
nested jar… (#73)
c798d01 is described below

commit c798d01138e9fecdd6422a2a8acce22ca8987924
Author: jenrryyou <flyaway.y...@qq.com>
AuthorDate: Sun Nov 22 23:17:56 2020 +0800

    [MSHADE-378] Shade plugin changes the compression level of nested jar… (#73)
    
    * [MSHADE-378] Shade plugin changes the compression level of nested jar 
entries
    
    * [MSHADE-378] Shade plugin changes the compression level of nested jar 
entries
    
    Co-authored-by: shaoyao <jeremy....@alibaba-inc.com>
---
 .../apache/maven/plugins/shade/DefaultShader.java  | 107 +++++++++++++++++++--
 .../maven/plugins/shade/DefaultShaderTest.java     |  67 +++++++++++++
 2 files changed, 165 insertions(+), 9 deletions(-)

diff --git a/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java 
b/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java
index 6f3c4e6..540eccd 100644
--- a/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java
+++ b/src/main/java/org/apache/maven/plugins/shade/DefaultShader.java
@@ -21,14 +21,17 @@ package org.apache.maven.plugins.shade;
 
 import java.io.BufferedOutputStream;
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.OutputStreamWriter;
+import java.io.PushbackInputStream;
 import java.io.Writer;
 import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.Enumeration;
@@ -42,6 +45,8 @@ import java.util.jar.JarFile;
 import java.util.jar.JarOutputStream;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.zip.CRC32;
+import java.util.zip.ZipEntry;
 import java.util.zip.ZipException;
 
 import org.apache.commons.lang3.StringUtils;
@@ -71,6 +76,7 @@ public class DefaultShader
     extends AbstractLogEnabled
     implements Shader
 {
+    private static final int BUFFER_SIZE = 32 * 1024;
 
     public void shade( ShadeRequest shadeRequest )
         throws IOException, MojoExecutionException
@@ -150,6 +156,73 @@ public class DefaultShader
         }
     }
 
+    /**
+     * {@link InputStream} that can peek ahead at zip header bytes.
+     */
+    private static class ZipHeaderPeekInputStream extends PushbackInputStream
+    {
+
+        private static final byte[] ZIP_HEADER = new byte[] {0x50, 0x4b, 0x03, 
0x04};
+
+        private static final int HEADER_LEN = 4;
+
+        protected ZipHeaderPeekInputStream( InputStream in )
+        {
+            super( in, HEADER_LEN );
+        }
+
+        public boolean hasZipHeader() throws IOException
+        {
+            final byte[] header = new byte[HEADER_LEN];
+            super.read( header, 0, HEADER_LEN );
+            super.unread( header );
+            return Arrays.equals( header, ZIP_HEADER );
+        }
+    }
+
+    /**
+     * Data holder for CRC and Size.
+     */
+    private static class CrcAndSize
+    {
+
+        private final CRC32 crc = new CRC32();
+
+        private long size;
+
+        CrcAndSize( File file ) throws IOException
+        {
+            try ( FileInputStream inputStream = new FileInputStream( file ) )
+            {
+                load( inputStream );
+            }
+        }
+
+        CrcAndSize( InputStream inputStream ) throws IOException
+        {
+            load( inputStream );
+        }
+
+        private void load( InputStream inputStream ) throws IOException
+        {
+            byte[] buffer = new byte[BUFFER_SIZE];
+            int bytesRead;
+            while ( ( bytesRead = inputStream.read( buffer ) ) != -1 )
+            {
+                this.crc.update( buffer, 0, bytesRead );
+                this.size += bytesRead;
+            }
+        }
+
+        public void setupStoredEntry( JarEntry entry )
+        {
+            entry.setSize( this.size );
+            entry.setCompressedSize( this.size );
+            entry.setCrc( this.crc.getValue() );
+            entry.setMethod( ZipEntry.STORED );
+        }
+    }
+
     private void shadeJars( ShadeRequest shadeRequest, Set<String> resources, 
List<ResourceTransformer> transformers,
                             RelocatorRemapper remapper, JarOutputStream jos, 
Multimap<String, File> duplicates )
         throws IOException, MojoExecutionException
@@ -255,7 +328,7 @@ public class DefaultShader
                         return;
                     }
 
-                    addResource( resources, jos, mappedName, entry.getTime(), 
in );
+                    addResource( resources, jos, mappedName, entry, jarFile );
                 }
                 else
                 {
@@ -584,19 +657,35 @@ public class DefaultShader
         resources.add( name );
     }
 
-    private void addResource( Set<String> resources, JarOutputStream jos, 
String name, long time,
-                              InputStream is )
-        throws IOException
+    private void addResource( Set<String> resources, JarOutputStream jos, 
String name, JarEntry originalEntry,
+                              JarFile jarFile ) throws IOException
     {
-        final JarEntry entry = new JarEntry( name );
+        ZipHeaderPeekInputStream inputStream = new ZipHeaderPeekInputStream( 
jarFile.getInputStream( originalEntry ) );
+        try
+        {
+            final JarEntry entry = new JarEntry( name );
 
-        entry.setTime( time );
+            // We should not change compressed level of uncompressed entries, 
otherwise JVM can't load these nested jars
+            if ( inputStream.hasZipHeader() && originalEntry.getMethod() == 
ZipEntry.STORED )
+            {
+                new CrcAndSize( inputStream ).setupStoredEntry( entry );
+                inputStream.close();
+                inputStream = new ZipHeaderPeekInputStream( 
jarFile.getInputStream( originalEntry ) );
+            }
 
-        jos.putNextEntry( entry );
 
-        IOUtil.copy( is, jos );
+            entry.setTime( originalEntry.getTime() );
 
-        resources.add( name );
+            jos.putNextEntry( entry );
+
+            IOUtil.copy( inputStream, jos );
+
+            resources.add( name );
+        }
+        finally
+        {
+            inputStream.close();
+        }
     }
 
     static class RelocatorRemapper
diff --git 
a/src/test/java/org/apache/maven/plugins/shade/DefaultShaderTest.java 
b/src/test/java/org/apache/maven/plugins/shade/DefaultShaderTest.java
index 7eedcf5..249258f 100644
--- a/src/test/java/org/apache/maven/plugins/shade/DefaultShaderTest.java
+++ b/src/test/java/org/apache/maven/plugins/shade/DefaultShaderTest.java
@@ -20,6 +20,7 @@ package org.apache.maven.plugins.shade;
  */
 
 import java.io.File;
+import java.io.FileInputStream;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.lang.reflect.Field;
@@ -33,7 +34,10 @@ import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
 import java.util.jar.JarOutputStream;
+import java.util.zip.CRC32;
+import java.util.zip.ZipEntry;
 
 import org.apache.maven.plugin.MojoExecutionException;
 import org.apache.maven.plugins.shade.filter.Filter;
@@ -45,6 +49,8 @@ import 
org.apache.maven.plugins.shade.resource.ResourceTransformer;
 import org.codehaus.plexus.logging.AbstractLogger;
 import org.codehaus.plexus.logging.Logger;
 import org.codehaus.plexus.logging.console.ConsoleLogger;
+import org.codehaus.plexus.util.IOUtil;
+import org.junit.Assert;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
 import org.objectweb.asm.ClassReader;
@@ -262,6 +268,67 @@ public class DefaultShaderTest
         }
     }
 
+    @Test
+    public void testShaderWithNestedJar() throws Exception
+    {
+        TemporaryFolder temporaryFolder = new TemporaryFolder();
+
+        final String innerJarFileName = "inner.jar";
+
+        temporaryFolder.create();
+        File innerJar = temporaryFolder.newFile( innerJarFileName );
+        try ( JarOutputStream jos = new JarOutputStream( new FileOutputStream( 
innerJar ) ) )
+        {
+            jos.putNextEntry( new JarEntry( "foo.txt" ) );
+            jos.write( "c1".getBytes( StandardCharsets.UTF_8 ) );
+            jos.closeEntry();
+        }
+
+        File outerJar = temporaryFolder.newFile( "outer.jar" );
+        try ( JarOutputStream jos = new JarOutputStream( new FileOutputStream( 
outerJar ) ) )
+        {
+            FileInputStream innerStream = new FileInputStream( innerJar );
+            byte[] bytes = IOUtil.toByteArray( innerStream, 32 * 1024 );
+            innerStream.close();
+            writeEntryWithoutCompression( innerJarFileName, bytes, jos );
+        }
+
+
+        ShadeRequest shadeRequest = new ShadeRequest();
+        shadeRequest.setJars( new LinkedHashSet<>( Collections.singleton( 
outerJar ) ) );
+        shadeRequest.setFilters( new ArrayList<Filter>() );
+        shadeRequest.setRelocators( new ArrayList<Relocator>() );
+        shadeRequest.setResourceTransformers( new 
ArrayList<ResourceTransformer>() );
+        File shadedFile = temporaryFolder.newFile( "shaded.jar" );
+        shadeRequest.setUberJar( shadedFile );
+
+        DefaultShader shader = newShader();
+        shader.shade( shadeRequest );
+
+        JarFile shadedJarFile = new JarFile( shadedFile );
+        JarEntry entry = shadedJarFile.getJarEntry( innerJarFileName );
+
+        //After shading, entry compression method should not be changed.
+        Assert.assertEquals( entry.getMethod(), ZipEntry.STORED );
+
+        temporaryFolder.delete();
+    }
+
+    private void writeEntryWithoutCompression( String entryName, byte[] 
entryBytes, JarOutputStream jos ) throws IOException
+    {
+        final JarEntry entry = new JarEntry( entryName );
+        final int size = entryBytes.length;
+        final CRC32 crc = new CRC32();
+        crc.update( entryBytes, 0, size );
+        entry.setSize( size );
+        entry.setCompressedSize( size );
+        entry.setMethod( ZipEntry.STORED );
+        entry.setCrc( crc.getValue() );
+        jos.putNextEntry( entry );
+        jos.write( entryBytes );
+        jos.closeEntry();
+    }
+
     private void shaderWithPattern( String shadedPattern, File jar, String[] 
excludes )
         throws Exception
     {

Reply via email to