Friday 17 October 2014

WebSockets with Spring 4 SockJS and STOMP

Introduction



RFC 6455, the WebSocket protocol defines the capability for the web applications to be two-way, full-duplex  between client and server for communication. WebSocket helps to make the web to be more interactive including Java applets, XMLHttpRequest, server-sent events, and others. Spring 4 has introduced spring-websocket compatible with the Java WebSocket API.

WebSocket programming requires fallback options as a number of browsers don't support WebSocket (like IE10 and above supports WebSocket) and some restrictive proxies may be configured in ways that either preclude the attempt to do HTTP upgrade or otherwise break connection after some time because it has remained opened for too long. Spring Framework provides such transparent fallback options based on the SockJS protocol.

WebSocket application might use a single URL only for the initial HTTP handshake. All messages thereafter share and flow on the same TCP CONNECTION. This points to an entirely different, asynchronous, event-driven, messaging architecture


When to use WebSocket

The best fit for WebSocket is in web applications where client and server need to exchange events at high frequency and at low latency. Prime candidates include but are not limited to applications in finance, games, collaboration, and others. Such applications are both very sensitive to time delays and also need to exchange a wide variety of messages at high frequency.

Spring WebSocket components

Spring WebSocket implementation has some core components -

WebSocket Handler

A WebSocket handler is a class that will receive requests from the websocket client.


@Configuration
@EnableScheduling
@ComponentScan("org.springframework.samples")
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {


@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/<some_name>").withSockJS();
}

}

Handshake Interceptor(Optional)

The websocket handshake interceptor is used to define and specify a class that intercepts the initial websocket handshake. Interceptors are purely optional.

public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor{

@Override
public boolean beforeHandshake(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {

return super.beforeHandshake(request, response, wsHandler, attributes);
}

@Override
public void afterHandshake(ServerHttpRequest request,
ServerHttpResponse response, WebSocketHandler wsHandler,
Exception ex) {

super.afterHandshake(request, response, wsHandler, ex);
}

}

Enable SockJS

Enabling SockJS at server side is easy with configuration 

@Configuration
@EnableScheduling
@ComponentScan("org.springframework.samples")
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {


@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/<some_name>").withSockJS();
}

}

STOMP


STOMP is the Simple (or Streaming) Text Orientated Messaging Protocol. STOMP provides an interoperable wire format so that STOMP clients can communicate with any STOMP message BROKERto provide easy and widespread messaging interoperability among many languages, platforms and BROKERS.

Spring  supports STOMP over WebSocket through the spring-messaging and spring-websocket modules. 

Here is an example of configuring a STOMP WebSocket endpoint with SockJS fallback options. The endpoint is available for clients to connect to at URL path /<app_name>/<end_point>:

@Configuration
@EnableScheduling
@ComponentScan("org.springframework.samples")
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {


@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/<end_point>").withSockJS();
}

@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/queue/", "/topic/");
registry.setApplicationDestinationPrefixes("/<app_name>");
}

}

These are the benefits for an application from using STOMP over WebSocket:

  • Standard message format
  • Application-level protocol with support for common messaging patterns
  • Client-side support, e.g. stomp.js, msgs.js
  • The ability to interpret, route, and process messages on both client and server-side
  • The option to plug a message BROKER like RabbitMQ, ActiveMQ, many others to broadcast messages 

Sending Messages

It is also possible to send a message to user destinations from any application component by injecting the SimpMessageTemplate.
@Service
public class TradeServiceImpl implements TradeService {

private final SimpMessageTemplate messagingTemplate;

@Autowired
public TradeServiceImpl(SimpMessageTemplate messagingTemplate) {
this.messagingTemplate = messagingTemplate;
}

// ...

public void afterTradeExecuted(Trade trade) {
this.messagingTemplate.convertAndSendToUser(
trade.getUserName(), "<some_end_point>", trade.getResult());
}

}

SockJS

SockJS has the capability to use a WebSocket API but fall back to non-WebSocket alternatives when necessary at runtime without the need to change application code. As of writing this article only the following browsers supports WebSocket
  • IE 10
  • FireFox 6
  • Crome 4
  • Safari 5
  • Opera 12.10
To use SockJS we need sock.js at client side and SockJS server side library at server side.

WebSocket SockJS Client

SockJS client supports Ajax/XHR streaming in IE 8, 9 via Microsoft’s XDomainRequest. That works across domains but does not support sending cookies.
Java config to set SockJS client library - 
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/<some_relative_url>").withSockJS()
                .setClientLibraryUrl("http://<host>:<post>/<myapp>/js/sockjs-client.js");
    }

    // ...


}

On the browser side, a client might connect as follows using stomp.js and the sockjs-client

var socket = new SockJS("/spring-websocket-portfolio/portfolio");
var stompClient = Stomp.over(socket);

stompClient.connect({}, function(frame) {

}

References:
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html
Working Code Example: 
https://github.com/badalb/spring-websocket-portfolio.git

No comments:

Post a Comment