40
WebSockets with Spring 4 Sergi Almar @sergialmar

WebSockets with Spring 4

Embed Size (px)

DESCRIPTION

Talk delivered at Java2Days 2013 on Websocket support in Spring 4 with SockJs and Stomp

Citation preview

Page 1: WebSockets with Spring 4

WebSockets with Spring 4Sergi Almar @sergialmar

Page 2: WebSockets with Spring 4

• CTO @ Voz.io

• Spring Certified Trainer

• javaHispano Core Member

• Spring I/O conference organiser

Who I am

Page 3: WebSockets with Spring 4

Social FeedsMultiplayer Games

Collaborative Apps

Clickstream DataFinancial Tickets Sports Updates

Multimedia Chat

Location-based AppsOnline Education

Page 4: WebSockets with Spring 4

• Polling

• Long Polling / Comet

• Flash

Real-Time Data on the Web

Page 5: WebSockets with Spring 4

Problem Applications need two-way communication Too many connections and overhead with ajax / comet

Page 6: WebSockets with Spring 4

WebSocketstwo-way communication done right

Page 7: WebSockets with Spring 4

• Real-time full duplex communication over TCP

• Uses port 80 / 443 (URL scheme: ws:// and wss://)

• Small overhead for text messages (frames)

• 0x00 for frame start, 0xFF for frame end (vs HTTP 1Kb)

• Ping / pong frames for staying alive

WebSocket Protocol / RFC 6455

Page 8: WebSockets with Spring 4

WebSocket HandshakeGET /mychat HTTP/1.1!Host: server.example.com!Upgrade: websocket!Connection: Upgrade!Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==!Sec-WebSocket-Protocol: chat!Sec-WebSocket-Version: 13!Origin: http://example.com!

client sends a WebSocket handshake request

HTTP/1.1 101 Switching Protocols!Upgrade: websocket!Connection: Upgrade!Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=!Sec-WebSocket-Protocol: chat!

server response

Page 9: WebSockets with Spring 4

JS WebSocket API var ws = new WebSocket("ws://www.java2days.com/ws");!! // When the connection is open, send some data to the server! ws.onopen = function () {! ws.send('Here I am!');! };!! // Log messages from the server! ws.onmessage = function (event) {! console.log('message: ' + event.data);! };!! ws.onclose = function (event) {! console.log('closed:' + event.code);! };!

Page 10: WebSockets with Spring 4

• Multiple implementations before the standard

• JSR-356 (May 2013)

• Reference implementation Tyrus (bundled with Glassfish 4)

• Rewrite across containers (tomcat 8.0.0-RC5, Jetty 9.1…)

Java WebSocket Implementations

Page 11: WebSockets with Spring 4

WebSockets.springify()

Page 12: WebSockets with Spring 4

• WebSockets are now supported in Spring 4

• Fallback options with SockJS

• STOMP over WebSocket

• Foundation for messaging architecture

Spring WebSockets

Page 13: WebSockets with Spring 4

WebSocket Handlers

public class EchoHandler extends TextWebSocketHandlerAdapter {!! @Override! public void handleTextMessage(WebSocketSession session, !! ! ! ! ! ! ! ! ! ! TextMessage message) throws Exception {! session.sendMessage(message);! }!! }

Page 14: WebSockets with Spring 4

WebSocket Config

@Configuration! @EnableWebSocket! public class WsConfig implements WebSocketConfigurer {!! @Override! public void registerWebSocketHandlers(!! ! ! ! ! ! ! ! ! ! ! WebSocketHandlerRegistry registry) {!! registry.addHandler(new EchoHandler(), "/echo");! }! }!

Page 15: WebSockets with Spring 4

• Previous example showed how to configure a global handler

• but you may want to have a stateful per-session handler

Per-Session Handler

@Configuration!@EnableWebSocket!public class WsConfig implements WebSocketConfigurer {!! @Bean! public WebSocketHandler echoHandler() {! return new PerConnectionWebSocketHandler(EchoHandler.class);! }!! @Override! public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {! registry.addHandler(echoHandler(), "/echo");! }!}

DI

Page 16: WebSockets with Spring 4

Can anyone join the party?

http://caniuse.com/#feat=websockets / DEC 2013

Page 17: WebSockets with Spring 4

SockJSdude, where are my socks?

Page 18: WebSockets with Spring 4

• Coherent, cross-browser, Javascript API for full duplex communication.

• Close to HTML5 WebSockets API

• Client and server side implementation (ruby, node…and also in spring-websockets)

SockJS

Page 19: WebSockets with Spring 4

SocksJS API var sock = new SockJS('http://www.java2days.com/ws');!! sock.onopen = function() {! sock.send('Here I am'); ! };!! sock.onmessage = function(event) {! console.log('message', e.data);! };!! sock.onclose = function() {! console.log('close');! };!

Page 20: WebSockets with Spring 4

• Base URL: /base_url

• Info test: /base_url/info

• Session URL: /base_url/server/session

SockJS URLs

Page 21: WebSockets with Spring 4

Enabling SocksJS

@Configuration! @EnableWebSocket! public class WsConfig implements WebSocketConfigurer {!! @Override! public void registerWebSocketHandlers(!! ! ! ! ! ! ! ! ! ! ! WebSocketHandlerRegistry registry) {!! registry.addHandler(new EchoHandler(), “/echo”).withSockJS();! }! }!

MessageHandler doesn’t change (SocketJsService delivers the message to the handler regardless of the protocol)

Page 22: WebSockets with Spring 4

• WebSockets are too low level and different from HTTP / REST

• We need asynchronous, event-driven, reactive programming style

• Mmmm, that sounds familiar: JMS, AMQP… but we still want to stick to the Spring MVC programming model

Problem

Page 23: WebSockets with Spring 4

Here’s were we are…

Page 24: WebSockets with Spring 4

…but we want something like this

Page 25: WebSockets with Spring 4

• Stomp over WebSocket

• Some Spring Integration types have been promoted to the core

• Message, MessageChannel, MessageHandler…

• @MessageMapping, @SubscribeEvent…

• New spring-messaging module

Solution

Page 26: WebSockets with Spring 4

STOMP

Page 27: WebSockets with Spring 4

• Simple interoperable protocol for asynchronous messaging

• Supported by Apache ActiveMQ, RabbitMQ, HornetQ…

• Frames modelled on HTTP

STOMP

COMMAND!header1:value1!header2:value2!!Body^@!

var socket = new SockJS('/myapp/echo');!var client = Stomp.over(socket);

Page 28: WebSockets with Spring 4

• SEND a message

• SUBSCRIBE / UNSUBSCRIBE from destination

• ACK / NACK the reception of a message (optional by default)

Client Frames

Page 29: WebSockets with Spring 4

• Convey a MESSAGE from subscription to the client

• Send RECEIPT when server has successfully processed a client frame

• Send an ERROR if something goes wrong

Server Frames

Page 30: WebSockets with Spring 4

Configuration!@Configuration!@EnableWebSocketMessageBroker!public class Config implements WebSocketMessageBrokerConfigurer {!! @Override! public void registerStompEndpoints(StompEndpointRegistry r){! r.addEndpoint("/stomp");! }!! @Override! public void configureMessageBroker(MessageBrokerConfigurer c){! c.enableSimpleBroker("/topic/");! c.setApplicationDestinationPrefixes("/app");! }!!}!

Subscriptions processed by spring

simple broker

Page 31: WebSockets with Spring 4

Sending MessagesstompClient.send("/app/trade", {}, JSON.stringify(trade));

SEND!destination:/app/trade!content-type:application/json!content-length:47!!{"action":"Sell","ticker":"DELL","shares":"10"}!

@MessageMapping("/trade")!public void executeTrade(Trade trade, Principal principal) {!! trade.setUsername(principal.getName());!! this.tradeService.executeTrade(trade);!}

stomp.js

supports ant-style patterns

prefix

Page 32: WebSockets with Spring 4

• Flexible handler method signatures

• @PathVariable, @Header/@Headers, @Payload, Message, Principal

• Message converters

Handler Methods

@Controller!public class ChatController {!! @MessageMapping("/message")! public void sendMessage(String message, Principal principal) {! // ...! }!}!

Page 33: WebSockets with Spring 4

• Can also return a value

!

!

!

!

• Or define the destination with @SendTo

Handler Methods

@MessageMapping("/message")! public String sendMessage(String message) {! return message.toUpperCase();! }

Return wrapped in a Message and sent to /topic/message

@MessageMapping("/message")! @SendTo("/topic/spring-room")! public String sendMessage(String message) {! return message.toUpperCase();! }!

Page 34: WebSockets with Spring 4

Intercepting SubscriptionsstompClient.subscribe("/app/positions", function(message) {! ...!});!

SUBSCRIBE!id:sub-0!destination:/app/positions!

@Controller!public class PortfolioController {!! @SubscribeEvent("/positions")! public List<Position> getPositions(Principal p) {! Portfolio portfolio = ...! return portfolio.getPositions();! }!} sent directly to the client, not going

though the message broker

Page 35: WebSockets with Spring 4

• User specific queues can be used

• /user/** kind of paths

• queues with unique id will be created

• Useful to send user related information or errors

User destinations

client.subscribe("/user/queue/private-messages", function(msg) {! // ...!});!!client.subscribe("/user/queue/errors", function(msg) {! // ...!});!

Page 36: WebSockets with Spring 4

Sending to user@Controller!public class ChatController {!! @MessageMapping("/message")! @SendToUser! public String sendMessage(String message) {! return message.toUpperCase();! }!! @MessageExceptionHandler! @SendToUser("/queue/errors")! public String handleException(IllegalStateException ex) {! return ex.getMessage();! }!!}!

Will be sent to /user/{username}/queue/message

Page 37: WebSockets with Spring 4

• Sending messages without handler methods

SimpMessagingTemplate

template.convertAndSend("/topic/chat", message;!template.convertAndSendToUser(user, dest, message;

Page 38: WebSockets with Spring 4

• Previous configuration used Spring’s simple broker

• Not suitable for clustering

• Subset of STOMP

• A message broker can be plugged-in instead

Using a Message Broker

Page 39: WebSockets with Spring 4

Message Broker Config

@Configuration!@EnableWebSocketMessageBroker!public class Config implements WebSocketMessageBrokerConfigurer{!! @Override! public void configureMessageBroker(MessageBrokerConfigurer c){! c.enableStompBrokerRelay("/queue/", "/topic/");! c.setApplicationDestinationPrefixes("/app");! }!}

Page 40: WebSockets with Spring 4

Thanks! @sergialmar