You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by bo...@apache.org on 2017/04/18 14:40:47 UTC

[2/4] commons-compress git commit: COMPRESS-385, add ArchiveStreamFactory; undo boneheaded RELEASE-NOTES.txt fiasco.

COMPRESS-385, add ArchiveStreamFactory; undo boneheaded RELEASE-NOTES.txt fiasco.


Project: http://git-wip-us.apache.org/repos/asf/commons-compress/repo
Commit: http://git-wip-us.apache.org/repos/asf/commons-compress/commit/2cf57511
Tree: http://git-wip-us.apache.org/repos/asf/commons-compress/tree/2cf57511
Diff: http://git-wip-us.apache.org/repos/asf/commons-compress/diff/2cf57511

Branch: refs/heads/master
Commit: 2cf575115748e9865daaa369507019141573b86c
Parents: 30fce9d
Author: tballison <ta...@mitre.org>
Authored: Fri Apr 14 12:35:01 2017 -0400
Committer: Stefan Bodewig <bo...@apache.org>
Committed: Tue Apr 18 14:40:25 2017 +0200

----------------------------------------------------------------------
 RELEASE-NOTES.txt                               |  12 --
 src/changes/changes.xml                         |   4 +
 .../archivers/ArchiveStreamFactory.java         | 110 +++++++++++--------
 .../compressors/CompressorStreamFactory.java    |   2 +-
 .../commons/compress/MockEvilInputStream.java   |  39 +++++++
 .../archivers/ArchiveStreamFactoryTest.java     |  44 ++++++++
 .../compressors/DetectCompressorTestCase.java   |  21 +---
 7 files changed, 158 insertions(+), 74 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-compress/blob/2cf57511/RELEASE-NOTES.txt
----------------------------------------------------------------------
diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index b16a4ea..21c9c4c 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -5,18 +5,6 @@ compression and archive formats.  These include: bzip2, gzip, pack200,
 lzma, xz, Snappy, traditional Unix Compress, DEFLATE and ar, cpio,
 jar, tar, zip, dump, 7z, arj.
 
-Release 1.14
-------------
-
-Commons Compress 1.13 is the first version to require Java 7 at
-runtime.
-
-Changes in this version include:
-
-New features:
-o detect(InputStream) now available in CompressorStreamFactory
-   Issue: Compress-385.
-
 Release 1.13
 ------------
 

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/2cf57511/src/changes/changes.xml
----------------------------------------------------------------------
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 44d57f0..f1ac318 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -44,6 +44,10 @@ The <action> type attribute can be add,update,fix,remove.
   <body>
     <release version="1.14" date="not released, yet"
              description="Release 1.14">
