mirror of
https://github.com/spring-projects/spring-boot.git
synced 2026-09-24 10:19:03 +00:00
Delete web server's temporary directories when the context is closed
Closes gh-9983
This commit is contained in:
+2
-2
@@ -43,8 +43,8 @@ public class MyCloudFoundryConfiguration {
|
||||
return new TomcatServletWebServerFactory() {
|
||||
|
||||
@Override
|
||||
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
|
||||
super.prepareContext(host, initializers);
|
||||
protected void prepareContext(Host host, ServletContextInitializer[] initializers, TempDirs tempDirs) {
|
||||
super.prepareContext(host, initializers, tempDirs);
|
||||
StandardContext child = new StandardContext();
|
||||
child.addLifecycleListener(new Tomcat.FixContextListener());
|
||||
child.setPath("/cloudfoundryapplication");
|
||||
|
||||
+2
-2
@@ -41,8 +41,8 @@ class MyCloudFoundryConfiguration {
|
||||
fun servletWebServerFactory(): TomcatServletWebServerFactory {
|
||||
return object : TomcatServletWebServerFactory() {
|
||||
|
||||
override fun prepareContext(host: Host, initializers: Array<ServletContextInitializer>) {
|
||||
super.prepareContext(host, initializers)
|
||||
override fun prepareContext(host: Host, initializers: Array<ServletContextInitializer>, tempDirs: TempDirs) {
|
||||
super.prepareContext(host, initializers, tempDirs)
|
||||
val child = StandardContext()
|
||||
child.addLifecycleListener(FixContextListener())
|
||||
child.path = "/cloudfoundryapplication"
|
||||
|
||||
+37
-4
@@ -63,6 +63,7 @@ import org.eclipse.jetty.session.DefaultSessionCache;
|
||||
import org.eclipse.jetty.session.FileSessionDataStore;
|
||||
import org.eclipse.jetty.session.SessionConfig;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.component.LifeCycle;
|
||||
import org.eclipse.jetty.util.resource.Resource;
|
||||
import org.eclipse.jetty.util.resource.ResourceFactory;
|
||||
import org.eclipse.jetty.util.resource.URLResourceFactory;
|
||||
@@ -161,7 +162,9 @@ public class JettyServletWebServerFactory extends JettyWebServerFactory
|
||||
InetSocketAddress address = new InetSocketAddress(getAddress(), port);
|
||||
Server server = createServer(address);
|
||||
context.setServer(server);
|
||||
configureWebAppContext(context, initializers);
|
||||
TempDirs tempDirs = new TempDirs(port);
|
||||
server.addEventListener(new CleanTempDirsListener(tempDirs));
|
||||
configureWebAppContext(context, tempDirs, initializers);
|
||||
server.setHandler(addHandlerWrappers(context));
|
||||
logger.info("Server initialized with port: " + port);
|
||||
if (this.getMaxConnections() > -1) {
|
||||
@@ -214,8 +217,23 @@ public class JettyServletWebServerFactory extends JettyWebServerFactory
|
||||
* Configure the given Jetty {@link WebAppContext} for use.
|
||||
* @param context the context to configure
|
||||
* @param initializers the set of initializers to apply
|
||||
* @deprecated since 4.1.0 for removal in 4.3.0 in favor of
|
||||
* {@link #configureWebAppContext(WebAppContext, TempDirs, ServletContextInitializer...)}
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "4.1.0")
|
||||
protected final void configureWebAppContext(WebAppContext context, ServletContextInitializer... initializers) {
|
||||
configureWebAppContext(context, new TempDirs(getPort()), initializers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the given Jetty {@link WebAppContext} for use.
|
||||
* @param context the context to configure
|
||||
* @param tempDirs to manage temporary directories
|
||||
* @param initializers the set of initializers to apply
|
||||
* @since 4.1.0
|
||||
*/
|
||||
protected final void configureWebAppContext(WebAppContext context, TempDirs tempDirs,
|
||||
ServletContextInitializer... initializers) {
|
||||
Assert.notNull(context, "'context' must not be null");
|
||||
context.clearAliasChecks();
|
||||
if (this.resourceLoader != null) {
|
||||
@@ -224,7 +242,7 @@ public class JettyServletWebServerFactory extends JettyWebServerFactory
|
||||
String contextPath = getSettings().getContextPath().toString();
|
||||
context.setContextPath(StringUtils.hasLength(contextPath) ? contextPath : "/");
|
||||
context.setDisplayName(getSettings().getDisplayName());
|
||||
configureDocumentRoot(context);
|
||||
configureDocumentRoot(context, tempDirs);
|
||||
if (getSettings().isRegisterDefaultServlet()) {
|
||||
addDefaultServlet(context);
|
||||
}
|
||||
@@ -289,11 +307,11 @@ public class JettyServletWebServerFactory extends JettyWebServerFactory
|
||||
}
|
||||
}
|
||||
|
||||
private void configureDocumentRoot(WebAppContext handler) {
|
||||
private void configureDocumentRoot(WebAppContext handler, TempDirs tempDirs) {
|
||||
DocumentRoot documentRoot = new DocumentRoot(logger);
|
||||
documentRoot.setDirectory(this.settings.getDocumentRoot());
|
||||
File root = documentRoot.getValidDirectory();
|
||||
File docBase = (root != null) ? root : createTempDir("jetty-docbase");
|
||||
File docBase = (root != null) ? root : tempDirs.createTempDir("jetty-docbase").toFile();
|
||||
try {
|
||||
ResourceFactory resourceFactory = handler.getResourceFactory();
|
||||
List<Resource> resources = new ArrayList<>();
|
||||
@@ -616,4 +634,19 @@ public class JettyServletWebServerFactory extends JettyWebServerFactory
|
||||
|
||||
}
|
||||
|
||||
private static class CleanTempDirsListener implements LifeCycle.Listener {
|
||||
|
||||
private final TempDirs tempDirs;
|
||||
|
||||
CleanTempDirsListener(TempDirs tempDirs) {
|
||||
this.tempDirs = tempDirs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lifeCycleStopped(LifeCycle event) {
|
||||
this.tempDirs.cleanup();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+38
-1
@@ -29,6 +29,8 @@ import java.util.Set;
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.Engine;
|
||||
import org.apache.catalina.Executor;
|
||||
import org.apache.catalina.Lifecycle;
|
||||
import org.apache.catalina.LifecycleEvent;
|
||||
import org.apache.catalina.LifecycleListener;
|
||||
import org.apache.catalina.Valve;
|
||||
import org.apache.catalina.connector.Connector;
|
||||
@@ -367,16 +369,34 @@ public class TomcatWebServerFactory extends AbstractConfigurableWebServerFactory
|
||||
this.useApr = useApr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link Tomcat} web server.
|
||||
* @return the web server.
|
||||
* @deprecated since 4.1.0 for removal in 4.3.0 in favor of
|
||||
* {@link #createTomcat(TempDirs)}
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "4.1.0")
|
||||
protected Tomcat createTomcat() {
|
||||
return createTomcat(new TempDirs(getPort()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@link Tomcat} web server.
|
||||
* @param tempDirs to manage temporary directories
|
||||
* @return the web server
|
||||
* @since 4.1.0
|
||||
*/
|
||||
protected Tomcat createTomcat(TempDirs tempDirs) {
|
||||
if (this.isDisableMBeanRegistry()) {
|
||||
Registry.disableRegistry();
|
||||
}
|
||||
Tomcat tomcat = new Tomcat();
|
||||
File baseDir = (getBaseDirectory() != null) ? getBaseDirectory() : createTempDir("tomcat");
|
||||
File baseDir = (getBaseDirectory() != null) ? getBaseDirectory() : tempDirs.createTempDir("tomcat").toFile();
|
||||
tomcat.setBaseDir(baseDir.getAbsolutePath());
|
||||
for (LifecycleListener listener : getDefaultServerLifecycleListeners()) {
|
||||
tomcat.getServer().addLifecycleListener(listener);
|
||||
}
|
||||
tomcat.getServer().addLifecycleListener(new CleanTempDirsListener(tempDirs));
|
||||
Connector connector = new Connector(getProtocol());
|
||||
connector.setThrowOnFailure(true);
|
||||
tomcat.getService().addConnector(connector);
|
||||
@@ -463,4 +483,21 @@ public class TomcatWebServerFactory extends AbstractConfigurableWebServerFactory
|
||||
}
|
||||
}
|
||||
|
||||
private static class CleanTempDirsListener implements LifecycleListener {
|
||||
|
||||
private final TempDirs tempDirs;
|
||||
|
||||
CleanTempDirsListener(TempDirs tempDirs) {
|
||||
this.tempDirs = tempDirs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lifecycleEvent(LifecycleEvent event) {
|
||||
if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
|
||||
this.tempDirs.cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+25
-5
@@ -16,7 +16,7 @@
|
||||
|
||||
package org.springframework.boot.tomcat.reactive;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.apache.catalina.Context;
|
||||
import org.apache.catalina.Host;
|
||||
@@ -68,20 +68,40 @@ public class TomcatReactiveWebServerFactory extends TomcatWebServerFactory
|
||||
|
||||
@Override
|
||||
public WebServer getWebServer(HttpHandler httpHandler) {
|
||||
Tomcat tomcat = createTomcat();
|
||||
TempDirs tempDirs = new TempDirs(getPort());
|
||||
Tomcat tomcat = createTomcat(tempDirs);
|
||||
TomcatHttpHandlerAdapter servlet = new TomcatHttpHandlerAdapter(httpHandler);
|
||||
prepareContext(tomcat.getHost(), servlet);
|
||||
prepareContext(tomcat.getHost(), servlet, tempDirs);
|
||||
return getTomcatWebServer(tomcat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the context.
|
||||
* @param host the host
|
||||
* @param servlet the servlet adapter
|
||||
* @deprecated since 4.1.0 for removal in 4.3.0 in favor of
|
||||
* {@link #prepareContext(Host, TomcatHttpHandlerAdapter, TempDirs)}
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "4.1.0")
|
||||
protected void prepareContext(Host host, TomcatHttpHandlerAdapter servlet) {
|
||||
File docBase = createTempDir("tomcat-docbase");
|
||||
prepareContext(host, servlet, new TempDirs(getPort()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the context.
|
||||
* @param host the host
|
||||
* @param servlet the servlet adapter
|
||||
* @param tempDirs to manage temporary directories
|
||||
* @since 4.1.0
|
||||
*/
|
||||
protected void prepareContext(Host host, TomcatHttpHandlerAdapter servlet, TempDirs tempDirs) {
|
||||
Path docBase = tempDirs.createTempDir("tomcat-docbase");
|
||||
TomcatEmbeddedContext context = new TomcatEmbeddedContext();
|
||||
WebResourceRoot resourceRoot = new StandardRoot(context);
|
||||
ignoringNoSuchMethodError(() -> resourceRoot.setReadOnly(true));
|
||||
context.setResources(resourceRoot);
|
||||
context.setPath("");
|
||||
context.setDocBase(docBase.getAbsolutePath());
|
||||
context.setDocBase(docBase.toAbsolutePath().toString());
|
||||
context.addLifecycleListener(new Tomcat.FixContextListener());
|
||||
ClassLoader parentClassLoader = ClassUtils.getDefaultClassLoader();
|
||||
context.setParentClassLoader(parentClassLoader);
|
||||
|
||||
+24
-3
@@ -161,12 +161,32 @@ public class TomcatServletWebServerFactory extends TomcatWebServerFactory
|
||||
|
||||
@Override
|
||||
public WebServer getWebServer(ServletContextInitializer... initializers) {
|
||||
Tomcat tomcat = createTomcat();
|
||||
prepareContext(tomcat.getHost(), initializers);
|
||||
TempDirs tempDirs = new TempDirs(getPort());
|
||||
Tomcat tomcat = createTomcat(tempDirs);
|
||||
prepareContext(tomcat.getHost(), initializers, tempDirs);
|
||||
return getTomcatWebServer(tomcat);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the context.
|
||||
* @param host the host
|
||||
* @param initializers the servlet context initializers
|
||||
* @deprecated since 4.1.0 for removal in 4.3.0 in favor of
|
||||
* {@link #prepareContext(Host, ServletContextInitializer[], TempDirs)}
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "4.1.0")
|
||||
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
|
||||
prepareContext(host, initializers, new TempDirs(getPort()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the context.
|
||||
* @param host the host
|
||||
* @param initializers the servlet context initializers
|
||||
* @param tempDirs to manage temporary directories
|
||||
* @since 4.1.0
|
||||
*/
|
||||
protected void prepareContext(Host host, ServletContextInitializer[] initializers, TempDirs tempDirs) {
|
||||
DocumentRoot documentRoot = new DocumentRoot(logger);
|
||||
documentRoot.setDirectory(this.settings.getDocumentRoot());
|
||||
File documentRootFile = documentRoot.getValidDirectory();
|
||||
@@ -179,7 +199,8 @@ public class TomcatServletWebServerFactory extends TomcatWebServerFactory
|
||||
context.setName(contextPath);
|
||||
context.setDisplayName(this.settings.getDisplayName());
|
||||
context.setPath(contextPath);
|
||||
File docBase = (documentRootFile != null) ? documentRootFile : createTempDir("tomcat-docbase");
|
||||
File docBase = (documentRootFile != null) ? documentRootFile
|
||||
: tempDirs.createTempDir("tomcat-docbase").toFile();
|
||||
context.setDocBase(docBase.getAbsolutePath());
|
||||
context.addLifecycleListener(new FixContextListener());
|
||||
ClassLoader parentClassLoader = (this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
|
||||
|
||||
+5
-3
@@ -104,7 +104,9 @@ class TomcatReactiveWebServerFactoryTests extends AbstractReactiveWebServerFacto
|
||||
assertThat(factory.getContextLifecycleListeners()).isEmpty();
|
||||
TomcatWebServer tomcatWebServer = (TomcatWebServer) factory.getWebServer(mock(HttpHandler.class));
|
||||
this.webServer = tomcatWebServer;
|
||||
assertThat(tomcatWebServer.getTomcat().getServer().findLifecycleListeners()).isEmpty();
|
||||
assertThat(tomcatWebServer.getTomcat().getServer().findLifecycleListeners())
|
||||
.extracting((l) -> l.getClass().getSimpleName())
|
||||
.containsExactly("CleanTempDirsListener");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -113,8 +115,8 @@ class TomcatReactiveWebServerFactoryTests extends AbstractReactiveWebServerFacto
|
||||
factory.setUseApr(true);
|
||||
TomcatWebServer tomcatWebServer = (TomcatWebServer) factory.getWebServer(mock(HttpHandler.class));
|
||||
this.webServer = tomcatWebServer;
|
||||
assertThat(tomcatWebServer.getTomcat().getServer().findLifecycleListeners()).singleElement()
|
||||
.isInstanceOf(AprLifecycleListener.class);
|
||||
assertThat(tomcatWebServer.getTomcat().getServer().findLifecycleListeners()).extracting(Object::getClass)
|
||||
.contains(AprLifecycleListener.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+5
-3
@@ -160,7 +160,9 @@ class TomcatServletWebServerFactoryTests extends AbstractServletWebServerFactory
|
||||
assertThat(factory.getContextLifecycleListeners()).isEmpty();
|
||||
TomcatWebServer tomcatWebServer = (TomcatWebServer) factory.getWebServer();
|
||||
this.webServer = tomcatWebServer;
|
||||
assertThat(tomcatWebServer.getTomcat().getServer().findLifecycleListeners()).isEmpty();
|
||||
assertThat(tomcatWebServer.getTomcat().getServer().findLifecycleListeners())
|
||||
.extracting((listener) -> listener.getClass().getSimpleName())
|
||||
.containsExactly("CleanTempDirsListener");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -169,8 +171,8 @@ class TomcatServletWebServerFactoryTests extends AbstractServletWebServerFactory
|
||||
factory.setUseApr(true);
|
||||
TomcatWebServer tomcatWebServer = (TomcatWebServer) factory.getWebServer();
|
||||
this.webServer = tomcatWebServer;
|
||||
assertThat(tomcatWebServer.getTomcat().getServer().findLifecycleListeners()).singleElement()
|
||||
.isInstanceOf(AprLifecycleListener.class);
|
||||
assertThat(tomcatWebServer.getTomcat().getServer().findLifecycleListeners()).extracting(Object::getClass)
|
||||
.contains(AprLifecycleListener.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+55
@@ -20,7 +20,9 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -33,6 +35,7 @@ import org.springframework.boot.ssl.SslBundles;
|
||||
import org.springframework.boot.web.error.ErrorPage;
|
||||
import org.springframework.boot.web.server.Ssl.ServerNameSslBundle;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.FileSystemUtils;
|
||||
|
||||
/**
|
||||
* Abstract base class for {@link ConfigurableWebServerFactory} implementations.
|
||||
@@ -215,7 +218,9 @@ public abstract class AbstractConfigurableWebServerFactory implements Configurab
|
||||
* Return the absolute temp dir for given web server.
|
||||
* @param prefix server name
|
||||
* @return the temp dir for given server.
|
||||
* @deprecated since 4.1.0 for removal in 4.3.0 in favor of {@link TempDirs}.
|
||||
*/
|
||||
@Deprecated(since = "4.1.0", forRemoval = true)
|
||||
protected final File createTempDir(String prefix) {
|
||||
try {
|
||||
File tempDir = Files.createTempDirectory(prefix + "." + getPort() + ".").toFile();
|
||||
@@ -228,4 +233,54 @@ public abstract class AbstractConfigurableWebServerFactory implements Configurab
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Manages temporary directories.
|
||||
*
|
||||
* @since 4.1.0
|
||||
*/
|
||||
public static class TempDirs {
|
||||
|
||||
private final Set<Path> dirs = new HashSet<>();
|
||||
|
||||
private final int port;
|
||||
|
||||
public TempDirs(int port) {
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a temporary directory and registers it for {@link #cleanup cleanup}.
|
||||
* @param prefix the directory prefix
|
||||
* @return the path to the temporary directory
|
||||
*/
|
||||
public Path createTempDir(String prefix) {
|
||||
try {
|
||||
Path directory = Files.createTempDirectory(prefix + "." + this.port + ".").toAbsolutePath();
|
||||
this.dirs.add(directory);
|
||||
return directory;
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new WebServerException(
|
||||
"Unable to create tempDir. java.io.tmpdir is set to " + System.getProperty("java.io.tmpdir"),
|
||||
ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all created temporary directories.
|
||||
*/
|
||||
public void cleanup() {
|
||||
for (Path dir : this.dirs) {
|
||||
try {
|
||||
FileSystemUtils.deleteRecursively(dir);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
this.dirs.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2012-present 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.web.server;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.web.server.AbstractConfigurableWebServerFactory.TempDirs;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link AbstractConfigurableWebServerFactory}.
|
||||
*
|
||||
* @author Moritz Halbritter
|
||||
*/
|
||||
class AbstractConfigurableWebServerFactoryTests {
|
||||
|
||||
@Test
|
||||
void shouldCleanUpTempDirs() throws IOException {
|
||||
TempDirs tempDirs = new TempDirs(1234);
|
||||
Path tempDir1 = tempDirs.createTempDir(getClass().getSimpleName());
|
||||
Path tempDir2 = tempDirs.createTempDir(getClass().getSimpleName());
|
||||
assertThat(tempDir1).isNotEqualTo(tempDir2);
|
||||
assertThat(tempDir1).exists();
|
||||
assertThat(tempDir2).exists();
|
||||
Files.writeString(tempDir1.resolve("test.txt"), "test");
|
||||
tempDirs.cleanup();
|
||||
assertThat(tempDir1).doesNotExist();
|
||||
assertThat(tempDir2).doesNotExist();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user