7. WebSocket Protocol / RFC 6455
•
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
9. 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);!
};!
10. Java WebSocket Implementations
•
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…)
12. Spring WebSockets
•
WebSockets are now supported in Spring 4
•
Fallback options with SockJS
•
STOMP over WebSocket
•
Foundation for messaging architecture
15. Per-Session Handler
Previous example showed how to configure a global handler
•
•
but you may want to have a stateful per-session handler
@Configuration!
@EnableWebSocket!
public class WsConfig implements WebSocketConfigurer {!
!
!
@Bean!
DI
public WebSocketHandler echoHandler() {!
return new PerConnectionWebSocketHandler(EchoHandler.class);!
}!
@Override!
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {!
registry.addHandler(echoHandler(), "/echo");!
}!
}
16. Can anyone join the party?
http://caniuse.com/#feat=websockets / DEC 2013
18. SockJS
•
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)
19. 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');!
};!
20. SockJS URLs
•
Base URL: /base_url
•
Info test: /base_url/info
•
Session URL: /base_url/server/session
21. 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)
22. Problem
•
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
25. Solution
•
Stomp over WebSocket
•
Some Spring Integration types have been promoted to
the core
•
•
•
Message, MessageChannel, MessageHandler…
@MessageMapping, @SubscribeEvent…
New spring-messaging module
27. STOMP
•
Simple interoperable protocol for asynchronous messaging
•
Supported by Apache ActiveMQ, RabbitMQ, HornetQ…
•
Frames modelled on HTTP
var socket = new SockJS('/myapp/echo');!
var client = Stomp.over(socket);
COMMAND!
header1:value1!
header2:value2!
!
Body^@!
28. Client Frames
•
SEND a message
•
SUBSCRIBE / UNSUBSCRIBE from destination
•
ACK / NACK the reception of a message (optional by
default)
29. Server Frames
•
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
30. 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/");!
Subscriptions
c.setApplicationDestinationPrefixes("/app");!
processed by spring
}!
!
}!
simple broker
32. Handler Methods
•
Flexible handler method signatures
•
@PathVariable, @Header/@Headers, @Payload, Message, Principal
•
Message converters
@Controller!
public class ChatController {!
!
@MessageMapping("/message")!
public void sendMessage(String message, Principal principal) {!
// ...!
}!
}!
33. Handler Methods
•
Can also return a value
!
!
!
Return wrapped in a Message and sent to /topic/message
@MessageMapping("/message")!
public String sendMessage(String message) {!
return message.toUpperCase();!
}
!
•
Or define the destination with @SendTo
@MessageMapping("/message")!
@SendTo("/topic/spring-room")!
public String sendMessage(String message) {!
return message.toUpperCase();!
}!
34. Intercepting Subscriptions
stompClient.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
35. User destinations
•
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
client.subscribe("/user/queue/private-messages", function(msg) {!
// ...!
});!
!
client.subscribe("/user/queue/errors", function(msg) {!
// ...!
});!
36. Sending to user
@Controller!
public class ChatController {!
Will be sent to /user/{username}/queue/message
!
@MessageMapping("/message")!
@SendToUser!
public String sendMessage(String message) {!
return message.toUpperCase();!
}!
!
@MessageExceptionHandler!
@SendToUser("/queue/errors")!
public String handleException(IllegalStateException ex) {!
return ex.getMessage();!
}!
!
}!
38. Using a Message Broker
•
Previous configuration used Spring’s simple broker
•
•
•
Not suitable for clustering
Subset of STOMP
A message broker can be plugged-in instead
39. Message Broker Config
@Configuration!
@EnableWebSocketMessageBroker!
public class Config implements WebSocketMessageBrokerConfigurer{!
!
@Override!
public void configureMessageBroker(MessageBrokerConfigurer c){!
c.enableStompBrokerRelay("/queue/", "/topic/");!
c.setApplicationDestinationPrefixes("/app");!
}!
}