diff --git a/.gitignore b/.gitignore
index 3fa1136eb0..b4c28f3b0e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,6 +8,7 @@
.classpath
.project
.settings
+.metadata
bin
build
lib/
@@ -28,4 +29,4 @@ overridedb.*
*.iws
.idea
*.jar
-.DS_Store
\ No newline at end of file
+.DS_Store
diff --git a/spring-boot-autoconfigure/pom.xml b/spring-boot-autoconfigure/pom.xml
index c040e7c133..ccab61becd 100644
--- a/spring-boot-autoconfigure/pom.xml
+++ b/spring-boot-autoconfigure/pom.xml
@@ -160,6 +160,11 @@
jetty-webapp
true
+
+ io.undertow
+ undertow-servlet
+ true
+
org.freemarker
freemarker
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/EmbeddedServletContainerAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/EmbeddedServletContainerAutoConfiguration.java
index 6ed5985c33..3461b0a94f 100644
--- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/EmbeddedServletContainerAutoConfiguration.java
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/EmbeddedServletContainerAutoConfiguration.java
@@ -16,6 +16,8 @@
package org.springframework.boot.autoconfigure.web;
+import io.undertow.Undertow;
+
import javax.servlet.Servlet;
import org.apache.catalina.startup.Tomcat;
@@ -37,6 +39,7 @@ import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomi
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
+import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@@ -87,6 +90,22 @@ public class EmbeddedServletContainerAutoConfiguration {
}
}
+
+ /**
+ * Nested configuration if Undertow is being used.
+ */
+ @Configuration
+ @ConditionalOnClass({ Servlet.class, Undertow.class })
+ @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
+ public static class EmbeddedUndertow {
+
+ @Bean
+ public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
+ return new UndertowEmbeddedServletContainerFactory();
+ }
+
+ }
+
/**
* Registers a {@link EmbeddedServletContainerCustomizerBeanPostProcessor}. Registered
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java
index 7fe8ced57e..e913718638 100644
--- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java
@@ -40,6 +40,7 @@ import org.springframework.boot.context.embedded.Ssl;
import org.springframework.boot.context.embedded.tomcat.TomcatConnectorCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
+import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.util.StringUtils;
@@ -71,6 +72,8 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
private String servletPath = "/";
private final Tomcat tomcat = new Tomcat();
+
+ private final Undertow undertow = new Undertow();
private final Map contextParameters = new HashMap();
@@ -78,6 +81,10 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
return this.tomcat;
}
+ public Undertow getUndertow() {
+ return this.undertow;
+ }
+
public String getContextPath() {
return this.contextPath;
}
@@ -179,7 +186,10 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
getTomcat()
.customizeTomcat((TomcatEmbeddedServletContainerFactory) container);
}
-
+ if (container instanceof UndertowEmbeddedServletContainerFactory) {
+ getUndertow().customizeUndertow(
+ (UndertowEmbeddedServletContainerFactory) container);
+ }
container.addInitializers(new InitParameterConfiguringServletContextInitializer(
getContextParameters()));
}
@@ -210,6 +220,62 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
return prefix + path;
}
+ public static class Undertow {
+ private Integer bufferSize;
+ private Integer buffersPerRegion;
+ private Integer ioThreads;
+ private Integer workerThreads;
+ private Boolean directBuffers;
+
+ public Integer getBufferSize() {
+ return this.bufferSize;
+ }
+
+ public void setBufferSize(Integer bufferSize) {
+ this.bufferSize = bufferSize;
+ }
+
+ public Integer getBuffersPerRegion() {
+ return this.buffersPerRegion;
+ }
+
+ public void setBuffersPerRegion(Integer buffersPerRegion) {
+ this.buffersPerRegion = buffersPerRegion;
+ }
+
+ public Integer getIoThreads() {
+ return this.ioThreads;
+ }
+
+ public void setIoThreads(Integer ioThreads) {
+ this.ioThreads = ioThreads;
+ }
+
+ public Integer getWorkerThreads() {
+ return this.workerThreads;
+ }
+
+ public void setWorkerThreads(Integer workerThreads) {
+ this.workerThreads = workerThreads;
+ }
+
+ public Boolean getDirectBuffers() {
+ return this.directBuffers;
+ }
+
+ public void setDirectBuffers(Boolean directBuffers) {
+ this.directBuffers = directBuffers;
+ }
+
+ void customizeUndertow(UndertowEmbeddedServletContainerFactory factory) {
+ factory.setBufferSize(bufferSize);
+ factory.setBuffersPerRegion(buffersPerRegion);
+ factory.setIoThreads(ioThreads);
+ factory.setWorkerThreads(workerThreads);
+ factory.setDirectBuffers(directBuffers);
+ }
+ }
+
public static class Tomcat {
private String accessLogPattern;
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/MultipartAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/MultipartAutoConfigurationTests.java
index 3d047230d7..bec1f66bca 100644
--- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/MultipartAutoConfigurationTests.java
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/MultipartAutoConfigurationTests.java
@@ -16,6 +16,10 @@
package org.springframework.boot.autoconfigure.web;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+
import javax.servlet.MultipartConfigElement;
import org.junit.After;
@@ -25,10 +29,16 @@ import org.junit.rules.ExpectedException;
import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
+import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.env.PropertySource;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.client.ClientHttpRequest;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@@ -71,10 +81,11 @@ public class MultipartAutoConfigurationTests {
}
@Test
- public void containerWithNothing() {
+ public void containerWithNothing() throws Exception {
this.context = new AnnotationConfigEmbeddedWebApplicationContext(
ContainerWithNothing.class, BaseConfiguration.class);
DispatcherServlet servlet = this.context.getBean(DispatcherServlet.class);
+ verify404();
assertNotNull(servlet.getMultipartResolver());
assertThat(this.context.getBeansOfType(StandardServletMultipartResolver.class)
.size(), equalTo(1));
@@ -112,6 +123,32 @@ public class MultipartAutoConfigurationTests {
}
}
+ @Test
+ public void containerWithNoMultipartUndertowConfiguration() {
+ this.context = new AnnotationConfigEmbeddedWebApplicationContext(
+ ContainerWithNoMultipartUndertow.class, BaseConfiguration.class);
+ DispatcherServlet servlet = this.context.getBean(DispatcherServlet.class);
+ verifyServletWorks();
+ assertNotNull(servlet.getMultipartResolver());
+ assertThat(this.context.getBeansOfType(StandardServletMultipartResolver.class)
+ .size(), equalTo(1));
+ assertThat(this.context.getBeansOfType(MultipartResolver.class).size(),
+ equalTo(1));
+ }
+
+ @Configuration
+ public static class ContainerWithNoMultipartUndertow {
+ @Bean
+ UndertowEmbeddedServletContainerFactory containerFactory() {
+ return new UndertowEmbeddedServletContainerFactory();
+ }
+
+ @Bean
+ WebController controller() {
+ return new WebController();
+ }
+ }
+
@Test
public void containerWithNoMultipartTomcatConfiguration() {
this.context = new AnnotationConfigEmbeddedWebApplicationContext(
@@ -148,6 +185,16 @@ public class MultipartAutoConfigurationTests {
verifyServletWorks();
}
+ @Test
+ public void containerWithAutomatedMultipartUndertowConfiguration() {
+ this.context = new AnnotationConfigEmbeddedWebApplicationContext(
+ ContainerWithEverythingUndertow.class, BaseConfiguration.class);
+ this.context.getBean(MultipartConfigElement.class);
+ verifyServletWorks();
+ assertSame(this.context.getBean(DispatcherServlet.class).getMultipartResolver(),
+ this.context.getBean(StandardServletMultipartResolver.class));
+ }
+
@Test
public void containerWithMultipartConfigDisabled() {
@@ -178,6 +225,16 @@ public class MultipartAutoConfigurationTests {
not(instanceOf(StandardServletMultipartResolver.class)));
}
+ private void verify404() throws Exception {
+ HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
+ ClientHttpRequest request = requestFactory.createRequest(new URI(
+ "http://localhost:"
+ + this.context.getEmbeddedServletContainer().getPort() + "/"),
+ HttpMethod.GET);
+ ClientHttpResponse response = request.execute();
+ assertEquals(HttpStatus.NOT_FOUND, response.getStatusCode());
+ }
+
private void verifyServletWorks() {
RestTemplate restTemplate = new RestTemplate();
assertEquals("Hello", restTemplate.getForObject("http://localhost:"
@@ -256,6 +313,27 @@ public class MultipartAutoConfigurationTests {
}
+ @Configuration
+ @EnableWebMvc
+ public static class ContainerWithEverythingUndertow {
+
+ @Bean
+ MultipartConfigElement multipartConfigElement() {
+ return new MultipartConfigElement("");
+ }
+
+ @Bean
+ UndertowEmbeddedServletContainerFactory containerFactory() {
+ return new UndertowEmbeddedServletContainerFactory();
+ }
+
+ @Bean
+ WebController webController() {
+ return new WebController();
+ }
+
+ }
+
public static class ContainerWithCustomMultipartResolver {
@Bean
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesAutoConfigurationTests.java
index b2ade70ece..cf684455ce 100644
--- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesAutoConfigurationTests.java
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/ServerPropertiesAutoConfigurationTests.java
@@ -32,6 +32,7 @@ import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomi
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
+import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
import org.springframework.boot.test.EnvironmentTestUtils;
import org.springframework.context.ApplicationContextException;
import org.springframework.context.annotation.Bean;
@@ -97,9 +98,9 @@ public class ServerPropertiesAutoConfigurationTests {
}
@Test
- public void customizeWithContainerFactory() throws Exception {
+ public void customizeWithJettyContainerFactory() throws Exception {
this.context = new AnnotationConfigEmbeddedWebApplicationContext();
- this.context.register(CustomContainerConfig.class,
+ this.context.register(CustomJettyContainerConfig.class,
ServerPropertiesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
@@ -111,6 +112,22 @@ public class ServerPropertiesAutoConfigurationTests {
// factory should take precedence...
assertEquals(3000, containerFactory.getPort());
}
+
+
+ @Test
+ public void customizeWithUndertowContainerFactory() throws Exception {
+ this.context = new AnnotationConfigEmbeddedWebApplicationContext();
+ this.context.register(CustomUndertowContainerConfig.class,
+ ServerPropertiesAutoConfiguration.class,
+ PropertyPlaceholderAutoConfiguration.class);
+ this.context.refresh();
+ containerFactory = this.context
+ .getBean(AbstractEmbeddedServletContainerFactory.class);
+ ServerProperties server = this.context.getBean(ServerProperties.class);
+ assertNotNull(server);
+ assertEquals(3000, containerFactory.getPort());
+ }
+
@Test
public void customizeTomcatWithCustomizer() throws Exception {
@@ -154,7 +171,7 @@ public class ServerPropertiesAutoConfigurationTests {
}
@Configuration
- protected static class CustomContainerConfig {
+ protected static class CustomJettyContainerConfig {
@Bean
public EmbeddedServletContainerFactory containerFactory() {
@@ -169,6 +186,26 @@ public class ServerPropertiesAutoConfigurationTests {
}
}
+
+ @Configuration
+ protected static class CustomUndertowContainerConfig {
+
+ @Bean
+ public EmbeddedServletContainerFactory containerFactory() {
+ UndertowEmbeddedServletContainerFactory factory = new UndertowEmbeddedServletContainerFactory();
+ factory.setPort(3000);
+ return factory;
+ }
+
+ @Bean
+ public EmbeddedServletContainerCustomizerBeanPostProcessor embeddedServletContainerCustomizerBeanPostProcessor() {
+ return new EmbeddedServletContainerCustomizerBeanPostProcessor();
+ }
+
+ }
+
+
+
@Configuration
protected static class CustomizeConfig {
diff --git a/spring-boot-dependencies/pom.xml b/spring-boot-dependencies/pom.xml
index 5d09759bff..bdeef0e508 100644
--- a/spring-boot-dependencies/pom.xml
+++ b/spring-boot-dependencies/pom.xml
@@ -130,6 +130,7 @@
1.7
2.0
1.6.3
+ 1.1.0.Final
3.0.2
@@ -289,6 +290,11 @@
spring-boot-starter-jta-bitronix
1.2.0.BUILD-SNAPSHOT
+
+ org.springframework.boot
+ spring-boot-starter-undertow
+ 1.2.0.BUILD-SNAPSHOT
+
org.springframework.boot
spring-boot-starter-log4j
@@ -701,6 +707,11 @@
tomcat-jsp-api
${tomcat.version}
+
+ io.undertow
+ undertow-servlet
+ ${undertow.version}
+
org.apache.velocity
velocity
diff --git a/spring-boot-samples/pom.xml b/spring-boot-samples/pom.xml
index b74248f595..0bda3a1d39 100644
--- a/spring-boot-samples/pom.xml
+++ b/spring-boot-samples/pom.xml
@@ -59,6 +59,8 @@
spring-boot-sample-tomcat-multi-connectors
spring-boot-sample-tomcat7-jsp
spring-boot-sample-traditional
+ spring-boot-sample-undertow
+ spring-boot-sample-undertow-ssl
spring-boot-sample-velocity
spring-boot-sample-web-freemarker
spring-boot-sample-web-groovy-templates
diff --git a/spring-boot-samples/spring-boot-sample-undertow-ssl/pom.xml b/spring-boot-samples/spring-boot-sample-undertow-ssl/pom.xml
new file mode 100644
index 0000000000..ee20b2b9d2
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-undertow-ssl/pom.xml
@@ -0,0 +1,53 @@
+
+
+ 4.0.0
+
+
+ org.springframework.boot
+ spring-boot-samples
+ 1.2.0.BUILD-SNAPSHOT
+
+ spring-boot-sample-undertow-ssl
+ Spring Boot Undertow SSL Sample
+ Spring Boot Undertow SSL Sample
+ http://projects.spring.io/spring-boot/
+
+ Pivotal Software, Inc.
+ http://www.spring.io
+
+
+ ${basedir}/../..
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-undertow
+
+
+ org.springframework
+ spring-webmvc
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.apache.httpcomponents
+ httpclient
+ test
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/spring-boot-samples/spring-boot-sample-undertow-ssl/src/main/java/sample/undertow/SampleUndertowSslApplication.java b/spring-boot-samples/spring-boot-sample-undertow-ssl/src/main/java/sample/undertow/SampleUndertowSslApplication.java
new file mode 100644
index 0000000000..dbeac870e5
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-undertow-ssl/src/main/java/sample/undertow/SampleUndertowSslApplication.java
@@ -0,0 +1,29 @@
+/*
+ * 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 sample.undertow;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SampleUndertowSslApplication {
+
+ public static void main(String[] args) throws Exception {
+ SpringApplication.run(SampleUndertowSslApplication.class, args);
+ }
+
+}
diff --git a/spring-boot-samples/spring-boot-sample-undertow-ssl/src/main/java/sample/undertow/service/HelloWorldService.java b/spring-boot-samples/spring-boot-sample-undertow-ssl/src/main/java/sample/undertow/service/HelloWorldService.java
new file mode 100644
index 0000000000..0865d25d2d
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-undertow-ssl/src/main/java/sample/undertow/service/HelloWorldService.java
@@ -0,0 +1,32 @@
+/*
+ * 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 sample.undertow.service;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Component
+public class HelloWorldService {
+
+ @Value("${name:World}")
+ private String name;
+
+ public String getHelloMessage() {
+ return "Hello " + this.name;
+ }
+
+}
diff --git a/spring-boot-samples/spring-boot-sample-undertow-ssl/src/main/java/sample/undertow/web/SampleController.java b/spring-boot-samples/spring-boot-sample-undertow-ssl/src/main/java/sample/undertow/web/SampleController.java
new file mode 100644
index 0000000000..78b5c0c776
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-undertow-ssl/src/main/java/sample/undertow/web/SampleController.java
@@ -0,0 +1,37 @@
+/*
+ * 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 sample.undertow.web;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import sample.undertow.service.HelloWorldService;
+
+@Controller
+public class SampleController {
+
+ @Autowired
+ private HelloWorldService helloWorldService;
+
+ @RequestMapping("/")
+ @ResponseBody
+ public String helloWorld() {
+ return this.helloWorldService.getHelloMessage();
+ }
+}
diff --git a/spring-boot-samples/spring-boot-sample-undertow-ssl/src/main/resources/application.properties b/spring-boot-samples/spring-boot-sample-undertow-ssl/src/main/resources/application.properties
new file mode 100644
index 0000000000..953abe0d6c
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-undertow-ssl/src/main/resources/application.properties
@@ -0,0 +1,4 @@
+server.port = 8443
+server.ssl.key-store = classpath:sample.jks
+server.ssl.key-store-password = secret
+server.ssl.key-password = password
\ No newline at end of file
diff --git a/spring-boot-samples/spring-boot-sample-undertow-ssl/src/main/resources/sample.jks b/spring-boot-samples/spring-boot-sample-undertow-ssl/src/main/resources/sample.jks
new file mode 100644
index 0000000000..6aa9a28053
Binary files /dev/null and b/spring-boot-samples/spring-boot-sample-undertow-ssl/src/main/resources/sample.jks differ
diff --git a/spring-boot-samples/spring-boot-sample-undertow-ssl/src/test/java/sample/undertow/SampleUndertowSslApplicationTests.java b/spring-boot-samples/spring-boot-sample-undertow-ssl/src/test/java/sample/undertow/SampleUndertowSslApplicationTests.java
new file mode 100644
index 0000000000..3b450065d3
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-undertow-ssl/src/test/java/sample/undertow/SampleUndertowSslApplicationTests.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2012-2014 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 sample.undertow;
+
+import org.apache.http.client.HttpClient;
+import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
+import org.apache.http.conn.ssl.SSLContextBuilder;
+import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
+import org.apache.http.impl.client.HttpClients;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.IntegrationTest;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.boot.test.TestRestTemplate;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Basic integration tests for demo application.
+ *
+ * @author Dave Syer
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringApplicationConfiguration(classes = SampleUndertowSslApplication.class)
+@WebAppConfiguration
+@IntegrationTest("server.port:0")
+@DirtiesContext
+public class SampleUndertowSslApplicationTests {
+
+ @Value("${local.server.port}")
+ private int port;
+
+ @Test
+ public void testHome() throws Exception {
+ SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
+ new SSLContextBuilder().loadTrustMaterial(null,
+ new TrustSelfSignedStrategy()).build());
+
+ HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory)
+ .build();
+
+ TestRestTemplate testRestTemplate = new TestRestTemplate();
+ ((HttpComponentsClientHttpRequestFactory) testRestTemplate.getRequestFactory())
+ .setHttpClient(httpClient);
+ ResponseEntity entity = testRestTemplate.getForEntity(
+ "https://localhost:" + this.port, String.class);
+ assertEquals(HttpStatus.OK, entity.getStatusCode());
+ assertEquals("Hello World", entity.getBody());
+ }
+
+}
diff --git a/spring-boot-samples/spring-boot-sample-undertow/pom.xml b/spring-boot-samples/spring-boot-sample-undertow/pom.xml
new file mode 100644
index 0000000000..2819c6ba94
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-undertow/pom.xml
@@ -0,0 +1,48 @@
+
+
+ 4.0.0
+
+
+ org.springframework.boot
+ spring-boot-samples
+ 1.2.0.BUILD-SNAPSHOT
+
+ spring-boot-sample-undertow
+ Spring Boot Undertow Sample
+ Spring Boot Undertow Sample
+ http://projects.spring.io/spring-boot/
+
+ Pivotal Software, Inc.
+ http://www.spring.io
+
+
+ ${basedir}/../..
+
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-undertow
+
+
+ org.springframework
+ spring-webmvc
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
diff --git a/spring-boot-samples/spring-boot-sample-undertow/src/main/java/sample/undertow/SampleUndertowApplication.java b/spring-boot-samples/spring-boot-sample-undertow/src/main/java/sample/undertow/SampleUndertowApplication.java
new file mode 100644
index 0000000000..a81adcd109
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-undertow/src/main/java/sample/undertow/SampleUndertowApplication.java
@@ -0,0 +1,29 @@
+/*
+ * 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 sample.undertow;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class SampleUndertowApplication {
+
+ public static void main(String[] args) throws Exception {
+ SpringApplication.run(SampleUndertowApplication.class, args);
+ }
+
+}
diff --git a/spring-boot-samples/spring-boot-sample-undertow/src/main/java/sample/undertow/service/HelloWorldService.java b/spring-boot-samples/spring-boot-sample-undertow/src/main/java/sample/undertow/service/HelloWorldService.java
new file mode 100644
index 0000000000..0865d25d2d
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-undertow/src/main/java/sample/undertow/service/HelloWorldService.java
@@ -0,0 +1,32 @@
+/*
+ * 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 sample.undertow.service;
+
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+@Component
+public class HelloWorldService {
+
+ @Value("${name:World}")
+ private String name;
+
+ public String getHelloMessage() {
+ return "Hello " + this.name;
+ }
+
+}
diff --git a/spring-boot-samples/spring-boot-sample-undertow/src/main/java/sample/undertow/web/SampleController.java b/spring-boot-samples/spring-boot-sample-undertow/src/main/java/sample/undertow/web/SampleController.java
new file mode 100644
index 0000000000..78b5c0c776
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-undertow/src/main/java/sample/undertow/web/SampleController.java
@@ -0,0 +1,37 @@
+/*
+ * 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 sample.undertow.web;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+import sample.undertow.service.HelloWorldService;
+
+@Controller
+public class SampleController {
+
+ @Autowired
+ private HelloWorldService helloWorldService;
+
+ @RequestMapping("/")
+ @ResponseBody
+ public String helloWorld() {
+ return this.helloWorldService.getHelloMessage();
+ }
+}
diff --git a/spring-boot-samples/spring-boot-sample-undertow/src/test/java/sample/undertow/SampleUndertowApplicationTests.java b/spring-boot-samples/spring-boot-sample-undertow/src/test/java/sample/undertow/SampleUndertowApplicationTests.java
new file mode 100644
index 0000000000..c786bf140f
--- /dev/null
+++ b/spring-boot-samples/spring-boot-sample-undertow/src/test/java/sample/undertow/SampleUndertowApplicationTests.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2012-2014 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 sample.undertow;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.test.IntegrationTest;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.boot.test.TestRestTemplate;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Basic integration tests for demo application.
+ *
+ * @author Ivan Sopov
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringApplicationConfiguration(classes = SampleUndertowApplication.class)
+@WebAppConfiguration
+@IntegrationTest("server.port:0")
+@DirtiesContext
+public class SampleUndertowApplicationTests {
+
+ @Value("${local.server.port}")
+ private int port;
+
+ @Test
+ public void testHome() throws Exception {
+ ResponseEntity entity = new TestRestTemplate().getForEntity(
+ "http://localhost:" + this.port, String.class);
+ assertEquals(HttpStatus.OK, entity.getStatusCode());
+ assertEquals("Hello World", entity.getBody());
+ }
+
+}
diff --git a/spring-boot-starters/pom.xml b/spring-boot-starters/pom.xml
index 014017a453..c93402ea3a 100644
--- a/spring-boot-starters/pom.xml
+++ b/spring-boot-starters/pom.xml
@@ -56,6 +56,7 @@
spring-boot-starter-test
spring-boot-starter-thymeleaf
spring-boot-starter-tomcat
+ spring-boot-starter-undertow
spring-boot-starter-velocity
spring-boot-starter-web
spring-boot-starter-websocket
diff --git a/spring-boot-starters/spring-boot-starter-undertow/pom.xml b/spring-boot-starters/spring-boot-starter-undertow/pom.xml
new file mode 100644
index 0000000000..0bcb12fc6b
--- /dev/null
+++ b/spring-boot-starters/spring-boot-starter-undertow/pom.xml
@@ -0,0 +1,27 @@
+
+
+ 4.0.0
+
+ org.springframework.boot
+ spring-boot-starters
+ 1.2.0.BUILD-SNAPSHOT
+
+ spring-boot-starter-undertow
+ Spring Boot Undertow Starter
+ Spring Boot Undertow Starter
+ http://projects.spring.io/spring-boot/
+
+ Pivotal Software, Inc.
+ http://www.spring.io
+
+
+ ${basedir}/../..
+
+
+
+ io.undertow
+ undertow-servlet
+
+
+
diff --git a/spring-boot/pom.xml b/spring-boot/pom.xml
index 2c50461ec5..bf47c8d141 100644
--- a/spring-boot/pom.xml
+++ b/spring-boot/pom.xml
@@ -129,6 +129,11 @@
jetty-util
true
+
+ io.undertow
+ undertow-servlet
+ true
+
org.hibernate
hibernate-entitymanager
diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainer.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainer.java
new file mode 100644
index 0000000000..43fb3912f5
--- /dev/null
+++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainer.java
@@ -0,0 +1,77 @@
+package org.springframework.boot.context.embedded.undertow;
+
+import io.undertow.Handlers;
+import io.undertow.Undertow;
+import io.undertow.Undertow.Builder;
+import io.undertow.server.HttpHandler;
+import io.undertow.server.handlers.PathHandler;
+import io.undertow.servlet.api.DeploymentManager;
+
+import javax.servlet.ServletException;
+
+import org.springframework.boot.context.embedded.EmbeddedServletContainer;
+import org.springframework.boot.context.embedded.EmbeddedServletContainerException;
+import org.springframework.util.StringUtils;
+
+/**
+ * @author Ivan Sopov
+ */
+public class UndertowEmbeddedServletContainer implements EmbeddedServletContainer {
+
+ private final DeploymentManager manager;
+ private final Builder builder;
+ private final String contextPath;
+ private final int port;
+ private final boolean autoStart;
+ private Undertow undertow;
+ private boolean started = false;
+
+ public UndertowEmbeddedServletContainer(Builder builder, DeploymentManager manager,
+ String contextPath, int port, boolean autoStart) {
+ this.builder = builder;
+ this.manager = manager;
+ this.contextPath = contextPath;
+ this.port = port;
+ this.autoStart = autoStart;
+ }
+
+ @Override
+ public synchronized void start() throws EmbeddedServletContainerException {
+ if (!this.autoStart) {
+ return;
+ }
+ if (undertow == null) {
+ try {
+ HttpHandler servletHandler = manager.start();
+ if (StringUtils.isEmpty(contextPath)) {
+ builder.setHandler(servletHandler);
+ }
+ else {
+ PathHandler pathHandler = Handlers.path().addPrefixPath(contextPath,
+ servletHandler);
+ builder.setHandler(pathHandler);
+ }
+ undertow = builder.build();
+ }
+ catch (ServletException ex) {
+ throw new EmbeddedServletContainerException(
+ "Unable to start embdedded Undertow", ex);
+ }
+ }
+ undertow.start();
+ started = true;
+ }
+
+ @Override
+ public synchronized void stop() throws EmbeddedServletContainerException {
+ if (started) {
+ started = false;
+ undertow.stop();
+ }
+ }
+
+ @Override
+ public int getPort() {
+ return port;
+ }
+}
\ No newline at end of file
diff --git a/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java
new file mode 100644
index 0000000000..ad112a6566
--- /dev/null
+++ b/spring-boot/src/main/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactory.java
@@ -0,0 +1,396 @@
+package org.springframework.boot.context.embedded.undertow;
+
+import static io.undertow.servlet.Servlets.defaultContainer;
+import static io.undertow.servlet.Servlets.deployment;
+import static io.undertow.servlet.Servlets.servlet;
+import static org.xnio.Options.SSL_CLIENT_AUTH_MODE;
+import static org.xnio.SslClientAuthMode.NOT_REQUESTED;
+import static org.xnio.SslClientAuthMode.REQUESTED;
+import static org.xnio.SslClientAuthMode.REQUIRED;
+import io.undertow.Undertow;
+import io.undertow.Undertow.Builder;
+import io.undertow.UndertowMessages;
+import io.undertow.server.handlers.resource.ClassPathResourceManager;
+import io.undertow.server.handlers.resource.FileResourceManager;
+import io.undertow.server.handlers.resource.Resource;
+import io.undertow.server.handlers.resource.ResourceChangeListener;
+import io.undertow.server.handlers.resource.ResourceManager;
+import io.undertow.server.handlers.resource.URLResource;
+import io.undertow.servlet.api.DeploymentInfo;
+import io.undertow.servlet.api.DeploymentManager;
+import io.undertow.servlet.api.InstanceFactory;
+import io.undertow.servlet.api.InstanceHandle;
+import io.undertow.servlet.api.ListenerInfo;
+import io.undertow.servlet.api.MimeMapping;
+import io.undertow.servlet.api.ServletStackTraces;
+import io.undertow.servlet.handlers.DefaultServlet;
+import io.undertow.servlet.util.ImmediateInstanceHandle;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.URL;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+
+import javax.net.ssl.KeyManager;
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.TrustManagerFactory;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.servlet.ServletException;
+
+import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
+import org.springframework.boot.context.embedded.EmbeddedServletContainer;
+import org.springframework.boot.context.embedded.ErrorPage;
+import org.springframework.boot.context.embedded.MimeMappings.Mapping;
+import org.springframework.boot.context.embedded.ServletContextInitializer;
+import org.springframework.boot.context.embedded.Ssl;
+import org.springframework.boot.context.embedded.Ssl.ClientAuth;
+import org.springframework.context.ResourceLoaderAware;
+import org.springframework.core.io.ResourceLoader;
+import org.springframework.util.ResourceUtils;
+import org.springframework.util.SocketUtils;
+
+/**
+ * @author Ivan Sopov
+ */
+public class UndertowEmbeddedServletContainerFactory extends
+ AbstractEmbeddedServletContainerFactory implements ResourceLoaderAware {
+
+ private ResourceLoader resourceLoader;
+
+ private Integer bufferSize;
+ private Integer buffersPerRegion;
+ private Integer ioThreads;
+ private Integer workerThreads;
+ private Boolean directBuffers;
+
+ /**
+ * Create a new
+ * {@link org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory}
+ * instance.
+ */
+ public UndertowEmbeddedServletContainerFactory() {
+ super();
+ }
+
+ /**
+ * Create a new
+ * {@link org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory}
+ * that listens for requests using the specified port.
+ *
+ * @param port the port to listen on
+ */
+ public UndertowEmbeddedServletContainerFactory(int port) {
+ super(port);
+ }
+
+ /**
+ * Create a new
+ * {@link org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory}
+ * with the specified context path and port.
+ *
+ * @param contextPath root the context path
+ * @param port the port to listen on
+ */
+ public UndertowEmbeddedServletContainerFactory(String contextPath, int port) {
+ super(contextPath, port);
+ }
+
+ @Override
+ public EmbeddedServletContainer getEmbeddedServletContainer(
+ ServletContextInitializer... initializers) {
+ DeploymentInfo servletBuilder = deployment();
+
+ servletBuilder.addListener(new ListenerInfo(
+ UndertowSpringServletContextListener.class,
+ new UndertowSpringServletContextListenerFactory(
+ new UndertowSpringServletContextListener(
+ mergeInitializers(initializers)))));
+
+ if (resourceLoader != null) {
+ servletBuilder.setClassLoader(resourceLoader.getClassLoader());
+ }
+ else {
+ servletBuilder.setClassLoader(getClass().getClassLoader());
+ }
+ servletBuilder.setContextPath(getContextPath());
+ servletBuilder.setDeploymentName("spring-boot");
+ if (isRegisterDefaultServlet()) {
+ servletBuilder.addServlet(servlet("default", DefaultServlet.class));
+ }
+ if (isRegisterJspServlet()) {
+ logger.error("JSPs are not supported with Undertow");
+ }
+ for (ErrorPage springErrorPage : getErrorPages()) {
+ if (springErrorPage.getStatus() != null) {
+ io.undertow.servlet.api.ErrorPage undertowErrorpage = new io.undertow.servlet.api.ErrorPage(
+ springErrorPage.getPath(), springErrorPage.getStatusCode());
+ servletBuilder.addErrorPage(undertowErrorpage);
+ }
+ else if (springErrorPage.getException() != null) {
+ io.undertow.servlet.api.ErrorPage undertowErrorpage = new io.undertow.servlet.api.ErrorPage(
+ springErrorPage.getPath(), springErrorPage.getException());
+ servletBuilder.addErrorPage(undertowErrorpage);
+ }
+ else {
+ io.undertow.servlet.api.ErrorPage undertowErrorpage = new io.undertow.servlet.api.ErrorPage(
+ springErrorPage.getPath());
+ servletBuilder.addErrorPage(undertowErrorpage);
+ }
+ }
+ servletBuilder.setServletStackTraces(ServletStackTraces.NONE);
+
+ File root = getValidDocumentRoot();
+ if (root != null && root.isDirectory()) {
+ servletBuilder.setResourceManager(new FileResourceManager(root, 0));
+ }
+ else if (root != null && root.isFile()) {
+ servletBuilder.setResourceManager(new JarResourcemanager(root));
+ }
+ else if (resourceLoader != null) {
+ servletBuilder.setResourceManager(new ClassPathResourceManager(resourceLoader
+ .getClassLoader(), ""));
+ }
+ else {
+ servletBuilder.setResourceManager(new ClassPathResourceManager(getClass()
+ .getClassLoader(), ""));
+ }
+ for (Mapping mimeMapping : getMimeMappings()) {
+ servletBuilder.addMimeMapping(new MimeMapping(mimeMapping.getExtension(),
+ mimeMapping.getMimeType()));
+ }
+
+ DeploymentManager manager = defaultContainer().addDeployment(servletBuilder);
+
+ manager.deploy();
+
+ manager.getDeployment().getSessionManager()
+ .setDefaultSessionTimeout(getSessionTimeout());
+
+ Builder builder = Undertow.builder();
+ if (bufferSize != null) {
+ builder.setBufferSize(bufferSize);
+ }
+ if (buffersPerRegion != null) {
+ builder.setBuffersPerRegion(buffersPerRegion);
+ }
+ if (ioThreads != null) {
+ builder.setIoThreads(ioThreads);
+ }
+ if (workerThreads != null) {
+ builder.setWorkerThreads(workerThreads);
+ }
+ if (directBuffers != null) {
+ builder.setDirectBuffers(directBuffers);
+ }
+
+ int realPort = getPort();
+ if (realPort == 0) {
+ realPort = SocketUtils.findAvailableTcpPort(40000);
+ }
+ if (getSsl() == null) {
+ builder.addHttpListener(realPort, "0.0.0.0");
+ }
+ else {
+ try {
+ Ssl ssl = getSsl();
+ SSLContext sslContext = SSLContext.getInstance(ssl.getProtocol());
+ sslContext.init(getKeyManagers(), getTrustManagers(), null);
+ builder.addHttpsListener(realPort, "0.0.0.0", sslContext);
+ if (ssl.getClientAuth() == ClientAuth.NEED) {
+ builder.setSocketOption(SSL_CLIENT_AUTH_MODE, REQUIRED);
+ }
+ else if (ssl.getClientAuth() == ClientAuth.WANT) {
+ builder.setSocketOption(SSL_CLIENT_AUTH_MODE, REQUESTED);
+ }
+ else {
+ builder.setSocketOption(SSL_CLIENT_AUTH_MODE, NOT_REQUESTED);
+ }
+ }
+ catch (NoSuchAlgorithmException e) {
+ throw new RuntimeException(e);
+ }
+ catch (KeyManagementException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return new UndertowEmbeddedServletContainer(builder, manager, getContextPath(),
+ realPort, realPort > 0);
+
+ }
+
+ private KeyManager[] getKeyManagers() {
+ try {
+ Ssl ssl = getSsl();
+
+ String keyStoreType = ssl.getKeyStoreType();
+ if (keyStoreType == null) {
+ keyStoreType = "JKS";
+ }
+ KeyStore keyStore = KeyStore.getInstance(keyStoreType);
+ URL url = ResourceUtils.getURL(ssl.getKeyStore());
+ keyStore.load(url.openStream(), ssl.getKeyStorePassword().toCharArray());
+
+ // Get key manager to provide client credentials.
+ KeyManagerFactory keyManagerFactory = KeyManagerFactory
+ .getInstance(KeyManagerFactory.getDefaultAlgorithm());
+ char[] keyPassword = ssl.getKeyPassword() != null ? ssl.getKeyPassword()
+ .toCharArray() : ssl.getKeyStorePassword().toCharArray();
+ keyManagerFactory.init(keyStore, keyPassword);
+ return keyManagerFactory.getKeyManagers();
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ private TrustManager[] getTrustManagers() {
+ try {
+ Ssl ssl = getSsl();
+
+ String trustStoreType = ssl.getTrustStoreType();
+ if (trustStoreType == null) {
+ trustStoreType = "JKS";
+ }
+ String trustStore = ssl.getTrustStore();
+ if (trustStore == null) {
+ return null;
+ }
+ KeyStore trustedKeyStore = KeyStore.getInstance(trustStoreType);
+ URL url = ResourceUtils.getURL(trustStore);
+ trustedKeyStore.load(url.openStream(), ssl.getTrustStorePassword()
+ .toCharArray());
+
+ TrustManagerFactory trustManagerFactory = TrustManagerFactory
+ .getInstance(TrustManagerFactory.getDefaultAlgorithm());
+ trustManagerFactory.init(trustedKeyStore);
+ return trustManagerFactory.getTrustManagers();
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ }
+
+ @Override
+ public void setResourceLoader(ResourceLoader resourceLoader) {
+ this.resourceLoader = resourceLoader;
+ }
+
+ public void setBufferSize(Integer bufferSize) {
+ this.bufferSize = bufferSize;
+ }
+
+ public void setBuffersPerRegion(Integer buffersPerRegion) {
+ this.buffersPerRegion = buffersPerRegion;
+ }
+
+ public void setIoThreads(Integer ioThreads) {
+ this.ioThreads = ioThreads;
+ }
+
+ public void setWorkerThreads(Integer workerThreads) {
+ this.workerThreads = workerThreads;
+ }
+
+ public void setDirectBuffers(Boolean directBuffers) {
+ this.directBuffers = directBuffers;
+ }
+
+ private static class JarResourcemanager implements ResourceManager {
+ private final String jarPath;
+
+ public JarResourcemanager(File jarFile) {
+ this(jarFile.getAbsolutePath());
+ }
+
+ public JarResourcemanager(String jarPath) {
+ this.jarPath = jarPath;
+ }
+
+ @Override
+ public void close() throws IOException {
+ // no code
+ }
+
+ @Override
+ public Resource getResource(String path) throws IOException {
+ URL url = new URL("jar:file:" + jarPath + "!" + path);
+ URLResource resource = new URLResource(url, url.openConnection(), path);
+ if (resource.getContentLength() < 0) {
+ return null;
+ }
+ return resource;
+ }
+
+ @Override
+ public boolean isResourceChangeListenerSupported() {
+ return false;
+ }
+
+ @Override
+ public void registerResourceChangeListener(ResourceChangeListener listener) {
+ throw UndertowMessages.MESSAGES.resourceChangeListenerNotSupported();
+
+ }
+
+ @Override
+ public void removeResourceChangeListener(ResourceChangeListener listener) {
+ throw UndertowMessages.MESSAGES.resourceChangeListenerNotSupported();
+
+ }
+
+ }
+
+ private static class UndertowSpringServletContextListenerFactory implements
+ InstanceFactory {
+
+ private final UndertowSpringServletContextListener listener;
+
+ public UndertowSpringServletContextListenerFactory(
+ UndertowSpringServletContextListener listener) {
+ this.listener = listener;
+ }
+
+ @Override
+ public InstanceHandle createInstance()
+ throws InstantiationException {
+ return new ImmediateInstanceHandle(
+ listener);
+ }
+
+ }
+
+ private static class UndertowSpringServletContextListener implements
+ ServletContextListener {
+ private final ServletContextInitializer[] initializers;
+
+ public UndertowSpringServletContextListener(
+ ServletContextInitializer... initializers) {
+ this.initializers = initializers;
+ }
+
+ @Override
+ public void contextInitialized(ServletContextEvent sce) {
+ try {
+ for (ServletContextInitializer initializer : initializers) {
+ initializer.onStartup(sce.getServletContext());
+ }
+ }
+ catch (ServletException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent sce) {
+ // no code
+ }
+ }
+
+}
diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/EmbeddedServletContainerMvcIntegrationTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/EmbeddedServletContainerMvcIntegrationTests.java
index c8f9ca2969..fd60f366a6 100644
--- a/spring-boot/src/test/java/org/springframework/boot/context/embedded/EmbeddedServletContainerMvcIntegrationTests.java
+++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/EmbeddedServletContainerMvcIntegrationTests.java
@@ -24,6 +24,7 @@ import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
+import org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@@ -68,7 +69,7 @@ public class EmbeddedServletContainerMvcIntegrationTests {
TomcatConfig.class);
doTest(this.context, "/hello");
}
-
+
@Test
public void jetty() throws Exception {
this.context = new AnnotationConfigEmbeddedWebApplicationContext(
@@ -76,6 +77,16 @@ public class EmbeddedServletContainerMvcIntegrationTests {
doTest(this.context, "/hello");
}
+ @Test
+ public void undertow() throws Exception {
+ this.context = new AnnotationConfigEmbeddedWebApplicationContext(
+ UndertowConfig.class);
+ doTest(this.context, "/hello");
+ }
+
+
+
+
@Test
public void advancedConfig() throws Exception {
this.context = new AnnotationConfigEmbeddedWebApplicationContext(
@@ -117,6 +128,15 @@ public class EmbeddedServletContainerMvcIntegrationTests {
return new JettyEmbeddedServletContainerFactory(0);
}
}
+
+ @Configuration
+ @Import(Config.class)
+ public static class UndertowConfig {
+ @Bean
+ public EmbeddedServletContainerFactory containerFactory() {
+ return new UndertowEmbeddedServletContainerFactory(0);
+ }
+ }
@Configuration
@EnableWebMvc
diff --git a/spring-boot/src/test/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactoryTests.java b/spring-boot/src/test/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactoryTests.java
new file mode 100644
index 0000000000..a25ded4dca
--- /dev/null
+++ b/spring-boot/src/test/java/org/springframework/boot/context/embedded/undertow/UndertowEmbeddedServletContainerFactoryTests.java
@@ -0,0 +1,34 @@
+package org.springframework.boot.context.embedded.undertow;
+import org.junit.Test;
+import org.springframework.boot.context.embedded.*;
+import org.springframework.http.HttpStatus;
+
+import static org.hamcrest.Matchers.equalTo;
+import static org.junit.Assert.assertThat;
+
+
+/**
+ * Tests for {@link org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory} and
+ * {@link org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainer}.
+ *
+ * @author Ivan Sopov
+ */
+public class UndertowEmbeddedServletContainerFactoryTests extends AbstractEmbeddedServletContainerFactoryTests
+{
+
+ @Override
+ protected UndertowEmbeddedServletContainerFactory getFactory()
+ {
+ return new UndertowEmbeddedServletContainerFactory();
+ }
+
+ @Test
+ public void errorPage404() throws Exception {
+ AbstractEmbeddedServletContainerFactory factory = getFactory();
+ factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/hello"));
+ this.container = factory.getEmbeddedServletContainer(new ServletRegistrationBean(new ExampleServlet(), "/hello"));
+ this.container.start();
+ assertThat(getResponse("http://localhost:8080/hello"), equalTo("Hello World"));
+ assertThat(getResponse("http://localhost:8080/not-found"), equalTo("Hello World"));
+ }
+}