Explore Spring 4 Features: WebSocket (Part I)

First of all Wish you a Very Happy New Year



WebSocket with Spring 4



One of the interesting features of Spring 4 is introduction of WebSockets in Spring.

Now the first Question which arises is what is WebSocket?

Definition:

WebSocket is a protocol which allows for communication between the client and the server/endpoint using a single TCP connection.

This protocol is an important new capability for web applications: full-duplex, two-way communication between client and server.

It aims at making web more interactive including Java applets, XMLHttpRequest.

An abstract of the actual process which happens behind the scene for WebSocket is:

During initialization of this protocol,HTTP is used only for the initial handshake, which relies on a mechanism built into

HTTP to request a protocol upgrade (or in this case a protocol switch) to which the server can respond with HTTP status 101

(switching protocols) if it agrees. Assuming the handshake succeeds the TCP socket underlying the HTTP upgrade request

remains open and both client and server can use it to send messages to each other.

SockJS protocol:

While using WebSockets, we will often encounter this term "SockJS protocol", what it actually is?

WebSocket is not supported is not supported in most of the browsers. The first Internet

Explorer version to support WebSocket is version 10.

Therefore to build a WebSocket application today, fallback options are required to simulate the WebSocket API where

necessary. Spring Framework provides such transparent fallback options based on the SockJS protocol. These options

can be enabled through configuration and do not require modifying the application otherwise.

Sub-Protocol Support in WebSocket

WebSocket does imply a messaging architecture but does not mandate the use of any specific messaging protocol.

It is a very thin layer over TCP that transforms a stream of bytes into a stream of messages (either text or binary)

and not much more. It is up to applications to interpret the meaning of a message.

The WebSocket Protocol is very low level. In the WebSocket protocol there is simply not enough information in an

incoming message for a framework or container to know how to route it or process it. For this reason only STOMP a Sub Protocol

came into the picture. STOMP can be defined as a common message format that both client and server can understand.

This is in brief a short introduction to WebSockets. While an elaborated version can be referred from

here and my understandings are inspired by this

On setup of the WebSocket process, I found a lot of resources on the web, but unfortunatIely none of them solved my issues regarding complete configuration of WebSockets in Spring.

Here I am trying to provide consolidated approach towards building a simple Spring WebSocket appl. Here I am sharing my experience with WebSockets:

The use case I have implemented is:

Phase I



The client is a JSP page.

The client connects, and sends message through a configured Spring Service component to the MessageBroker.

The Broker after some time sends a confirmation message to the client.

Phase II



The client is a JSP page, and it subscribes to a Message Broker (Here implemented with the help of ActiveMQ) over

WebCocket using Stomp Messaging protocol and SockJS as fallback options.

Now, when the client connects through WebSocket to the application it starts receiving feeds from the Mesrsage Broker.

as the client has already subscribed to it. The input to the Message Broker has been provided by the application,

through a asynchronous task.

Now, we will set the WebSocket Process in Action.

The following sets of jars needs to be in classpath as a first step to setup:

1) Spring modules 4.1.2 (spring-websocket-4.1.2.RELEASE.jar,spring-messaging-4.1.2.RELEASE.jar
2) gs-collections-api-5.1.0.jar
3) gs-collections-5.1.0.jar
4) jsr166e-1.0.jar
5) reactor-net-1.1.5.RELEASE.jar
6) reactor-core-1.1.5.RELEASE.jar
7) quartz-2.2.0.jar
8) netty-all-4.0.11.Final.jar
9) disruptor-3.2.0.jar
10) jackson-annotations-2.4.3.jar
11) jackson-core-2.4.3.jar
Phase I

For external Messaging I have used ActiveMQ message Broker.

To configure ActiveMQ for WebSockets the only change needed to be made in "activemq.xml" in the conf folder of ActiveMQ. The change is:

<transportConnector name="stomp" uri="stomp://localhost:61613?trace=true"/> 
in the <transportConnectors> block.
This change is needed to be made as we are assuming that we will be using STOMP protocol for message exchange between client and server.The Client code is:

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
    <%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
    
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>

<script src="//cdn.jsdelivr.net/sockjs/0.3.4/sockjs.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/stomp.js/2.3.3/stomp.js"></script>

<script>
var stompClient = null;


function connect() {
 // var options = {protocols_whitelist: ["websocket", "xhr-streaming", "xdr-streaming", "xhr-polling", "xdr-polling", "iframe-htmlfile", "iframe-eventsource", "iframe-xhr-polling"], debug: true}; 
  var socket = new SockJS('http://localhost:7001/SpringJavaConfig-0.0.1-SNAPSHOT/config/hello');
    stompClient = Stomp.over(socket);
    stompClient.connect({}, function(frame) {
        
        alert('Connected: '+frame);
        stompClient.subscribe('/queue/myQueue.Queue', function(greeting){
            alert(JSON.parse(greeting.body).content);
        });
        
        
        stompClient.subscribe('/queue/myQueue.queue', function(greeting){
            alert("Second client:"+JSON.parse(greeting.body).content);
        });
        
    },function(error) {
     
     alert("The error is:---->"+error);
     
    });
    
   }


