WebSocket Client API

As discussed in the API Overview chapter, WebSocket has the notion of server and client components. Everything we have seen so far in the previous chapters is applicable to both these aspects. A WebSocket client endpoint

  • implements some business logic (just like it's server side counterpart)

  • connects to existing WebSocket (server) endpoints

In this lesson, we will look at some of the specifics of the Client API and explore topics like

  • Developing annotated and programmatic endpoints

  • Different ways of using them to connect to existing WebSocket server endpoints (they are different for annotated and programmatic client endpoints)

Annotated clients

The code for annotated client endpoints is not very different from their server side counterparts.

Notice the usage of the @ClientEndpoint annotation

//annotated client endpoint in action

@ClientEndpoint
public class AnnotatedChatClient {

    private ClientEndpointConfig clientConfig;
    private String user;
    @OnOpen
        public void connected(Session session, EndpointConfig clientConfig){
        this.clientConfig = (ClientEndpointConfig) clientConfig;
        this.user = session.getUserPrincipal().getName();
        System.out.println("User " + user + " connected to Chat room");
    }
    @OnMessage
    public void connected(String msg){
        System.out.println("Message from chat server: " + msg);
    }
    @OnClose
    public void disconnected(Session session, CloseReason reason){
        System.out.println("User "+ user + " disconnected as a result of "+ reason.getReasonPhrase());
    }
    @OnError
    public void disconnected(Session session, Throwable error){
        System.out.println("Error communicating with server: " + error.getMessage());
    }
}

Programmatic clients

Again, not much of a difference here - extending the java.websocket.Endpoint class is required to implement a programmatic client

//a bare bone implementation of a programmatic endpoint

public class WeatherClient extends Endpoint {
    private Session session;
    @Override
    public void onOpen(Session session, EndpointConfig config) {
        this.session = session;
        try {
        //sends back the session ID to the peer
        this.session.getBasicRemote().sendText("Session ID: " + this.session.getId());
        } catch (IOException ex) {
        throw new RuntimeException(ex);
        }
    }
}

Understanding the nuances

At first glance, the WebSocket Client API might seem a little odd. Think about other client libraries e.g. a HTTP client, REST client, a custom client for some server side component - their underlying concept is to invoke operations on the component they are connecting to (mostly a server side component). In the case of the Java WebSocket Client API, its slighlty different

  • Business logic is implemented in the form of callback method implementation (annotated client) or overriding methods of a superclass (programmatic client)

  • The connectivity logic is invoked separately (more on this in the next section)

It's important to bear this in mind and align the mental model while working with the client API

Using the Client API to connect to a server endpoint

Looking at the above code samples, its not too hard to figure out that the client API implementation is almost exactly the same as server side endpoints. We write the business logic which gets invoked once the client interacts with the server - before that, it needs to connect with one

In order to initiate a connection to a server endpoint using the WebSocket client API, use the methods available in the javax.websocket.WebSocketContainer. They take the form of overloaded methods - table below provides a summary

Endpoint type

Method

Notes

Annotated

connectToServer(Class<?> annotatedEndpointClass, URI path)

--

Annotated

connectToServer(Object annotatedEndpointInstance, URI path)

no injection support

Programmatic

connectToServer(Class<? extends Endpoint> endpointClass, ClientEndpointConfig cec, URI path)

--

Programmatic

connectToServer(Endpoint endpointInstance, ClientEndpointConfig cec, URI path)

no injection support

Connecting annotated client endpoints

Option 1: Using the class type

WebSockerContainer.connectToServer(AnnotatedChatClient.class,
URI.create("ws://javaee-chat.com"));

Option 2: Using a concrete instance of the client endpoint implementation

WebSockerContainer.connectToServer(new AnnotatedChatClient(),
URI.create("ws://javaee-chat.com"));

The caveat - if the above client code is executed within a JavaEE container, it will not be able to enjoy dependency injection support. More details on this in the Tying in with Java EE Platform chapter

Connecting programmatic client endpoints

Programmatic client endpoints follow the same strategy as their annotated counterparts except for the fact the method requires a ClientEndpointConfig

Client side Configuration has been discussed in depth in the Configuration lesson. For now its just enough to know this fact and understand the samples below

Option 1: Using the class type

WebSockerContainer.connectToServer(WeatherClient.class,
ClientEndpointConfig.Builder.create().build(), //fluent API
URI.create("ws://weather-tracker.com"));

Option 2: Using a concrete instance of the client endpoint implementation

WebSockerContainer.connectToServer(new WeatherClient(),
ClientEndpointConfig.Builder.create().build(), //fluent API
URI.create("ws://weather-tracker.com"));

The caveat - if this client is executed within a JavaEE container, it will not be able to enjoy dependency injection support. More in the Tying in with the Java EE Platform chapter

Client side Configurator

Client side configurators help enapsulate some come common logic across all instances of a client - specifically around intercepting the opening handshake with a server side enpoint. This section just serves as a pointer to the detailed discussion of this topics which is more relevant to the Configurattion chapter

Up next

We will explore how to configure Server and Client side WebSocket endpoints

Last updated