Configuration
In this lesson, we will dive into configuration parameters associated with WebSocket endpoints. Simply put, Configuration is nothing but a bunch of (meta) data associated with an endpoint (server or client). You will learn about
  • Server endpoint configuration (for both annotated and programmatic endpoints)
  • Client endpoint configuration (for both annotated and programmatic endpoints)
  • (Server & client) Endpoint Configurators
From an API perspective, a WebSocket endpoint Configuration is represented by the EndpointConfig interface which is extended by ServerEndpointConfig and ClientEndpointConfig for server and client respectively

Server configuration

Before we dive into the details, here is a quick snapshot of the related interfaces
Class/Interface
Description
ServerEndpointConfig
A derivative of EndpointConfig interface which is specific for configuration related to server side WebSocket endpoints
ServerEndpointConfig.Builder
Used only for programmatic server endpoints to build a ServerEndpointConfig instance
ServerEndpointConfig.Configurator
An interface whose custom implementation allows sharing of global (for all endpoints) available logic/state as well opportunity to intercept the WebSocket handshake process (via method override)

Configuring annotated server endpoints

Annotated server endpoints are configured implicitly via the elements of the @ServerEndpoint annotation. The WebSocket container picks up the value from the annotation elements and creates an instance of EndpointConfig behind the scenes
//annotated server endpoint with all its configuration elements
@ServerEnpdoint(
value = "/chat/",
configurator = ChatEndpointConfigurator.class, //discussed later
decoders = JSONToChatObjectDecoder.class,
encoders = ChatObjectToJSONEncoder.class,
subprotocols = {"chat"}
)
public class ChatServer {
//business logic...
}
The EndpointConfig instance is automatically injected (at run time by the WebSocket container) as a parameter of the @OnOpen method
//server endpoint configuration in action
@OnOpen
public void onOpenCallback(Session session, EndpointConfig epConfig){
ServerEndpointConfig serverConfig = (ServerEndpointConfig) epConfig;
Map<String, Object> globalPropertiesMap = serverConfig.getUserProperties();
......
}

Configuring programmatic server endpoints

Programmatic endpoints need (explicit) coding as far as configuration is concerned. This is because of the fact that programmatic endpoints are deployed differently and need an instance of ServerEndpointConfig
Don't worry about the deployment aspect since it's covered in detail in the next lesson
Here is where the fluent builder ServerEndpointConfig.Builder comes into picture. Let's look at an example which demonstrates it's usage
ServerEndpointConfig serverConfig = ServerEndpointConfig.Builder
.create(StockTrackerEndpoint.class , "/pop-stocks/").
.configurator(StockTrackerConfigurator.getInstance()) //discussed later
.decoders(JSONToStockTickerObject.class)
.encoders(StockTickerObjectToJSON.class)
.build();
An instance of ServerEndpointConfig is made available in the onOpen method of the javax.websocket.Endpoint (as a parameter)
public class ProgrammaticChatClient extends Endpoint {
@Override
public void onOpen(Session session, EndpointConfig config){
ServerEndpointConfig serverConfig = (ServerEndpointConfig) epConfig;
.....
}
}

Client configuration

You must have built a fair understanding about WebSocket clients from the WebSocket Client API lesson. They too have configuration parameters associated with them which are used while connecting to WebSocket server endpoints. Before we dive into the details, here is a quick snapshot of the related interfaces
Class/Interface
Description
ClientEndPointConfig
A derivative of EndpointConfig interface which is specific for configuration related to client side WebSocket endpoints
ClientEndpointConfig.Builder
Used only for programmatic client endpoints to build a ClientEndpointConfig instance
ClientEndpointConfig.Configurator
The client side equivalent of ServerEndpointConfig.Configurator

Configuring annotated client endpoints

Annotated client endpoints are configured implicitly via the elements of the @ClientEndpoint annotation
@ClientEndpoint(
configurator = ChatClientEndpointConfigurator.class, //discussed later
decoders = JSONToChatObjectDecoder.class,
encoders = ChatObjectToJSONEncoder.class,
subprotocols = {"chat"}
)
public class ChatClient {
//business logic...
}
This instance is automatically injected (at runtime) as a parameter of the @OnOpen method
//server endpoint configuration in action
@OnOpen
public void onOpenCallback(Session session, EndpointConfig epConfig){
ClientEndpointConfig clientConfig = (ClientEndpointConfig) epConfig;
......
}

Configuring programmatic client endpoints

Just like their server side counterparts, configuration for programmatic clients can be coded using a fluent builder API - ClientEndpointConfig
ClientEndpointConfig cec = ClientEndpointConfig.Builder
.configurator(ChatClientConfigurator.getInstance()) //discussed later
.decoders(JSONToStockTickerObject.class)
.encoders(StockTickerObjectToJSON.class)
.build();
This configuration object is used while initiating connection to a WebSocket endpoint. Please refer to the WebSocket Client API chapter for code samples

Additional notes

  • As you might have already noticed, there is not much of a difference b/w annotated client and server side configurations, except for the fact that a client endpoint does not have the concept of a path or a URL where its listening for connections - that's something that a server endpoint does
  • An EndpointConfig instance provides the capability to store (global) properties which are common to all instances of an endpoint. It does so by providing a getUserProperties() method which exposes a mutable Map

The big picture

