WebSocket client API

Simone Bordet simone.bordet at gmail.com
Mon Oct 19 21:37:06 UTC 2015


Hi,

On Sat, Oct 17, 2015 at 8:56 PM, Pavel Rappo <pavel.rappo at oracle.com> wrote:
>>>  * WebSocket has become an abstract class
>>
>> What is the rationale behind this ?
>> Just to avoid subclassing ?
>> Why subclassing is negated ?
>> I think it's best it remains an interface: it would allow people to
>> write wrappers, use java.lang.reflect.Proxy, etc.
>
> This argument can be used for pretty much any class out there. You see, it's a
> question of whether subtyping of X should be allowed or disabled, if X has not
> been designed with it in mind.

It's pretty trivial to convert WebSocket to an interface, and would
allow applications much more flexibility.
There are tons of frameworks out there that rely on interfaces to
provide method interception (e.g. AOP, Spring Proxies, etc.).
I think it would be a mistake to make WebSocket non subclassable / non
implementable.
There are a ton of use cases: logging, filtering, transformation of
content, buffering, etc.

> If Builder.header was with "add" semantics, it wouldn't be possible to remove
> added headers. But now I probably agree with you. In this case it would be
> better for the API to provide an "add" behaviour, mostly because it's more
> compile time checking friendly.

Now I am confused :)

Are you saying that if you do:

builder.headers("foo", "bar", "baz").headers("one", "two", "three")

then "foo", "bar" and "baz" are removed ?

> Easier? In some cases, probably. On the other WebSocket.request(long) is an
> internal part of the API that only the Listener should talk to (the same is with
> Flow.Subscriber and Flow.Subscription).

In proxies and WebSocket applications in general, it's the opposite:
these applications want to call request(long) after they have written
data, not after they read it, to produce backpressure.
And the write code is typically far, far away from the Listener code,
which means that you have to carry around the LongConsumer, which is
cumbersome.

>>>  * send methods return CompletableFuture<WebSocket>
>>
>> This parameter is typically not used.
>
> It's too early to refer to something in this API as "typically" :-)
> Or you mean for CF in general?

I mean the WebSocket type parameter.
It is typically not used because the WebSocket object must be present
in the lexical scope, otherwise you cannot call the sendXXX methods
that returns the CF.
Hence the CF parameter is typically not used - it's indeed totally redundant.
I'm fine with sendXXX() methods to return CF<Void>.

> But I'm against of merging flowController with WebSocket.

Yet, you did not bring any technical reasons for the split, and
"separation of concerns" is just not enough because it can mean
anything (and it may even be wrong).

We are exchanging more parameters to onOpen() (the LongConsumer), more
boilerplate that applications must write, more classes to avoid the
boilerplate and bloat the API (an additional AbstractListener), more
work for applications, more forced allocation, versus one more method
in WebSocket that would get rid of all of that.

If you write a chat application with these API, you will see that
having WebSocket.request() simplifies the code by *a lot*.

I have done that, and I am reporting my impressions.
If you have an example where splitting WebSocket and LongConsumer is
actually beneficial to application writing (as in less and clearer
code), I am interested in learning from it.

> As for the API, well CompletableFuture is surely more nice compared to a bare
> Consumer. But let me try to implement both things and we could choose later.
> We probably don't want to double the number of sending methods, do we? :)

Agreed, either CF or Consumer, not both.

Thanks !

-- 
Simone Bordet
http://bordet.blogspot.com
---
Finally, no matter how good the architecture and design are,
to deliver bug-free software with optimal performance and reliability,
the implementation technique must be flawless.   Victoria Livschitz


More information about the net-dev mailing list