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();
+ }
+
+}