You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2019/08/09 18:03:24 UTC

[commons-io] branch master updated: Added TaggedReader, ClosedReader and BrokenReader. (#85)

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

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-io.git


The following commit(s) were added to refs/heads/master by this push:
     new 3615e14  Added TaggedReader, ClosedReader and BrokenReader. (#85)
3615e14 is described below

commit 3615e14865491b792e64619a1584c2b026232253
Author: Rob Spoor <ro...@users.noreply.github.com>
AuthorDate: Fri Aug 9 20:03:20 2019 +0200

    Added TaggedReader, ClosedReader and BrokenReader. (#85)
    
    * Added TaggedReader, ClosedReader and BrokenReader.
    
    * Fixed the since version of TaggedReader
    
    * Marked parameters as final.
    
    * Replaced an incorrect tab with spaces.
---
 .../org/apache/commons/io/input/BrokenReader.java  | 122 ++++++++++++++++++++
 .../org/apache/commons/io/input/TaggedReader.java  | 116 +++++++++++++++++++
 .../apache/commons/io/input/BrokenReaderTest.java  | 118 +++++++++++++++++++
 .../apache/commons/io/input/TaggedReaderTest.java  | 127 +++++++++++++++++++++
 4 files changed, 483 insertions(+)

diff --git a/src/main/java/org/apache/commons/io/input/BrokenReader.java b/src/main/java/org/apache/commons/io/input/BrokenReader.java
new file mode 100644
index 0000000..2b7c42e
--- /dev/null
+++ b/src/main/java/org/apache/commons/io/input/BrokenReader.java
@@ -0,0 +1,122 @@
+/*
+ * 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.io.input;
+
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * Broken reader. This reader always throws an {@link IOException} from
+ * all the {@link Reader} methods where the exception is declared.
+ * <p>
+ * This class is mostly useful for testing error handling in code that uses a
+ * reader.
+ *
+ * @since 2.7
+ */
+public class BrokenReader extends Reader {
+
+    /**
+     * The exception that is thrown by all methods of this class.
+     */
+    private final IOException exception;
+
+    /**
+     * Creates a new reader that always throws the given exception.
+     *
+     * @param exception the exception to be thrown
+     */
+    public BrokenReader(final IOException exception) {
+        this.exception = exception;
+    }
+
+    /**
+     * Creates a new reader that always throws an {@link IOException}
+     */
+    public BrokenReader() {
+        this(new IOException("Broken reader"));
+    }
+
+    /**
+     * Throws the configured exception.
+     *
+     * @param cbuf ignored
+     * @param off ignored
+     * @param len ignored
+     * @return nothing
+     * @throws IOException always thrown
+     */
+    @Override
+    public int read(final char[] cbuf, final int off, final int len) throws IOException {
+        throw exception;
+    }
+
+    /**
+     * Throws the configured exception.
+     *
+     * @param n ignored
+     * @return nothing
+     * @throws IOException always thrown
+     */
+    @Override
+    public long skip(final long n) throws IOException {
+        throw exception;
+    }
+
+    /**
+     * Throws the configured exception.
+     *
+     * @return nothing
+     * @throws IOException always thrown
+     */
+    @Override
+    public boolean ready() throws IOException {
+        throw exception;
+    }
+
+    /**
+     * Throws the configured exception.
+     *
+     * @param readAheadLimit ignored
+     * @throws IOException always thrown
+     */
+    @Override
+    public void mark(final int readAheadLimit) throws IOException {
+        throw exception;
+    }
+
+    /**
+     * Throws the configured exception.
+     *
+     * @throws IOException always thrown
+     */
+    @Override
+    public synchronized void reset() throws IOException {
+        throw exception;
+    }
+
+    /**
+     * Throws the configured exception.
+     *
+     * @throws IOException always thrown
+     */
+    @Override
+    public void close() throws IOException {
+        throw exception;
+    }
+
+}
diff --git a/src/main/java/org/apache/commons/io/input/TaggedReader.java b/src/main/java/org/apache/commons/io/input/TaggedReader.java
new file mode 100644
index 0000000..b771792
--- /dev/null
+++ b/src/main/java/org/apache/commons/io/input/TaggedReader.java
@@ -0,0 +1,116 @@
+/*
+ * 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.io.input;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Serializable;
+import java.util.UUID;
+
+import org.apache.commons.io.TaggedIOException;
+
+/**
+ * A reader decorator that tags potential exceptions so that the
+ * reader that caused the exception can easily be identified. This is
+ * done by using the {@link TaggedIOException} class to wrap all thrown
+ * {@link IOException}s. See below for an example of using this class.
+ * <pre>
+ * TaggedReader reader = new TaggedReader(...);
+ * try {
+ *     // Processing that may throw an IOException either from this reader
+ *     // or from some other IO activity like temporary files, etc.
+ *     processReader(reader);
+ * } catch (IOException e) {
+ *     if (reader.isCauseOf(e)) {
+ *         // The exception was caused by this reader.
+ *         // Use e.getCause() to get the original exception.
+ *     } else {
+ *         // The exception was caused by something else.
+ *     }
+ * }
+ * </pre>
+ * <p>
+ * Alternatively, the {@link #throwIfCauseOf(Throwable)} method can be
+ * used to let higher levels of code handle the exception caused by this
+ * reader while other processing errors are being taken care of at this
+ * lower level.
+ * <pre>
+ * TaggedReader reader = new TaggedReader(...);
+ * try {
+ *     processReader(reader);
+ * } catch (IOException e) {
+ *     reader.throwIfCauseOf(e);
+ *     // ... or process the exception that was caused by something else
+ * }
+ * </pre>
+ *
+ * @see TaggedIOException
+ * @since 2.7
+ */
+public class TaggedReader extends ProxyReader {
+
+    /**
+     * The unique tag associated with exceptions from reader.
+     */
+    private final Serializable tag = UUID.randomUUID();
+
+    /**
+     * Creates a tagging decorator for the given reader.
+     *
+     * @param proxy reader to be decorated
+     */
+    public TaggedReader(final Reader proxy) {
+        super(proxy);
+    }
+
+    /**
+     * Tests if the given exception was caused by this reader.
+     *
+     * @param exception an exception
+     * @return {@code true} if the exception was thrown by this reader,
+     *         {@code false} otherwise
+     */
+    public boolean isCauseOf(final Throwable exception) {
+        return TaggedIOException.isTaggedWith(exception, tag);
+    }
+
+    /**
+     * Re-throws the original exception thrown by this reader. This method
+     * first checks whether the given exception is a {@link TaggedIOException}
+     * wrapper created by this decorator, and then unwraps and throws the
+     * original wrapped exception. Returns normally if the exception was
+     * not thrown by this reader.
+     *
+     * @param throwable an exception
+     * @throws IOException original exception, if any, thrown by this reader
+     */
+    public void throwIfCauseOf(final Throwable throwable) throws IOException {
+        TaggedIOException.throwCauseIfTaggedWith(throwable, tag);
+    }
+
+    /**
+     * Tags any IOExceptions thrown, wrapping and re-throwing.
+     *
+     * @param e The IOException thrown
+     * @throws IOException if an I/O error occurs
+     */
+    @Override
+    protected void handleIOException(final IOException e) throws IOException {
+        throw new TaggedIOException(e, tag);
+    }
+
+}
diff --git a/src/test/java/org/apache/commons/io/input/BrokenReaderTest.java b/src/test/java/org/apache/commons/io/input/BrokenReaderTest.java
new file mode 100644
index 0000000..c61b309
--- /dev/null
+++ b/src/test/java/org/apache/commons/io/input/BrokenReaderTest.java
@@ -0,0 +1,118 @@
+/*
+ * 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.io.input;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.Reader;
+
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * JUnit Test Case for {@link BrokenReader}.
+ */
+@SuppressWarnings("ResultOfMethodCallIgnored")
+public class BrokenReaderTest {
+
+    private IOException exception;
+
+    private Reader reader;
+
+    @Before
+    public void setUp() {
+        exception = new IOException("test exception");
+        reader = new BrokenReader(exception);
+    }
+
+    @Test
+    public void testRead() {
+        try {
+            reader.read();
+            fail("Expected exception not thrown.");
+        } catch (final IOException e) {
+            assertEquals(exception, e);
+        }
+
+        try {
+            reader.read(new char[1]);
+            fail("Expected exception not thrown.");
+        } catch (final IOException e) {
+            assertEquals(exception, e);
+        }
+
+        try {
+            reader.read(new char[1], 0, 1);
+            fail("Expected exception not thrown.");
+        } catch (final IOException e) {
+            assertEquals(exception, e);
+        }
+    }
+
+    @Test
+    public void testSkip() {
+        try {
+            reader.skip(1);
+            fail("Expected exception not thrown.");
+        } catch (final IOException e) {
+            assertEquals(exception, e);
+        }
+    }
+
+    @Test
+    public void testReady() {
+        try {
+            reader.ready();
+            fail("Expected exception not thrown.");
+        } catch (final IOException e) {
+            assertEquals(exception, e);
+        }
+    }
+
+    @Test
+    public void testMark() {
+        try {
+            reader.mark(1);
+            fail("Expected exception not thrown.");
+        } catch (final IOException e) {
+            assertEquals(exception, e);
+        }
+    }
+
+    @Test
+    public void testReset() {
+        try {
+            reader.reset();
+            fail("Expected exception not thrown.");
+        } catch (final IOException e) {
+            assertEquals(exception, e);
+        }
+    }
+
+    @Test
+    public void testClose() {
+        try {
+            reader.close();
+            fail("Expected exception not thrown.");
+        } catch (final IOException e) {
+            assertEquals(exception, e);
+        }
+    }
+
+}
diff --git a/src/test/java/org/apache/commons/io/input/TaggedReaderTest.java b/src/test/java/org/apache/commons/io/input/TaggedReaderTest.java
new file mode 100644
index 0000000..6be26c8
--- /dev/null
+++ b/src/test/java/org/apache/commons/io/input/TaggedReaderTest.java
@@ -0,0 +1,127 @@
+/*
+ * 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.io.input;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.util.UUID;
+
+import org.apache.commons.io.TaggedIOException;
+import org.junit.Test;
+
+/**
+ * JUnit Test Case for {@link TaggedReader}.
+ */
+public class TaggedReaderTest  {
+
+    @Test
+    public void testEmptyReader() throws IOException {
+        final Reader reader = new TaggedReader(new ClosedReader());
+        assertFalse(reader.ready());
+        assertEquals(-1, reader.read());
+        assertEquals(-1, reader.read(new char[1]));
+        assertEquals(-1, reader.read(new char[1], 0, 1));
+        reader.close();
+    }
+
+    @Test
+    public void testNormalReader() throws IOException {
+        final Reader reader = new TaggedReader(new StringReader("abc"));
+        assertTrue(reader.ready());
+        assertEquals('a', reader.read());
+        final char[] buffer = new char[1];
+        assertEquals(1, reader.read(buffer));
+        assertEquals('b', buffer[0]);
+        assertEquals(1, reader.read(buffer, 0, 1));
+        assertEquals('c', buffer[0]);
+        assertEquals(-1, reader.read());
+        reader.close();
+    }
+
+    @Test
+    public void testBrokenReader() {
+        final IOException exception = new IOException("test exception");
+        final TaggedReader reader =
+            new TaggedReader(new BrokenReader(exception));
+
+        // Test the ready() method
+        try {
+            reader.ready();
+            fail("Expected exception not thrown.");
+        } catch (final IOException e) {
+            assertTrue(reader.isCauseOf(e));
+            try {
+                reader.throwIfCauseOf(e);
+                fail("Expected exception not thrown.");
+            } catch (final IOException e2) {
+                assertEquals(exception, e2);
+            }
+        }
+
+        // Test the read() method
+        try {
+            reader.read();
+            fail("Expected exception not thrown.");
+        } catch (final IOException e) {
+            assertTrue(reader.isCauseOf(e));
+            try {
+                reader.throwIfCauseOf(e);
+                fail("Expected exception not thrown.");
+            } catch (final IOException e2) {
+                assertEquals(exception, e2);
+            }
+        }
+
+        // Test the close() method
+        try {
+            reader.close();
+            fail("Expected exception not thrown.");
+        } catch (final IOException e) {
+            assertTrue(reader.isCauseOf(e));
+            try {
+                reader.throwIfCauseOf(e);
+                fail("Expected exception not thrown.");
+            } catch (final IOException e2) {
+                assertEquals(exception, e2);
+            }
+        }
+    }
+
+    @Test
+    public void testOtherException() throws Exception {
+        final IOException exception = new IOException("test exception");
+        final Reader closed = new ClosedReader();
+        final TaggedReader reader = new TaggedReader(closed);
+
+        assertFalse(reader.isCauseOf(exception));
+        assertFalse(reader.isCauseOf(
+                new TaggedIOException(exception, UUID.randomUUID())));
+
+        reader.throwIfCauseOf(exception);
+
+        reader.throwIfCauseOf(
+                    new TaggedIOException(exception, UUID.randomUUID()));
+        reader.close();
+    }
+
+}