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 2018/05/02 04:16:45 UTC

commons-compress git commit: COMPRESS-443 provide a custom InputStream for special skip problems

Repository: commons-compress
Updated Branches:
  refs/heads/master ffb618d50 -> e15c80ae3


COMPRESS-443 provide a custom InputStream for special skip problems


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

Branch: refs/heads/master
Commit: e15c80ae3fafe8903f220a6232c37a77b919c60e
Parents: ffb618d
Author: Stefan Bodewig <bo...@apache.org>
Authored: Wed May 2 06:16:14 2018 +0200
Committer: Stefan Bodewig <bo...@apache.org>
Committed: Wed May 2 06:16:14 2018 +0200

----------------------------------------------------------------------
 src/changes/changes.xml                         |  4 +
 .../utils/SkipShieldingInputStream.java         | 51 +++++++++++
 src/site/xdoc/limitations.xml                   | 15 ++++
 .../utils/SkipShieldingInputStreamTest.java     | 91 ++++++++++++++++++++
 4 files changed, 161 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/commons-compress/blob/e15c80ae/src/changes/changes.xml
----------------------------------------------------------------------
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 11f0193..44a69ab 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -74,6 +74,10 @@ The <action> type attribute can be add,update,fix,remove.
         Added a unit test that is supposed to fail if we break the
         OSGi manifest entries again.
       </action>
+      <action issue="COMPRESS-443" type="add" date="2018-05-02">
+        Add a new SkipShieldingInputStream class that can be used wit
+        streams that throw an IOException whne skip is invoked.
+      </action>
     </release>
     <release version="1.16.1" date="2018-02-10"
              description="Release 1.16.1">

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/e15c80ae/src/main/java/org/apache/commons/compress/utils/SkipShieldingInputStream.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/commons/compress/utils/SkipShieldingInputStream.java b/src/main/java/org/apache/commons/compress/utils/SkipShieldingInputStream.java
new file mode 100644
index 0000000..09b5ce2
--- /dev/null
+++ b/src/main/java/org/apache/commons/compress/utils/SkipShieldingInputStream.java
@@ -0,0 +1,51 @@
+/*
+ *  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.commons.compress.utils;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * A wrapper that overwrites {@link #skip} and delegates to {@link #read} instead.
+ *
+ * <p>Some implementations of {@link InputStream} implement {@link
+ * InputStream#skip} in a way that throws an expecption if the stream
+ * is not seekable - {@link System#in System.in} is known to behave
+ * that way. For such a stream it is impossible to invoke skip at all
+ * and you have to read from the stream (and discard the data read)
+ * instead. Skipping is potentially much faster than reading so we do
+ * want to invoke {@code skip} when possible. We provide this class so
+ * you can wrap your own {@link InputStream} in it if you encounter
+ * problems with {@code skip} throwing an excpetion.</p>
+ *
+ * @since 1.17
+ */
+public class SkipShieldingInputStream extends FilterInputStream {
+    private static final int SKIP_BUFFER_SIZE = 8192;
+    // we can use a shared buffer as the content is discarded anyway
+    private static final byte[] SKIP_BUFFER = new byte[SKIP_BUFFER_SIZE];
+    public SkipShieldingInputStream(InputStream in) {
+        super(in);
+    }
+
+    @Override
+    public long skip(long n) throws IOException {
+        return n < 0 ? 0 : read(SKIP_BUFFER, 0, (int) Math.min(n, SKIP_BUFFER_SIZE));
+    }
+}

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/e15c80ae/src/site/xdoc/limitations.xml
----------------------------------------------------------------------
diff --git a/src/site/xdoc/limitations.xml b/src/site/xdoc/limitations.xml
index 9fc45f6..c78adcc 100644
--- a/src/site/xdoc/limitations.xml
+++ b/src/site/xdoc/limitations.xml
@@ -25,6 +25,21 @@
      Commons Compress&#x2122; grouped by the archiving/compression
      format they apply to.</p>
 
+     <section name="General">
+       <ul>
+         <li>Several implementations of decompressors and unarchivers will
+         invoke <a
+         href="https://docs.oracle.com/javase/10/docs/api/java/io/InputStream.html#skip(long)"><code>skip</code></a>
+         on the underlying <code>InputStream</code> which may throw an
+         <code>IOException</code> in some stream implementations. One
+         known case where this happens is when using
+         <code>System.in</code> as input. If you encounter an
+         exception with a message like "Illegal seek" we recommend you
+         wrap your stream in a <code>SkipShieldingInputStream</code>
+         from our utils package before passing it to Compress.</li>
+       </ul>
+     </section>
+
      <section name="7Z">
        <ul>
          <li>the format requires the otherwise optional <a

http://git-wip-us.apache.org/repos/asf/commons-compress/blob/e15c80ae/src/test/java/org/apache/commons/compress/utils/SkipShieldingInputStreamTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/commons/compress/utils/SkipShieldingInputStreamTest.java b/src/test/java/org/apache/commons/compress/utils/SkipShieldingInputStreamTest.java
new file mode 100644
index 0000000..5ae69cf
--- /dev/null
+++ b/src/test/java/org/apache/commons/compress/utils/SkipShieldingInputStreamTest.java
@@ -0,0 +1,91 @@
+/*
+ *  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.commons.compress.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class SkipShieldingInputStreamTest {
+
+    @Test
+    public void skipDelegatesToRead() throws IOException {
+        try (InputStream i = new SkipShieldingInputStream(new InputStream() {
+                @Override
+                public long skip(long n) {
+                    Assert.fail("skip invoked");
+                    return -1;
+                }
+                @Override
+                public int read() {
+                    return -1;
+                }
+                @Override
+                public int read(byte[] b, int off, int len) {
+                    return len;
+                }
+            })) {
+            Assert.assertEquals(100, i.skip(100));
+        }
+    }
+
+    @Test
+    public void skipHasAnUpperBoundOnRead() throws IOException {
+        try (InputStream i = new SkipShieldingInputStream(new InputStream() {
+                @Override
+                public long skip(long n) {
+                    Assert.fail("skip invoked");
+                    return -1;
+                }
+                @Override
+                public int read() {
+                    return -1;
+                }
+                @Override
+                public int read(byte[] b, int off, int len) {
+                    return len;
+                }
+            })) {
+            Assert.assertTrue(Integer.MAX_VALUE > i.skip(Long.MAX_VALUE));
+        }
+    }
+
+    @Test
+    public void skipSwallowsNegativeArguments() throws IOException {
+        try (InputStream i = new SkipShieldingInputStream(new InputStream() {
+                @Override
+                public long skip(long n) {
+                    Assert.fail("skip invoked");
+                    return -1;
+                }
+                @Override
+                public int read() {
+                    return -1;
+                }
+                @Override
+                public int read(byte[] b, int off, int len) {
+                    Assert.fail("read invoked");
+                    return len;
+                }
+            })) {
+            Assert.assertEquals(0, i.skip(Long.MIN_VALUE));
+        }
+    }
+
+}