websockets with spring 4
DESCRIPTION
Talk delivered at Java2Days 2013 on Websocket support in Spring 4 with SockJs and StompTRANSCRIPT
![Page 1: WebSockets with Spring 4](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/1.jpg)
WebSockets with Spring 4Sergi Almar @sergialmar
![Page 2: WebSockets with Spring 4](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/2.jpg)
• CTO @ Voz.io
• Spring Certified Trainer
• javaHispano Core Member
• Spring I/O conference organiser
Who I am
![Page 3: WebSockets with Spring 4](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/3.jpg)
Social FeedsMultiplayer Games
Collaborative Apps
Clickstream DataFinancial Tickets Sports Updates
Multimedia Chat
Location-based AppsOnline Education
![Page 4: WebSockets with Spring 4](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/4.jpg)
• Polling
• Long Polling / Comet
• Flash
Real-Time Data on the Web
![Page 5: WebSockets with Spring 4](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/5.jpg)
Problem Applications need two-way communication Too many connections and overhead with ajax / comet
![Page 6: WebSockets with Spring 4](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/6.jpg)
WebSocketstwo-way communication done right
![Page 7: WebSockets with Spring 4](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/7.jpg)
• 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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/8.jpg)
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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/9.jpg)
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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/10.jpg)
• 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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/11.jpg)
WebSockets.springify()
![Page 12: WebSockets with Spring 4](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/12.jpg)
• 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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/13.jpg)
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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/14.jpg)
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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/15.jpg)
• 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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/16.jpg)
Can anyone join the party?
http://caniuse.com/#feat=websockets / DEC 2013
![Page 17: WebSockets with Spring 4](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/17.jpg)
SockJSdude, where are my socks?
![Page 18: WebSockets with Spring 4](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/18.jpg)
• 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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/19.jpg)
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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/20.jpg)
• Base URL: /base_url
• Info test: /base_url/info
• Session URL: /base_url/server/session
SockJS URLs
![Page 21: WebSockets with Spring 4](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/21.jpg)
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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/22.jpg)
• 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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/23.jpg)
Here’s were we are…
![Page 24: WebSockets with Spring 4](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/24.jpg)
…but we want something like this
![Page 25: WebSockets with Spring 4](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/25.jpg)
• 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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/26.jpg)
STOMP
![Page 27: WebSockets with Spring 4](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/27.jpg)
• 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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/28.jpg)
• 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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/29.jpg)
• 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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/30.jpg)
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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/31.jpg)
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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/32.jpg)
• 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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/33.jpg)
• 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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/34.jpg)
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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/35.jpg)
• 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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/36.jpg)
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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/37.jpg)
• Sending messages without handler methods
SimpMessagingTemplate
template.convertAndSend("/topic/chat", message;!template.convertAndSendToUser(user, dest, message;
![Page 38: WebSockets with Spring 4](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/38.jpg)
• 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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/39.jpg)
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](https://reader030.vdocuments.mx/reader030/viewer/2022013114/555822cbd8b42a5e468b5091/html5/thumbnails/40.jpg)
Thanks! @sergialmar