/*
* Copyright 2014-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.integration.websocket.config;
import java.util.Collection;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.integration.config.IntegrationConfigurationInitializer;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.util.ClassUtils;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.handler.AbstractHandlerMapping;
import org.springframework.web.socket.config.annotation.DelegatingWebSocketConfiguration;
import org.springframework.web.socket.config.annotation.ServletWebSocketHandlerRegistry;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
/**
* The WebSocket Integration infrastructure {@code beanFactory} initializer.
*
* @author Artem Bilan
* @author Gary Russell
*
* @since 4.1
*/
public class WebSocketIntegrationConfigurationInitializer implements IntegrationConfigurationInitializer {
private static final Log logger = LogFactory.getLog(WebSocketIntegrationConfigurationInitializer.class);
private static final boolean servletPresent = ClassUtils.isPresent("javax.servlet.Servlet",
WebSocketIntegrationConfigurationInitializer.class.getClassLoader());
private static final String WEB_SOCKET_HANDLER_MAPPING_BEAN_NAME = "integrationWebSocketHandlerMapping";
@Override
public void initialize(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof BeanDefinitionRegistry) {
this.registerEnableWebSocketIfNecessary((BeanDefinitionRegistry) beanFactory);
}
else {
logger.warn("'DelegatingWebSocketConfiguration' isn't registered because 'beanFactory'" +
" isn't an instance of `BeanDefinitionRegistry`.");
}
}
/**
* Register a {@link WebSocketHandlerMappingFactoryBean} which could also be overridden
* by the user by simply using {@link org.springframework.web.socket.config.annotation.EnableWebSocket}
* <p>
* In addition, checks if the {@code javax.servlet.Servlet} class is present on the classpath.
* When Spring Integration WebSocket support is used only as a WebSocket client,
* there is no reason to use and register the Spring WebSocket server components.
* <p>
* Note, there is no XML equivalent for
* the {@link org.springframework.web.socket.config.annotation .EnableWebSocket}
* in the Spring WebSocket. therefore this registration can be used to process
* {@link WebSocketConfigurer} implementations without annotation configuration.
* From other side it can be used to replace
* {@link org.springframework.web.socket.config.annotation.EnableWebSocket} in the Spring Integration
* applications when {@link org.springframework.integration.config.EnableIntegration} is in use.
*/
private void registerEnableWebSocketIfNecessary(BeanDefinitionRegistry registry) {
if (servletPresent) {
if (!registry.containsBeanDefinition("defaultSockJsTaskScheduler")) {
BeanDefinitionBuilder sockJsTaskSchedulerBuilder =
BeanDefinitionBuilder.genericBeanDefinition(ThreadPoolTaskScheduler.class)
.addPropertyValue("threadNamePrefix", "SockJS-")
.addPropertyValue("poolSize", Runtime.getRuntime().availableProcessors())
.addPropertyValue("removeOnCancelPolicy", true);
registry.registerBeanDefinition("defaultSockJsTaskScheduler",
sockJsTaskSchedulerBuilder.getBeanDefinition());
}
if (!registry.containsBeanDefinition(DelegatingWebSocketConfiguration.class.getName()) &&
!registry.containsBeanDefinition(WEB_SOCKET_HANDLER_MAPPING_BEAN_NAME)) {
BeanDefinitionBuilder enableWebSocketBuilder =
BeanDefinitionBuilder.genericBeanDefinition(WebSocketHandlerMappingFactoryBean.class)
.setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
.addPropertyReference("sockJsTaskScheduler", "defaultSockJsTaskScheduler");
registry.registerBeanDefinition(WEB_SOCKET_HANDLER_MAPPING_BEAN_NAME,
enableWebSocketBuilder.getBeanDefinition());
}
}
}
private static class WebSocketHandlerMappingFactoryBean extends AbstractFactoryBean<HandlerMapping>
implements ApplicationContextAware {
private final IntegrationServletWebSocketHandlerRegistry registry =
new IntegrationServletWebSocketHandlerRegistry();
private ThreadPoolTaskScheduler sockJsTaskScheduler;
private ApplicationContext applicationContext;
public void setSockJsTaskScheduler(ThreadPoolTaskScheduler sockJsTaskScheduler) {
this.sockJsTaskScheduler = sockJsTaskScheduler;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
protected HandlerMapping createInstance() throws Exception {
Collection<WebSocketConfigurer> webSocketConfigurers =
((ListableBeanFactory) getBeanFactory()).getBeansOfType(WebSocketConfigurer.class).values();
for (WebSocketConfigurer configurer : webSocketConfigurers) {
configurer.registerWebSocketHandlers(this.registry);
}
if (this.registry.requiresTaskScheduler()) {
this.registry.setTaskScheduler(this.sockJsTaskScheduler);
}
AbstractHandlerMapping handlerMapping = this.registry.getHandlerMapping();
handlerMapping.setApplicationContext(this.applicationContext);
return handlerMapping;
}
@Override
public Class<?> getObjectType() {
return HandlerMapping.class;
}
}
private static class IntegrationServletWebSocketHandlerRegistry extends ServletWebSocketHandlerRegistry {
@Override
public boolean requiresTaskScheduler() {
return super.requiresTaskScheduler();
}
@Override
public void setTaskScheduler(TaskScheduler scheduler) {
super.setTaskScheduler(scheduler);
}
}
}