This is an automated email from the ASF dual-hosted git repository. veithen pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/ws-axiom.git
The following commit(s) were added to refs/heads/master by this push: new 857d0146f AXIOM-506: Make DataHandlerBlob distinguish between read and write errors 857d0146f is described below commit 857d0146fb2fec245f149346fe223511c13a160a Author: Andreas Veithen <andreas.veit...@gmail.com> AuthorDate: Wed Sep 18 12:42:00 2024 +0100 AXIOM-506: Make DataHandlerBlob distinguish between read and write errors --- .../axiom/util/activation/DataHandlerBlob.java | 10 ++- .../axiom/util/activation/OutputStreamWrapper.java | 95 ++++++++++++++++++++++ .../util/activation/DataHandlerUtilsTest.java | 72 ++++++++++++++++ .../axiom/testutils/io/ExceptionOutputStream.java | 3 +- 4 files changed, 176 insertions(+), 4 deletions(-) diff --git a/axiom-jakarta-activation/src/main/java/org/apache/axiom/util/activation/DataHandlerBlob.java b/axiom-jakarta-activation/src/main/java/org/apache/axiom/util/activation/DataHandlerBlob.java index 424c39733..75d7e6ccc 100644 --- a/axiom-jakarta-activation/src/main/java/org/apache/axiom/util/activation/DataHandlerBlob.java +++ b/axiom-jakarta-activation/src/main/java/org/apache/axiom/util/activation/DataHandlerBlob.java @@ -45,11 +45,15 @@ final class DataHandlerBlob implements Blob { @Override public void writeTo(OutputStream out) throws StreamCopyException { + OutputStreamWrapper wrapper = new OutputStreamWrapper(out); try { - dataHandler.writeTo(out); + dataHandler.writeTo(wrapper); } catch (IOException ex) { - // TODO(AXIOM-506): maybe we can do some wrapping to determine the operation that failed - throw new StreamCopyException(StreamCopyException.WRITE, ex); + IOException wrapperException = wrapper.getException(); + if (wrapperException != null) { + throw new StreamCopyException(StreamCopyException.WRITE, wrapperException); + } + throw new StreamCopyException(StreamCopyException.READ, ex); } } diff --git a/axiom-jakarta-activation/src/main/java/org/apache/axiom/util/activation/OutputStreamWrapper.java b/axiom-jakarta-activation/src/main/java/org/apache/axiom/util/activation/OutputStreamWrapper.java new file mode 100644 index 000000000..8fca99791 --- /dev/null +++ b/axiom-jakarta-activation/src/main/java/org/apache/axiom/util/activation/OutputStreamWrapper.java @@ -0,0 +1,95 @@ +/* + * 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.axiom.util.activation; + +import java.io.IOException; +import java.io.OutputStream; + +final class OutputStreamWrapper extends OutputStream { + private final OutputStream parent; + private IOException exception; + + OutputStreamWrapper(OutputStream parent) { + this.parent = parent; + } + + IOException getException() { + return exception; + } + + @Override + public void close() throws IOException { + try { + parent.close(); + } catch (IOException ex) { + if (exception == null) { + exception = ex; + } + throw ex; + } + } + + @Override + public void flush() throws IOException { + try { + parent.flush(); + } catch (IOException ex) { + if (exception == null) { + exception = ex; + } + throw ex; + } + } + + @Override + public void write(int b) throws IOException { + try { + parent.write(b); + } catch (IOException ex) { + if (exception == null) { + exception = ex; + } + throw ex; + } + } + + @Override + public void write(byte[] b) throws IOException { + try { + parent.write(b); + } catch (IOException ex) { + if (exception == null) { + exception = ex; + } + throw ex; + } + } + + @Override + public void write(byte[] b, int off, int len) throws IOException { + try { + parent.write(b, off, len); + } catch (IOException ex) { + if (exception == null) { + exception = ex; + } + throw ex; + } + } +} diff --git a/axiom-jakarta-activation/src/test/java/org/apache/axiom/util/activation/DataHandlerUtilsTest.java b/axiom-jakarta-activation/src/test/java/org/apache/axiom/util/activation/DataHandlerUtilsTest.java new file mode 100644 index 000000000..d685d9f1e --- /dev/null +++ b/axiom-jakarta-activation/src/test/java/org/apache/axiom/util/activation/DataHandlerUtilsTest.java @@ -0,0 +1,72 @@ +package org.apache.axiom.util.activation; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertThrows; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.axiom.ext.io.StreamCopyException; +import org.apache.axiom.testutils.io.ExceptionOutputStream; +import org.apache.commons.io.output.NullOutputStream; +import org.junit.jupiter.api.Test; + +import jakarta.activation.DataHandler; +import jakarta.activation.DataSource; +import jakarta.mail.util.ByteArrayDataSource; + +public class DataHandlerUtilsTest { + @Test + public void testToBlobWriteError() { + ExceptionOutputStream out = new ExceptionOutputStream(0); + StreamCopyException ex = + assertThrows( + StreamCopyException.class, + () -> { + DataHandlerUtils.toBlob( + new DataHandler( + new ByteArrayDataSource( + new byte[10], + "application/octet-stream"))) + .writeTo(out); + }); + assertThat(ex.getOperation()).isEqualTo(StreamCopyException.WRITE); + assertThat(ex.getCause()).isSameAs(out.getException()); + } + + @Test + public void testToBlobReadError() { + DataSource ds = + new DataSource() { + @Override + public String getContentType() { + return "application/octet-stream"; + } + + @Override + public InputStream getInputStream() throws IOException { + throw new IOException("Read error"); + } + + @Override + public String getName() { + return null; + } + + @Override + public OutputStream getOutputStream() throws IOException { + throw new UnsupportedOperationException(); + } + }; + StreamCopyException ex = + assertThrows( + StreamCopyException.class, + () -> { + DataHandlerUtils.toBlob(new DataHandler(ds)) + .writeTo(NullOutputStream.INSTANCE); + }); + assertThat(ex.getOperation()).isEqualTo(StreamCopyException.READ); + assertThat(ex.getCause().getMessage()).isEqualTo("Read error"); + } +} diff --git a/testing/testutils/src/main/java/org/apache/axiom/testutils/io/ExceptionOutputStream.java b/testing/testutils/src/main/java/org/apache/axiom/testutils/io/ExceptionOutputStream.java index 6f419de7d..1edabbd79 100644 --- a/testing/testutils/src/main/java/org/apache/axiom/testutils/io/ExceptionOutputStream.java +++ b/testing/testutils/src/main/java/org/apache/axiom/testutils/io/ExceptionOutputStream.java @@ -31,9 +31,10 @@ public class ExceptionOutputStream extends OutputStream { @Override public void write(int b) throws IOException { - if (--remaining == 0) { + if (remaining == 0) { throw exception = new IOException("Maximum number of bytes written"); } + remaining--; } public IOException getException() {