From 5689821240c24ac50e8ce6bf77c4a815b1d7761a Mon Sep 17 00:00:00 2001 From: Stuart Douglas Date: Wed, 3 Nov 2021 11:00:45 +1100 Subject: [PATCH] Clarify under what circumstances onError is called Fixes #433 Signed-off-by: Stuart Douglas --- .../java/jakarta/servlet/ReadListener.java | 14 +++++++++++- .../jakarta/servlet/ServletInputStream.java | 17 +++++++++++++- .../jakarta/servlet/ServletOutputStream.java | 22 +++++++++++++++++-- .../java/jakarta/servlet/WriteListener.java | 14 +++++++++++- spec/src/main/asciidoc/servlet-spec-body.adoc | 3 +++ 5 files changed, 65 insertions(+), 5 deletions(-) diff --git a/api/src/main/java/jakarta/servlet/ReadListener.java b/api/src/main/java/jakarta/servlet/ReadListener.java index 758be6c74..bdfba146e 100644 --- a/api/src/main/java/jakarta/servlet/ReadListener.java +++ b/api/src/main/java/jakarta/servlet/ReadListener.java @@ -48,7 +48,19 @@ public interface ReadListener extends EventListener { public void onAllDataRead() throws IOException; /** - * Invoked when an error occurs processing the request. + * Invoked when an error occurs reading data. This listener will be invoked if there is a problem with the + * underlying connection while data is being read from the stream. We consider data to be being read when the following + * conditions are met: + * + * + * + * If these conditions are not met and the stream is still open then any failure notification will not be delivered + * until {@link ServletInputStream#isReady()} is invoked. {@code isReady} must return false in this situation, and + * then the failure will be delivered to this method. * * @param t the throwable to indicate why the read operation failed */ diff --git a/api/src/main/java/jakarta/servlet/ServletInputStream.java b/api/src/main/java/jakarta/servlet/ServletInputStream.java index 4d07a063c..c86cec8f0 100644 --- a/api/src/main/java/jakarta/servlet/ServletInputStream.java +++ b/api/src/main/java/jakarta/servlet/ServletInputStream.java @@ -101,6 +101,15 @@ public int readLine(byte[] b, int off, int len) throws IOException { /** * Returns true if data can be read without blocking else returns false. * + * If an attempt is made to read from the stream when the stream is in async mode and this method has not returned + * {@code true} the method will throw an {@link IllegalStateException}. + *

+ * If an error occurs and {@link ReadListener#onError(Throwable)} is invoked then this method will always return + * false, as no further IO operations are allowed after {@code onError} notification. + *

+ * Note that due to the requirement for {@code read} to never throw in async mode, this method must return false + * if a call to {@code read} would result in an exception. + * * @return true if data can be obtained without blocking, otherwise returns false. * * @since Servlet 3.1 @@ -108,7 +117,13 @@ public int readLine(byte[] b, int off, int len) throws IOException { public abstract boolean isReady(); /** - * Instructs the ServletInputStream to invoke the provided {@link ReadListener} when it is possible to read + * Instructs the ServletInputStream to invoke the provided {@link ReadListener} when it is possible to read. + *

+ * Note that after this method has been called methods on this stream that are documented to throw {@link IOException} will + * no longer throw these exceptions directly, instead any exception that occurs will be reported via + * {@link ReadListener#onError(Throwable)}. Please refer to this method for more information. This only applies to + * {@code IOException}, other exception types may still be thrown (e.g. methods can throw {@link IllegalStateException} + * if {@link #isReady()} has not returned true). * * @param readListener the {@link ReadListener} that should be notified when it's possible to read. * diff --git a/api/src/main/java/jakarta/servlet/ServletOutputStream.java b/api/src/main/java/jakarta/servlet/ServletOutputStream.java index e97328f01..1315ca2ce 100644 --- a/api/src/main/java/jakarta/servlet/ServletOutputStream.java +++ b/api/src/main/java/jakarta/servlet/ServletOutputStream.java @@ -273,6 +273,15 @@ public void println(double d) throws IOException { /** * This method can be used to determine if data can be written without blocking. + *

+ * If an attempt is made to write to the stream when the stream is in async mode and this method has not returned + * {@code true} the method will throw an {@link IllegalStateException}. + *

+ * If an error occurs and {@link WriteListener#onError(Throwable)} is invoked then this method will always return + * false, as no further IO operations are allowed after {@code onError} notification. + *

+ * Note that due to the requirement for {@code write} to never throw in async mode, this method must return false + * if a call to {@code write} would result in an exception. * * @return true if a write to this ServletOutputStream will succeed, otherwise returns * false. @@ -283,8 +292,17 @@ public void println(double d) throws IOException { /** * Instructs the ServletOutputStream to invoke the provided {@link WriteListener} when it is possible to - * write - * + * write. + *

+ * Note that after this method has been called methods on this stream that are documented to throw {@link IOException} will + * no longer throw these exceptions directly, instead any exception that occurs will be reported via + * {@link WriteListener#onError(Throwable)}. Please refer to this method for more information. This only applies to + * {@code IOException}, other exception types may still be thrown (e.g. methods can throw {@link IllegalStateException} + * if {@link #isReady()} has not returned true). + *

+ * Once this method has been called {@link #flush()} and {@link #close()} become asynchronous operations, they will be + * performed in the background and any problems will be reported through the {@link WriteListener#onError(Throwable)} + * method. * * @param writeListener the {@link WriteListener} that should be notified when it's possible to write * diff --git a/api/src/main/java/jakarta/servlet/WriteListener.java b/api/src/main/java/jakarta/servlet/WriteListener.java index be95242f5..8ae9ed8b8 100644 --- a/api/src/main/java/jakarta/servlet/WriteListener.java +++ b/api/src/main/java/jakarta/servlet/WriteListener.java @@ -39,7 +39,19 @@ public interface WriteListener extends EventListener { public void onWritePossible() throws IOException; /** - * Invoked when an error occurs writing data using the non-blocking APIs. + * Invoked when an error occurs writing data using the non-blocking APIs. This listener will be invoked if there is + * a problem with the underlying connection while data is being written to the stream. We consider data to be being + * written when any of the following conditions are met: + * + *

+ * + * If these conditions are not met and the stream is still open then any failure notification will not be delivered + * until {@link ServletOutputStream#isReady()} is invoked. {@code isReady} must return false in this situation, and + * then the failure will be delivered to the {@link #onError(Throwable)} method. * * @param t the throwable to indicate why the write operation failed */ diff --git a/spec/src/main/asciidoc/servlet-spec-body.adoc b/spec/src/main/asciidoc/servlet-spec-body.adoc index 278fc0601..c11fe655a 100644 --- a/spec/src/main/asciidoc/servlet-spec-body.adoc +++ b/spec/src/main/asciidoc/servlet-spec-body.adoc @@ -8546,6 +8546,9 @@ Jakarta Servlet {spec-version} specification developed under the Jakarta EE Work === Changes Since Jakarta Servlet 5.0 +link:https://github.com/eclipse-ee4j/servlet-api/issues/433[Issue 433]:: +Clarify how IO errors are handled by async streams. + link:https://github.com/eclipse-ee4j/servlet-api/issues/18[Issue 18]:: Clarify the decoding and normalization of URI paths.