diff -r 52a9f6b74e43 -r 2a96e88888b2 src/java.net.http/share/classes/java/net/http/WebSocket.java --- a/src/java.net.http/share/classes/java/net/http/WebSocket.java Sat Mar 17 18:01:01 2018 +0000 +++ b/src/java.net.http/share/classes/java/net/http/WebSocket.java Sun Mar 18 14:31:36 2018 +0000 @@ -26,7 +26,6 @@ package java.net.http; import java.io.IOException; -import java.net.ProtocolException; import java.net.URI; import java.nio.ByteBuffer; import java.time.Duration; @@ -67,12 +66,10 @@ * {@code WebSocket} will not pass {@code null} arguments to methods of * {@code Listener}. * - * @implSpec Methods of {@code WebSocket} are failure-atomic in respect to - * {@code NullPointerException}, {@code IllegalArgumentException} and - * {@code IllegalStateException}. That is, if a method throws said exception, or - * a returned {@code CompletableFuture} completes exceptionally with said - * exception, the {@code WebSocket} will behave as if the method has not been - * invoked at all. + *

The state of a {@code WebSocket} is not changed by the invocations that + * throw or return a {@code CompletableFuture} that completes with one of the + * {@code NullPointerException}, {@code IllegalArgumentException}, + * {@code IllegalStateException} exceptions. * *

