001/* 002 * Copyright 2002-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.socket.config.annotation; 018 019import org.springframework.beans.factory.config.CustomScopeConfigurer; 020import org.springframework.context.annotation.Bean; 021import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; 022import org.springframework.messaging.converter.MappingJackson2MessageConverter; 023import org.springframework.messaging.simp.SimpSessionScope; 024import org.springframework.messaging.simp.annotation.support.SimpAnnotationMethodMessageHandler; 025import org.springframework.messaging.simp.broker.AbstractBrokerMessageHandler; 026import org.springframework.messaging.simp.config.AbstractMessageBrokerConfiguration; 027import org.springframework.messaging.simp.stomp.StompBrokerRelayMessageHandler; 028import org.springframework.messaging.simp.user.SimpUserRegistry; 029import org.springframework.messaging.simp.user.UserSessionRegistryAdapter; 030import org.springframework.web.servlet.HandlerMapping; 031import org.springframework.web.socket.WebSocketHandler; 032import org.springframework.web.socket.config.WebSocketMessageBrokerStats; 033import org.springframework.web.socket.handler.WebSocketHandlerDecoratorFactory; 034import org.springframework.web.socket.messaging.DefaultSimpUserRegistry; 035import org.springframework.web.socket.messaging.SubProtocolWebSocketHandler; 036import org.springframework.web.socket.messaging.WebSocketAnnotationMethodMessageHandler; 037 038/** 039 * Extends {@link AbstractMessageBrokerConfiguration} and adds configuration for 040 * receiving and responding to STOMP messages from WebSocket clients. 041 * 042 * <p>Typically used in conjunction with 043 * {@link EnableWebSocketMessageBroker @EnableWebSocketMessageBroker} but can 044 * also be extended directly. 045 * 046 * @author Rossen Stoyanchev 047 * @author Artem Bilan 048 * @since 4.0 049 */ 050public abstract class WebSocketMessageBrokerConfigurationSupport extends AbstractMessageBrokerConfiguration { 051 052 private WebSocketTransportRegistration transportRegistration; 053 054 055 @Override 056 protected SimpAnnotationMethodMessageHandler createAnnotationMethodMessageHandler() { 057 return new WebSocketAnnotationMethodMessageHandler( 058 clientInboundChannel(), clientOutboundChannel(), brokerMessagingTemplate()); 059 } 060 061 @Override 062 @SuppressWarnings("deprecation") 063 protected SimpUserRegistry createLocalUserRegistry() { 064 org.springframework.messaging.simp.user.UserSessionRegistry sessionRegistry = userSessionRegistry(); 065 if (sessionRegistry != null) { 066 return new UserSessionRegistryAdapter(sessionRegistry); 067 } 068 return new DefaultSimpUserRegistry(); 069 } 070 071 @Bean 072 @SuppressWarnings("deprecation") 073 public HandlerMapping stompWebSocketHandlerMapping() { 074 WebSocketHandler handler = decorateWebSocketHandler(subProtocolWebSocketHandler()); 075 WebMvcStompEndpointRegistry registry = new WebMvcStompEndpointRegistry( 076 handler, getTransportRegistration(), userSessionRegistry(), messageBrokerTaskScheduler()); 077 registry.setApplicationContext(getApplicationContext()); 078 registerStompEndpoints(registry); 079 return registry.getHandlerMapping(); 080 } 081 082 @Bean 083 public WebSocketHandler subProtocolWebSocketHandler() { 084 return new SubProtocolWebSocketHandler(clientInboundChannel(), clientOutboundChannel()); 085 } 086 087 protected WebSocketHandler decorateWebSocketHandler(WebSocketHandler handler) { 088 for (WebSocketHandlerDecoratorFactory factory : getTransportRegistration().getDecoratorFactories()) { 089 handler = factory.decorate(handler); 090 } 091 return handler; 092 } 093 094 protected final WebSocketTransportRegistration getTransportRegistration() { 095 if (this.transportRegistration == null) { 096 this.transportRegistration = new WebSocketTransportRegistration(); 097 configureWebSocketTransport(this.transportRegistration); 098 } 099 return this.transportRegistration; 100 } 101 102 protected void configureWebSocketTransport(WebSocketTransportRegistration registry) { 103 } 104 105 protected abstract void registerStompEndpoints(StompEndpointRegistry registry); 106 107 @Bean 108 public static CustomScopeConfigurer webSocketScopeConfigurer() { 109 CustomScopeConfigurer configurer = new CustomScopeConfigurer(); 110 configurer.addScope("websocket", new SimpSessionScope()); 111 return configurer; 112 } 113 114 @Bean 115 public WebSocketMessageBrokerStats webSocketMessageBrokerStats() { 116 AbstractBrokerMessageHandler relayBean = stompBrokerRelayMessageHandler(); 117 StompBrokerRelayMessageHandler brokerRelay = (relayBean instanceof StompBrokerRelayMessageHandler ? 118 (StompBrokerRelayMessageHandler) relayBean : null); 119 120 // Ensure STOMP endpoints are registered 121 stompWebSocketHandlerMapping(); 122 123 WebSocketMessageBrokerStats stats = new WebSocketMessageBrokerStats(); 124 stats.setSubProtocolWebSocketHandler((SubProtocolWebSocketHandler) subProtocolWebSocketHandler()); 125 stats.setStompBrokerRelay(brokerRelay); 126 stats.setInboundChannelExecutor(clientInboundChannelExecutor()); 127 stats.setOutboundChannelExecutor(clientOutboundChannelExecutor()); 128 stats.setSockJsTaskScheduler(messageBrokerTaskScheduler()); 129 return stats; 130 } 131 132 @Override 133 protected MappingJackson2MessageConverter createJacksonConverter() { 134 MappingJackson2MessageConverter messageConverter = super.createJacksonConverter(); 135 // Use Jackson builder in order to have JSR-310 and Joda-Time modules registered automatically 136 messageConverter.setObjectMapper(Jackson2ObjectMapperBuilder.json() 137 .applicationContext(getApplicationContext()).build()); 138 return messageConverter; 139 } 140 141}