Slide 1

Slide 1 text

Real-­‐time  with  Spring:     SSE  and  WebSockets   Sergi  Almar   @sergialmar

Slide 2

Slide 2 text

Agenda • HTML5  Realtime   • SSE   • Intro   • Spring  4.2  support   • WebSocket   • Intro   • Spring  WebSocket   • Security 2

Slide 3

Slide 3 text

Realtime  in  HTML5 3 SSE WebSocket

Slide 4

Slide 4 text

Server-­‐sent  Events • uni-­‐directional  (server  push)   • built  on  top  of  HTTP   • long-­‐lived  HTTP  connection   • EventSource  API

Slide 5

Slide 5 text

Server-­‐sent  Events

Slide 6

Slide 6 text

Server-­‐sent  Events  Client var source = new EventSource('/metrics-sub'); source.onmessage = function(e) { var freeMemory = JSON.parse(e.data); $("#freeMem").text(freeMemory.value); }; 6

Slide 7

Slide 7 text

Spring  SSE 7 @RequestMapping("/metrics-sub") public SseEmitter subscribeMetrics() { SseEmitter sseEmitter = new SseEmitter(); // Save emitter return sseEmitter; } • New  support  in  the  upcoming  Spring  4.2

Slide 8

Slide 8 text

SSE  DEMO

Slide 9

Slide 9 text

WebSockets • 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

Slide 10

Slide 10 text

The  Lifecycle 10 browser server HTTP can we upgrade to WebSocket? I want you to talk to me as well! HTTP 101 Yes! Talk WebSocket to me! WebSocket here’s a frame WebSocket here’s a frame WebSocket here’s a frame close connection

Slide 11

Slide 11 text

WebSockets

Slide 12

Slide 12 text

WebSocket  Client  Side 12 var ws = new WebSocket("ws://www.springio.net/ws"); // When the connection is open, send some data to the server ws.onopen = function () { ws.send('Here I am!'); }; ws.onmessage = function (event) { console.log('message: ' + event.data); }; ws.onclose = function (event) { console.log('closed:' + event.code); };

Slide 13

Slide 13 text

Spring  WebSockets • Why  should  we  use  Spring  WebSockets  instead   of  plain  JSR-­‐356?   • Fallback  options  with  SockJS   • Support  for  STOMP  subprotocol     • Security  (with  Spring  Security)   • Integration  with  messaging  components  and   Spring  programming  style 13

Slide 14

Slide 14 text

Endpoint  Configuration 14 @Configuration public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport { public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint(“/ws") } ... } .withSockJS(); Use SockJS for fallback options

Slide 15

Slide 15 text

Destination  Configuration 15 @Configuration public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport { ... @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/queue/", "/topic/"); registry.setApplicationDestinationPrefixes("/app"); } } Broker destinations Application destinations

Slide 16

Slide 16 text

The  Workflow 16 clientInboudChannel clientOutboundChannel WebSocket frame WebSocket 
 Endpoint sends message Message
 Handlers processed by sends to WebSocket frame

Slide 17

Slide 17 text

Subscription  Handling 17 stompClient.subscribe(“/app/chat.participants", callback); SUBSCRIBE id:sub-1 destination:/app/chat.participants @SubscribeMapping("/chat.participants") public Collection retrieveParticipants() { return participantRepository.getActiveSessions().values(); }

Slide 18

Slide 18 text

Message  Handling 18 stompClient.send(‘/app/chat.message’, {}, JSON.stringify({message: ‘hi there’})); SEND destination:/app/chat.message content-length:22 {“message": "hi there"} @MessageMapping("/chat.message") public ChatMessage filterMessage(@Payload ChatMessage message, Principal principal) { checkProfanityAndSanitize(message); return message; }

Slide 19

Slide 19 text

Handler  Method  Response 19 • SimpAnnotationMethodMessageHandler   doesn’t  know  about  STOMP  semantics   • Return  values  are  sent  to  a  STOMP  message   broker  via  the  brokerChannel  (doesn’t  apply  to   @SubscriptionMapping)   • can  be  the  built-­‐in  simple  broker   • or  a  full  blown  message  broker

Slide 20

Slide 20 text

Broker  Destinations • Two  options:   • Use  the  built-­‐in  broker   • Use  a  full-­‐blown  message  broker 20 BrokerMessage Handler clientInboudChannel SimpAnnotation MethodMessage Handler brokerChannel

Slide 21

Slide 21 text

Simple  Broker • Built-­‐in  broker,  only  supports  a  subset  of   STOMP  commands   • Stores  everything  in  memory   • SimpleBrokerMessageHandler  will  be   subscribed  to  inboundClientChannel  and   brokerChannel 21 @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/queue/", "/topic/"); registry.setApplicationDestinationPrefixes("/app"); }

Slide 22

Slide 22 text

Full  Blown  STOMP  Broker • Brokers  with  STOMP  support:  RabbitMQ,   Apache  ActiveMQ,  HornetQ,  OpenMQ… 22 @Autowired private Environment env; @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableStompBrokerRelay("/queue/", "/topic/") .setRelayHost(env.getRequiredProperty("rabbitmq.host")) .setRelayPort(env.getRequiredProperty("rabbitmq.stompport", Integer.class)) .setSystemLogin(env.getRequiredProperty("rabbitmq.user")) .setSystemPasscode(env.getRequiredProperty("rabbitmq.password")) .setClientLogin(env.getRequiredProperty("rabbitmq.user")) .setClientPasscode(env.getRequiredProperty("rabbitmq.password")) .setVirtualHost(env.getRequiredProperty("rabbitmq.vh")); }

Slide 23

Slide 23 text

WEBSOCKET  DEMO   HTTPS://GITHUB.COM/SALMAR/SPRING-­‐WEBSOCKET-­‐CHAT

Slide 24

Slide 24 text

WEBSOCKET  SECURITY   (SPRING  SECURITY  4)

Slide 25

Slide 25 text

Security  Message  Flow 25 clientInboudChannel new message SecurityContextChannelInterceptor ChannelSecurityInterceptor MessageHandler AccessDecision Manager delegates SecurityContext sets polls MessageExpressionVoter

Slide 26

Slide 26 text

WebSocket  Security  Config 26 @Configuration public class WebSocketSecurityConfig extends AbstractSecurityWebSocketMessageBrokerConfigurer { @Override protected void configureInbound(MessageSecurityMetadataSourceRegistry messages) { messages .antMatchers(SimpMessageType.MESSAGE, ”/user/queue/errors").permitAll() .antMatchers("/topic/admin/*").hasRole("ADMIN") .anyMessage().hasRole("USER"); } } •

Slide 27

Slide 27 text

THANKS!   Q&A   @SERGIALMAR