A {@code WebSocket} invokes methods of its listener in a thread-safe * manner. @@ -211,23 +208,29 @@ /** * The receiving interface of {@code WebSocket}. * - *

A {@code WebSocket} invokes methods on its listener when it receives - * messages or encounters events. The invoking {@code WebSocket} is passed - * as an argument to {@code Listener}'s methods. A {@code WebSocket} invokes - * methods on its listener in a thread-safe manner. + *

A {@code WebSocket} invokes methods on the associated listener when + * it receives messages or encounters events. A {@code WebSocket} invokes + * methods on the listener in a thread-safe manner. * *

Messages received by the {@code Listener} conform to the WebSocket - * Protocol, otherwise {@code onError} with a {@link ProtocolException} is - * invoked. - * - *

Unless otherwise stated if a listener's method throws an exception or - * a {@code CompletionStage} returned from a method completes exceptionally, + * Protocol, otherwise {@code onError} with a {@link IOException} is invoked. + * Any {@code IOException} raised by {@code WebSocket} will result in an + * invocation of {@code onError} with that exception. Unless otherwise + * stated if a listener's method throws an exception or a + * {@code CompletionStage} returned from a method completes exceptionally, * the {@code WebSocket} will invoke {@code onError} with this exception. * *

If a listener's method returns {@code null} rather than a * {@code CompletionStage}, {@code WebSocket} will behave as if the listener * returned a {@code CompletionStage} that is already completed normally. * + * @apiNote Methods of {@code Listener} have a {@code WebSocket} parameter + * which holds an invoking {@code WebSocket} at runtime. A careful attention + * is required if a listener is associated with more than a single + * {@code WebSocket}. In this case invocations related to different + * instances of {@code WebSocket} may not be ordered and may even happen + * concurrently. + * * @since 11 */ interface Listener { @@ -402,7 +405,8 @@ } /** - * A Close message has been received. + * Receives a Close message indicating the {@code WebSocket}'s input has + * been closed. * *

This is the last invocation from the {@code WebSocket}. By the * time this invocation begins the {@code WebSocket}'s input will have @@ -415,21 +419,30 @@ * {@code 1000 <= code <= 65535}. The {@code reason} is a string which * has an UTF-8 representation not longer than {@code 123} bytes. * - *

Return a {@code CompletionStage} that will be used by the - * {@code WebSocket} as a signal that it may close the output. The - * {@code WebSocket} will close the output at the earliest of completion - * of the returned {@code CompletionStage} or invoking a - * {@link WebSocket#sendClose(int, String) sendClose} method. - * - *

If an exception is thrown from this method or a - * {@code CompletionStage} returned from it completes exceptionally, - * the resulting behaviour is undefined. + *

If the {@code WebSocket}'s output is not already closed, the + * {@code CompletionStage} returned by this method will be used as an + * indication that the {@code WebSocket}'s output may be closed. The + * {@code WebSocket} will close its output at the earliest of completion + * of the returned {@code CompletionStage} or invoking either of the + * {@code sendClose} or {@code abort} methods. * * @apiNote Returning a {@code CompletionStage} that never completes, - * effectively disables the automatic closure of the output. + * effectively disables the reciprocating closure of the output. + * + *

To specify a custom closure code and/or reason code the sendClose + * may be invoked from inside onClose call: + *

{@code
+         *  public CompletionStage onClose(WebSocket webSocket,
+         *                             int statusCode,
+         *                             String reason) {
+         *      webSocket.sendClose(CUSTOM_STATUS_CODE, CUSTOM_REASON);
+         *      return new CompletableFuture();
+         *  }
+         * }
* * @implSpec The default implementation of this method returns - * {@code null}, signaling that the output may be closed. + * {@code null}, indicating that the output should be closed + * immediately. * * @param webSocket * the WebSocket on which the message has been received @@ -449,7 +462,7 @@ } /** - * An unrecoverable error has occurred. + * An error has occurred. * *

This is the last invocation from the {@code WebSocket}. By the * time this invocation begins both {@code WebSocket}'s input and output @@ -500,10 +513,8 @@ /** * Sends a Text message with characters from the given {@code CharSequence}. * - *

To send a Text message invoke this method only after the previous - * Text or Binary message has been sent. The character sequence must not be - * modified until the {@code CompletableFuture} returned from this method - * has completed. + *

The character sequence must not be modified until the + * {@code CompletableFuture} returned from this method has completed. * *

A {@code CompletableFuture} returned from this method can * complete exceptionally with: @@ -513,7 +524,7 @@ * or if a previous Binary message has been sent with * {@code isLast == false} *

  • {@link IOException} - - * if an I/O error occurs + * if an I/O error occurs, or if the output is closed * * * @implNote If a partial or malformed UTF-16 sequence is passed to this @@ -534,11 +545,10 @@ /** * Sends a Binary message with bytes from the given {@code ByteBuffer}. * - *

    To send a Binary message invoke this method only after the previous - * Text or Binary message has been sent. The message consists of bytes from - * the buffer's position to its limit. Upon normal completion of a - * {@code CompletableFuture} returned from this method the buffer will have - * no remaining bytes. The buffer must not be accessed until after that. + *

    The message consists of bytes from the buffer's position to its + * limit. Upon normal completion of a {@code CompletableFuture} returned + * from this method the buffer will have no remaining bytes. The buffer must + * not be accessed until after that. * *

    The {@code CompletableFuture} returned from this method can * complete exceptionally with: @@ -548,7 +558,7 @@ * or if a previous Text message has been sent with * {@code isLast == false} *

  • {@link IOException} - - * if an I/O error occurs + * if an I/O error occurs, or if the output is closed * * * @param message @@ -573,10 +583,12 @@ *

    The {@code CompletableFuture} returned from this method can * complete exceptionally with: *

    * * @param message @@ -598,10 +610,12 @@ *

    The {@code CompletableFuture} returned from this method can * complete exceptionally with: *

    * * @param message @@ -613,8 +627,8 @@ CompletableFuture sendPong(ByteBuffer message); /** - * Sends a Close message with the given status code and the reason, - * initiating an orderly closure. + * Initiates an orderly closure of this {@code WebSocket}'s output by + * sending a Close message with the given status code and the reason. * *

    The {@code statusCode} is an integer from the range * {@code 1000 <= code <= 4999}. Status codes {@code 1002}, {@code 1003}, @@ -623,29 +637,33 @@ * status codes is implementation-specific. The {@code reason} is a string * that has an UTF-8 representation not longer than {@code 123} bytes. * - *

    Use the provided integer constant {@link #NORMAL_CLOSURE} as a status - * code and an empty string as a reason in a typical case. - * *

    A {@code CompletableFuture} returned from this method can * complete exceptionally with: *

    * - *

    By the time the {@code CompletableFuture} returned from this method - * completes normally, the output will have been closed. + *

    Unless the {@code CompletableFuture} returned from this method + * completes with {@code IllegalArgumentException}, or the method throws + * {@code NullPointerException}, the output will be closed. + * + *

    If not already closed, the input remains open until it is + * {@linkplain Listener#onClose(WebSocket, int, String) closed} by the server, + * or {@code abort} is invoked, or an + * {@linkplain Listener#onError(WebSocket, Throwable) error} occurs. * - * @implSpec An endpoint sending a Close message might not receive a - * complementing Close message in a timely manner for a variety of reasons. - * The {@code WebSocket} implementation is responsible for providing a - * closure mechanism that guarantees that once {@code sendClose} method has - * been invoked the {@code WebSocket} will close regardless of whether or - * not a Close frame has been received and without further intervention from - * the user of this API. Method {@code sendClose} is designed to be, - * possibly, the last call from the user of this API. + * @apiNote Use the provided integer constant {@link #NORMAL_CLOSURE} as a + * status code and an empty string as a reason in a typical case + *

    {@code
    +     *     CompletableFuture webSocket = ...
    +     *     webSocket.thenCompose(ws -> ws.sendText("Hello, ", false))
    +     *              .thenCompose(ws -> ws.sendText("world!", true))
    +     *              .thenCompose(ws -> ws.sendClose(WebSocket.NORMAL_CLOSURE, ""))
    +     *              .join();
    +     * }
    * * @param statusCode * the status code @@ -681,8 +699,7 @@ String getSubprotocol(); /** - * Tells whether or not this {@code WebSocket} is permanently closed - * for sending messages. + * Tells whether this {@code WebSocket}'s output is closed. * *

    If this method returns {@code true}, subsequent invocations will also * return {@code true}. @@ -692,8 +709,7 @@ boolean isOutputClosed(); /** - * Tells whether or not this {@code WebSocket} is permanently closed - * for receiving messages. + * Tells whether this {@code WebSocket}'s input is closed. * *

    If this method returns {@code true}, subsequent invocations will also * return {@code true}. @@ -703,15 +719,11 @@ boolean isInputClosed(); /** - * Closes this {@code WebSocket} abruptly. + * Closes this {@code WebSocket}'s input and output abruptly. * *

    When this method returns both the input and the output will have been - * closed. Subsequent invocations will have no effect. - * - * @apiNote Depending on its implementation, the state (for example, whether - * or not a message is being transferred at the moment) and possible errors - * while releasing associated resources, this {@code WebSocket} may invoke - * its listener's {@code onError}. + * closed. Any pending send operations will fail with {@code IOException}. + * Subsequent invocations of {@code abort()} will have no effect. */ void abort(); }