The Little Problems That Cause Big Headaches
The Java Foundation Classes (JFC) provide a mechanism for applications to create and destroy temporary files. Using the
class, an application can create a temporary file and mark it for deletion quite simply, as seen in the following code:
File tempFile = File.createTempFile("myApp", ".tmp");
deleteOnExit() is designed to allow an application to clean up files when the Java Virtual Machine (JVM) exits. In a perfect world, this would handle all cases. However, I didn't see this behavior in my application. Although the file was being marked for deletion, it definitely wasn't being deleted. To complicate matters, I soon discovered that the file was being deleted when the application was run on Linux. It was time to do some research.
After some detective work, I finally had an answer to the problem. Two open Java bugs were to blame. First was bug 4171239: "java.io.File.deleteOnExit does not work on open files (win32)." Unfortunately, a combination of the JVM design and the Win32 operating system does not allow the files to be deleted if they are open at exit. So all I would have to do is ensure I close all the files before exit, right? Wrong. This leads to bug 4950148: "
ClassLoader should have an explicit disposal mechanism." It turns out that an URLClassLoader, which is used to load classes from a JAR file, keeps the stream to the JAR open during the lifetime of the JVM. I was stuck. A Win32 JVM can't delete an open file, and an
URLClassLoader always keeps the file open. I had to think outside the box.
A simple solution would be to have my application always create temporary files with the same prefix, then delete all files with that prefix at some other time. The problem is that solution would not allow multiple instances of the application to run, since there would be a race condition between one instance creating temporaries and another deleting them. I also could unwittingly choose the same prefix as a different application, accidentally deleting its temp files. I needed something more robust and reusable, so I could make sure that all my projects cleaned up like they should.
Design: Building a Better File Trap
What I needed was a temporary file manager: a class that could create temporary files for me, and ensure that they were destroyed at some point in the future. Due to the bugs in the JVM, I was willing to accept that the temporary files may not get cleaned up until the next run of the application. Even with this relaxation in the requirements, I could still guarantee that the user would have only one set of temporary files on their system between runs.
In most cases, the best solution is the simplest one. Since the JVM could already delete files as long as the file was not open, I wanted to leverage this functionality as much as possible. I knew that when a JVM first starts up, it doesn't have any temp files open. Therefore, it can perform a quick cleanup step to delete any files that may be lying around from the last run. However, when creating a temporary file, the file must be marked as locked, so no other application using the same temporary file management scheme will try to clean up active files. To reduce the number of lock files, I decided to put all of the temp files for a given instance of the application into a single directory and lock the directory.
Now that I had a solution designed, it was time to code it up.