Annotated and programmatic endpoint configuration are handled differently, but the end result is the same. Below is a table which illustrates this point for both server and client endpoints
  • For server endpoints, the table shows the mapping b/w corresponding element of the @ServerEndpoint annotation, the corresponding method in ServerEndpointConfig as well as appropriate the method in the ServerEndpointConfig.Builder, and
@ServerEndpoint annotation element
ServerEndpointConfig method
ServerEndpointConfig.Builder method
value
getPath()
create(Class<?> endpointClass, String path)
configurator
getConfigurator()
configurator(ServerEndpointConfig.Configurator sec)
decoders
getDecoders()
decoders(List<Class<? extends Decoder>> decoders)
encoders
getEncoders()
encoders(List<Class<? extends Encoder>> encoders)
subprotocols
getSubprotocols()
subprotocols(List<String> subprotocols)
  • In case of client endpoints, the table shows the mapping b/w corresponding element of the @ClientEndpoint annotation, the corresponding method in ClientEndpointConfig as well as appropriate the method in the ClientEndpointConfig.Builder
@ClientEndpoint annotation element
ClientEndpointConfig method
ClientEndpointConfig.Builder method
configurator
getConfigurator()
configurator(ClientEndpointConfig.Configurator clientEndpointConfigurator)
decoders
getDecoders()
decoders(List<Class<? extends Decoder>> decoders)
encoders
getEncoders()
encoders(List<Class<? extends Encoder>> encoders)
subprotocols
getPreferredSubprotocols()
preferredSubprotocols(List<String> preferredSubprotocols)

Configurators

Basics

Configurators (which in my opinion could have been named differently) are applicable to both server and client side WebSocket endpoints. These are components which can intercept handshake phase of the WebSocket connection lifecycle. They can be used to implement a bunch of things such as
  • customizing the WebSocket handshake process
  • plugging in a custom implementation for producing endpoint instances
  • implementing common logic which can be used by all endpoint instances which are configured using the Configuration with which the Configurator is associated
If the developer does not override (provide a custom implementation) of a Configurator a default one is internally used by the container

Server side

The table below provides an overview. It lists out the methods of a ServerEndpointConfig.Configurator which needs to be overridden to provide custom behavior
Capbility
Method in ServerEndpointConfig.Configurator
Details
Handshake modification
void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response)
a custom implementation can override this method in order to modify the HandshakeResponse created by the runtime (in response to the handshake request)
Customizing endpoint creation
<T> T getEndpointInstance(Class<T> endpointClass)
overriding this method allows you to hook into the WebSocket endpoint substantiation process
Origin check
boolean checkOrigin(String originHeaderValue)
it provides the value of the HTTP Origin header sent by the client during the handshake process in order to enforce security checks if required
Subprotocol negotiation
List<Extension> getNegotiatedExtensions(List<Extension> installed, List<Extension> requested)
selects the appropriate subprotocol depending upon the best match b/w what the server supports and what is being requested by the client (empty if there is no match)
Extension negotiation
String getNegotiatedSubprotocol(List<String> supported, List<String> requested)
similar to the subprotocol, the extension support can be negotiated as well
The catch If you choose to customize the endpoint creation process, (Java EE) container services like dependency injection might not available since the container default convention is being overridden
Let's look at en example
//custom configurator
public class CustomServerEndpointConfigurator extends ServerEndpointConfig.Configurator {
@Override
public <T> T getEndpointInstance(Class<T> endpointClass){
//override the default behavior by providing a 'Singleton'
return (T) StockTickerEndpoint.getInstance();
}
@Override
public boolean checkOrigin(String originHeaderValue){
//just audit this
audit(originHeaderValue);
return true;
}
private String user;
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response){
//introspect the request headers
System.out.println(request);
//the authenticated user
this.user = request.getUserPrincipal().getName();
}
@Override
public List<Extension> getNegotiatedExtensions(List<Extension> installed, List<Extension> requested){
//invoke default implementation
return super.getNegotiatedExtensions(installed, requested);
}
@Override
public String getNegotiatedSubprotocol(List<String> supported, List<String> requested){
//invoke default implementation
return super.getNegotiatedSubprotocol(supported, requested);
}
}
//declaring the custom configuration
@ServerEndpoint(value = "/letschat" , configurator = CustomServerEndpointConfigurator.class)
public class AnnotatedServerEndpointExample {
//call back life cycle method(s) implementation...
}

Client side

Client configurators are similar in sprirt to their server counterparts. They slightly less complicated and just define hooks for inercepting the phases before and after the handshake
Capbility
Method in ClientEndpointConfig.Configurator
Intercept Handshake
void afterResponse(HandshakeResponse hr), beforeRequest(Map<String,List<String>> headers)
//custom configurator
public class CustomClientEndpointConfigurator extends ClientEndpointConfig.Configurator {
@Override
public void beforeRequest(Map<String,List<String>> headers){
//mutate the header
String token = ...;
headers.put("X-token" , Arrays.asList(token));
}
@Override
public void afterResponse(HandshakeResponse hr){
//introspect the handshake response
System.out.println(hr.getHeaders());
}
}
//declaring the client configuration
@ClientEndpoint(configurator = CustomClientEndpointConfigurator.class)
public class AnnotatedClientEndpointExample {
//call back life cycle method(s) implementation...
}

Let's move on...

.. and take a closer look at the Deployment related aspects
Copy link
On this page
Server configuration
Configuring annotated server endpoints
Configuring programmatic server endpoints
Client configuration
Configuring annotated client endpoints
Configuring programmatic client endpoints
The big picture
Configurators
Basics
Server side
Client side
Let's move on...