From 826d79be3e0f1758acb5da9429707b1371940de4 Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Fri, 8 Jan 2021 15:46:55 +0100 Subject: [PATCH] Clarify behaviour of WebServerFactory in reference guide Closes gh-24705 --- .../spring-boot-docs/build.gradle | 1 + .../src/docs/asciidoc/howto.adoc | 59 +++------------- .../docs/asciidoc/spring-boot-features.adoc | 24 +++---- .../TomcatMultipleConnectorsExample.java | 68 +++++++++++++++++++ .../TomcatServerCustomizerExample.java | 41 +++++++++++ .../UndertowMultipleListenersExample.java | 51 ++++++++++++++ 6 files changed, 182 insertions(+), 62 deletions(-) create mode 100644 spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/context/embedded/TomcatMultipleConnectorsExample.java create mode 100644 spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/context/embedded/TomcatServerCustomizerExample.java create mode 100644 spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/context/embedded/UndertowMultipleListenersExample.java diff --git a/spring-boot-project/spring-boot-docs/build.gradle b/spring-boot-project/spring-boot-docs/build.gradle index 1825bca67c..74db9eb7f5 100644 --- a/spring-boot-project/spring-boot-docs/build.gradle +++ b/spring-boot-project/spring-boot-docs/build.gradle @@ -71,6 +71,7 @@ dependencies { implementation("com.zaxxer:HikariCP") implementation("io.micrometer:micrometer-core") implementation("io.projectreactor.netty:reactor-netty") + implementation("io.undertow:undertow-core") implementation("jakarta.servlet:jakarta.servlet-api") implementation("org.apache.commons:commons-dbcp2") implementation("org.apache.kafka:kafka-streams") diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto.adoc index eb427c45b6..25395c1f5e 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/howto.adoc @@ -720,6 +720,11 @@ The example below is for Tomcat with the `spring-boot-starter-web` (Servlet stac } ---- +NOTE: Spring Boot uses that infrastructure internally to auto-configure the server. +Auto-configured `WebServerFactoryCustomizer` beans have an order of `0` and will be processed before any user-defined customizers, unless it has an explicit order that states otherwise. + +Once you've got access to a `WebServerFactory` using the customizer, you can use it to configure specific parts, like connectors, server resources, or the server itself - all using server-specific APIs. + In addition Spring Boot provides: [[howto-configure-webserver-customizers]] @@ -744,10 +749,8 @@ In addition Spring Boot provides: | `NettyReactiveWebServerFactory` |=== -Once you've got access to a `WebServerFactory`, you can often add customizers to it to configure specific parts, like connectors, server resources, or the server itself - all using server-specific APIs. - -As a last resort, you can also declare your own `WebServerFactory` component, which will override the one provided by Spring Boot. -In this case, you can't rely on configuration properties in the `server` namespace anymore. +As a last resort, you can also declare your own `WebServerFactory` bean, which will override the one provided by Spring Boot. +When you do so, auto-configured customizers are still applied on your custom factory, so use that option carefully. @@ -892,7 +895,7 @@ If you use YAML, single backslashes are sufficient, and a value equivalent to th NOTE: You can trust all proxies by setting the `internal-proxies` to empty (but do not do so in production). -You can take complete control of the configuration of Tomcat's `RemoteIpValve` by switching the automatic one off (to do so, set `server.forward-headers-strategy=NONE`) and adding a new valve instance in a `TomcatServletWebServerFactory` bean. +You can take complete control of the configuration of Tomcat's `RemoteIpValve` by switching the automatic one off (to do so, set `server.forward-headers-strategy=NONE`) and adding a new valve instance using a `WebServerFactoryCustomizer` bean. @@ -902,35 +905,7 @@ You can add an `org.apache.catalina.connector.Connector` to the `TomcatServletWe [source,java,indent=0,subs="verbatim,quotes,attributes"] ---- - @Bean - public ServletWebServerFactory servletContainer() { - TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory(); - tomcat.addAdditionalTomcatConnectors(createSslConnector()); - return tomcat; - } - - private Connector createSslConnector() { - Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol"); - Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler(); - try { - File keystore = new ClassPathResource("keystore").getFile(); - File truststore = new ClassPathResource("keystore").getFile(); - connector.setScheme("https"); - connector.setSecure(true); - connector.setPort(8443); - protocol.setSSLEnabled(true); - protocol.setKeystoreFile(keystore.getAbsolutePath()); - protocol.setKeystorePass("changeit"); - protocol.setTruststoreFile(truststore.getAbsolutePath()); - protocol.setTruststorePass("changeit"); - protocol.setKeyAlias("apitester"); - return connector; - } - catch (IOException ex) { - throw new IllegalStateException("can't access keystore: [" + keystore - + "] or truststore: [" + truststore + "]", ex); - } - } +include::{code-examples}/context/embedded/TomcatMultipleConnectorsExample.java[tag=configuration] ---- @@ -974,19 +949,7 @@ Add an `UndertowBuilderCustomizer` to the `UndertowServletWebServerFactory` and [source,java,indent=0,subs="verbatim,quotes,attributes"] ---- - @Bean - public UndertowServletWebServerFactory servletWebServerFactory() { - UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory(); - factory.addBuilderCustomizers(new UndertowBuilderCustomizer() { - - @Override - public void customize(Builder builder) { - builder.addHttpListener(8080, "0.0.0.0"); - } - - }); - return factory; - } +include::{code-examples}/context/embedded/UndertowMultipleListenersExample.java[tag=configuration] ---- @@ -2367,7 +2330,7 @@ You can switch on the valve by adding some entries to `application.properties`, ---- (The presence of either of those properties switches on the valve. -Alternatively, you can add the `RemoteIpValve` by adding a `TomcatServletWebServerFactory` bean.) +Alternatively, you can add the `RemoteIpValve` by customizing the `TomcatServletWebServerFactory` using a `WebServerFactoryCustomizer` bean.) To configure Spring Security to require a secure channel for all (or some) requests, consider adding your own `WebSecurityConfigurerAdapter` that adds the following `HttpSecurity` configuration: diff --git a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc index 1f7b93d8ac..3dbdd7e443 100644 --- a/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/docs/asciidoc/spring-boot-features.adoc @@ -3115,30 +3115,26 @@ The following example shows programmatically setting the port: } ---- -NOTE: `TomcatServletWebServerFactory`, `JettyServletWebServerFactory` and `UndertowServletWebServerFactory` are dedicated variants of `ConfigurableServletWebServerFactory` that have additional customization setter methods for Tomcat, Jetty and Undertow respectively. +`TomcatServletWebServerFactory`, `JettyServletWebServerFactory` and `UndertowServletWebServerFactory` are dedicated variants of `ConfigurableServletWebServerFactory` that have additional customization setter methods for Tomcat, Jetty and Undertow respectively. +The following example shows how to customize `TomcatServletWebServerFactory` that provides access to Tomcat-specific configuration options: + +[source,java,indent=0,subs="verbatim,quotes,attributes"] +---- +include::{code-examples}/context/embedded/TomcatServerCustomizerExample.java[tag=configuration] +---- [[boot-features-customizing-configurableservletwebserverfactory-directly]] ===== Customizing ConfigurableServletWebServerFactory Directly -If the preceding customization techniques are too limited, you can register the `TomcatServletWebServerFactory`, `JettyServletWebServerFactory`, or `UndertowServletWebServerFactory` bean yourself. - -[source,java,indent=0] ----- - @Bean - public ConfigurableServletWebServerFactory webServerFactory() { - TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); - factory.setPort(9000); - factory.setSessionTimeout(10, TimeUnit.MINUTES); - factory.addErrorPages(new ErrorPage(HttpStatus.NOT_FOUND, "/notfound.html")); - return factory; - } ----- +For more advanced use cases that require you to extend from `ServletWebServerFactory`, you can expose a bean of such type yourself. Setters are provided for many configuration options. Several protected method "`hooks`" are also provided should you need to do something more exotic. See the {spring-boot-module-api}/web/servlet/server/ConfigurableServletWebServerFactory.html[source code documentation] for details. +NOTE: Auto-configured customizers are still applied on your custom factory, so use that option carefully. + [[boot-features-jsp-limitations]] diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/context/embedded/TomcatMultipleConnectorsExample.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/context/embedded/TomcatMultipleConnectorsExample.java new file mode 100644 index 0000000000..b584448ed5 --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/context/embedded/TomcatMultipleConnectorsExample.java @@ -0,0 +1,68 @@ +/* + * Copyright 2012-2021 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 + * + * https://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.docs.context.embedded; + +import java.io.IOException; +import java.net.URL; + +import org.apache.catalina.connector.Connector; +import org.apache.coyote.http11.Http11NioProtocol; + +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.ResourceUtils; + +/** + * Example configuration for configuring Tomcat with an additional {@link Connector}. + * + * @author Stephane Nicoll + */ +@Configuration(proxyBeanMethods = false) +public class TomcatMultipleConnectorsExample { + + // tag::configuration[] + @Bean + public WebServerFactoryCustomizer sslConnectorCustomizer() { + return (tomcat) -> tomcat.addAdditionalTomcatConnectors(createSslConnector()); + } + + private Connector createSslConnector() { + Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol"); + Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler(); + try { + URL keystore = ResourceUtils.getURL("keystore"); + URL truststore = ResourceUtils.getURL("truststore"); + connector.setScheme("https"); + connector.setSecure(true); + connector.setPort(8443); + protocol.setSSLEnabled(true); + protocol.setKeystoreFile(keystore.toString()); + protocol.setKeystorePass("changeit"); + protocol.setTruststoreFile(truststore.toString()); + protocol.setTruststorePass("changeit"); + protocol.setKeyAlias("apitester"); + return connector; + } + catch (IOException ex) { + throw new IllegalStateException("Fail to create ssl connector", ex); + } + } + // end::configuration[] + +} diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/context/embedded/TomcatServerCustomizerExample.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/context/embedded/TomcatServerCustomizerExample.java new file mode 100644 index 0000000000..6e17bb63ac --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/context/embedded/TomcatServerCustomizerExample.java @@ -0,0 +1,41 @@ +/* + * Copyright 2012-2021 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 + * + * https://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.docs.context.embedded; + +import java.time.Duration; + +import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.stereotype.Component; + +/** + * Example of a {@link WebServerFactoryCustomizer} that uses a more narrowed server type. + * + * @author Stephane Nicoll + */ +// tag::configuration[] +@Component +public class TomcatServerCustomizerExample implements WebServerFactoryCustomizer { + + @Override + public void customize(TomcatServletWebServerFactory server) { + server.addConnectorCustomizers( + (tomcatConnector) -> tomcatConnector.setAsyncTimeout(Duration.ofSeconds(20).toMillis())); + } + +} +// end::configuration[] diff --git a/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/context/embedded/UndertowMultipleListenersExample.java b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/context/embedded/UndertowMultipleListenersExample.java new file mode 100644 index 0000000000..bad053f8a9 --- /dev/null +++ b/spring-boot-project/spring-boot-docs/src/main/java/org/springframework/boot/docs/context/embedded/UndertowMultipleListenersExample.java @@ -0,0 +1,51 @@ +/* + * Copyright 2012-2021 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 + * + * https://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.docs.context.embedded; + +import io.undertow.Undertow.Builder; + +import org.springframework.boot.web.embedded.undertow.UndertowBuilderCustomizer; +import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; +import org.springframework.boot.web.server.WebServerFactoryCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * Example configuration for configuring Undertow with an additional listener. + * + * @author Stephane Nicoll + */ +@Configuration(proxyBeanMethods = false) +public class UndertowMultipleListenersExample { + + // tag::configuration[] + @Bean + public WebServerFactoryCustomizer undertowListenerCustomizer() { + return (factory) -> { + factory.addBuilderCustomizers(new UndertowBuilderCustomizer() { + + @Override + public void customize(Builder builder) { + builder.addHttpListener(8080, "0.0.0.0"); + } + + }); + }; + } + // end::configuration[] + +}