diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java index 45e86d0f68..4c1acd0954 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/ServerProperties.java @@ -609,6 +609,11 @@ public class ServerProperties { */ private List additionalTldSkipPatterns = new ArrayList<>(); + /** + * Static resource configuration. + */ + private final Resource resource = new Resource(); + public int getMaxThreads() { return this.maxThreads; } @@ -741,6 +746,10 @@ public class ServerProperties { this.additionalTldSkipPatterns = additionalTldSkipPatterns; } + public Resource getResource() { + return this.resource; + } + /** * Tomcat access log properties. */ @@ -880,6 +889,26 @@ public class ServerProperties { } + /** + * Tomcat static resource properties. + */ + public static class Resource { + + /** + * Time-to-live in milliseconds of the static resource cache. + */ + private Long cacheTtl; + + public Long getCacheTtl() { + return this.cacheTtl; + } + + public void setCacheTtl(Long cacheTtl) { + this.cacheTtl = cacheTtl; + } + + } + } /** diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizer.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizer.java index 31e711a88a..c557786755 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizer.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizer.java @@ -24,6 +24,7 @@ import javax.servlet.ServletException; import javax.servlet.SessionCookieConfig; import io.undertow.UndertowOptions; +import org.apache.catalina.Lifecycle; import org.apache.catalina.valves.AccessLogValve; import org.apache.catalina.valves.RemoteIpValve; import org.apache.coyote.AbstractProtocol; @@ -41,6 +42,7 @@ import org.eclipse.jetty.server.handler.HandlerWrapper; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.autoconfigure.web.ServerProperties.Session; +import org.springframework.boot.autoconfigure.web.ServerProperties.Tomcat.Resource; import org.springframework.boot.cloud.CloudPlatform; import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer; import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory; @@ -259,6 +261,7 @@ public class DefaultServletWebServerFactoryCustomizer factory.getTldSkipPatterns() .addAll(tomcatProperties.getAdditionalTldSkipPatterns()); } + customizeStaticResources(serverProperties.getTomcat().getResource(), factory); } private static void customizeAcceptCount(TomcatServletWebServerFactory factory, @@ -384,6 +387,20 @@ public class DefaultServletWebServerFactoryCustomizer .setMapperContextRootRedirectEnabled(redirectContextRoot)); } + private static void customizeStaticResources(Resource resource, + TomcatServletWebServerFactory factory) { + if (resource.getCacheTtl() == null) { + return; + } + factory.addContextCustomizers((context) -> { + context.addLifecycleListener((event) -> { + if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) { + context.getResources().setCacheTtl(resource.getCacheTtl()); + } + }); + }); + } + } private static class UndertowCustomizer { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizerTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizerTests.java index b4372cc5e8..d7e8ca1bd0 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizerTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/servlet/DefaultServletWebServerFactoryCustomizerTests.java @@ -31,6 +31,7 @@ import javax.servlet.SessionTrackingMode; import org.apache.catalina.Context; import org.apache.catalina.Valve; +import org.apache.catalina.startup.Tomcat; import org.apache.catalina.valves.AccessLogValve; import org.apache.catalina.valves.RemoteIpValve; import org.apache.coyote.AbstractProtocol; @@ -602,6 +603,25 @@ public class DefaultServletWebServerFactoryCustomizerTests { verify(factory, never()).setAccessLogEnabled(anyBoolean()); } + @Test + public void customTomcatStaticResourceCacheTtl() { + Map map = new HashMap<>(); + map.put("server.tomcat.resource.cache-ttl", "10000"); + bindProperties(map); + TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(0); + this.customizer.customize(factory); + TomcatWebServer embeddedFactory = (TomcatWebServer) factory.getWebServer(); + embeddedFactory.start(); + try { + Tomcat tomcat = embeddedFactory.getTomcat(); + Context context = (Context) tomcat.getHost().findChildren()[0]; + assertThat(context.getResources().getCacheTtl()).isEqualTo(10000L); + } + finally { + embeddedFactory.stop(); + } + } + private void triggerInitializers(ConfigurableServletWebServerFactory factory, ServletContext servletContext) throws ServletException { verify(factory, atLeastOnce()).addInitializers(this.initializersCaptor.capture()); diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 16c49157a3..1453ea95db 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -243,6 +243,7 @@ content into your application; rather pick only the properties that you need. server.tomcat.protocol-header-https-value=https # Value of the protocol header that indicates that the incoming request uses SSL. server.tomcat.redirect-context-root= # Whether requests to the context root should be redirected by appending a / to the path. server.tomcat.remote-ip-header= # Name of the http header from which the remote ip is extracted. For instance `X-FORWARDED-FOR` + spring.tomcat.resource.cache-ttl=5000 # Time-to-live in milliseconds of the static resource cache. server.tomcat.uri-encoding=UTF-8 # Character encoding to use to decode the URI. server.undertow.accesslog.dir= # Undertow access log directory. server.undertow.accesslog.enabled=false # Enable access log.