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}