diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 02c63aaaf22..d46177adf73 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -2927,13 +2927,6 @@ https://github.com/undertow-io/undertow[Undertow] servers. Most developers use t appropriate "`Starter`" to obtain a fully configured instance. By default, the embedded server listens for HTTP requests on port `8080`. -WARNING: If you choose to use Tomcat on https://www.centos.org/[CentOS], be aware that, by -default, a temporary directory is used to store compiled JSPs, file uploads, and so on. -This directory may be deleted by `tmpwatch` while your application is running, leading to -failures. To avoid this behavior, you may want to customize your `tmpwatch` configuration -such that `tomcat.*` directories are not deleted or configure `server.tomcat.basedir` such -that embedded Tomcat uses a different location. - [[boot-features-embedded-container-servlets-filters-listeners]] diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java index f92a1e3fb0f..ee01f082572 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2012-2019 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. @@ -200,6 +200,12 @@ public class TomcatServletWebServerFactory extends AbstractServletWebServerFacto resetDefaultLocaleMapping(context); addLocaleMappings(context); context.setUseRelativeRedirects(false); + try { + context.setCreateUploadTargets(true); + } + catch (NoSuchMethodError ex) { + // Tomcat is < 8.5.39. Continue. + } configureTldSkipPatterns(context); WebappLoader loader = new WebappLoader(context.getParentClassLoader()); loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName()); diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java index e7fdcdb8672..9eea6d28215 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/embedded/tomcat/TomcatServletWebServerFactoryTests.java @@ -18,6 +18,7 @@ package org.springframework.boot.web.embedded.tomcat; import java.io.File; import java.io.IOException; +import java.net.URISyntaxException; import java.net.URL; import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; @@ -26,11 +27,17 @@ import java.util.Arrays; import java.util.Locale; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; import javax.naming.InitialContext; import javax.naming.NamingException; +import javax.servlet.MultipartConfigElement; +import javax.servlet.ServletContext; import javax.servlet.ServletException; +import javax.servlet.ServletRegistration.Dynamic; import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; import org.apache.catalina.Container; import org.apache.catalina.Context; @@ -58,9 +65,20 @@ import org.mockito.InOrder; import org.springframework.boot.testsupport.rule.OutputCapture; import org.springframework.boot.web.server.WebServerException; +import org.springframework.boot.web.servlet.ServletContextInitializer; import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactory; import org.springframework.boot.web.servlet.server.AbstractServletWebServerFactoryTests; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.util.FileSystemUtils; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.fail; @@ -443,6 +461,48 @@ public class TomcatServletWebServerFactoryTests this.webServer.start(); } + @Test + public void nonExistentUploadDirectoryIsCreatedUponMultipartUpload() + throws IOException, URISyntaxException { + TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(0); + AtomicReference servletContextReference = new AtomicReference<>(); + factory.addInitializers(new ServletContextInitializer() { + + @Override + public void onStartup(ServletContext servletContext) throws ServletException { + servletContextReference.set(servletContext); + Dynamic servlet = servletContext.addServlet("upload", new HttpServlet() { + + @Override + protected void doPost(HttpServletRequest req, + HttpServletResponse resp) + throws ServletException, IOException { + req.getParts(); + } + + }); + servlet.addMapping("/upload"); + servlet.setMultipartConfig(new MultipartConfigElement((String) null)); + } + + }); + this.webServer = factory.getWebServer(); + this.webServer.start(); + File temp = (File) servletContextReference.get() + .getAttribute(ServletContext.TEMPDIR); + FileSystemUtils.deleteRecursively(temp); + RestTemplate restTemplate = new RestTemplate(); + HttpHeaders headers = new HttpHeaders(); + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("file", new ByteArrayResource(new byte[1024 * 1024])); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + HttpEntity> requestEntity = new HttpEntity<>(body, + headers); + ResponseEntity response = restTemplate + .postForEntity(getLocalUrl("/upload"), requestEntity, String.class); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + } + @Override protected JspServlet getJspServlet() throws ServletException { Tomcat tomcat = ((TomcatWebServer) this.webServer).getTomcat();