January 19, 2014 1 Comment
One exciting new feature of Spring 4 is the support for WebSocket, SockJS and STOMP messaging. This allows two way communication between the server and its clients in a Spring MVC web application using the standard point-to-point and publish-subscribe messaging protocols. In this post, I will demonstrate how to set up a basic boilerplate project to start using this new feature. It is in part based on this article.
First we need to add the Spring messaging modules in the POM file:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>4.0.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>4.0.0.RELEASE</version> </dependency>
Next, we need to add the message broker config to the Spring MVC config XML file.
<beans ... xmlns:websocket="http://www.springframework.org/schema/websocket" xsi:schemaLocation=" ... http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket-4.0.xsd"> <websocket:message-broker application-destination-prefix="/app"> <websocket:stomp-endpoint path="/hello"> <websocket:sockjs/> </websocket:stomp-endpoint> <websocket:simple-broker prefix="/topic"/> </websocket:message-broker> <!-- Other MVC config omitted here-->
The main thing here is the set up of the message broker for handling the message exchange between the server and its clients. This is done via the <message-broker> and its child tags. The tag <websocket:simple-broker> indicates we are using in-memory message broker.
It is easy to understand together with the server and client codes so I will include them below first before attempting to give a bit more explanations by cross-referencing the client and server codes.
Below is my Spring MVC Controller
@Controller public class MessageController { @MessageMapping("/hello") @SendTo("/topic/greetings") public Greeting greeting(HelloMessage message) throws Exception { return new Greeting("Hello, " + message.getName() + "!"); } }
The method argument HelloMessage and output Greeting are just POJOs representing the body of the messages being sent and returned.
public class Greeting { private String content; public Greeting(String content) { this.content = content; } public String getContent() { return content; } } public class HelloMessage { private String name; public String getName() { return name; } }
On the client side, I use the sockJS protocol fallback option as outlined in the Spring documentation. The javascript codes are included below
// Create stomp client over sockJS protocol (see Note 1) var socket = new SockJS("/hello"); var stompClient = Stomp.over(socket); // callback function to be called when stomp client is connected to server (see Note 2) var connectCallback = function() { alert("connected!"); stompClient.subscribe('/topic/greetings', function(greeting){ alert(JSON.parse(greeting.body).content); }); }; // callback function to be called when stomp client could not connect to server (see Note 3) var errorCallback = function(error) { // display the error's message header: alert(error.headers.message); }; // Connect as guest (Note 4) stompClient.connect("guest", "guest", connectCallback, errorCallback);
<websocket:stomp-endpoint path=”/hello”>
< websocket:sockjs/>
< /websocket:stomp-endpoint>
stompClient.subscribe(‘/topic/greetings’, function(greeting){
alert(JSON.parse(greeting.body).content);
});
Now we are ready to send messages from the client, e.g. using the following javascript function
// function to send message
function fnSayHi() {
stompClient.send("/app/hello, JSON.stringify({ 'name': 'Joe' }));
}
The message will be sent to the Spring MVC message handler method greeting() as defined via the annotation @MessageMapping(“/hello”).
<websocket:message-broker application-destination-prefix=”/app”>
Note the prefix “/app” is defined in the Spring config as application-destination-prefix attribute of the message broker: Note also, the use of @SendTo annotation to direct the message to a given destination. I repeat the controller method below
@MessageMapping("/hello") @SendTo("/topic/greetings") public Greeting greeting(HelloMessage message) throws Exception { return new Greeting("Hello, " + message.getName() + "!"); }
That’s it for now.