Commit 7392e2b4 authored by Andy Wilkinson's avatar Andy Wilkinson

Merge branch 'undertow'

parents 21115f29 1864d790
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
.classpath .classpath
.project .project
.settings .settings
.metadata
bin bin
build build
lib/ lib/
...@@ -27,5 +28,4 @@ overridedb.* ...@@ -27,5 +28,4 @@ overridedb.*
*.ipr *.ipr
*.iws *.iws
.idea .idea
*.jar *.jar
.DS_Store \ No newline at end of file
\ No newline at end of file
...@@ -160,6 +160,11 @@ ...@@ -160,6 +160,11 @@
<artifactId>jetty-webapp</artifactId> <artifactId>jetty-webapp</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.freemarker</groupId> <groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId> <artifactId>freemarker</artifactId>
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
package org.springframework.boot.autoconfigure.web; package org.springframework.boot.autoconfigure.web;
import io.undertow.Undertow;
import javax.servlet.Servlet; import javax.servlet.Servlet;
import org.apache.catalina.startup.Tomcat; import org.apache.catalina.startup.Tomcat;
...@@ -37,6 +39,7 @@ import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomi ...@@ -37,6 +39,7 @@ import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomi
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; 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.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
...@@ -51,6 +54,7 @@ import org.springframework.util.ObjectUtils; ...@@ -51,6 +54,7 @@ import org.springframework.util.ObjectUtils;
* *
* @author Phillip Webb * @author Phillip Webb
* @author Dave Syer * @author Dave Syer
* @author Ivan Sopov
*/ */
@Order(Ordered.HIGHEST_PRECEDENCE) @Order(Ordered.HIGHEST_PRECEDENCE)
@Configuration @Configuration
...@@ -88,6 +92,21 @@ public class EmbeddedServletContainerAutoConfiguration { ...@@ -88,6 +92,21 @@ 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 * Registers a {@link EmbeddedServletContainerCustomizerBeanPostProcessor}. Registered
* via {@link ImportBeanDefinitionRegistrar} for early registration. * via {@link ImportBeanDefinitionRegistrar} for early registration.
......
...@@ -40,6 +40,7 @@ import org.springframework.boot.context.embedded.Ssl; ...@@ -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.TomcatConnectorCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer; import org.springframework.boot.context.embedded.tomcat.TomcatContextCustomizer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; 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.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
...@@ -52,6 +53,7 @@ import org.springframework.util.StringUtils; ...@@ -52,6 +53,7 @@ import org.springframework.util.StringUtils;
* @author Dave Syer * @author Dave Syer
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Andy Wilkinson * @author Andy Wilkinson
* @author Ivan Sopov
*/ */
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = false) @ConfigurationProperties(prefix = "server", ignoreUnknownFields = false)
public class ServerProperties implements EmbeddedServletContainerCustomizer { public class ServerProperties implements EmbeddedServletContainerCustomizer {
...@@ -72,12 +74,18 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer { ...@@ -72,12 +74,18 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
private final Tomcat tomcat = new Tomcat(); private final Tomcat tomcat = new Tomcat();
private final Undertow undertow = new Undertow();
private final Map<String, String> contextParameters = new HashMap<String, String>(); private final Map<String, String> contextParameters = new HashMap<String, String>();
public Tomcat getTomcat() { public Tomcat getTomcat() {
return this.tomcat; return this.tomcat;
} }
public Undertow getUndertow() {
return this.undertow;
}
public String getContextPath() { public String getContextPath() {
return this.contextPath; return this.contextPath;
} }
...@@ -179,7 +187,10 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer { ...@@ -179,7 +187,10 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
getTomcat() getTomcat()
.customizeTomcat((TomcatEmbeddedServletContainerFactory) container); .customizeTomcat((TomcatEmbeddedServletContainerFactory) container);
} }
if (container instanceof UndertowEmbeddedServletContainerFactory) {
getUndertow().customizeUndertow(
(UndertowEmbeddedServletContainerFactory) container);
}
container.addInitializers(new InitParameterConfiguringServletContextInitializer( container.addInitializers(new InitParameterConfiguringServletContextInitializer(
getContextParameters())); getContextParameters()));
} }
...@@ -396,4 +407,66 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer { ...@@ -396,4 +407,66 @@ public class ServerProperties implements EmbeddedServletContainerCustomizer {
} }
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(this.bufferSize);
factory.setBuffersPerRegion(this.buffersPerRegion);
factory.setIoThreads(this.ioThreads);
factory.setWorkerThreads(this.workerThreads);
factory.setDirectBuffers(this.directBuffers);
}
}
} }
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
package org.springframework.boot.autoconfigure.web; package org.springframework.boot.autoconfigure.web;
import java.net.URI;
import javax.servlet.MultipartConfigElement; import javax.servlet.MultipartConfigElement;
import org.junit.After; import org.junit.After;
...@@ -25,10 +27,16 @@ import org.junit.rules.ExpectedException; ...@@ -25,10 +27,16 @@ import org.junit.rules.ExpectedException;
import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext; import org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext;
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; 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.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
import org.springframework.core.env.PropertySource; 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.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
...@@ -55,6 +63,7 @@ import static org.mockito.Mockito.mock; ...@@ -55,6 +63,7 @@ import static org.mockito.Mockito.mock;
* @author Greg Turnquist * @author Greg Turnquist
* @author Dave Syer * @author Dave Syer
* @author Josh Long * @author Josh Long
* @author Ivan Sopov
*/ */
public class MultipartAutoConfigurationTests { public class MultipartAutoConfigurationTests {
...@@ -71,10 +80,11 @@ public class MultipartAutoConfigurationTests { ...@@ -71,10 +80,11 @@ public class MultipartAutoConfigurationTests {
} }
@Test @Test
public void containerWithNothing() { public void containerWithNothing() throws Exception {
this.context = new AnnotationConfigEmbeddedWebApplicationContext( this.context = new AnnotationConfigEmbeddedWebApplicationContext(
ContainerWithNothing.class, BaseConfiguration.class); ContainerWithNothing.class, BaseConfiguration.class);
DispatcherServlet servlet = this.context.getBean(DispatcherServlet.class); DispatcherServlet servlet = this.context.getBean(DispatcherServlet.class);
verify404();
assertNotNull(servlet.getMultipartResolver()); assertNotNull(servlet.getMultipartResolver());
assertThat(this.context.getBeansOfType(StandardServletMultipartResolver.class) assertThat(this.context.getBeansOfType(StandardServletMultipartResolver.class)
.size(), equalTo(1)); .size(), equalTo(1));
...@@ -112,6 +122,32 @@ public class MultipartAutoConfigurationTests { ...@@ -112,6 +122,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 @Test
public void containerWithNoMultipartTomcatConfiguration() { public void containerWithNoMultipartTomcatConfiguration() {
this.context = new AnnotationConfigEmbeddedWebApplicationContext( this.context = new AnnotationConfigEmbeddedWebApplicationContext(
...@@ -148,6 +184,16 @@ public class MultipartAutoConfigurationTests { ...@@ -148,6 +184,16 @@ public class MultipartAutoConfigurationTests {
verifyServletWorks(); 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 @Test
public void containerWithMultipartConfigDisabled() { public void containerWithMultipartConfigDisabled() {
...@@ -178,6 +224,16 @@ public class MultipartAutoConfigurationTests { ...@@ -178,6 +224,16 @@ public class MultipartAutoConfigurationTests {
not(instanceOf(StandardServletMultipartResolver.class))); 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() { private void verifyServletWorks() {
RestTemplate restTemplate = new RestTemplate(); RestTemplate restTemplate = new RestTemplate();
assertEquals("Hello", restTemplate.getForObject("http://localhost:" assertEquals("Hello", restTemplate.getForObject("http://localhost:"
...@@ -256,6 +312,27 @@ public class MultipartAutoConfigurationTests { ...@@ -256,6 +312,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 { public static class ContainerWithCustomMultipartResolver {
@Bean @Bean
......
...@@ -32,6 +32,7 @@ import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomi ...@@ -32,6 +32,7 @@ import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomi
import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.EmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; 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.boot.test.EnvironmentTestUtils;
import org.springframework.context.ApplicationContextException; import org.springframework.context.ApplicationContextException;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
...@@ -46,6 +47,7 @@ import static org.mockito.Mockito.verify; ...@@ -46,6 +47,7 @@ import static org.mockito.Mockito.verify;
* Tests for {@link ServerPropertiesAutoConfiguration}. * Tests for {@link ServerPropertiesAutoConfiguration}.
* *
* @author Dave Syer * @author Dave Syer
* @author Ivan Sopov
*/ */
public class ServerPropertiesAutoConfigurationTests { public class ServerPropertiesAutoConfigurationTests {
...@@ -97,9 +99,9 @@ public class ServerPropertiesAutoConfigurationTests { ...@@ -97,9 +99,9 @@ public class ServerPropertiesAutoConfigurationTests {
} }
@Test @Test
public void customizeWithContainerFactory() throws Exception { public void customizeWithJettyContainerFactory() throws Exception {
this.context = new AnnotationConfigEmbeddedWebApplicationContext(); this.context = new AnnotationConfigEmbeddedWebApplicationContext();
this.context.register(CustomContainerConfig.class, this.context.register(CustomJettyContainerConfig.class,
ServerPropertiesAutoConfiguration.class, ServerPropertiesAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class); PropertyPlaceholderAutoConfiguration.class);
this.context.refresh(); this.context.refresh();
...@@ -112,6 +114,20 @@ public class ServerPropertiesAutoConfigurationTests { ...@@ -112,6 +114,20 @@ public class ServerPropertiesAutoConfigurationTests {
assertEquals(3000, containerFactory.getPort()); 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 @Test
public void customizeTomcatWithCustomizer() throws Exception { public void customizeTomcatWithCustomizer() throws Exception {
containerFactory = mock(TomcatEmbeddedServletContainerFactory.class); containerFactory = mock(TomcatEmbeddedServletContainerFactory.class);
...@@ -154,7 +170,7 @@ public class ServerPropertiesAutoConfigurationTests { ...@@ -154,7 +170,7 @@ public class ServerPropertiesAutoConfigurationTests {
} }
@Configuration @Configuration
protected static class CustomContainerConfig { protected static class CustomJettyContainerConfig {
@Bean @Bean
public EmbeddedServletContainerFactory containerFactory() { public EmbeddedServletContainerFactory containerFactory() {
...@@ -170,6 +186,23 @@ public class ServerPropertiesAutoConfigurationTests { ...@@ -170,6 +186,23 @@ 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 @Configuration
protected static class CustomizeConfig { protected static class CustomizeConfig {
......
...@@ -127,6 +127,7 @@ ...@@ -127,6 +127,7 @@
<thymeleaf-layout-dialect.version>1.2.7</thymeleaf-layout-dialect.version> <thymeleaf-layout-dialect.version>1.2.7</thymeleaf-layout-dialect.version>
<thymeleaf-extras-data-attribute.version>1.3</thymeleaf-extras-data-attribute.version> <thymeleaf-extras-data-attribute.version>1.3</thymeleaf-extras-data-attribute.version>
<tomcat.version>8.0.15</tomcat.version> <tomcat.version>8.0.15</tomcat.version>
<undertow.version>1.1.0.Final</undertow.version>
<velocity.version>1.7</velocity.version> <velocity.version>1.7</velocity.version>
<velocity-tools.version>2.0</velocity-tools.version> <velocity-tools.version>2.0</velocity-tools.version>
<wsdl4j.version>1.6.3</wsdl4j.version> <wsdl4j.version>1.6.3</wsdl4j.version>
...@@ -289,6 +290,11 @@ ...@@ -289,6 +290,11 @@
<artifactId>spring-boot-starter-jta-bitronix</artifactId> <artifactId>spring-boot-starter-jta-bitronix</artifactId>
<version>1.2.0.BUILD-SNAPSHOT</version> <version>1.2.0.BUILD-SNAPSHOT</version>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
<version>1.2.0.BUILD-SNAPSHOT</version>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j</artifactId> <artifactId>spring-boot-starter-log4j</artifactId>
...@@ -534,6 +540,16 @@ ...@@ -534,6 +540,16 @@
<artifactId>metrics-servlets</artifactId> <artifactId>metrics-servlets</artifactId>
<version>${dropwizard-metrics.version}</version> <version>${dropwizard-metrics.version}</version>
</dependency> </dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-core</artifactId>
<version>${undertow.version}</version>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
<version>${undertow.version}</version>
</dependency>
<dependency> <dependency>
<groupId>javax.cache</groupId> <groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId> <artifactId>cache-api</artifactId>
......
...@@ -158,8 +158,8 @@ for our starter REST application: ...@@ -158,8 +158,8 @@ for our starter REST application:
Spring Boot makes `-D` arguments available as properties accessible from a Spring Spring Boot makes `-D` arguments available as properties accessible from a Spring
`Environment` instance. The `server.port` configuration property is fed to the embedded `Environment` instance. The `server.port` configuration property is fed to the embedded
Tomcat or Jetty instance which then uses it when it starts up. The `$PORT` environment Tomcat, Jetty or Undertow instance which then uses it when it starts up. The `$PORT`
variable is assigned to us by the Heroku PaaS. environment variable is assigned to us by the Heroku PaaS.
Heroku by default will use Java 1.6. This is fine as long as your Maven or Gradle build Heroku by default will use Java 1.6. This is fine as long as your Maven or Gradle build
is set to use the same version (Maven users can use the `java.version` property). If you is set to use the same version (Maven users can use the `java.version` property). If you
......
...@@ -556,6 +556,61 @@ of ways. Or the nuclear option is to add your own `JettyEmbeddedServletContainer ...@@ -556,6 +556,61 @@ of ways. Or the nuclear option is to add your own `JettyEmbeddedServletContainer
[[howto-use-undertow-instead-of-tomcat]]
=== Use Undertow instead of Tomcat
Using Undertow instead of Tomcat is very similar to <<howto-use-jetty-instead-of-tomcat,
using Jetty instead of Tomcat>>. You need to exclude the Tomcat dependencies and include
the Undertow starter instead.
Example in Maven:
[source,xml,indent=0,subs="verbatim,quotes,attributes"]
----
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
----
Example in Gradle:
[source,groovy,indent=0,subs="verbatim,quotes,attributes"]
----
configurations {
compile.exclude module: "spring-boot-starter-tomcat"
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-web:{spring-boot-version}")
compile("org.springframework.boot:spring-boot-starter-undertow:{spring-boot-version}")
// ...
}
----
[[howto-configure-undertow]]
=== Configure Undertow
Generally you can follow the advice from
_<<howto-discover-build-in-options-for-external-properties>>_ about
`@ConfigurationProperties` (`ServerProperties` and `ServerProperties.Undertow are the
main ones here), but also look at
`EmbeddedServletContainerCustomizer`. Once you have access to the
`UndertowEmbeddedServletContainerFactory` you can use an `UndertowBuilderCustomizer` to
modify Undertow's configuration to meet your needs. Or the nuclear option is to add your
own `UndertowEmbeddedServletContainerFactory`.
[[howto-use-tomcat-8]] [[howto-use-tomcat-8]]
=== Use Tomcat 8 === Use Tomcat 8
Tomcat 8 works with Spring Boot, but the default is to use Tomcat 7 (so we can support Tomcat 8 works with Spring Boot, but the default is to use Tomcat 7 (so we can support
......
...@@ -810,8 +810,8 @@ possible. ...@@ -810,8 +810,8 @@ possible.
[[boot-features-developing-web-applications]] [[boot-features-developing-web-applications]]
== Developing web applications == Developing web applications
Spring Boot is well suited for web application development. You can easily create a Spring Boot is well suited for web application development. You can easily create a
self-contained HTTP server using embedded Tomcat or Jetty. Most web applications will self-contained HTTP server using embedded Tomcat, Jetty, or Undertow. Most web
use the `spring-boot-starter-web` module to get up and running quickly. applications will use the `spring-boot-starter-web` module to get up and running quickly.
If you haven't yet developed a Spring Boot web application you can follow the If you haven't yet developed a Spring Boot web application you can follow the
"Hello World!" example in the "Hello World!" example in the
...@@ -1093,9 +1093,9 @@ asks for them to be scanned in its `Filter` registration). ...@@ -1093,9 +1093,9 @@ asks for them to be scanned in its `Filter` registration).
[[boot-features-embedded-container]] [[boot-features-embedded-container]]
=== Embedded servlet container support === Embedded servlet container support
Spring Boot includes support for embedded Tomcat and Jetty servers. Most developers will Spring Boot includes support for embedded Tomcat, Jetty, and Undertow servers. Most
simply use the appropriate '`Starter POM`' to obtain a fully configured instance. By developers will simply use the appropriate '`Starter POM`' to obtain a fully configured
default both Tomcat and Jetty will listen for HTTP requests on port `8080`. instance. By default the embedded server will listen for HTTP requests on port `8080`.
...@@ -1121,8 +1121,9 @@ interface. ...@@ -1121,8 +1121,9 @@ interface.
Under the hood Spring Boot uses a new type of `ApplicationContext` for embedded Under the hood Spring Boot uses a new type of `ApplicationContext` for embedded
servlet container support. The `EmbeddedWebApplicationContext` is a special servlet container support. The `EmbeddedWebApplicationContext` is a special
type of `WebApplicationContext` that bootstraps itself by searching for a single type of `WebApplicationContext` that bootstraps itself by searching for a single
`EmbeddedServletContainerFactory` bean. Usually a `TomcatEmbeddedServletContainerFactory` `EmbeddedServletContainerFactory` bean. Usually a `TomcatEmbeddedServletContainerFactory`,
or `JettyEmbeddedServletContainerFactory` will have been auto-configured. `JettyEmbeddedServletContainerFactory`, or `UndertowEmbeddedServletContainerFactory` will
have been auto-configured.
NOTE: You usually won't need to be aware of these implementation classes. Most NOTE: You usually won't need to be aware of these implementation classes. Most
applications will be auto-configured and the appropriate `ApplicationContext` and applications will be auto-configured and the appropriate `ApplicationContext` and
...@@ -1176,8 +1177,8 @@ methods. ...@@ -1176,8 +1177,8 @@ methods.
[[boot-features-customizing-configurableembeddedservletcontainerfactory-directly]] [[boot-features-customizing-configurableembeddedservletcontainerfactory-directly]]
===== Customizing ConfigurableEmbeddedServletContainer directly ===== Customizing ConfigurableEmbeddedServletContainer directly
If the above customization techniques are too limited, you can register the If the above customization techniques are too limited, you can register the
`TomcatEmbeddedServletContainerFactory` or `JettyEmbeddedServletContainerFactory` bean `TomcatEmbeddedServletContainerFactory`, `JettyEmbeddedServletContainerFactory` or
yourself. `UndertowEmbeddedServletContainerFactory` bean yourself.
[source,java,indent=0] [source,java,indent=0]
---- ----
...@@ -1208,6 +1209,8 @@ packaged as an executable archive), there are some limitations in the JSP suppor ...@@ -1208,6 +1209,8 @@ packaged as an executable archive), there are some limitations in the JSP suppor
* Jetty does not currently work as an embedded container with JSPs. * Jetty does not currently work as an embedded container with JSPs.
* Undertow does not support JSPs
There is a {github-code}/spring-boot-samples/spring-boot-sample-web-jsp[JSP sample] so There is a {github-code}/spring-boot-samples/spring-boot-sample-web-jsp[JSP sample] so
you can see how to set things up. you can see how to set things up.
......
...@@ -348,6 +348,9 @@ swap specific technical facets. ...@@ -348,6 +348,9 @@ swap specific technical facets.
|`spring-boot-starter-tomcat` |`spring-boot-starter-tomcat`
|Import Spring Boot's default HTTP engine (Tomcat). |Import Spring Boot's default HTTP engine (Tomcat).
|`spring-boot-starter-undertow`
|Imports the Undertow HTTP engine (to be used as an alternative to Tomcat)
|=== |===
TIP: For a list of additional community contributed starter POMs, see the TIP: For a list of additional community contributed starter POMs, see the
......
...@@ -59,6 +59,8 @@ ...@@ -59,6 +59,8 @@
<module>spring-boot-sample-tomcat-multi-connectors</module> <module>spring-boot-sample-tomcat-multi-connectors</module>
<module>spring-boot-sample-tomcat7-jsp</module> <module>spring-boot-sample-tomcat7-jsp</module>
<module>spring-boot-sample-traditional</module> <module>spring-boot-sample-traditional</module>
<module>spring-boot-sample-undertow</module>
<module>spring-boot-sample-undertow-ssl</module>
<module>spring-boot-sample-velocity</module> <module>spring-boot-sample-velocity</module>
<module>spring-boot-sample-web-freemarker</module> <module>spring-boot-sample-web-freemarker</module>
<module>spring-boot-sample-web-groovy-templates</module> <module>spring-boot-sample-web-groovy-templates</module>
......
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<!-- Your own application should inherit from spring-boot-starter-parent -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-samples</artifactId>
<version>1.2.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-undertow-ssl</artifactId>
<name>Spring Boot Undertow SSL Sample</name>
<description>Spring Boot Undertow SSL Sample</description>
<url>http://projects.spring.io/spring-boot/</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
/*
* 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.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);
}
}
/*
* 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.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;
}
}
/*
* 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.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();
}
}
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
/*
* 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 Ivan Sopov
*/
@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<String> entity = testRestTemplate.getForEntity(
"https://localhost:" + this.port, String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertEquals("Hello World", entity.getBody());
}
}
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<!-- Your own application should inherit from spring-boot-starter-parent -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-samples</artifactId>
<version>1.2.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-sample-undertow</artifactId>
<name>Spring Boot Undertow Sample</name>
<description>Spring Boot Undertow Sample</description>
<url>http://projects.spring.io/spring-boot/</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
/*
* 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.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);
}
}
/*
* 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.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;
}
}
/*
* 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.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();
}
}
/*
* 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<String> entity = new TestRestTemplate().getForEntity(
"http://localhost:" + this.port, String.class);
assertEquals(HttpStatus.OK, entity.getStatusCode());
assertEquals("Hello World", entity.getBody());
}
}
...@@ -56,6 +56,7 @@ ...@@ -56,6 +56,7 @@
<module>spring-boot-starter-test</module> <module>spring-boot-starter-test</module>
<module>spring-boot-starter-thymeleaf</module> <module>spring-boot-starter-thymeleaf</module>
<module>spring-boot-starter-tomcat</module> <module>spring-boot-starter-tomcat</module>
<module>spring-boot-starter-undertow</module>
<module>spring-boot-starter-velocity</module> <module>spring-boot-starter-velocity</module>
<module>spring-boot-starter-web</module> <module>spring-boot-starter-web</module>
<module>spring-boot-starter-websocket</module> <module>spring-boot-starter-websocket</module>
......
<?xml version="1.0" encoding="UTF-8"?>
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starters</artifactId>
<version>1.2.0.BUILD-SNAPSHOT</version>
</parent>
<artifactId>spring-boot-starter-undertow</artifactId>
<name>Spring Boot Undertow Starter</name>
<description>Spring Boot Undertow Starter</description>
<url>http://projects.spring.io/spring-boot/</url>
<organization>
<name>Pivotal Software, Inc.</name>
<url>http://www.spring.io</url>
</organization>
<properties>
<main.basedir>${basedir}/../..</main.basedir>
</properties>
<dependencies>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-core</artifactId>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
<exclusions>
<exclusion>
<groupId>org.jboss.spec.javax.servlet</groupId>
<artifactId>jboss-servlet-api_3.1_spec</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
</dependencies>
</project>
...@@ -129,6 +129,11 @@ ...@@ -129,6 +129,11 @@
<artifactId>jetty-util</artifactId> <artifactId>jetty-util</artifactId>
<optional>true</optional> <optional>true</optional>
</dependency> </dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.hibernate</groupId> <groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId> <artifactId>hibernate-entitymanager</artifactId>
......
/*
* 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 org.springframework.boot.context.embedded.undertow;
import io.undertow.Undertow.Builder;
/**
* Callback interface that can be used to customize an Undertow {@link Builder}.
*
* @author Andy Wilkinson
* @since 1.2.0
* @see UndertowEmbeddedServletContainerFactory
*/
public interface UndertowBuilderCustomizer {
/**
* @param builder the {@code Builder} to customize
*/
void customize(Builder builder);
}
/*
* 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 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;
/**
* {@link EmbeddedServletContainer} that can be used to control an embedded Undertow
* server. Typically this class should be created using
* {@link UndertowEmbeddedServletContainerFactory} and not directly.
*
* @author Ivan Sopov
* @author Andy Wilkinson
* @since 1.2.0
* @see UndertowEmbeddedServletContainer
*/
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 (this.undertow == null) {
try {
HttpHandler servletHandler = this.manager.start();
if (StringUtils.isEmpty(this.contextPath)) {
this.builder.setHandler(servletHandler);
}
else {
PathHandler pathHandler = Handlers.path().addPrefixPath(
this.contextPath, servletHandler);
this.builder.setHandler(pathHandler);
}
this.undertow = this.builder.build();
}
catch (ServletException ex) {
throw new EmbeddedServletContainerException(
"Unable to start embdedded Undertow", ex);
}
}
this.undertow.start();
this.started = true;
}
@Override
public synchronized void stop() throws EmbeddedServletContainerException {
if (this.started) {
this.started = false;
this.undertow.stop();
}
}
@Override
public int getPort() {
return this.port;
}
}
\ No newline at end of file
/*
* 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.
*/
/**
* Support for Undertow {@link org.springframework.boot.context.embedded.EmbeddedServletContainer EmbeddedServletContainers}.
*
* @see org.springframework.boot.context.embedded.undertow.UndertowEmbeddedServletContainerFactory
*/
package org.springframework.boot.context.embedded.undertow;
...@@ -24,6 +24,7 @@ import org.junit.Test; ...@@ -24,6 +24,7 @@ import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory; import org.springframework.boot.context.embedded.jetty.JettyEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory; 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.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Import;
...@@ -48,6 +49,7 @@ import static org.junit.Assert.assertThat; ...@@ -48,6 +49,7 @@ import static org.junit.Assert.assertThat;
* {@link EmbeddedServletContainer}s running Spring MVC. * {@link EmbeddedServletContainer}s running Spring MVC.
* *
* @author Phillip Webb * @author Phillip Webb
* @author Ivan Sopov
*/ */
public class EmbeddedServletContainerMvcIntegrationTests { public class EmbeddedServletContainerMvcIntegrationTests {
...@@ -76,6 +78,13 @@ public class EmbeddedServletContainerMvcIntegrationTests { ...@@ -76,6 +78,13 @@ public class EmbeddedServletContainerMvcIntegrationTests {
doTest(this.context, "/hello"); doTest(this.context, "/hello");
} }
@Test
public void undertow() throws Exception {
this.context = new AnnotationConfigEmbeddedWebApplicationContext(
UndertowConfig.class);
doTest(this.context, "/hello");
}
@Test @Test
public void advancedConfig() throws Exception { public void advancedConfig() throws Exception {
this.context = new AnnotationConfigEmbeddedWebApplicationContext( this.context = new AnnotationConfigEmbeddedWebApplicationContext(
...@@ -118,6 +127,15 @@ public class EmbeddedServletContainerMvcIntegrationTests { ...@@ -118,6 +127,15 @@ public class EmbeddedServletContainerMvcIntegrationTests {
} }
} }
@Configuration
@Import(Config.class)
public static class UndertowConfig {
@Bean
public EmbeddedServletContainerFactory containerFactory() {
return new UndertowEmbeddedServletContainerFactory(0);
}
}
@Configuration @Configuration
@EnableWebMvc @EnableWebMvc
public static class Config { public static class Config {
......
/*
* 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 org.springframework.boot.context.embedded.undertow;
import io.undertow.Undertow.Builder;
import java.util.Arrays;
import org.junit.Test;
import org.mockito.InOrder;
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory;
import org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactoryTests;
import org.springframework.boot.context.embedded.ErrorPage;
import org.springframework.boot.context.embedded.ExampleServlet;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.http.HttpStatus;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
/**
* Tests for {@link UndertowEmbeddedServletContainerFactory} and
* {@link UndertowEmbeddedServletContainer} .
*
* @author Ivan Sopov
* @author Andy Wilkinson
*/
public class UndertowEmbeddedServletContainerFactoryTests extends
AbstractEmbeddedServletContainerFactoryTests {
@Override
protected UndertowEmbeddedServletContainerFactory getFactory() {
return new UndertowEmbeddedServletContainerFactory(0);
}
@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(getLocalUrl("/hello")), equalTo("Hello World"));
assertThat(getResponse(getLocalUrl("/not-found")), equalTo("Hello World"));
}
@Test
public void setNullUndertowBuilderCustomizersThrows() {
UndertowEmbeddedServletContainerFactory factory = getFactory();
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("undertowBuilderCustomizers must not be null");
factory.setUndertowBuilderCustomizers(null);
}
@Test
public void addNullContextCustomizersThrows() {
UndertowEmbeddedServletContainerFactory factory = getFactory();
this.thrown.expect(IllegalArgumentException.class);
this.thrown.expectMessage("undertowBuilderCustomizers must not be null");
factory.addUndertowBuilderCustomizers((UndertowBuilderCustomizer[]) null);
}
@Test
public void builderCustomizers() throws Exception {
UndertowEmbeddedServletContainerFactory factory = getFactory();
UndertowBuilderCustomizer[] customizers = new UndertowBuilderCustomizer[4];
for (int i = 0; i < customizers.length; i++) {
customizers[i] = mock(UndertowBuilderCustomizer.class);
}
factory.setUndertowBuilderCustomizers(Arrays.asList(customizers[0],
customizers[1]));
factory.addUndertowBuilderCustomizers(customizers[2], customizers[3]);
this.container = factory.getEmbeddedServletContainer();
InOrder ordered = inOrder((Object[]) customizers);
for (UndertowBuilderCustomizer customizer : customizers) {
ordered.verify(customizer).customize((Builder) anyObject());
}
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment