Commit 4ede9cd1 authored by Phillip Webb's avatar Phillip Webb

Support websockets with Tomcat 8.0.8

Fixes gh-1210
parent 54f1b29d
...@@ -34,6 +34,7 @@ import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletCon ...@@ -34,6 +34,7 @@ import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletCon
import org.springframework.boot.context.web.NonEmbeddedServletContainerFactory; import org.springframework.boot.context.web.NonEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils; import org.springframework.util.ReflectionUtils;
import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.WebSocketHandler;
...@@ -45,6 +46,7 @@ import org.springframework.web.socket.WebSocketHandler; ...@@ -45,6 +46,7 @@ import org.springframework.web.socket.WebSocketHandler;
* already be there. * already be there.
* *
* @author Dave Syer * @author Dave Syer
* @author Phillip Webb
*/ */
@Configuration @Configuration
@ConditionalOnClass(name = "org.apache.tomcat.websocket.server.WsSci", value = { @ConditionalOnClass(name = "org.apache.tomcat.websocket.server.WsSci", value = {
...@@ -56,38 +58,35 @@ public class WebSocketAutoConfiguration { ...@@ -56,38 +58,35 @@ public class WebSocketAutoConfiguration {
private static final String TOMCAT_8_LISTENER_TYPE = "org.apache.tomcat.util.descriptor.web.ApplicationListener"; private static final String TOMCAT_8_LISTENER_TYPE = "org.apache.tomcat.util.descriptor.web.ApplicationListener";
private static final String WS_LISTENER = "org.apache.tomcat.websocket.server.WsContextListener";
private static Log logger = LogFactory.getLog(WebSocketAutoConfiguration.class); private static Log logger = LogFactory.getLog(WebSocketAutoConfiguration.class);
@Bean @Bean
@ConditionalOnMissingBean(name = "websocketContainerCustomizer") @ConditionalOnMissingBean(name = "websocketContainerCustomizer")
public EmbeddedServletContainerCustomizer websocketContainerCustomizer() { public EmbeddedServletContainerCustomizer websocketContainerCustomizer() {
return new EmbeddedServletContainerCustomizer() {
EmbeddedServletContainerCustomizer customizer = new EmbeddedServletContainerCustomizer() {
@Override @Override
public void customize(ConfigurableEmbeddedServletContainer container) { public void customize(ConfigurableEmbeddedServletContainer container) {
if (container instanceof NonEmbeddedServletContainerFactory) { if (container instanceof NonEmbeddedServletContainerFactory) {
logger.info("NonEmbeddedServletContainerFactory detected. Websockets support should be native so this normally is not a problem."); logger.info("NonEmbeddedServletContainerFactory detected. Websockets "
+ "support should be native so this normally is not a problem.");
return; return;
} }
if (!(container instanceof TomcatEmbeddedServletContainerFactory)) { Assert.state(container instanceof TomcatEmbeddedServletContainerFactory,
throw new IllegalStateException( "Websockets are currently only supported in Tomcat (found "
"Websockets are currently only supported in Tomcat (found " + container.getClass() + "). ");
+ container.getClass() + "). "); TomcatEmbeddedServletContainerFactory tomcatContainer = (TomcatEmbeddedServletContainerFactory) container;
} tomcatContainer.addContextCustomizers(new TomcatContextCustomizer() {
((TomcatEmbeddedServletContainerFactory) container) @Override
.addContextCustomizers(new TomcatContextCustomizer() { public void customize(Context context) {
@Override addListener(context, findListenerType());
public void customize(Context context) { }
addListener(context, findListenerType()); });
}
});
} }
}; };
return customizer;
} }
private static Class<?> findListenerType() { private static Class<?> findListenerType() {
...@@ -97,23 +96,30 @@ public class WebSocketAutoConfiguration { ...@@ -97,23 +96,30 @@ public class WebSocketAutoConfiguration {
if (ClassUtils.isPresent(TOMCAT_8_LISTENER_TYPE, null)) { if (ClassUtils.isPresent(TOMCAT_8_LISTENER_TYPE, null)) {
return ClassUtils.resolveClassName(TOMCAT_8_LISTENER_TYPE, null); return ClassUtils.resolveClassName(TOMCAT_8_LISTENER_TYPE, null);
} }
throw new UnsupportedOperationException( // With Tomcat 8.0.8 ApplicationListener is not required
"Cannot find Tomcat 7 or 8 ApplicationListener class"); return null;
} }
/** /**
* Instead of registering the WsSci directly as a ServletContainerInitializer, we use * Instead of registering the WsSci directly as a ServletContainerInitializer, we use
* the ApplicationListener provided by Tomcat. Unfortunately the ApplicationListener * the ApplicationListener provided by Tomcat. Unfortunately the ApplicationListener
* class moved packages in Tomcat 8 so we have to do it reflectively. * class moved packages in Tomcat 8 and been deleted in 8.0.8 so we have to use
* * reflection.
* @param context the current context * @param context the current context
* @param listenerType the type of listener to add * @param listenerType the type of listener to add
*/ */
private static void addListener(Context context, Class<?> listenerType) { private static void addListener(Context context, Class<?> listenerType) {
Object instance = BeanUtils.instantiateClass(ClassUtils if (listenerType == null) {
.getConstructorIfAvailable(listenerType, String.class, boolean.class), ReflectionUtils.invokeMethod(ClassUtils.getMethod(context.getClass(),
"org.apache.tomcat.websocket.server.WsContextListener", false); "addApplicationListener", String.class), context, WS_LISTENER);
ReflectionUtils.invokeMethod(ClassUtils.getMethod(context.getClass(),
"addApplicationListener", listenerType), context, instance); }
else {
Object instance = BeanUtils.instantiateClass(
ClassUtils.getConstructorIfAvailable(listenerType, String.class,
boolean.class), WS_LISTENER, false);
ReflectionUtils.invokeMethod(ClassUtils.getMethod(context.getClass(),
"addApplicationListener", listenerType), context, instance);
}
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment