diff --git a/spring-boot-project/spring-boot-dependencies/pom.xml b/spring-boot-project/spring-boot-dependencies/pom.xml
index 710ea9be44..99f7db0b33 100644
--- a/spring-boot-project/spring-boot-dependencies/pom.xml
+++ b/spring-boot-project/spring-boot-dependencies/pom.xml
@@ -202,7 +202,7 @@
2.3.0
2.0.1
3.0.3.RELEASE
- 9.0.16
+ 9.0.17
4.0.9
2.0.19.Final
3325375
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 3fef32d1ca..fe5842dccc 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
@@ -3207,13 +3207,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 9f2903286f..b3b7d3c324 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
@@ -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 ce080c8161..b80312128a 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,10 +27,17 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
+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 +66,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.assertj.core.api.Assertions.assertThatExceptionOfType;
@@ -457,6 +476,48 @@ public class TomcatServletWebServerFactoryTests
assertThat(context.getClearReferencesThreadLocals()).isFalse();
}
+ @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();