Browse Source

Merge branch 'feature/websocket'

pull/33/head
Dave Syer 13 years ago
parent
commit
6c9c4e0a69
  1. 5
      spring-boot-autoconfigure/pom.xml
  2. 151
      spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/WebSocketAutoConfiguration.java
  3. 3
      spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories
  4. 4
      spring-boot-cli/src/test/java/org/springframework/boot/cli/SampleIntegrationTests.java
  5. 7
      spring-boot-dependencies/pom.xml
  6. 4
      spring-boot-integration-tests/pom.xml
  7. 1
      spring-boot-samples/pom.xml
  8. 25
      spring-boot-samples/spring-boot-sample-websocket/pom.xml
  9. 75
      spring-boot-samples/spring-boot-sample-websocket/src/main/java/org/springframework/boot/samples/websocket/config/SampleWebSocketsApplication.java
  10. 11
      spring-boot-samples/spring-boot-sample-websocket/src/main/resources/static/echo.html
  11. 18
      spring-boot-samples/spring-boot-sample-websocket/src/main/resources/static/snake.html
  12. 46
      spring-boot-samples/spring-boot-sample-websocket/src/test/java/org/springframework/boot/samples/websocket/echo/SampleWebSocketsApplicationTests.java
  13. 7
      spring-boot-starters/spring-boot-starter-parent/pom.xml
  14. 7
      spring-boot-starters/spring-boot-starter-parent/src/main/maven/pom.xml
  15. 17
      spring-boot-starters/spring-boot-starter-websocket/pom.xml
  16. 49
      spring-boot/src/main/java/org/springframework/boot/BeanDefinitionLoader.java
  17. 72
      spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java
  18. 8
      spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java
  19. 3
      spring-boot/src/test/resources/application.properties

5
spring-boot-autoconfigure/pom.xml

@ -71,6 +71,11 @@ @@ -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
spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/websocket/WebSocketAutoConfiguration.java

@ -0,0 +1,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>/&lt;beanName&gt;/**</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;
}
}
}

3
spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories

@ -12,4 +12,5 @@ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ @@ -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

4
spring-boot-cli/src/test/java/org/springframework/boot/cli/SampleIntegrationTests.java

@ -29,6 +29,7 @@ import org.junit.BeforeClass; @@ -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 { @@ -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
spring-boot-dependencies/pom.xml

@ -7,7 +7,7 @@ @@ -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 @@ @@ -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>

4
spring-boot-integration-tests/pom.xml

@ -119,6 +119,10 @@ @@ -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>

1
spring-boot-samples/pom.xml

@ -26,6 +26,7 @@ @@ -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>

25
spring-boot-samples/spring-boot-sample-websocket/_om.xml → spring-boot-samples/spring-boot-sample-websocket/pom.xml

@ -14,8 +14,8 @@ @@ -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 @@ @@ -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 @@ @@ -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>

75
spring-boot-samples/spring-boot-sample-websocket/src/main/java/org/springframework/boot/samples/websocket/config/SampleWebSocketsApplication.java

@ -16,38 +16,24 @@ @@ -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 { @@ -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 { @@ -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;
}
}

11
spring-boot-samples/spring-boot-sample-websocket/src/main/resources/static/echo.html

@ -49,6 +49,7 @@ @@ -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 @@ @@ -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.');

18
spring-boot-samples/spring-boot-sample-websocket/src/main/resources/static/snake.html

@ -49,6 +49,7 @@ @@ -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 @@ @@ -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 @@ @@ -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.

46
spring-boot-samples/spring-boot-sample-websocket/src/test/java/org/springframework/boot/samples/websocket/echo/StandardClientApp.java → spring-boot-samples/spring-boot-sample-websocket/src/test/java/org/springframework/boot/samples/websocket/echo/SampleWebSocketsApplicationTests.java

@ -17,32 +17,64 @@ package org.springframework.boot.samples.websocket.echo; @@ -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 { @@ -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());
}

7
spring-boot-starters/spring-boot-starter-parent/pom.xml

@ -2,7 +2,7 @@ @@ -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 @@ @@ -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>

7
spring-boot-starters/spring-boot-starter-parent/src/main/maven/pom.xml

@ -2,7 +2,7 @@ @@ -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 @@ @@ -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>

17
spring-boot-starters/spring-boot-starter-websocket/pom.xml

@ -11,7 +11,7 @@ @@ -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 @@ @@ -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 @@ @@ -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>

49
spring-boot/src/main/java/org/springframework/boot/BeanDefinitionLoader.java

@ -16,9 +16,11 @@ @@ -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; @@ -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; @@ -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 { @@ -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 + "'");
}

72
spring-boot/src/main/java/org/springframework/boot/context/embedded/tomcat/TomcatEmbeddedServletContainerFactory.java

@ -18,6 +18,7 @@ package org.springframework.boot.context.embedded.tomcat; @@ -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; @@ -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; @@ -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 @@ -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 @@ -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);
}
}
}

8
spring-boot/src/test/java/org/springframework/boot/SpringApplicationTests.java

@ -275,6 +275,14 @@ public class SpringApplicationTests { @@ -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 {

3
spring-boot/src/test/resources/application.properties

@ -1 +1,2 @@ @@ -1 +1,2 @@
foo: bucket
foo: bucket
sample.app.test.prop: *
Loading…
Cancel
Save