diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml
index 36e9e72a6c..f4e3fc4631 100644
--- a/spring-boot-autoconfigure/pom.xml
+++ b/spring-boot-autoconfigure/pom.xml
@@ -71,6 +71,11 @@
spring-web
true
+
+ org.springframework
+ spring-websocket
+ true
+
org.springframework
spring-webmvc
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/WebSocketAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/WebSocketAutoConfiguration.java
new file mode 100644
index 0000000000..6887a8f8db
--- /dev/null
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/WebSocketAutoConfiguration.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2012-2013 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.boot.autoconfigure.websocket;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.ServletContainerInitializer;
+
+import org.apache.catalina.Context;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.config.BeanPostProcessor;
+import org.springframework.boot.autoconfigure.AutoConfigureBefore;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration;
+import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
+import org.springframework.util.ClassUtils;
+import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
+import org.springframework.web.socket.WebSocketHandler;
+import org.springframework.web.socket.sockjs.SockJsHttpRequestHandler;
+import org.springframework.web.socket.sockjs.SockJsService;
+import org.springframework.web.socket.sockjs.support.AbstractSockJsService;
+import org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsService;
+
+/**
+ * Auto configuration for websockets (and sockjs in particular). Users should be able to
+ * just define beans of type {@link WebSocketHandler}. If spring-websocket
is
+ * detected on the classpath then we add a {@link DefaultSockJsService} and an MVC handler
+ * mapping to /<beanName>/**
for all of the
+ * WebSocketHandler
beans that have a bean name beginning with "/".
+ *
+ * @author Dave Syer
+ */
+@Configuration
+@ConditionalOnClass({ WebSocketHandler.class })
+@AutoConfigureBefore(EmbeddedServletContainerAutoConfiguration.class)
+public class WebSocketAutoConfiguration {
+
+ private static class WebSocketEndpointPostProcessor implements BeanPostProcessor {
+
+ private Map prefixes = new HashMap();
+
+ @Override
+ public Object postProcessBeforeInitialization(Object bean, String beanName)
+ throws BeansException {
+ return bean;
+ }
+
+ @Override
+ public Object postProcessAfterInitialization(Object bean, String beanName)
+ throws BeansException {
+ if (bean instanceof WebSocketHandler && beanName.startsWith("/")) {
+ this.prefixes.put(beanName, (WebSocketHandler) bean);
+ }
+ return bean;
+ }
+
+ public WebSocketHandler getHandler(String prefix) {
+ return this.prefixes.get(prefix);
+ }
+
+ public String[] getPrefixes() {
+ return this.prefixes.keySet().toArray(new String[this.prefixes.size()]);
+ }
+
+ }
+
+ @Bean
+ public WebSocketEndpointPostProcessor webSocketEndpointPostProcessor() {
+ return new WebSocketEndpointPostProcessor();
+ }
+
+ @Bean
+ @ConditionalOnMissingBean(SockJsService.class)
+ public DefaultSockJsService sockJsService() {
+ DefaultSockJsService service = new DefaultSockJsService(sockJsTaskScheduler());
+ service.setSockJsClientLibraryUrl("https://cdn.sockjs.org/sockjs-0.3.4.min.js");
+ service.setWebSocketsEnabled(true);
+ return service;
+ }
+
+ @Bean
+ public SimpleUrlHandlerMapping handlerMapping(SockJsService sockJsService,
+ Collection handlers) {
+
+ WebSocketEndpointPostProcessor processor = webSocketEndpointPostProcessor();
+ Map urlMap = new HashMap();
+ for (String prefix : webSocketEndpointPostProcessor().getPrefixes()) {
+ urlMap.put(prefix + "/**", new SockJsHttpRequestHandler(sockJsService,
+ processor.getHandler(prefix)));
+ }
+
+ if (sockJsService instanceof AbstractSockJsService) {
+ ((AbstractSockJsService) sockJsService).setValidSockJsPrefixes(processor
+ .getPrefixes());
+ }
+ SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
+ handlerMapping.setOrder(-1);
+ handlerMapping.setUrlMap(urlMap);
+
+ return handlerMapping;
+ }
+
+ @Bean
+ @ConditionalOnMissingBean(name = "sockJsTaskScheduler")
+ public ThreadPoolTaskScheduler sockJsTaskScheduler() {
+ ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
+ taskScheduler.setThreadNamePrefix("SockJS-");
+ return taskScheduler;
+ }
+
+ @Configuration
+ @ConditionalOnClass(name = "org.apache.tomcat.websocket.server.WsSci")
+ protected static class TomcatWebSocketConfiguration {
+ @Bean
+ public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
+ TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory() {
+ @Override
+ protected void postProcessContext(Context context) {
+ context.addServletContainerInitializer(
+ (ServletContainerInitializer) BeanUtils
+ .instantiate(ClassUtils.resolveClassName(
+ "org.apache.tomcat.websocket.server.WsSci",
+ null)), null);
+ }
+ };
+ return factory;
+ }
+ }
+
+}
diff --git a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
index 311bbdf231..1c157bdde3 100644
--- a/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
+++ b/spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
@@ -12,4 +12,5 @@ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.web.EmbeddedServletContainerAutoConfiguration,\
org.springframework.boot.autoconfigure.web.ServerPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.web.MultipartAutoConfiguration,\
-org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration
+org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration,\
+org.springframework.boot.autoconfigure.websocket.WebSocketAutoConfiguration
diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml
index af55b82e01..ee7a9e4ee0 100644
--- a/spring-boot-dependencies/pom.xml
+++ b/spring-boot-dependencies/pom.xml
@@ -7,7 +7,7 @@
0.5.0.BUILD-SNAPSHOT
pom
- 4.0.0.M2
+ 4.0.0.BUILD-SNAPSHOT
3.2.0.M2
2.2.4.RELEASE
2.2.0.RELEASE
diff --git a/spring-boot-samples/spring-boot-sample-websocket/pom.xml b/spring-boot-samples/spring-boot-sample-websocket/pom.xml
index b897c0e878..eac5cc7fe4 100644
--- a/spring-boot-samples/spring-boot-sample-websocket/pom.xml
+++ b/spring-boot-samples/spring-boot-sample-websocket/pom.xml
@@ -15,8 +15,7 @@
1.7
8.0.0-RC1
- 4.0.0.BUILD-SNAPSHOT
- org.springframework.boot.samples.websocket.config.ApplicationConfiguration
+ org.springframework.boot.samples.websocket.config.SampleWebSocketsApplication
@@ -29,13 +28,6 @@
org.springframework.boot
spring-boot-starter-actuator
-
-
- org.eclipse.jetty.websocket
- websocket-client
- 9.0.3.v20130506
- test
-
@@ -47,17 +39,4 @@
-
-
- tomcat-snapshots
- https://repository.apache.org/content/repositories/snapshots
-
- true
-
-
- false
-
-
-
-
diff --git a/spring-boot-samples/spring-boot-sample-websocket/src/main/java/org/springframework/boot/samples/websocket/config/SampleWebSocketsApplication.java b/spring-boot-samples/spring-boot-sample-websocket/src/main/java/org/springframework/boot/samples/websocket/config/SampleWebSocketsApplication.java
index 6269a7e005..2adde70e43 100644
--- a/spring-boot-samples/spring-boot-sample-websocket/src/main/java/org/springframework/boot/samples/websocket/config/SampleWebSocketsApplication.java
+++ b/spring-boot-samples/spring-boot-sample-websocket/src/main/java/org/springframework/boot/samples/websocket/config/SampleWebSocketsApplication.java
@@ -16,16 +16,8 @@
package org.springframework.boot.samples.websocket.config;
-import java.util.HashMap;
-import java.util.Map;
-
-import org.apache.catalina.Context;
-import org.apache.catalina.startup.Tomcat;
-import org.apache.tomcat.websocket.server.WsSci;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
-import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.boot.samples.websocket.client.GreetingService;
import org.springframework.boot.samples.websocket.client.SimpleGreetingService;
import org.springframework.boot.samples.websocket.echo.DefaultEchoService;
@@ -35,19 +27,13 @@ import org.springframework.boot.samples.websocket.snake.SnakeWebSocketHandler;
import org.springframework.boot.web.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
-import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
-import org.springframework.web.servlet.DispatcherServlet;
-import org.springframework.web.servlet.handler.SimpleUrlHandlerMapping;
import org.springframework.web.socket.WebSocketHandler;
-import org.springframework.web.socket.server.support.WebSocketHttpRequestHandler;
-import org.springframework.web.socket.sockjs.SockJsHttpRequestHandler;
-import org.springframework.web.socket.sockjs.SockJsService;
-import org.springframework.web.socket.sockjs.transport.handler.DefaultSockJsService;
import org.springframework.web.socket.support.PerConnectionWebSocketHandler;
@Configuration
+@EnableAutoConfiguration
public class SampleWebSocketsApplication extends SpringBootServletInitializer {
-
+
@Override
protected Class>[] getConfigClasses() {
return new Class>[] { SampleWebSocketsApplication.class };
@@ -57,22 +43,6 @@ public class SampleWebSocketsApplication extends SpringBootServletInitializer {
SpringApplication.run(SampleWebSocketsApplication.class, args);
}
- @ConditionalOnClass(Tomcat.class)
- @Configuration
- @EnableAutoConfiguration
- protected static class InitializationConfiguration {
- @Bean
- public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
- TomcatEmbeddedServletContainerFactory factory = new TomcatEmbeddedServletContainerFactory() {
- @Override
- protected void postProcessContext(Context context) {
- context.addServletContainerInitializer(new WsSci(), null);
- }
- };
- return factory;
- }
- }
-
@Bean
public EchoService echoService() {
return new DefaultEchoService("Did you say \"%s\"?");
@@ -83,47 +53,14 @@ public class SampleWebSocketsApplication extends SpringBootServletInitializer {
return new SimpleGreetingService();
}
- @Bean
- public SimpleUrlHandlerMapping handlerMapping() {
-
- SockJsService sockJsService = new DefaultSockJsService(sockJsTaskScheduler());
-
- Map urlMap = new HashMap();
-
- urlMap.put("/echo", new WebSocketHttpRequestHandler(echoWebSocketHandler()));
- urlMap.put("/snake", new WebSocketHttpRequestHandler(snakeWebSocketHandler()));
-
- urlMap.put("/sockjs/echo/**", new SockJsHttpRequestHandler(sockJsService, echoWebSocketHandler()));
- urlMap.put("/sockjs/snake/**", new SockJsHttpRequestHandler(sockJsService, snakeWebSocketHandler()));
-
- SimpleUrlHandlerMapping handlerMapping = new SimpleUrlHandlerMapping();
- handlerMapping.setOrder(-1);
- handlerMapping.setUrlMap(urlMap);
-
- return handlerMapping;
- }
-
- @Bean
- public DispatcherServlet dispatcherServlet() {
- DispatcherServlet servlet = new DispatcherServlet();
- servlet.setDispatchOptionsRequest(true);
- return servlet;
- }
-
- @Bean
+ @Bean(name = "/echo")
public WebSocketHandler echoWebSocketHandler() {
return new PerConnectionWebSocketHandler(EchoWebSocketHandler.class);
}
- @Bean
+ @Bean(name = "/snake")
public WebSocketHandler snakeWebSocketHandler() {
return new SnakeWebSocketHandler();
}
- @Bean
- public ThreadPoolTaskScheduler sockJsTaskScheduler() {
- ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
- taskScheduler.setThreadNamePrefix("SockJS-");
- return taskScheduler;
- }
}
diff --git a/spring-boot-samples/spring-boot-sample-websocket/src/main/resources/static/echo.html b/spring-boot-samples/spring-boot-sample-websocket/src/main/resources/static/echo.html
index cbaef719dd..42081d4e19 100644
--- a/spring-boot-samples/spring-boot-sample-websocket/src/main/resources/static/echo.html
+++ b/spring-boot-samples/spring-boot-sample-websocket/src/main/resources/static/echo.html
@@ -49,6 +49,7 @@
margin: 0;
}
+