spring-boot
spring-boot copied to clipboard
Delete Tomcat's temporary directories when the context is closed
Using Spring Boot 1.5.1.
The directory /tmp/tomcat-* are not deleted when stopping a Spring Boot application. The directory is created using (in org.springframework.boot.context.embedded.AbstractEmbeddedServletContainerFactory) :
File tempDir = File.createTempFile(prefix + ".", "." + getPort());
tempDir.delete();
tempDir.mkdir();
tempDir.deleteOnExit();
return tempDir;
I think that deleteOnExit() method doesn't work if the directory is not empty. I made a little standard Java test:
public static void main(String[] args) throws IOException, InterruptedException {
File tempDir = File.createTempFile("prefix", "suffix");
tempDir.delete();
tempDir.mkdir();
tempDir.deleteOnExit();
try (FileOutputStream os = new FileOutputStream(new File(tempDir, "noempty.txt"))) {
os.write("This a not an empty file".getBytes());
}
Thread.sleep(Long.MAX_VALUE);
}
Then I send a linux signal kill -15 <pid> but the folder 'prefix*' is still here.
I think that deleteOnExit() method doesn't work if the directory is not empty
That's correct. We're simply making the best effort we can to clean up the directory when the JVM exits. Sometimes the directory won't be used so we can at least clean up the empty directory on exit. Beyond that, you should configure your OS's standard mechanism for cleaning up tmp when appropriate.
As mentionned on #5009, we cannot always use /tmp as a tomcat's temporary directory.
A JVM shutdown hook could resolve this issue
import org.springframework.util.FileSystemUtils;
...
Runtime.getRuntime().addShutdownHook(new Thread(() -> FileSystemUtils.deleteRecursively(tempDir)));
I can make a Pull Request, if you want.
As far as I know, there's nothing stopping you from using /tmp, you just need to configure the OS to only clean it up when appropriate.
A JVM shutdown hook could resolve this issue
Thanks for the suggestion, but if we always cleared out the whole directory on exit, we'd then get complaints from users who wanted it to remain. For example, users of JSPs may want the directory to remain so that unchanged JSPs don't need to be recompiled.
It would be really nice to be able to delete such folder if we wanted to: I find this very annoying as I sometimes restart my microservices very frequently, and I'd be perfectly fine with a shutdown hook written in my server code. Is there any way to know where such folder is created, or to provide spring boot with a hint where to create it? Please note that I cannot simply change the value of java.io.tmpdir to my liking, as other libraries are using it as well.
@bbossola server.tomcat.basedir controls the location. We only create a temp dir if that property isn't specified.
Thanks, that solves my issue :)
Tomcat. file is always generated in the TMP directory after the project is started. How to control how tomcat. file will not be generated in the TMP directory?
@jamesSkyDream That question has been answered by @snicoll two comments above yours: https://github.com/spring-projects/spring-boot/issues/9983#issuecomment-355227395.
@wilkinsona I have found that the shutdown hook created by
https://github.com/spring-projects/spring-boot/blob/b164b16c21734a8581b39553594f102a75bb9738/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/AbstractConfigurableWebServerFactory.java#L176
is never effective since there are other directories that get created underneath that top level directory. I am always left with the following directory tree
/tmp
\- tomcat.123456789.8080
\- work
\- Tomcat
\- localhost
\- ROOT
The directory tree is empty, but will not be cleaned up since the top level tomcat.* directory contains an empty directory. Perhaps a better way to do this would be to create a shutdown hook that would remove the entire directory tree if it does not contain any files.
That being said, I am not sure I understand what you mean by
Thanks for the suggestion, but if we always cleared out the whole directory on exit, we'd then get complaints from users who wanted it to remain. For example, users of JSPs may want the directory to remain so that unchanged JSPs don't need to be recompiled.
The directory created using File.createTempFile()
https://github.com/spring-projects/spring-boot/blob/b164b16c21734a8581b39553594f102a75bb9738/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/server/AbstractConfigurableWebServerFactory.java#L173
is guaranteed to be unique and will never be reused after the process exits so it should be safe to force deletion even if the directory does contain files.
is never effective since there are other directories that get created underneath that top level directory
This has already been discussed above
is guaranteed to be unique and will never be reused after the process exits
That's a good point. We could use recursive deletion only in the case where a temporary directory is being used rather than a user-configured directory. That said, I'm not convinced that the extra complexity is worthwhile. The OS already contains a mechanism for cleaning things up.
We'll have another look at this to see how tricky it might be.
When just deleting an empty directory the ordering of the deletion doesn't matter. If we start deleting the entire directory hierarchy including files that Tomcat has written, the ordering becomes important. That means that we can't use a shutdown hook as the hook that closes the context (and stops Tomcat) and the hook that deletes the directory hierarchy may run in any order or in parallel.
To implement this, I think we'd need to add something to the WebServer implementation that triggers the deletion of the temporary directory hierarchy as part of its stop processing.
Ergh. I think we should push this back then. It's not too urgent for 2.2.x
Thanks, we are facing the same issue where many of these empty folders are created for each test run and not deleted
This needs to resolve by an application rather than putting a daily cron to blindly delete everything from /tmp that is related to tomcat. What if you delete the folder that is currently being used by tomcat? will the app function without any issue? why not come up with a clean solution that the spring-boot app can handle unused folder deletion from /tmp on its own?
This needs to resolve by an application
We agree. That's why this issue is open. It's tracking making this improvement to Spring Boot.
rather than putting a daily cron to blindly delete everything from /tmp that is related to tomcat
Irrespective of this issue, /tmp clean up should not do that. Instead, it should only delete files that have not been accessed for a certain amount of time. This is what tmpwatch does, for example.
Thanks, @wilkinsona for your reply. Do we have any ticket open for this issue for the Springboot project other than this github issue #9983
Thanks Dev
No. One open issue is sufficient.
ok, thanks @wilkinsona any idea on a timeline, when the issue is going to be fixed?
We don’t have any firm plans at the moment and have quite a lot of other, higher priority, things taking our time at the moment. If you or someone else in the community would like to speed things up, a pull request along the lines of what I described above would be welcome. A note of caution, though: from what I remember of when we last looked at this in detail the changes are not straightforward.
Ok, thanks @wilkinsona
We have 47k of these temp dirs just hangin' out in just one of our environments. I really don't like the idea of writing a cron job to clean these up. Any updates on this?
Thank you -- just checking in since that was over a year ago. (Not trying to be pushy.)
Implement the org.springframework.context.SmartLifecycle interface. In the start() method, use the following code to get the temporary folder path, then remove it in the stop() method:
import org.apache.catalina.Container;
import org.apache.catalina.core.StandardContext;
import org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;
import org.springframework.boot.web.embedded.tomcat.TomcatWebServer;
import org.springframework.context.ApplicationContext;
final ApplicationContext applicationContext = ...;
final ServletWebServerApplicationContext serverContext = (ServletWebServerApplicationContext)applicationContext;
final TomcatWebServer tomcatWebServer = (TomcatWebServer)serverContext.getWebServer();
final Container containers[] = tomcatWebServer.getTomcat().getHost().findChildren();
if (containers==null || containers.length<=0) { return; } // end if
final StandardContext context = (StandardContext)containers[0];
final String relWorkDir = context.getWorkDir();
final String absWorkDir = context.getWorkPath();
final int p = absWorkDir.lastIndexOf(relWorkDir);
if (p>=0) {
final String tomcatBasedir = absWorkDir.substring(0, p); // <-- here you go
}
Hello,
I made a PR for this subjet that will resolve the problem without breaking the old behaviour.
By default, the system will dontinue to delete the directory only if it is empty. But if you override the property server.tmp-deletion-strategy, you will be able to delete everything.
Let me know if the PR is legit, or if It needs some updates
if we use tomcat LifeCycle, we could do something like
one other approche I had imagine was to have in AbstractConfigurableWebServerFactory:
protected final File createTempDir(String prefix) {
try {
File tempDir = Files.createTempDirectory(prefix + "." + getPort() + ".").toFile();
runInShutdown(() -> this.deletionStrategy.deleteOnShutdown(tempDir));
return tempDir;
}
catch (IOException ex) {
throw new WebServerException(
"Unable to create tempDir. java.io.tmpdir is set to " + System.getProperty("java.io.tmpdir"), ex);
}
}
protected void runInShutdown(Runnable shutdownHook) {
Runtime.getRuntime().addShutdownHook(new Thread(shutdownHook));
}
and in TomcatReactiveWebServerFactory :
@Override
protected void runInShutdown(Runnable shutdownHook) {
serverLifecycleListeners.add(
event -> {
if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT) {
shutdownHook.run();
}
}
);
}
however this solution is great for tomcat, maybe does not awnser the global probleme with Jetty
So just my 5 cents why this should maybe get fixed 8 years after initially being reported...
When running on Windows this might cause your login or other applications to become slower:
- Windows does not automatically/properly clean up the Temp directory (see https://superuser.com/a/296827, https://superuser.com/q/318497 and https://superuser.com/a/744622)
cleanmgr.exedoes not always delete all temp files, it also has to be executed manually- Windows 10+ has the
Storage Senseoption, however it's not active by default and if it's active it will only run by default when free disk space is low
- Windows login might be affected by these temp files: https://borncity.com/win/2023/03/23/windows-login-to-client-in-a-domain-extremely-slow-because-of-temp-files/
Over the past years a ton of these tomcat directories accumulated on my dev machine, which caused significant slow downs. After deleting all temp files and rebooting:
- The login now takes ~5s instead of up to 1min
- Outlook now starts within 30s instead of 2-3mins