+      <action issue="COMPRESS-385" type="add" date="2017-04-14">
+        Add static detect(InputStream in) to CompressorStreamFactory
+        and ArchiveStreamFactory
+      </action>
       <action issue="COMPRESS-378" type="fix" date="2017-01-09">
         SnappyCompressorInputStream slides the window too early
         leading to ArrayIndexOutOfBoundsExceptions for some streams.

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/2cf57511/src/main/java/org/apache/commons/compress/archivers/ArchiveStreamFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/archivers/ArchiveStreamFactory.java b/src/main/java/org/apache/commons/compress/archivers/ArchiveStreamFactory.java
index 6ca0437..cc51458 100644
--- a/src/main/java/org/apache/commons/compress/archivers/ArchiveStreamFactory.java
+++ b/src/main/java/org/apache/commons/compress/archivers/ArchiveStreamFactory.java
@@ -473,6 +473,16 @@ public class ArchiveStreamFactory implements ArchiveStreamProvider {
      */
     public ArchiveInputStream createArchiveInputStream(final InputStream in)
             throws ArchiveException {
+        return createArchiveInputStream(detect(in), in);
+    }
+
+    /**
+     * Try to determine the type of Archiver
+     * @param in input stream
+     * @return type of archiver if found
+     * @throws ArchiveException if an archiver cannot be detected in the stream
+     */
+    public static String detect(InputStream in) throws ArchiveException {
         if (in == null) {
             throw new IllegalArgumentException("Stream must not be null.");
         }
@@ -483,62 +493,72 @@ public class ArchiveStreamFactory implements ArchiveStreamProvider {
 
         final byte[] signature = new byte[SIGNATURE_SIZE];
         in.mark(signature.length);
+        int signatureLength = -1;
         try {
-            int signatureLength = IOUtils.readFully(in, signature);
+            signatureLength = IOUtils.readFully(in, signature);
             in.reset();
-            if (ZipArchiveInputStream.matches(signature, signatureLength)) {
-                return createArchiveInputStream(ZIP, in);
-            } else if (JarArchiveInputStream.matches(signature, signatureLength)) {
-                return createArchiveInputStream(JAR, in);
-            } else if (ArArchiveInputStream.matches(signature, signatureLength)) {
-                return createArchiveInputStream(AR, in);
-            } else if (CpioArchiveInputStream.matches(signature, signatureLength)) {
-                return createArchiveInputStream(CPIO, in);
-            } else if (ArjArchiveInputStream.matches(signature, signatureLength)) {
-                return createArchiveInputStream(ARJ, in);
-            } else if (SevenZFile.matches(signature, signatureLength)) {
-                throw new StreamingNotSupportedException(SEVEN_Z);
-            }
+        } catch (IOException e) {
+            throw new ArchiveException("IOException while reading signature.");
+        }
 
-            // Dump needs a bigger buffer to check the signature;
-            final byte[] dumpsig = new byte[DUMP_SIGNATURE_SIZE];
-            in.mark(dumpsig.length);
+        if (JarArchiveInputStream.matches(signature, signatureLength)) {
+            return JAR;
+        } else if (ZipArchiveInputStream.matches(signature, signatureLength)) {
+            return ZIP;
+        } else if (ArArchiveInputStream.matches(signature, signatureLength)) {
+            return AR;
+        } else if (CpioArchiveInputStream.matches(signature, signatureLength)) {
+            return CPIO;
+        } else if (ArjArchiveInputStream.matches(signature, signatureLength)) {
+            return ARJ;
+        } else if (SevenZFile.matches(signature, signatureLength)) {
+            throw new StreamingNotSupportedException(SEVEN_Z);
+        }
+
+        // Dump needs a bigger buffer to check the signature;
+        final byte[] dumpsig = new byte[DUMP_SIGNATURE_SIZE];
+        in.mark(dumpsig.length);
+        try {
             signatureLength = IOUtils.readFully(in, dumpsig);
             in.reset();
-            if (DumpArchiveInputStream.matches(dumpsig, signatureLength)) {
-                return createArchiveInputStream(DUMP, in);
-            }
+        } catch (IOException e) {
+            throw new ArchiveException("IOException while reading dump signature");
+        }
+        if (DumpArchiveInputStream.matches(dumpsig, signatureLength)) {
+            return DUMP;
+        }
 
-            // Tar needs an even bigger buffer to check the signature; read the first block
-            final byte[] tarHeader = new byte[TAR_HEADER_SIZE];
-            in.mark(tarHeader.length);
+        // Tar needs an even bigger buffer to check the signature; read the first block
+        final byte[] tarHeader = new byte[TAR_HEADER_SIZE];
+        in.mark(tarHeader.length);
+        try {
             signatureLength = IOUtils.readFully(in, tarHeader);
             in.reset();
-            if (TarArchiveInputStream.matches(tarHeader, signatureLength)) {
-                return createArchiveInputStream(TAR, in);
-            }
-            // COMPRESS-117 - improve auto-recognition
-            if (signatureLength >= TAR_HEADER_SIZE) {
-                TarArchiveInputStream tais = null;
-                try {
-                    tais = new TarArchiveInputStream(new ByteArrayInputStream(tarHeader));
-                    // COMPRESS-191 - verify the header checksum
-                    if (tais.getNextTarEntry().isCheckSumOK()) {
-                        return createArchiveInputStream(TAR, in);
-                    }
-                } catch (final Exception e) { // NOPMD
-                    // can generate IllegalArgumentException as well
-                    // as IOException
-                    // autodetection, simply not a TAR
-                    // ignored
-                } finally {
-                    IOUtils.closeQuietly(tais);
+        } catch (IOException e) {
+            throw new ArchiveException("IOException while reading tar signature");
+        }
+        if (TarArchiveInputStream.matches(tarHeader, signatureLength)) {
+            return TAR;
+        }
+
+        // COMPRESS-117 - improve auto-recognition
+        if (signatureLength >= TAR_HEADER_SIZE) {
+            TarArchiveInputStream tais = null;
+            try {
+                tais = new TarArchiveInputStream(new ByteArrayInputStream(tarHeader));
+                // COMPRESS-191 - verify the header checksum
+                if (tais.getNextTarEntry().isCheckSumOK()) {
+                    return TAR;
                 }
+            } catch (final Exception e) { // NOPMD
+                // can generate IllegalArgumentException as well
+                // as IOException
+                // autodetection, simply not a TAR
+                // ignored
+            } finally {
+                IOUtils.closeQuietly(tais);
             }
-        } catch (final IOException e) {
-            throw new ArchiveException("Could not use reset and mark operations.", e);
         }
-
         throw new ArchiveException("No Archiver found for the stream signature");
     }
 

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/2cf57511/src/main/java/org/apache/commons/compress/compressors/CompressorStreamFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/compressors/CompressorStreamFactory.java b/src/main/java/org/apache/commons/compress/compressors/CompressorStreamFactory.java
index c347cca..87c1209 100644
--- a/src/main/java/org/apache/commons/compress/compressors/CompressorStreamFactory.java
+++ b/src/main/java/org/apache/commons/compress/compressors/CompressorStreamFactory.java
@@ -400,7 +400,7 @@ public class CompressorStreamFactory implements CompressorStreamProvider {
             signatureLength = IOUtils.readFully(in, signature);
             in.reset();
         } catch (IOException e) {
-            throw new CompressorException("Failed while reading signature from InputStream.", e);
+            throw new CompressorException("IOException while reading signature.", e);
         }
 
         if (BZip2CompressorInputStream.matches(signature, signatureLength)) {

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/2cf57511/src/test/java/org/apache/commons/compress/MockEvilInputStream.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/compress/MockEvilInputStream.java b/src/test/java/org/apache/commons/compress/MockEvilInputStream.java
new file mode 100644
index 0000000..884cf50
--- /dev/null
+++ b/src/test/java/org/apache/commons/compress/MockEvilInputStream.java
@@ -0,0 +1,39 @@
+package org.apache.commons.compress;/*
+ * 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 java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Simple mock InputStream that always throws an IOException
+ * when {@link #read()} or {@link #read(byte[], int, int)}
+ * is called.
+ */
+public class MockEvilInputStream extends InputStream {
+
+    @Override
+    public int read() throws IOException {
+        throw new IOException("Evil");
+    }
+
+    @Override
+    public int read(byte[] bytes, int offset, int length) throws IOException {
+        throw new IOException("Evil");
+    }
+}
+

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/2cf57511/src/test/java/org/apache/commons/compress/archivers/ArchiveStreamFactoryTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/compress/archivers/ArchiveStreamFactoryTest.java b/src/test/java/org/apache/commons/compress/archivers/ArchiveStreamFactoryTest.java
index 4236b28..27e2790 100644
--- a/src/test/java/org/apache/commons/compress/archivers/ArchiveStreamFactoryTest.java
+++ b/src/test/java/org/apache/commons/compress/archivers/ArchiveStreamFactoryTest.java
@@ -32,6 +32,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.lang.reflect.Field;
 
+import org.apache.commons.compress.MockEvilInputStream;
 import org.apache.commons.compress.archivers.arj.ArjArchiveInputStream;
 import org.apache.commons.compress.archivers.cpio.CpioArchiveInputStream;
 import org.apache.commons.compress.archivers.dump.DumpArchiveInputStream;
@@ -226,6 +227,49 @@ public class ArchiveStreamFactoryTest {
         DUMP_DEFAULT = dflt;
     }
 
+    @Test
+    public void testDetect() throws Exception {
+        for (String extension : new String[]{
+                ArchiveStreamFactory.ARJ,
+                ArchiveStreamFactory.CPIO,
+                ArchiveStreamFactory.DUMP,
+                ArchiveStreamFactory.JAR,
+                ArchiveStreamFactory.TAR,
+ //TODO-- figure out how to differentiate btwn JAR and ZIP
+ //               ArchiveStreamFactory.ZIP
+        }) {
+            assertEquals(extension, detect("bla."+extension));
+        }
+
+        try {
+            ArchiveStreamFactory.detect(new BufferedInputStream(new ByteArrayInputStream(new byte[0])));
+            fail("shouldn't be able to detect empty stream");
+        } catch (ArchiveException e) {
+            assertEquals("No Archiver found for the stream signature", e.getMessage());
+        }
+
+        try {
+            ArchiveStreamFactory.detect(null);
+            fail("shouldn't be able to detect null stream");
+        } catch (IllegalArgumentException e) {
+            assertEquals("Stream must not be null.", e.getMessage());
+        }
+
+        try {
+            ArchiveStreamFactory.detect(new BufferedInputStream(new MockEvilInputStream()));
+            fail("Expected ArchiveException");
+        } catch (ArchiveException e) {
+            assertEquals("IOException while reading signature.", e.getMessage());
+        }
+    }
+
+    private String detect(String resource) throws IOException, ArchiveException {
+        try(InputStream in = new BufferedInputStream(new FileInputStream(
+                getFile(resource)))) {
+            return ArchiveStreamFactory.detect(in);
+        }
+    }
+
     static final TestData[] TESTS = {
         new TestData("bla.arj", ArchiveStreamFactory.ARJ, false, ARJ_DEFAULT, FACTORY, "charsetName"),
         new TestData("bla.arj", ArchiveStreamFactory.ARJ, false, "UTF-8", FACTORY_UTF8, "charsetName"),

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/2cf57511/src/test/java/org/apache/commons/compress/compressors/DetectCompressorTestCase.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/compress/compressors/DetectCompressorTestCase.java b/src/test/java/org/apache/commons/compress/compressors/DetectCompressorTestCase.java
index 187d931..8ccf579 100644
--- a/src/test/java/org/apache/commons/compress/compressors/DetectCompressorTestCase.java
+++ b/src/test/java/org/apache/commons/compress/compressors/DetectCompressorTestCase.java
@@ -31,6 +31,7 @@ import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
+import org.apache.commons.compress.MockEvilInputStream;
 import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
 import org.apache.commons.compress.compressors.deflate.DeflateCompressorInputStream;
 import org.apache.commons.compress.compressors.gzip.GzipCompressorInputStream;
@@ -121,6 +122,7 @@ public final class DetectCompressorTestCase {
 
     @Test
     public void testDetect() throws Exception {
+
         assertEquals(CompressorStreamFactory.BZIP2, detect("bla.txt.bz2"));
         assertEquals(CompressorStreamFactory.GZIP, detect("bla.tgz"));
         assertEquals(CompressorStreamFactory.PACK200, detect("bla.pack"));
@@ -131,7 +133,7 @@ public final class DetectCompressorTestCase {
             CompressorStreamFactory.detect(new BufferedInputStream(new ByteArrayInputStream(new byte[0])));
             fail("shouldn't be able to detect empty stream");
         } catch (CompressorException e) {
-            assertTrue(e.getMessage().contains("No Compressor found"));
+            assertEquals("No Compressor found for the stream signature.", e.getMessage());
         }
 
         try {
@@ -142,10 +144,10 @@ public final class DetectCompressorTestCase {
         }
 
         try {
-            CompressorStreamFactory.detect(new BufferedInputStream(new BadInputStream()));
+            CompressorStreamFactory.detect(new BufferedInputStream(new MockEvilInputStream()));
             fail("Expected IOException");
         } catch (CompressorException e) {
-            assertEquals("Failed while reading signature from InputStream.", e.getMessage());
+            assertEquals("IOException while reading signature.", e.getMessage());
         }
 
 
@@ -216,17 +218,4 @@ public final class DetectCompressorTestCase {
                    new BufferedInputStream(new FileInputStream(
                        getFile(resource))));
     }
-
-    private static class BadInputStream extends InputStream {
-        @Override
-        public int read() throws IOException {
-            throw new IOException("Bad");
-        }
-
-        @Override
-        public int read(byte[] bytes, int offset, int length) throws IOException {
-            throw new IOException("Bad");
-        }
-    }
-
 }