function sendName() {
   // var name = document.getElementById('name').value;
   alert("Sending Data");
    stompClient.send("/app/hello1", {}, JSON.stringify({ 'name': 'DISCO' }));
}
function disconnect() {
    stompClient.disconnect();
    //setConnected(false);
    //console.log("Disconnected");
}

</script> 
</head>
<body>
<br><br>
<a href="javascript:connect();">Connect Web Socket</a><br>
<a href="javascript:sendName();">Send Data</a><br>
<a href="javascript:disconnect();">DisConnect Web Socket</a>
</body>
</html>

We will describe the client code once we go through the server side components, then it would be easier for us to co-relate.

To configure WebSocket at the server side the first Component is SpringWebSocketConfig.class

It is basically a configutation class.

@Configuration
@EnableWebSocketMessageBroker
public class SpringWebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

 
 public void configureMessageBroker(MessageBrokerRegistry config) {
  //config.enableSimpleBroker("/topic","/queue");
  config.enableStompBrokerRelay("/topic","/queue");
  config.setApplicationDestinationPrefixes("/app");
 }

 
 public void registerStompEndpoints(StompEndpointRegistry registry) {
  registry.addEndpoint("/hello").withSockJS();
 }


}

The annotation "EnableWebSocketMessageBroker" broker-backed messaging over WebSocket using a higher-level messaging sub-protocol.

This class is extended by WebSocket Configuration class.Here we have customized configuration by extending the class "AbstractWebSocketMessageBrokerConfigurer".

We need to implement two methods of this class configureMessageBroker() and registerStompEndpoints().In configureMessageBroker(), the parameter passed is MessageBrokerRegistry.This class registers message

broker. We can enable simple broker for topic and queue.
topic : Use topic when there are more than one subscribers for a message.

queue : Use queue for peer to peer communication.

Here we have registered both queue and topic as

config.enableStompBrokerRelay("/topic","/queue");
The StompEndpointRegistry register an end point for the STOMP (Sub Protocol) over WebSocket

registry.addEndpoint("/hello").withSockJS();
It adds end point for STOMP communication and also enables SockJS. The client can send the message using the URL "/app/hello"

So, in the above client Code, in the JS function sendName we have used "/app/hello" to send the message

( Now, we are getting a little idea of what is happening behind the scene......... :-) Don't worry )

Now let's look at controller.

 
 @Controller
public class WebSocketController {

  @Autowired
 private SimpMessagingTemplate template;

 
 @MessageMapping("/hello1")
    //@SendTo("/topic/greetings")
 @SendTo("/queue/myQueue.queue")
    public Greeting greeting(HelloMessage message) throws Exception {
  
  System.out.println(" **** In WebSocket Controller **** ");
  
        Thread.sleep(3000); // simulated delay
  
        
  this.template.convertAndSend("/queue/myQueue.queue", new Greeting("Hello, Subhankar ---->" + new Date(System.currentTimeMillis()) + "!"));
  
  Thread.sleep(1000);
        return new Greeting("Hello, " + message.getName() + "!");
    }

 
}

 
 
 
and the HelloMessage.java bean is:

 
 public class HelloMessage {

    private String name;

    public String getName() {
        return name;
    }

}
 
 
Now, in the sendName() JS method, if we could remember we are using:

 stompClient.send("/app/hello", {}, JSON.stringify({ 'name': 'DISCO' }))
 
to send messages to the client side.

So,@MessageMapping("/hello1") is used to map the messages to this endpoint for the URL "/app/hello1" as specified

by the stompClient, and the WebSocket framework automatically converts the JSON String as specified by

JSON.stringify to the bean of type HelloMessage and it is passed as parameter to the controller method.

@SendTo specifies the destination to which the result would be directed after the processing in the

controller method has ended.

This functionality could also be performed by org.springframework.messaging.simp.SimpMessagingTemplate class

To bootstrap the whole application, the ApplicationConfig is:

public class SpringApplicationInitializer implements WebApplicationInitializer 

{

 public void onStartup(ServletContext servletContext) throws ServletException 
 {
  AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
  context.register(SpringWebSocketConfig.class);
  
 }

}

The phase II I will share in my next post. Till then keep Coding and please share your Opinions and Views

Comments

Popular posts from this blog

Use of @Configurable annotation.

Spring WS - Part 5

Spring WS - Part 4