Step by Step Learning WebSocket (2) Programming WebSocket

meet Part One In this article, we use programmable WebSocket to implement the previous example:


Server Endpoint, no longer using the Server Endpoint annotation:

public class ProgramerServer extends Endpoint {
    @Override
    public void onOpen(Session session, EndpointConfig edc) {
        System.out.println("Somebody is coming!");
        
        session.addMessageHandler(new MessageHandler.Whole<String>() {

            @Override
            public void onMessage(String message) {
                System.out.println(message); 
                try {
                    session.getBasicRemote().sendText("it is sickening");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

   @Override
   public void onClose(Session session, CloseReason closeReason) {
        // NO-OP by default
    }
   @Override
    public void onError(Session session, Throwable throwable) {
        // NO-OP by default
    }
}

Instead of inheriting an Endpoint extraction class, we find that Endpoint provides three methods: onOpen, onClose, and onError.

Compared with the four sets in declarative WebSocket: @OnOpen, @OnClose, @OnMessage, @OnError, there is less @OnMessage.

What do you call back after receiving the message? As you can see from the above code, MessageHandler added to session has a similar method, onMessage. Yes, that's him. It is the handler's onMessage method that receives the message for invocation.


Is the logic of the two programming methods different? In fact, for declarative programming, it is also through MessageHandler callback @OnMessage tag method. Only this process is packaged in declarative programming mode by Tomcat et al.


For declarative programming, Tomcat converts it into the pattern of this article. POJO in declarative programming does not inherit the Endpoint abstraction class. Tomcat constructs a subclass of Endpoint, called PojoEndpoint Server in Tomcat 8. The following inheritance relationships:

public class PojoEndpointServer extends PojoEndpointBase
public abstract class PojoEndpointBase extends Endpoint.

The POJO class entrusted to us by PojoEndpoint Server can be used to run the back end. The same is true.

@ The POJO of the ClientEndpoint annotation corresponds to the PojoEndpoint Client.)


Did you find that you can't configure the mapping path of the endpoint without the Server Endpoint annotation? Here we need to declare a Server Application Config entity (remember also the javax.rs.ws.core.Application in Restful WS?) To accomplish this function:

public class MyApplicationConfig implements ServerApplicationConfig{

    @Override
    public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> allClasses) {
        return null;
    }

    @Override
    public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> end) {
        ServerEndpointConfig sec =  ServerEndpointConfig.Builder.create(ProgramerServer.class, "/chat")
        .configurator(new ServerEndpointConfig.Configurator(){
        }).build();
        
        
       return new HashSet<ServerEndpointConfig>(){{
           add(sec);
       }};
    }
}

GetEndpoint Config builds a set of ServerEndpoint Configs. Why didn't the previous declarative WebSocket need this? Similarly, Tomcat can build it through the @ServerEndpoint annotation in the declarative WebSocket. See Tomcat code:

 @Override
    public void addEndpoint(Class<?> pojo) throws DeploymentException {

        ServerEndpoint annotation = pojo.getAnnotation(ServerEndpoint.class);
      
        // ServerEndpointConfig
        ServerEndpointConfig sec;
        Class<? extends Configurator> configuratorClazz =
                annotation.configurator();
        Configurator configurator = null;
        if (!configuratorClazz.equals(Configurator.class)) {
            try {
                configurator = annotation.configurator().newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                throw new DeploymentException(sm.getString(
                        "serverContainer.configuratorFail",
                        annotation.configurator().getName(),
                        pojo.getClass().getName()), e);
            }
        }
        sec = ServerEndpointConfig.Builder.create(pojo, path).
                decoders(Arrays.asList(annotation.decoders())).
                encoders(Arrays.asList(annotation.encoders())).
                subprotocols(Arrays.asList(annotation.subprotocols())).
                configurator(configurator).
                build();

        addEndpoint(sec);
    }

Tomcat constructs a Server Endpoint Config for each Server Endpoint.


Put the two classes into the War package at the same time and deploy them to Tomcat. A WebSocket server will be OK.


Now you can use Client from the previous article to access this WebSocket. Or you're tired of Annocation. Have a programmable Client.

public class ProgramerClient extends Endpoint {

    @Override
    public void onOpen(Session session, EndpointConfig edc) {
        System.out.println("I was accpeted by her!");
        session.addMessageHandler(new MessageHandler.Whole<String>() {

            @Override
            public void onMessage(String message) {
                System.out.println("she say: " + message); 
            }
        });
    }

}

Why is there no onClose,onError method? Because there is a default implementation in Endpoint, there is no overload.

public class Client {
    public static void main(String[] args) throws DeploymentException, IOException, InterruptedException {

        WebSocketContainer ws = ContainerProvider.getWebSocketContainer();
        String url = "ws://localhost:8080/ChatWeb2/chat";
        
        ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().configurator(new MyClientConfigurator()).build();
        Session session = ws.connectToServer(ProgramerClient.class,cec,URI.create(url));
        session.getBasicRemote().sendText("Hello,chick!");
        Thread.currentThread().sleep(10000);
    }
}

Wait, it's a little different. Of course, there is no ClientEndpoint, and of course there is no @ClientEndpoint.Configurator field (remember the structure of @ClientEndpoint?)

Of course, there is no Client Endpoint Config. So we need to add one ourselves.


As you can see, programmatic WebSocket endpoints are much more complex than Annotation. Using Annotation prompts makes programming easy.

For WebSocket containers (that is, Tomcat in this article, etc.), you need to translate this Annotation prompt into execution code.


In order to give you an overall understanding of the two modes, we have skipped the details in the middle. I hope it won't create obstacles to your understanding.


Keywords: Java Session Tomcat Programming less

Added by FlipinMonkeyPie on Mon, 27 May 2019 02:47:34 +0300