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 b358a63 Added TaggedWriter, ClosedWriter and BrokenWriter. (#86) b358a63 is described below commit b358a63b22b9cb614bdc8830a3c88ae256e77387 Author: Rob Spoor <robti...@users.noreply.github.com> AuthorDate: Fri Aug 9 01:15:01 2019 +0200 Added TaggedWriter, ClosedWriter and BrokenWriter. (#86) --- .../org/apache/commons/io/output/BrokenWriter.java | 87 ++++++++++++++ .../org/apache/commons/io/output/ClosedWriter.java | 65 +++++++++++ .../org/apache/commons/io/output/TaggedWriter.java | 116 +++++++++++++++++++ .../apache/commons/io/output/BrokenWriterTest.java | 87 ++++++++++++++ .../apache/commons/io/output/ClosedWriterTest.java | 58 ++++++++++ .../apache/commons/io/output/TaggedWriterTest.java | 126 +++++++++++++++++++++ 6 files changed, 539 insertions(+) diff --git a/src/main/java/org/apache/commons/io/output/BrokenWriter.java b/src/main/java/org/apache/commons/io/output/BrokenWriter.java new file mode 100644 index 0000000..15737e1 --- /dev/null +++ b/src/main/java/org/apache/commons/io/output/BrokenWriter.java @@ -0,0 +1,87 @@ +/* + * 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.output; + +import java.io.IOException; +import java.io.Writer; + +/** + * Broken writer. This writer always throws an {@link IOException} from + * all {@link Writer} methods. + * <p> + * This class is mostly useful for testing error handling in code that uses a + * writer. + * + * @since 2.0 + */ +public class BrokenWriter extends Writer { + + /** + * The exception that is thrown by all methods of this class. + */ + private final IOException exception; + + /** + * Creates a new writer that always throws the given exception. + * + * @param exception the exception to be thrown + */ + public BrokenWriter(final IOException exception) { + this.exception = exception; + } + + /** + * Creates a new writer that always throws an {@link IOException} + */ + public BrokenWriter() { + this(new IOException("Broken writer")); + } + + /** + * Throws the configured exception. + * + * @param cbuf ignored + * @param off ignored + * @param len ignored + * @throws IOException always thrown + */ + @Override + public void write(final char[] cbuf, final int off, final int len) throws IOException { + throw exception; + } + + /** + * Throws the configured exception. + * + * @throws IOException always thrown + */ + @Override + public void flush() 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/output/ClosedWriter.java b/src/main/java/org/apache/commons/io/output/ClosedWriter.java new file mode 100644 index 0000000..a596c12 --- /dev/null +++ b/src/main/java/org/apache/commons/io/output/ClosedWriter.java @@ -0,0 +1,65 @@ +/* + * 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.output; + +import java.io.IOException; +import java.io.Writer; + +/** + * Closed writer. This writer throws an exception on all attempts to + * write something to it. + * <p> + * Typically uses of this class include testing for corner cases in methods + * that accept a writer and acting as a sentinel value instead of + * a {@code null} writer. + * + * @since 2.7 + */ +public class ClosedWriter extends Writer { + + /** + * A singleton. + */ + public static final ClosedWriter CLOSED_WRITER = new ClosedWriter(); + + /** + * Throws an {@link IOException} to indicate that the writer is closed. + * + * @param cbuf ignored + * @param off ignored + * @param len ignored + * @throws IOException always thrown + */ + @Override + public void write(final char[] cbuf, final int off, final int len) throws IOException { + throw new IOException("write(" + new String(cbuf) + ", " + off + ", " + len + ") failed: stream is closed"); + } + + /** + * Throws an {@link IOException} to indicate that the stream is closed. + * + * @throws IOException always thrown + */ + @Override + public void flush() throws IOException { + throw new IOException("flush() failed: stream is closed"); + } + + @Override + public void close() throws IOException { + } +} diff --git a/src/main/java/org/apache/commons/io/output/TaggedWriter.java b/src/main/java/org/apache/commons/io/output/TaggedWriter.java new file mode 100644 index 0000000..c086d26 --- /dev/null +++ b/src/main/java/org/apache/commons/io/output/TaggedWriter.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.output; + +import java.io.IOException; +import java.io.Serializable; +import java.io.Writer; +import java.util.UUID; + +import org.apache.commons.io.TaggedIOException; + +/** + * A writer 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. + * writeToWriter(writer); + * } catch (IOException e) { + * if (writer.isCauseOf(e)) { + * // The exception was caused by this writer. + * // Use e.getCause() to get the original exception. + * } else { + * // The exception was caused by something else. + * } + * } + * </pre> + * <p> + * Alternatively, the {@link #throwIfCauseOf(Exception)} method can be + * used to let higher levels of code handle the exception caused by this + * writer while other processing errors are being taken care of at this + * lower level. + * <pre> + * TaggedWriter writer = new TaggedWriter(...); + * try { + * writeToWriter(writer); + * } catch (IOException e) { + * writer.throwIfCauseOf(e); + * // ... or process the exception that was caused by something else + * } + * </pre> + * + * @see TaggedIOException + * @since 2.0 + */ +public class TaggedWriter extends ProxyWriter { + + /** + * The unique tag associated with exceptions from writer. + */ + private final Serializable tag = UUID.randomUUID(); + + /** + * Creates a tagging decorator for the given writer. + * + * @param proxy writer to be decorated + */ + public TaggedWriter(final Writer proxy) { + super(proxy); + } + + /** + * Tests if the given exception was caused by this writer. + * + * @param exception an exception + * @return {@code true} if the exception was thrown by this writer, + * {@code false} otherwise + */ + public boolean isCauseOf(final Exception exception) { + return TaggedIOException.isTaggedWith(exception, tag); + } + + /** + * Re-throws the original exception thrown by this writer. 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 writer. + * + * @param exception an exception + * @throws IOException original exception, if any, thrown by this writer + */ + public void throwIfCauseOf(final Exception exception) throws IOException { + TaggedIOException.throwCauseIfTaggedWith(exception, 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/output/BrokenWriterTest.java b/src/test/java/org/apache/commons/io/output/BrokenWriterTest.java new file mode 100644 index 0000000..a6d978a --- /dev/null +++ b/src/test/java/org/apache/commons/io/output/BrokenWriterTest.java @@ -0,0 +1,87 @@ +/* + * 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.output; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +import java.io.IOException; +import java.io.Writer; + +import org.junit.Before; +import org.junit.Test; + +/** + * JUnit Test Case for {@link BrokenWriter}. + */ +public class BrokenWriterTest { + + private IOException exception; + + private Writer writer; + + @Before + public void setUp() { + exception = new IOException("test exception"); + writer = new BrokenWriter(exception); + } + + @Test + public void testWrite() { + try { + writer.write(1); + fail("Expected exception not thrown."); + } catch (final IOException e) { + assertEquals(exception, e); + } + + try { + writer.write(new char[1]); + fail("Expected exception not thrown."); + } catch (final IOException e) { + assertEquals(exception, e); + } + + try { + writer.write(new char[1], 0, 1); + fail("Expected exception not thrown."); + } catch (final IOException e) { + assertEquals(exception, e); + } + } + + @Test + public void testFlush() { + try { + writer.flush(); + fail("Expected exception not thrown."); + } catch (final IOException e) { + assertEquals(exception, e); + } + } + + @Test + public void testClose() { + try { + writer.close(); + fail("Expected exception not thrown."); + } catch (final IOException e) { + assertEquals(exception, e); + } + } + +} diff --git a/src/test/java/org/apache/commons/io/output/ClosedWriterTest.java b/src/test/java/org/apache/commons/io/output/ClosedWriterTest.java new file mode 100644 index 0000000..2089307 --- /dev/null +++ b/src/test/java/org/apache/commons/io/output/ClosedWriterTest.java @@ -0,0 +1,58 @@ +/* + * 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.output; + +import static org.junit.Assert.fail; + +import java.io.IOException; + +import org.junit.Test; + +/** + * JUnit Test Case for {@link ClosedWriter}. + */ +public class ClosedWriterTest { + + /** + * Test the <code>write(cbuf, off, len)</code> method. + * @throws Exception + */ + @Test + public void testWrite() throws Exception { + try (ClosedWriter cw = new ClosedWriter()) { + cw.write(new char[0], 0, 0); + fail("write(cbuf, off, len)"); + } catch (final IOException e) { + // expected + } + } + + /** + * Test the <code>flush()</code> method. + * @throws Exception + */ + @Test + public void testFlush() throws Exception { + try (ClosedWriter cw = new ClosedWriter()) { + cw.flush(); + fail("flush()"); + } catch (final IOException e) { + // expected + } + } + +} diff --git a/src/test/java/org/apache/commons/io/output/TaggedWriterTest.java b/src/test/java/org/apache/commons/io/output/TaggedWriterTest.java new file mode 100644 index 0000000..13340ff --- /dev/null +++ b/src/test/java/org/apache/commons/io/output/TaggedWriterTest.java @@ -0,0 +1,126 @@ +/* + * 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.output; + +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.Writer; +import java.util.UUID; + +import org.apache.commons.io.TaggedIOException; +import org.junit.Test; + +/** + * JUnit Test Case for {@link TaggedWriter}. + */ +public class TaggedWriterTest { + + @Test + public void testNormalWriter() { + try (final StringBuilderWriter buffer = new StringBuilderWriter()) { + try (final Writer writer = new TaggedWriter(buffer)) { + writer.write('a'); + writer.write(new char[] { 'b' }); + writer.write(new char[] { 'c' }, 0, 1); + writer.flush(); + } + assertEquals(3, buffer.getBuilder().length()); + assertEquals('a', buffer.getBuilder().charAt(0)); + assertEquals('b', buffer.getBuilder().charAt(1)); + assertEquals('c', buffer.getBuilder().charAt(2)); + } catch (final IOException e) { + fail("Unexpected exception thrown"); + } + } + + @Test + public void testBrokenWriter() { + final IOException exception = new IOException("test exception"); + final TaggedWriter writer = + new TaggedWriter(new BrokenWriter(exception)); + + // Test the write() method + try { + writer.write(new char[] { 'x' }, 0, 1); + fail("Expected exception not thrown."); + } catch (final IOException e) { + assertTrue(writer.isCauseOf(e)); + try { + writer.throwIfCauseOf(e); + fail("Expected exception not thrown."); + } catch (final IOException e2) { + assertEquals(exception, e2); + } + } + + // Test the flush() method + try { + writer.flush(); + fail("Expected exception not thrown."); + } catch (final IOException e) { + assertTrue(writer.isCauseOf(e)); + try { + writer.throwIfCauseOf(e); + fail("Expected exception not thrown."); + } catch (final IOException e2) { + assertEquals(exception, e2); + } + } + + // Test the close() method + try { + writer.close(); + fail("Expected exception not thrown."); + } catch (final IOException e) { + assertTrue(writer.isCauseOf(e)); + try { + writer.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"); + try (final Writer closed = new ClosedWriter(); + final TaggedWriter writer = new TaggedWriter(closed)) { + + assertFalse(writer.isCauseOf(exception)); + assertFalse(writer.isCauseOf(new TaggedIOException(exception, UUID.randomUUID()))); + + try { + writer.throwIfCauseOf(exception); + } catch (final IOException e) { + fail("Unexpected exception thrown"); + } + + try { + writer.throwIfCauseOf(new TaggedIOException(exception, UUID.randomUUID())); + } catch (final IOException e) { + fail("Unexpected exception thrown"); + } + } + } + +}