jsr356-experts@websocket-spec.java.net

[jsr356-experts] Reworking Configuration/Deployment APIs

From: Danny Coward <danny.coward_at_oracle.com>
Date: Thu, 07 Feb 2013 16:43:15 -0800

Hi folks,

This is a rather long email, but please bear with me !

The majority of the remaining spec issues we have relate to the
deployment/configuration aspects of deploying endpoints. As a reminder,
in the public draft spec we have APIs for deploying client endpoints,
and a (adjustable) WAR scan for deploying server endpoints. Our spec
issues are a mix of clarifying those mechanisms, and a handful of
requests for extra deployment/configuration functionality, like the
return of dynamic server endpoint deployment, or endpoints as singletons.

But before we get to those, I want to make sure that what we have
already spec-ed out works well. Based on a long look at it, and our own
experience implementing it: I think there's some basic problems in this
area need fixing with some refactoring/API massaging before we can build
on it with other features.

I thought I lay out my thinking before proposing anything - please feel
free to comment/share your thoughts here too.


* The API as it stands

You'll remember that we're using various *EndpointConfiguration classes
to represent the configuration of a logical endpoint. The configuration
breaks out into 3 parts:-

A) the data required to deploy the endpoint (for example, for a server
endpoint: path, endpoint class, subprotocols, extensions).
B) various algorithms needed for the opening handshake (e.g. URI match,
subprotocol determination, handshake intercept), default implementations
provided by the container, overridable by the developer
C) arbitrary developer provided initialization code / data (e.g.
initialization and access to cached database connection ). I know we
have an RFE to factor that (back) to the EndpointFactory concept.

We still need all of this functionality. Now to how its used:-

For server deployment of annotated endpoints, the container scans the
WAR file for them, and the resulting list can be filtered in the
ServerApplicationConfiguration.getAnnotatedEndpoints() call. That seems
to work well: simple for the easy cases, configurable if you need it.

For server deployment of programmatic endpoints, the
*ServerEndpointConfig classes are scanned for in the WAR file, and can
be filtered in the
ServerApplicationConfiguration.getProgrammaticEndpoints. This part
doesn't work well and is a good segue into the problems, listed in order
of seriousness.


* Where the API doesn't support the functionality well

1) Programmatic endpoint deployment is really heavyweight, even in the
most trivial of cases:
ServerApplicationConfiguration.getProgrammaticEndpoints is requiring the
developer provide a *ServerEndpointConfiguration implementation *class*
to contain all the configuration data for the logical endpoint.
Therefore, the only way the developer can provide configuration data
(see A) is to do so in the constructor of a subtype of
ServerEndpointConfiguration. So even in the simplest case of deploying
to /foo, the API is forcing the developer to create a whole class just
to get the path info there. Such as:-

public MyFooEndpointConfig extends DefaultServerConfiguration {
     public MyEndpointConfig() {
         super.setPath("/foo");
     }
}

Very heavy duty just to supply a path. So I think this one is very
problematic.

Potential solutions center on asking the developer to provide
*ServerEndpointConfiguration instances to the container for deployment,
not classes. So the return of
ServerApplicationConfiguration.getProgrammaticEndpoints would be a set
of *ServerEndpointConfiguration instances, not classes. A relatively
simple change for a big reduction complexity.

2) Configuration data can be changed after the endpoint has been
deployed, for example

public void onOpen(Session s, EndpointConfig e) {
     ((DefaultServerConfiguration) e).setPath("/totally-new-path"); //
btw setPath is only supposed to be used during deployment...
     ((DefaultServerConfiguration)
e).setSubprotocols(totallyNewSubprotocols);
     // what does this all mean ? same sort of thing is possible on
client deployment too.
}

I think that's a pretty undesirable side effect of the API as it stands.
Our use case for developers being able to supply their own configuration
objects was that they could provide custom behavior during the
handshake, or attach custom behavior to the endpoint. Not that they
could rewrite the config data at runtime. I'd like to eliminate the
possibility of the kind of example above by more restricted forms of
config object creation.

3) Currently, configuration *data* defined in the annotation can be
overridden.

  @WebSocketEndpoint(
             value="/innocently", // no-one can override this
             subprotocols = {"configuring", "this", "endpoint"}, //
no-one can override this
             configuration=MyOverridingConfig.class // provides custom
handshake-time behavior
             )

public MyOverridingConfig extends DefaultServerConfiguration {
     public String getPath() {
         super.setProtocols(new ArrayList());
         return "/ha-ha";
     }
}

while not so problematic as the others, I think its going to be
misleading to developer to have config data held both in the annotation
and possibly in a custom config as well, especially since, again, the
usecase for custom configuration was overriding the default handshake
intercepts, not futzing with the annotation data. So I'd like to tighten
this one up too by some more restricted forms of config object creation
and possible refactoring of the config classes: what you see is perhaps
what you should get in the annotation data.

So these are the issues I'd like to address before we get to the others.

I've *sketched* out a couple possibilities in
branches/config-api-experimental, and
config-api-experimental-handshakeconfigurator, and will continue to
experiment over the next few days.

Thanks,

- Danny
-- 
<http://www.oracle.com> 	*Danny Coward *
Java EE
Oracle Corporation