Deployment

A WebSocket container implementation has to detect and deploy both annotated and programmatic server endpoints. This chapter gives an overview of how WebSocket endpoints are deployed in Java EE (which includes a Servlet container)

Deploying annotated server endpoints

This is the easy part. Once you implement your annotated (decorated with @ServerEndpoint) WebSocket server endpoint, the Servlet runtime weaves it scanning magic and extracts all such annotated classes from the WAR (and JARs within), deploys them and ensures they are ready for action

Deploying programmatic server endpoints

Things are different in case of programmatic server endpoints (which extend javax.websocket.Endpoint) - they are not deployed automatically by the container unless we utilize the javax.websocket.server.ServerApplicationConfig class

Let's look at an example

//A 'chat' club using programmatic web socket endpoint style

public class ChatClub extends Endpoint {
....
@Override
public void onOpen(Session joinee, EndpointConfig config) {
    System.out.println("Peer " + joinee.getId() + " connected");
    joinee.getRemoteBasic().sendText("Welcome to the Chat Club. The first rule of Chat Club is ..... don't talk, just type");
    joinee.addMessageHandler(new MessageHandler.Whole<String>() {
        @Override
        public void onMessage(String message) {
            try {
            joinee.getBasicRemote().sendText("You sent \n"+message+"\nRead the rulez.. again!");
            } catch (IOException ex) {
            throw new RuntimeException(ex);
            }
          }
        });
    }
    ....
}

Assuming this is the only (programmatic) endpoint we want to deploy, this is how you would do it

//custom implementation to guide the deployment of our programmatic endpoint

public class CustomServerAppConfigProvider implements ServerApplicationConfig {

    @Override
    public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> endpointClasses) {
        Set<ServerEndpointConfig> result = new HashSet<>();
        for (Class epClass : endpointClasses) {
            if (epClass.equals(ChatClub.class)) {
            ServerEndpointConfig sec = ServerEndpointConfig.Builder.create(epClass, "/chatclub").build();
            result.add(sec);
           }
        }
        return result;
    }

    @Override
    public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {
        // we do not have annotated endpoints. if we did, they will not be deployed !
        return Collections.emptySet();
    }
}

Once the WebSocket container detects an implementation of ServerApplicationConfig, it invokes it.

Things to note

  • Using ServerApplicationConfig is compulsory in order to deploy programmatic endpoints

  • getEndpointConfigs method is for deploying programmatic endpoints

  • getAnnotatedEndpointClasses method is for annotated endpoints

  • Collections.emptySet() is returned from getAnnotatedEndpointClasses since it is assumed that there are no annotated endpoints

But why do we even need this for annotated endpoints ? Our annotated endpoints get deployed automagically... correct ? Well its partially correct since it's applicable to a scenario where we deploy annotated endpoints only The next section provides more details...

Deploying annotated & programmatic server endpoints together

When you have a combination of programmatic and annotated endpoints in your application, then you have to make use of a custom subclass of ServerApplicationConfig and explicitly return set of all annotated endpoints from the getAnnotatedEndpointClasses method

//return ALL the auto-detected (scanned) annotated endpoints which the container will deploy

....
@Override
public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {
    return scanned;
}
....

Please note that, using this method, you can choose to restrict the endpoints (annotated and programmatic) being deployed i.e. not all the detected endpoints need to be (will be) deployed

Going back to the question which was posed above.. here is the answer

This is how it works..

If the WebSocket container finds implementation of the ServerApplicationConfig class, it uses the same to deploy both programmatic and annotated endpoints. The result of the WAR scanning process (by the Servlet container) is passed on the methods of this class

  • For getEndpointConfigs, the set (java.util.Set) of detected programmatic endpoints are passed as the method parameter

  • For getAnnotatedEndpointClasses method, the set (java.util.Set) of detected annotated endpoints are passed as the method parameter

Summary of deployment scenarios

Programmatic API for deploying endpoints

In addition to the above, WebSocket specification also provides a Programmatic API (not to be confused with programmatic endpoints) to deploy endpoints.

Why is it needed ?

The above mentioned deployment strategies rely on the Servlet scanning mechanism for endpoint discovery. The programmatic APIs provides an alternative route and can be used in web (Servlet) container without the automatic scanning as well as in case WebSocket endpoints are deployed in standalone mode

The API to be used in this case is javax.websocket.server.ServerContainer - it has separate methods for annotated (void addEndpoint(Class<?> endpointClass)) and programmatic endpoints (void addEndpoint(ServerEndpointConfig serverConfig))

The instance of ServerContainer is obtained in different ways depending on whether the application is executing in a Servlet (web container) or standalone mode

Usage in a Servlet container

Reference to an instance of the ServerContainer interface is made available by using the javax.websocket.server.ServerContainer attribute in javax.servlet.ServletContext

@WebListener
public class ServletCtxBasedDeploymentStrategy implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce){
        //obtain the instance
        ServerContainer sc = (ServerContainer) sce.getServletContext().getAttribute("javax.websocket.server.ServerContainer"); 

        //trigger endpoint deployment
        deployAnnotatedEndpoint(sc);
        deployProgEndpoint(sc);
    }

    private void deployAnnotatedEndpoint(ServerContainer container) {
        container.addEndpoint(StockTicker.class);
        container.addEndpoint(WeatherTracker.class);
    }

    private void deployProgEndpoint(ServerContainer container) {
        container.addEndpoint(ServerEndpointConfig.Builder.create(ChatClub.class, "/chatclub").build());
        container.addEndpoint(ServerEndpointConfig.Builder.create(RealTimeLocationTracker.class, "/location").build());
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce){}
}

Additional notes

  • Standalone usage of ServerContainer is depended upon how a specific runtime allows the developer to obtain its instance

  • This is not be mixed with the above mentioned deployment strategy which uses ServerApplicationConfig. The implementation will ignore duplicate endpoints (submitted by the separate deployment techniques) since this is mandated by the specification

In the next chapter ...

... we will look at how the WebSocket components work in concert with the other Java EE APIs from a platform context

Last updated