26 package jdk.internal.net.http.websocket; |
26 package jdk.internal.net.http.websocket; |
27 |
27 |
28 import java.io.IOException; |
28 import java.io.IOException; |
29 import java.nio.ByteBuffer; |
29 import java.nio.ByteBuffer; |
30 import java.util.concurrent.CompletableFuture; |
30 import java.util.concurrent.CompletableFuture; |
|
31 import java.util.function.BiConsumer; |
31 |
32 |
32 /* |
33 /* |
33 * Transport needs some way to asynchronously notify the send operation has been |
34 * A WebSocket view of the underlying communication channel. This view provides |
34 * completed. It can have several different designs each of which has its own |
35 * an asynchronous exchange of WebSocket messages rather than asynchronous |
35 * pros and cons: |
36 * exchange of bytes. |
36 * |
37 * |
37 * (1) void sendMessage(..., Callback) |
38 * Methods sendText, sendBinary, sendPing, sendPong and sendClose initiate a |
38 * (2) CompletableFuture<T> sendMessage(...) |
39 * corresponding operation and return a CompletableFuture (CF) which will |
39 * (3) CompletableFuture<T> sendMessage(..., Callback) |
40 * complete once the operation has completed (succeeded or failed). |
40 * (4) boolean sendMessage(..., Callback) throws IOException |
|
41 * ... |
|
42 * |
41 * |
43 * If Transport's users use CFs, (1) forces these users to create CFs and pass |
42 * These methods are designed such that their clients may take an advantage on |
44 * them to the callback. If any additional (dependant) action needs to be |
43 * possible implementation optimizations. Namely, these methods: |
45 * attached to the returned CF, this means an extra object (CF) must be created |
|
46 * in (2). (3) and (4) solves both issues, however (4) does not abstract out |
|
47 * when exactly the operation has been performed. So the handling code needs to |
|
48 * be repeated twice. And that leads to 2 different code paths (more bugs). |
|
49 * Unless designed for this, the user should not assume any specific order of |
|
50 * completion in (3) (e.g. callback first and then the returned CF). |
|
51 * |
44 * |
52 * The only parametrization of Transport<T> used is Transport<WebSocket>. The |
45 * 1. May return null which is considered the same as a CF completed normally |
53 * type parameter T was introduced solely to avoid circular dependency between |
46 * 2. Accept an arbitrary attachment to complete a CF with |
54 * Transport and WebSocket. After all, instances of T are used solely to |
47 * 3. Accept an action to take once the operation has completed |
55 * complete CompletableFutures. Transport doesn't care about the exact type of |
|
56 * T. |
|
57 * |
48 * |
58 * This way the Transport is fully in charge of creating CompletableFutures. |
49 * All of the above allows not to create unnecessary instances of CF. |
59 * On the one hand, Transport may use it to cache/reuse CompletableFutures. On |
50 * For example, if a message has been sent straight away, there's no need to |
60 * the other hand, the class that uses Transport, may benefit by not converting |
51 * create a CF (given the parties agree on the meaning of null and are prepared |
61 * from CompletableFuture<K> returned from Transport, to CompletableFuture<V> |
52 * to handle it). |
62 * needed by the said class. |
53 * If the result of a returned CF is useless to the client, they may specify the |
|
54 * exact instance (attachment) they want the CF to complete with. Thus, no need |
|
55 * to create transforming stages (e.g. thenApply(useless -> myResult)). |
|
56 * If there is the same action that needs to be done each time the CF completes, |
|
57 * the client may pass it directly to the method instead of creating a dependant |
|
58 * stage (e.g. whenComplete(action)). |
63 */ |
59 */ |
64 public interface Transport<T> { |
60 public interface Transport { |
65 |
61 |
66 CompletableFuture<T> sendText(CharSequence message, boolean isLast); |
62 <T> CompletableFuture<T> sendText(CharSequence message, |
|
63 boolean isLast, |
|
64 T attachment, |
|
65 BiConsumer<? super T, ? super Throwable> action); |
67 |
66 |
68 CompletableFuture<T> sendBinary(ByteBuffer message, boolean isLast); |
67 <T> CompletableFuture<T> sendBinary(ByteBuffer message, |
|
68 boolean isLast, |
|
69 T attachment, |
|
70 BiConsumer<? super T, ? super Throwable> action); |
69 |
71 |
70 CompletableFuture<T> sendPing(ByteBuffer message); |
72 <T> CompletableFuture<T> sendPing(ByteBuffer message, |
|
73 T attachment, |
|
74 BiConsumer<? super T, ? super Throwable> action); |
71 |
75 |
72 CompletableFuture<T> sendPong(ByteBuffer message); |
76 <T> CompletableFuture<T> sendPong(ByteBuffer message, |
|
77 T attachment, |
|
78 BiConsumer<? super T, ? super Throwable> action); |
73 |
79 |
74 CompletableFuture<T> sendClose(int statusCode, String reason); |
80 <T> CompletableFuture<T> sendClose(int statusCode, |
|
81 String reason, |
|
82 T attachment, |
|
83 BiConsumer<? super T, ? super Throwable> action); |
75 |
84 |
76 void request(long n); |
85 void request(long n); |
77 |
86 |
78 /* |
87 /* |
79 * Why is this method needed? Since Receiver operates through callbacks |
88 * Why is this method needed? Since receiving of messages operates through |
80 * this method allows to abstract out what constitutes as a message being |
89 * callbacks this method allows to abstract out what constitutes as a |
81 * received (i.e. to decide outside this type when exactly one should |
90 * message being received (i.e. to decide outside this type when exactly one |
82 * decrement the demand). |
91 * should decrement the demand). |
83 */ |
92 */ |
84 void acknowledgeReception(); |
93 void acknowledgeReception(); // TODO: hide |
85 |
94 |
86 void closeOutput() throws IOException; |
95 void closeOutput() throws IOException; |
87 |
96 |
88 void closeInput() throws IOException; |
97 void closeInput() throws IOException; |
89 } |
98 } |