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.