mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-10-01 23:09:01 +00:00
Merge branch 'feature/websocket'
This commit is contained in:
@@ -71,6 +71,11 @@
|
||||
<artifactId>spring-web</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-websocket</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-webmvc</artifactId>
|
||||
|
||||
+151
@@ -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 <code>spring-websocket</code> is
|
||||
* detected on the classpath then we add a {@link DefaultSockJsService} and an MVC handler
|
||||
* mapping to <code>/<beanName>/**</code> for all of the
|
||||
* <code>WebSocketHandler</code> 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<String, WebSocketHandler> prefixes = new HashMap<String, WebSocketHandler>();
|
||||
|
||||
@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<WebSocketHandler> handlers) {
|
||||
|
||||
WebSocketEndpointPostProcessor processor = webSocketEndpointPostProcessor();
|
||||
Map<String, Object> urlMap = new HashMap<String, Object>();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
|
||||
+3
-1
@@ -29,6 +29,7 @@ import org.junit.BeforeClass;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.OutputCapture;
|
||||
import org.springframework.boot.cli.command.CleanCommand;
|
||||
import org.springframework.boot.cli.command.RunCommand;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -66,8 +67,9 @@ public class SampleIntegrationTests {
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
public void setup() throws Exception {
|
||||
System.setProperty("disableSpringSnapshotRepos", "true");
|
||||
new CleanCommand().run("org.springframework");
|
||||
}
|
||||
|
||||
@After
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<version>0.5.0.BUILD-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<properties>
|
||||
<spring.version>4.0.0.M2</spring.version>
|
||||
<spring.version>4.0.0.BUILD-SNAPSHOT</spring.version>
|
||||
<spring.security.version>3.2.0.M2</spring.security.version>
|
||||
<spring.integration.version>2.2.4.RELEASE</spring.integration.version>
|
||||
<spring.batch.version>2.2.0.RELEASE</spring.batch.version>
|
||||
@@ -273,6 +273,11 @@
|
||||
<artifactId>spring-tx</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-websocket</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
|
||||
@@ -119,6 +119,10 @@
|
||||
<configuration>
|
||||
<settingsFile>src/it/settings.xml</settingsFile>
|
||||
<projectsDirectory>${main.basedir}/spring-boot-samples/</projectsDirectory>
|
||||
<pomExcludes>
|
||||
<!-- temporarily suspend integration test (Bamboo doesn't like it, WTF?) -->
|
||||
<pomExclude>spring-boot-sample-websocket/pom.xml</pomExclude>
|
||||
</pomExcludes>
|
||||
<localRepositoryPath>${project.build.directory}/local-repo</localRepositoryPath>
|
||||
<skipInvocation>${skipTests}</skipInvocation>
|
||||
</configuration>
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
<module>spring-boot-sample-traditional</module>
|
||||
<module>spring-boot-sample-web-static</module>
|
||||
<module>spring-boot-sample-web-ui</module>
|
||||
<module>spring-boot-sample-websocket</module>
|
||||
<module>spring-boot-sample-xml</module>
|
||||
</modules>
|
||||
<build>
|
||||
|
||||
+4
-21
@@ -14,8 +14,8 @@
|
||||
|
||||
<properties>
|
||||
<java.version>1.7</java.version>
|
||||
<tomcat.version>8.0-SNAPSHOT</tomcat.version>
|
||||
<start-class>org.springframework.boot.samples.websocket.config.ApplicationConfiguration</start-class>
|
||||
<tomcat.version>8.0.0-RC1</tomcat.version>
|
||||
<start-class>org.springframework.boot.samples.websocket.config.SampleWebSocketsApplication</start-class>
|
||||
</properties>
|
||||
|
||||
|
||||
@@ -23,14 +23,10 @@
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
</dependency>
|
||||
<!-- For SockJS -->
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.websocket</groupId>
|
||||
<artifactId>websocket-client</artifactId>
|
||||
<version>9.0.3.v20130506</version>
|
||||
<scope>test</scope>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
@@ -43,17 +39,4 @@
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>tomcat-snapshots</id>
|
||||
<url>https://repository.apache.org/content/repositories/snapshots</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
</project>
|
||||
+6
-69
@@ -16,38 +16,24 @@
|
||||
|
||||
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;
|
||||
import org.springframework.boot.samples.websocket.echo.EchoService;
|
||||
import org.springframework.boot.samples.websocket.echo.EchoWebSocketHandler;
|
||||
import org.springframework.boot.samples.websocket.snake.SnakeWebSocketHandler;
|
||||
import org.springframework.boot.web.SpringServletInitializer;
|
||||
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.SockJsService;
|
||||
import org.springframework.web.socket.sockjs.support.DefaultSockJsService;
|
||||
import org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler;
|
||||
import org.springframework.web.socket.support.PerConnectionWebSocketHandler;
|
||||
|
||||
@Configuration
|
||||
public class SampleWebSocketsApplication extends SpringServletInitializer {
|
||||
|
||||
@EnableAutoConfiguration
|
||||
public class SampleWebSocketsApplication extends SpringBootServletInitializer {
|
||||
|
||||
@Override
|
||||
protected Class<?>[] getConfigClasses() {
|
||||
return new Class<?>[] { SampleWebSocketsApplication.class };
|
||||
@@ -57,22 +43,6 @@ public class SampleWebSocketsApplication extends SpringServletInitializer {
|
||||
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 SpringServletInitializer {
|
||||
return new SimpleGreetingService();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SimpleUrlHandlerMapping handlerMapping() {
|
||||
|
||||
SockJsService sockJsService = new DefaultSockJsService(sockJsTaskScheduler());
|
||||
|
||||
Map<String, Object> urlMap = new HashMap<String, Object>();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-9
@@ -49,6 +49,7 @@
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
<script src="http://cdn.sockjs.org/sockjs-0.3.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
var ws = null;
|
||||
|
||||
@@ -60,15 +61,7 @@
|
||||
|
||||
function connect() {
|
||||
var target = document.getElementById('target').value;
|
||||
target = "ws://" + window.location.host + target
|
||||
if ('WebSocket' in window) {
|
||||
ws = new WebSocket(target);
|
||||
} else if ('MozWebSocket' in window) {
|
||||
ws = new MozWebSocket(target);
|
||||
} else {
|
||||
alert('WebSocket is not supported by this browser.');
|
||||
return;
|
||||
}
|
||||
ws = new SockJS(target);
|
||||
ws.onopen = function () {
|
||||
setConnected(true);
|
||||
log('Info: WebSocket connection opened.');
|
||||
|
||||
+4
-14
@@ -49,6 +49,7 @@
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
<script src="http://cdn.sockjs.org/sockjs-0.3.min.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<noscript><h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websockets rely on Javascript being enabled. Please enable
|
||||
@@ -110,11 +111,7 @@
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
if (window.location.protocol == 'http:') {
|
||||
Game.connect('ws://' + window.location.host + '/snake');
|
||||
} else {
|
||||
Game.connect('wss://' + window.location.host + '/snake');
|
||||
}
|
||||
Game.connect();
|
||||
};
|
||||
|
||||
Game.setDirection = function(direction) {
|
||||
@@ -185,15 +182,8 @@
|
||||
};
|
||||
})();
|
||||
|
||||
Game.connect = (function(host) {
|
||||
if ('WebSocket' in window) {
|
||||
Game.socket = new WebSocket(host);
|
||||
} else if ('MozWebSocket' in window) {
|
||||
Game.socket = new MozWebSocket(host);
|
||||
} else {
|
||||
Console.log('Error: WebSocket is not supported by this browser.');
|
||||
return;
|
||||
}
|
||||
Game.connect = (function() {
|
||||
Game.socket = new SockJS("/snake");
|
||||
|
||||
Game.socket.onopen = function () {
|
||||
// Socket open.. start the game loop.
|
||||
|
||||
+39
-7
@@ -17,32 +17,64 @@ package org.springframework.boot.samples.websocket.echo;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.samples.websocket.client.GreetingService;
|
||||
import org.springframework.boot.samples.websocket.client.SimpleClientWebSocketHandler;
|
||||
import org.springframework.boot.samples.websocket.client.SimpleGreetingService;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.boot.samples.websocket.config.SampleWebSocketsApplication;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.socket.client.WebSocketConnectionManager;
|
||||
import org.springframework.web.socket.client.endpoint.StandardWebSocketClient;
|
||||
|
||||
public class StandardClientApp {
|
||||
public class SampleWebSocketsApplicationTests {
|
||||
|
||||
private static Log logger = LogFactory.getLog(StandardClientApp.class);
|
||||
private static Log logger = LogFactory.getLog(SampleWebSocketsApplicationTests.class);
|
||||
|
||||
private static final String WS_URI = "ws://localhost:8080/echo";
|
||||
private static final String WS_URI = "ws://localhost:8080/echo/websocket";
|
||||
|
||||
private static ConfigurableApplicationContext context;
|
||||
|
||||
@BeforeClass
|
||||
public static void start() throws Exception {
|
||||
Future<ConfigurableApplicationContext> future = Executors
|
||||
.newSingleThreadExecutor().submit(
|
||||
new Callable<ConfigurableApplicationContext>() {
|
||||
@Override
|
||||
public ConfigurableApplicationContext call() throws Exception {
|
||||
return (ConfigurableApplicationContext) SpringApplication
|
||||
.run(SampleWebSocketsApplication.class);
|
||||
}
|
||||
});
|
||||
context = future.get(30, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void stop() {
|
||||
if (context != null) {
|
||||
context.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runAndWait() throws Exception {
|
||||
ApplicationContext context = SpringApplication.run(ClientConfiguration.class, "--spring.main.web_environment=false");
|
||||
assertEquals(0, context.getBean(ClientConfiguration.class).latch.getCount());
|
||||
ConfigurableApplicationContext context = (ConfigurableApplicationContext) SpringApplication.run(ClientConfiguration.class, "--spring.main.web_environment=false");
|
||||
long count = context.getBean(ClientConfiguration.class).latch.getCount();
|
||||
context.close();
|
||||
assertEquals(0, count);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@@ -53,7 +85,7 @@ public class StandardClientApp {
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
logger.info("Waiting for response: latch=" + latch.getCount());
|
||||
latch.await();
|
||||
latch.await(10, TimeUnit.SECONDS);
|
||||
logger.info("Got response: latch=" + latch.getCount());
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<!-- This POM file that can be used as a parent for your own builds. It provides
|
||||
generally useful dependencies and plugins. NOTE: If you are editing a local
|
||||
checkout of this file, be sure to modify 'spring-boot-starters/src/main/parent/pom.xml'. -->
|
||||
checkout of this file, be sure to modify 'spring-boot-starter-parent/src/main/parent/pom.xml'. -->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
@@ -88,6 +88,11 @@
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<!-- This POM file that can be used as a parent for your own builds. It provides
|
||||
generally useful dependencies and plugins. NOTE: If you are editing a local
|
||||
checkout of this file, be sure to modify 'spring-boot-starters/src/main/parent/pom.xml'. -->
|
||||
checkout of this file, be sure to modify 'spring-boot-starter-parent/src/main/parent/pom.xml'. -->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
@@ -88,6 +88,11 @@
|
||||
<artifactId>spring-boot-starter-security</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
<dependencies>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<packaging>jar</packaging>
|
||||
<properties>
|
||||
<main.basedir>${basedir}/../..</main.basedir>
|
||||
<tomcat.version>8.0-SNAPSHOT</tomcat.version>
|
||||
<tomcat.version>8.0.0-RC1</tomcat.version>
|
||||
</properties>
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
@@ -35,14 +35,13 @@
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-boot-up-tomcat</artifactId>
|
||||
<artifactId>spring-boot-starter-tomcat</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-websocket</artifactId>
|
||||
<version>${spring.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.tomcat.embed</groupId>
|
||||
@@ -53,16 +52,4 @@
|
||||
<artifactId>tomcat-embed-logging-juli</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>tomcat-snapshots</id>
|
||||
<url>https://repository.apache.org/content/repositories/snapshots</url>
|
||||
<snapshots>
|
||||
<enabled>true</enabled>
|
||||
</snapshots>
|
||||
<releases>
|
||||
<enabled>false</enabled>
|
||||
</releases>
|
||||
</repository>
|
||||
</repositories>
|
||||
</project>
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
|
||||
package org.springframework.boot;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.beans.factory.BeanDefinitionStoreException;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.BeanNameGenerator;
|
||||
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
|
||||
@@ -26,7 +28,6 @@ import org.springframework.context.annotation.AnnotatedBeanDefinitionReader;
|
||||
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.io.DefaultResourceLoader;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
@@ -49,7 +50,7 @@ import org.springframework.util.StringUtils;
|
||||
*/
|
||||
class BeanDefinitionLoader {
|
||||
|
||||
private static final ResourceLoader DEFAULT_RESOURCE_LOADER = new DefaultResourceLoader();
|
||||
private static final ResourceLoader DEFAULT_RESOURCE_LOADER = new PathMatchingResourcePatternResolver();
|
||||
|
||||
private Object[] sources;
|
||||
|
||||
@@ -153,22 +154,50 @@ class BeanDefinitionLoader {
|
||||
}
|
||||
|
||||
private int load(CharSequence source) {
|
||||
String sourceString = xmlReader.getEnvironment().resolvePlaceholders(source.toString());
|
||||
try {
|
||||
// Use class utils so that period separated nested class names work
|
||||
return load(ClassUtils.forName(source.toString(), null));
|
||||
return load(ClassUtils.forName(sourceString, null));
|
||||
}
|
||||
catch (ClassNotFoundException ex) {
|
||||
// swallow exception and continue
|
||||
}
|
||||
|
||||
Resource loadedResource = (this.resourceLoader != null ? this.resourceLoader
|
||||
: DEFAULT_RESOURCE_LOADER).getResource(source.toString());
|
||||
if (loadedResource != null && loadedResource.exists()) {
|
||||
return load(loadedResource);
|
||||
ResourceLoader loader = this.resourceLoader != null ?
|
||||
this.resourceLoader : DEFAULT_RESOURCE_LOADER;
|
||||
|
||||
int loadCount = 0;
|
||||
if( loader instanceof ResourcePatternResolver ) {
|
||||
// Resource pattern matching available.
|
||||
try {
|
||||
Resource[] resources = ((ResourcePatternResolver) loader).getResources(sourceString);
|
||||
for(Resource resource : resources) {
|
||||
if( resource.exists() ) {
|
||||
loadCount += load(resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new BeanDefinitionStoreException(
|
||||
"Could not resolve bean definition resource pattern [" + sourceString + "]", ex);
|
||||
}
|
||||
}
|
||||
Package packageResource = findPackage(source);
|
||||
if (packageResource != null) {
|
||||
return load(packageResource);
|
||||
if( !(loader instanceof ResourcePatternResolver) ) {
|
||||
// Can only load single resources by absolute URL.
|
||||
Resource loadedResource = loader.getResource(sourceString);
|
||||
if (loadedResource != null && loadedResource.exists()) {
|
||||
return load(loadedResource);
|
||||
}
|
||||
}
|
||||
if( loadCount > 0 ) {
|
||||
return loadCount;
|
||||
}
|
||||
else {
|
||||
// Attempt to treat the source as a package name, common to all PatternResolver types
|
||||
Package packageResource = findPackage(source);
|
||||
if (packageResource != null) {
|
||||
return load(packageResource);
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Invalid source '" + source + "'");
|
||||
}
|
||||
|
||||
+67
-5
@@ -18,6 +18,7 @@ package org.springframework.boot.context.embedded.tomcat;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -34,6 +35,7 @@ import org.apache.catalina.loader.WebappLoader;
|
||||
import org.apache.catalina.startup.Tomcat;
|
||||
import org.apache.catalina.startup.Tomcat.FixContextListener;
|
||||
import org.apache.coyote.AbstractProtocol;
|
||||
import org.springframework.beans.BeanUtils;
|
||||
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainer;
|
||||
import org.springframework.boot.context.embedded.EmbeddedServletContainerException;
|
||||
@@ -45,6 +47,7 @@ import org.springframework.context.ResourceLoaderAware;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* {@link EmbeddedServletContainerFactory} that can be used to create
|
||||
@@ -213,11 +216,7 @@ public class TomcatEmbeddedServletContainerFactory extends
|
||||
context.getPipeline().addValve(valve);
|
||||
}
|
||||
for (ErrorPage errorPage : getErrorPages()) {
|
||||
org.apache.catalina.deploy.ErrorPage tomcatPage = new org.apache.catalina.deploy.ErrorPage();
|
||||
tomcatPage.setLocation(errorPage.getPath());
|
||||
tomcatPage.setExceptionType(errorPage.getExceptionName());
|
||||
tomcatPage.setErrorCode(errorPage.getStatusCode());
|
||||
context.addErrorPage(tomcatPage);
|
||||
new TomcatErrorPage(errorPage).addToContext(context);
|
||||
}
|
||||
for (MimeMappings.Mapping mapping : getMimeMappings()) {
|
||||
context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
|
||||
@@ -380,4 +379,67 @@ public class TomcatEmbeddedServletContainerFactory extends
|
||||
this.tomcatContextCustomizers.addAll(Arrays.asList(tomcatContextCustomizers));
|
||||
}
|
||||
|
||||
private static class TomcatErrorPage {
|
||||
|
||||
private String location;
|
||||
private String exceptionType;
|
||||
private int errorCode;
|
||||
|
||||
private Object nativePage;
|
||||
|
||||
public TomcatErrorPage(ErrorPage errorPage) {
|
||||
this.location = errorPage.getPath();
|
||||
this.exceptionType = errorPage.getExceptionName();
|
||||
this.errorCode = errorPage.getStatusCode();
|
||||
this.nativePage = createNativePage(errorPage);
|
||||
}
|
||||
|
||||
private Object createNativePage(ErrorPage errorPage) {
|
||||
Object nativePage = null;
|
||||
try {
|
||||
if (ClassUtils.isPresent("org.apache.catalina.deploy.ErrorPage", null)) {
|
||||
nativePage = new org.apache.catalina.deploy.ErrorPage();
|
||||
}
|
||||
else {
|
||||
if (ClassUtils.isPresent(
|
||||
"org.apache.tomcat.util.descriptor.web.ErrorPage", null)) {
|
||||
nativePage = BeanUtils.instantiate(ClassUtils.forName(
|
||||
"org.apache.tomcat.util.descriptor.web.ErrorPage", null));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ClassNotFoundException e) {
|
||||
}
|
||||
catch (LinkageError e) {
|
||||
}
|
||||
return nativePage;
|
||||
}
|
||||
|
||||
public void addToContext(Context context) {
|
||||
Assert.state(this.nativePage != null,
|
||||
"Neither Tomcat 7 nor 8 detected so no native error page exists");
|
||||
if (ClassUtils.isPresent("org.apache.catalina.deploy.ErrorPage", null)) {
|
||||
org.apache.catalina.deploy.ErrorPage errorPage = (org.apache.catalina.deploy.ErrorPage) this.nativePage;
|
||||
errorPage.setLocation(this.location);
|
||||
errorPage.setErrorCode(this.errorCode);
|
||||
errorPage.setExceptionType(this.exceptionType);
|
||||
context.addErrorPage(errorPage);
|
||||
}
|
||||
else {
|
||||
callMethod(this.nativePage, "setLocation", this.location, String.class);
|
||||
callMethod(this.nativePage, "setErrorCode", this.errorCode, int.class);
|
||||
callMethod(this.nativePage, "setExceptionType", this.exceptionType,
|
||||
String.class);
|
||||
callMethod(context, "addErrorPage", this.nativePage,
|
||||
this.nativePage.getClass());
|
||||
}
|
||||
}
|
||||
|
||||
private void callMethod(Object target, String name, Object value, Class<?> type) {
|
||||
Method method = ReflectionUtils.findMethod(target.getClass(), name, type);
|
||||
ReflectionUtils.invokeMethod(method, target, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -275,6 +275,14 @@ public class SpringApplicationTests {
|
||||
application, "initialSources");
|
||||
assertThat(initialSources.toArray(), equalTo(sources));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wildcardSources() {
|
||||
Object[] sources = { "classpath:org/springframework/boot/sample-${sample.app.test.prop}.xml" };
|
||||
TestSpringApplication application = new TestSpringApplication(sources);
|
||||
application.setWebEnvironment(false);
|
||||
application.run();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void run() throws Exception {
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
foo: bucket
|
||||
foo: bucket
|
||||
sample.app.test.prop: *
|
||||
Reference in New Issue
Block a user