You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by tr...@apache.org on 2018/01/09 05:24:08 UTC

svn commit: r1820625 - in /jackrabbit/commons/filevault/trunk/vault-core/src: main/java/org/apache/jackrabbit/vault/fs/impl/io/ main/java/org/apache/jackrabbit/vault/fs/io/ test/java/org/apache/jackrabbit/vault/fs/io/ test/resources/org/apache/jackrabb...

Author: tripod
Date: Tue Jan  9 05:24:07 2018
New Revision: 1820625

URL: http://svn.apache.org/viewvc?rev=1820625&view=rev
Log:
JCRVLT-257 ZipException: invalid code lengths set (closes #20)

Added:
    jackrabbit/commons/filevault/trunk/vault-core/src/test/java/org/apache/jackrabbit/vault/fs/io/JarExporterTest.java
    jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/
    jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/
    jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/
    jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/.content.xml
    jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/content/
    jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/content/.content.xml
    jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/content/dam/
    jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/content/dam/.content.xml
    jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/content/dam/asf_logo.png
Modified:
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/CompressionUtil.java
    jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/JarExporter.java

Modified: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/CompressionUtil.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/CompressionUtil.java?rev=1820625&r1=1820624&r2=1820625&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/CompressionUtil.java (original)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/io/CompressionUtil.java Tue Jan  9 05:24:07 2018
@@ -16,11 +16,17 @@
  */
 package org.apache.jackrabbit.vault.fs.impl.io;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.zip.Deflater;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+import java.util.zip.ZipOutputStream;
 
 import javax.annotation.Nonnull;
 import javax.jcr.RepositoryException;
@@ -43,6 +49,11 @@ public final class CompressionUtil {
     private static final Logger log = LoggerFactory.getLogger(CompressionUtil.class);
 
     /**
+     * If set to true the compression level can be changed for individual jar entries, otherwise false.
+     */
+    public static final boolean ENV_SUPPORTS_COMPRESSION_LEVEL_CHANGE = checkEnvironmentSupportsCompressionSwitch();
+
+    /**
      * Minimum length to run the auto-detection algorithm in Byte.
      */
     private static final long MIN_AUTO_DETECTION_LENGTH = 115 * 1024;
@@ -185,4 +196,49 @@ public final class CompressionUtil {
         return len * r < 438 * len;
     }
 
+    /**
+     * Check if writing a JarOutputStream supports switching the compression level for individual
+     * JarEntries. There are known issues with recent zlib versions and java which might result in broken
+     * packages being exported, when they contain already compressed binary entries according to CompressionUtil.
+     * <p/>
+     * for more information see:
+     * https://issues.apache.org/jira/browse/JCRVLT-257
+     * https://github.com/madler/zlib/issues/305
+     *
+     * @return {@code true} if the environment supports switching compression levels
+     */
+    private static boolean checkEnvironmentSupportsCompressionSwitch() {
+        Throwable exception = null;
+        ByteArrayOutputStream byteArrayOut = new ByteArrayOutputStream();
+        byte[] nullBytes = new byte[1024 * 1024];
+        try (ZipOutputStream zipOut = new ZipOutputStream(byteArrayOut)) {
+            zipOut.setLevel(Deflater.BEST_SPEED);
+            zipOut.putNextEntry(new ZipEntry("deflated.bin"));
+            zipOut.write(nullBytes);
+            zipOut.closeEntry();
+            zipOut.putNextEntry(new ZipEntry("stored.bin"));
+            zipOut.setLevel(Deflater.BEST_COMPRESSION);
+            zipOut.write(nullBytes);
+            zipOut.closeEntry();
+        } catch (Throwable e) {
+            exception = e;
+        }
+        if (exception == null) {
+            try (ZipInputStream zipIn = new ZipInputStream(new ByteArrayInputStream(byteArrayOut.toByteArray()))) {
+                for (int i = 0; i < 2; i++) {
+                    zipIn.getNextEntry();
+                    while (zipIn.read(nullBytes) >= 0) {
+                    }
+                }
+            } catch (Throwable e) {
+                exception = e;
+            }
+        }
+
+        if (exception != null) {
+            log.info("The current environment doesn't support switching compression level for individual JarEntries, see JCRVLT-257");
+            return false;
+        }
+        return true;
+    }
 }

Modified: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/JarExporter.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/JarExporter.java?rev=1820625&r1=1820624&r2=1820625&view=diff
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/JarExporter.java (original)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/io/JarExporter.java Tue Jan  9 05:24:07 2018
@@ -164,7 +164,9 @@ public class JarExporter extends Abstrac
             throws RepositoryException, IOException {
         ZipEntry e = new ZipEntry(getPlatformFilePath(file, relPath));
         Artifact a = file.getArtifact();
-        boolean compress = compressedLevel || CompressionUtil.isCompressible(a) >= 0;
+        boolean compress = compressedLevel
+                || !CompressionUtil.ENV_SUPPORTS_COMPRESSION_LEVEL_CHANGE
+                || CompressionUtil.isCompressible(a) >= 0;
         if (!compress) {
             jOut.setLevel(NO_COMPRESSION);
         }
@@ -209,7 +211,8 @@ public class JarExporter extends Abstrac
 
     public void write(ZipFile zip, ZipEntry entry) throws IOException {
         track("A", entry.getName());
-        if (!compressedLevel) {
+        boolean changeCompressionLevel = !compressedLevel && CompressionUtil.ENV_SUPPORTS_COMPRESSION_LEVEL_CHANGE;
+        if (changeCompressionLevel) {
             // The entry to be written is assumed to be incompressible
             jOut.setLevel(NO_COMPRESSION);
         }
@@ -224,7 +227,7 @@ public class JarExporter extends Abstrac
             in.close();
         }
         jOut.closeEntry();
-        if (!compressedLevel) {
+        if (changeCompressionLevel) {
             jOut.setLevel(level);
         }
     }

Added: jackrabbit/commons/filevault/trunk/vault-core/src/test/java/org/apache/jackrabbit/vault/fs/io/JarExporterTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/test/java/org/apache/jackrabbit/vault/fs/io/JarExporterTest.java?rev=1820625&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/test/java/org/apache/jackrabbit/vault/fs/io/JarExporterTest.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/test/java/org/apache/jackrabbit/vault/fs/io/JarExporterTest.java Tue Jan  9 05:24:07 2018
@@ -0,0 +1,121 @@
+/*
+ * 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.jackrabbit.vault.fs.io;
+
+import static java.util.zip.Deflater.BEST_COMPRESSION;
+import static java.util.zip.Deflater.BEST_SPEED;
+import static java.util.zip.Deflater.NO_COMPRESSION;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Date;
+import java.util.zip.ZipException;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.fs.api.AccessType;
+import org.apache.jackrabbit.vault.fs.api.Artifact;
+import org.apache.jackrabbit.vault.fs.api.SerializationType;
+import org.apache.jackrabbit.vault.fs.api.VaultFile;
+import org.junit.Test;
+
+public class JarExporterTest {
+
+    /**
+     * This test verifies that writing entries that can be compressed together with entries that are already compressed according to
+     * {@link org.apache.jackrabbit.vault.fs.impl.io.CompressionUtil} to the same file does result in a readable jar.
+     * <p/>
+     * There are certain environments that don't support changing the compression level for individual entries due to defects in the jdk
+     * and breaking changes made in recent zlib versions.
+     *
+     * @link https://issues.apache.org/jira/browse/JCRVLT-257
+     * @link https://github.com/madler/zlib/issues/305
+     */
+    @Test
+    public void testEntriesWithSuppressedCompression() throws RepositoryException, IOException {
+        Mocks m = new Mocks("org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression");
+        for (int level : new int[] { NO_COMPRESSION, BEST_COMPRESSION, BEST_SPEED }) {
+            File target = File.createTempFile("testEntriesWithSuppressedCompression", ".zip", null);
+            ZipStreamArchive archive = null;
+            try {
+                JarExporter exporter = new JarExporter(target, level);
+                exporter.open();
+                exporter.writeFile(m.mockFile(".content.xml", "application/xml"), null);
+                exporter.writeFile(m.mockFile("content/.content.xml", "application/xml"), null);
+                exporter.writeFile(m.mockFile("content/dam/.content.xml", "application/xml"), null);
+                // export a file that according to org.apache.jackrabbit.vault.fs.impl.io.CompressionUtil should not be compressed
+                exporter.writeFile(m.mockFile("content/dam/asf_logo.png", "image/png"), null);
+                exporter.close();
+                // now read the zip file
+                archive = new ZipStreamArchive(new FileInputStream(target));
+                archive.open(false); // this will throw, when the zip file is corrupt
+                // 8 entries including root and jcr_root
+                assertEquals("Wrong entry count for level " + level, 8, countEntries(archive.getRoot()));
+            } catch (ZipException ex) {
+                throw new AssertionError("Zip failed for level " + level, ex);
+            } finally {
+                if (archive != null) {
+                    archive.close();
+                }
+                target.delete();
+            }
+        }
+    }
+
+    private int countEntries(Archive.Entry entry) {
+        int c = 1;
+        for (Archive.Entry child : entry.getChildren()) {
+            c += countEntries(child);
+        }
+        return c;
+    }
+
+    private class Mocks {
+
+        private final String basePath;
+
+        Mocks(String basePath) {
+            this.basePath = basePath;
+        }
+
+        private VaultFile mockFile(String relPath, String contentType) throws RepositoryException, IOException {
+            InputStream in = this.getClass().getClassLoader().getResourceAsStream(basePath + '/' + relPath);
+            Artifact a = mockArtifact(in, contentType);
+            VaultFile vaultFile = mock(VaultFile.class);
+            when(vaultFile.getPath()).thenReturn("/" + relPath);
+            when(vaultFile.getArtifact()).thenReturn(a);
+
+            return vaultFile;
+        }
+
+        private Artifact mockArtifact(InputStream in, String contentType) throws RepositoryException, IOException {
+            Artifact a = mock(Artifact.class);
+            when(a.getSerializationType()).thenReturn(SerializationType.GENERIC);
+            when(a.getContentType()).thenReturn(contentType);
+            when(a.getLastModified()).thenReturn(new Date().getTime());
+            when(a.getPreferredAccess()).thenReturn(AccessType.STREAM);
+            when(a.getInputStream()).thenReturn(in);
+
+            return a;
+        }
+    }
+}

Added: jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/.content.xml
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/.content.xml?rev=1820625&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/.content.xml (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/.content.xml Tue Jan  9 05:24:07 2018
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
+<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal"
+    jcr:mixinTypes="[rep:AccessControllable,rep:RepoAccessControllable]"
+    jcr:primaryType="rep:root"/>

Added: jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/content/.content.xml
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/content/.content.xml?rev=1820625&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/content/.content.xml (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/content/.content.xml Tue Jan  9 05:24:07 2018
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
+<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal"
+    jcr:mixinTypes="[rep:AccessControllable]"
+    jcr:primaryType="sling:OrderedFolder">
+    <dam/>
+</jcr:root>

Added: jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/content/dam/.content.xml
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/content/dam/.content.xml?rev=1820625&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/content/dam/.content.xml (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/content/dam/.content.xml Tue Jan  9 05:24:07 2018
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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.
+  -->
+<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:rep="internal"
+    jcr:mixinTypes="[mix:lockable,rep:AccessControllable]"
+    jcr:primaryType="sling:Folder"/>

Added: jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/content/dam/asf_logo.png
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/content/dam/asf_logo.png?rev=1820625&view=auto
==============================================================================
Binary files jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/content/dam/asf_logo.png (added) and jackrabbit/commons/filevault/trunk/vault-core/src/test/resources/org/apache/jackrabbit/vault/fs/io/JarExporter/testEntriesWithSuppressedCompression/content/dam/asf_logo.png Tue Jan  9 05:24:07 2018 differ