> On 12 Feb 2018, at 19:29, Chuck Davis <cjgun...@gmail.com> wrote: > > <snip> > > There is never a way to tell how much data may be > returned. >
Reactive Streams speak in terms of numbers of invocations to the onNext method. Not the amount of data. (Please disregard if that's not what you meant.) > <snip> > > I am under the impression that this new API requires the developer to > buffer the messages until all the data is complete. As I recall (it's > been a few months since I implemented) decoders handled that in the > old API and if I am correct that is going to put a lot more work on > the plate of developers. If my understanding is correct I'd suggest > listeners should do the buffering and deliver a complete message. If > it's written once in the implementation it would save thousands of > developer hours recreating the same wheel. At least this approach > makes sense in my use case. Partial pieces of serialized objects are > useless. > Different developers have different requirements. Some would benefit from having the ability to receive partial messages. If this API only provided whole message receiving, it would not be possible to satisfy those developers. If in your case it makes no sense to use partial messages, you can still use the API. It will only require some extra work though. In the simplest case the amount of work is not that big really: @Override public CompletionStage<?> onText(WebSocket webSocket, CharSequence message, WebSocket.MessagePart part) { webSocket.request(1); String str = null; switch (part) { case FIRST: case PART: text.append(message); return null; case LAST: text.append(message); str = text.toString(); text.setLength(0); break; case WHOLE: str = message.toString(); break; } processWholeText(str); return null; } private void processWholeText(String string) { // -- your code here -- } However, with this approach you will need to go the extra mile if you ever need to talk to WebSocket.request outside of the context of the associated listener. Because the WebSocket client will continue to speak in terms of the number of unspecified (could be whole, could be partial) messages. We might provide this "buffering" ability internally in WebSocket in the future. One option would be to add an intermediate method to the WebSocket.Builder such that this method would give an extra guarantee for the attached listener: Builder receiveWholeMessages(boolean whole) Another option would be to provide a slightly different listener that would reflect this behaviour in a more type-safe manner (i.e. the listener won't have those extra MessagePart arguments in the onText and onBinary methods): CompletableFuture<WebSocket> buildAsync(URI uri, WholeListener listener) And these are probably not the only possible options. It does however seem that optimization-wise there is not much to be gained from doing this. As different parts of a whole message (i.e. payloads of WebSocket frames) do not occupy contiguous parts of the underlying memory. In order to be concatenated, payload pieces would need to be cut out of their frames first. Which means copying. In this context it might have been better if we decided to use ByteBuffer[] instead of ByteBuffer in the first place. But that seemed like an overkill back then. > > Can you expound a little more on this: "Finally, each of the > listener's methods asks the user to signal when the user has processed > the message it bears [2]." All I see is that the method returns a > CompletionStage which triggers a procession of CompletionStage objects > (not sure yet how this ends except by returning a null?) and I don't > see in the docs how this signals the listener except that the method > exits and may be called again. I'm sorry to be so dense here. > Let me explain this the following way. With WebSocket there is an exchange of messages. You create a new message for the WebSocket client and tell it to send the message by calling one of the send methods. You'd probably like to know though when the WebSocket client has actually processed the message. That's where a CompletableFuture returned from the method comes in handy. When the WebSocket client completes this future, you know the message has been processed. Now consider that when the WebSocket client receives a message it calls one of the receive methods on you. The WebSocket client needs you to tell it when you finished processing the message it gave you. You do this by returning a CompletionStage from the method. Once this stage reaches the WebSocket client, the client then attaches dependant actions on this stage internally. But that's details of the implementation, the point is the scenario is really symmetric. Now, in the common case, when you process the message synchronously (before the call to the receive method ends) you should return an already completed CompletionStage. Why? Because by the time the method returns, you will have processed the message. Since this case will probably be the most common, it was designed so that you just return null. Consequently, you can always return a stage that completes later than when you processed the message. But you must never return a stage that completes earlier than that. Think about it as a trivial protocol for handing over the objects between asynchronous operations. Who owns the buffer (or the character sequence) and when. Hopefully this answers your question. > > Finally, is there a target release to graduate from incubator? I'm > kinda stuck until the dust settles. May it hit jdk10 or 11? > "Incubating Feature. Will be removed in a future release." sounds > so.....ominous! > > <snip> > A short answer: JDK 11 A bit longer one: http://mail.openjdk.java.net/pipermail/net-dev/2018-February/011155.html Thanks, -Pavel