Skip to content

Commit daedcb7

Browse files
committed
New file cleaner that keeps track of files pending deletion, and deletes
them only when an associated marker object is reclaimed by the garbage collector. This class avoids the need to use File.deleteOnExit() in a server environment. Obtained from: Noel Bergman git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/io/trunk@140546 13f79535-47bb-0310-9956-ffa450edef68
1 parent a0e7772 commit daedcb7

2 files changed

Lines changed: 246 additions & 0 deletions

File tree

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
/*
2+
* Copyright 2001-2004 The Apache Software Foundation.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.apache.commons.io;
17+
18+
import java.io.File;
19+
import java.lang.ref.PhantomReference;
20+
import java.lang.ref.ReferenceQueue;
21+
import java.util.Collection;
22+
import java.util.Vector;
23+
24+
/**
25+
* Keeps track of files awaiting deletion, and deletes them when an associated
26+
* marker object is reclaimed by the garbage collector.
27+
*
28+
* @author Noel Bergman
29+
* @author Martin Cooper
30+
*
31+
* @version $Id: FileCleaner.java,v 1.1 2004/03/18 06:04:14 martinc Exp $
32+
*/
33+
public class FileCleaner {
34+
35+
/**
36+
* Queue of <code>Tracker</code> instances being watched.
37+
*/
38+
private static ReferenceQueue /* Tracker */ q = new ReferenceQueue();
39+
40+
/**
41+
* Collection of <code>Tracker</code> instances in existence.
42+
*/
43+
private static Collection /* Tracker */ trackers = new Vector();
44+
45+
/**
46+
* The thread that will clean up registered files.
47+
*/
48+
private static Thread reaper = new Thread("File Reaper") {
49+
50+
/**
51+
* Run the reaper thread that will delete files as their associated
52+
* marker objects are reclaimed by the garbage collector.
53+
*/
54+
public void run() {
55+
for (;;) {
56+
Tracker tracker = null;
57+
try {
58+
// Wait for a tracker to remove.
59+
tracker = (Tracker) q.remove();
60+
} catch(Exception _) {
61+
continue;
62+
}
63+
64+
tracker.delete();
65+
tracker.clear();
66+
trackers.remove(tracker);
67+
}
68+
}
69+
};
70+
71+
/**
72+
* The static initializer that starts the reaper thread.
73+
*/
74+
static {
75+
reaper.setPriority(Thread.MAX_PRIORITY);
76+
reaper.setDaemon(true);
77+
reaper.start();
78+
}
79+
80+
/**
81+
* Track the specified file, using the provided marker, deleting the file
82+
* when the marker instance is garbage collected.
83+
*
84+
* @param file The file to be tracked.
85+
* @param marker The marker object used to track the file.
86+
*/
87+
public static void track(File file, Object marker) {
88+
trackers.add(new Tracker(file, marker, q));
89+
}
90+
91+
/**
92+
* Track the specified file, using the provided marker, deleting the file
93+
* when the marker instance is garbage collected.
94+
*
95+
* @param path The full path to the file to be tracked.
96+
* @param marker The marker object used to track the file.
97+
*/
98+
public static void track(String path, Object marker) {
99+
trackers.add(new Tracker(path, marker, q));
100+
}
101+
102+
/**
103+
* Retrieve the number of files currently being tracked, and therefore
104+
* awaiting deletion.
105+
*
106+
* @return the number of files being tracked.
107+
*/
108+
public static int getTrackCount() {
109+
return trackers.size();
110+
}
111+
112+
/**
113+
* Inner class which acts as the reference for a file pending deletion.
114+
*/
115+
private static class Tracker extends PhantomReference {
116+
117+
/**
118+
* The full path to the file being tracked.
119+
*/
120+
private String path;
121+
122+
/**
123+
* Constructs an instance of this class from the supplied parameters.
124+
*
125+
* @param file The file to be tracked.
126+
* @param marker The marker object used to track the file.
127+
* @param q The queue on to which the tracker will be pushed.
128+
*/
129+
public Tracker(File file, Object marker, ReferenceQueue q) {
130+
this(file.getPath(), marker, q);
131+
}
132+
133+
/**
134+
* Constructs an instance of this class from the supplied parameters.
135+
*
136+
* @param path The full path to the file to be tracked.
137+
* @param marker The marker object used to track the file.
138+
* @param q The queue on to which the tracker will be pushed.
139+
*/
140+
public Tracker(String path, Object marker, ReferenceQueue q) {
141+
super(marker, q);
142+
this.path = path;
143+
}
144+
145+
/**
146+
* Deletes the file associated with this tracker instance.
147+
*
148+
* @return <code>true</code> if the file was deleted successfully;
149+
* <code>false</code> otherwise.
150+
*/
151+
public boolean delete() {
152+
return new File(path).delete();
153+
}
154+
}
155+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright 2004 The Apache Software Foundation.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.apache.commons.io;
17+
18+
import java.io.File;
19+
import java.io.IOException;
20+
import java.io.RandomAccessFile;
21+
22+
import junit.framework.Test;
23+
import junit.framework.TestSuite;
24+
import junit.textui.TestRunner;
25+
26+
import org.apache.commons.io.testtools.FileBasedTestCase;
27+
28+
/**
29+
* This is used to test FileCleaner for correctness.
30+
*
31+
* @author Noel Bergman
32+
* @author Martin Cooper
33+
*
34+
* @version $Id: FileCleanerTestCase.java,v 1.1 2004/03/18 06:04:14 martinc Exp $
35+
36+
* @see FileCleaner
37+
*/
38+
public class FileCleanerTestCase extends FileBasedTestCase {
39+
40+
private File testFile;
41+
42+
public static void main(String[] args) {
43+
TestRunner.run(suite());
44+
}
45+
46+
public static Test suite() {
47+
return new TestSuite(FileCleanerTestCase.class);
48+
}
49+
50+
public FileCleanerTestCase(String name) throws IOException {
51+
super(name);
52+
53+
testFile = new File(getTestDirectory(), "file-test.txt");
54+
}
55+
56+
/** @see junit.framework.TestCase#setUp() */
57+
protected void setUp() throws Exception {
58+
getTestDirectory().mkdirs();
59+
}
60+
61+
/** @see junit.framework.TestCase#tearDown() */
62+
protected void tearDown() throws Exception {
63+
FileUtils.deleteDirectory(getTestDirectory());
64+
}
65+
66+
/**
67+
* Test the FileCleaner implementation.
68+
*/
69+
public void testFileCleaner() throws Exception {
70+
String path = testFile.getPath();
71+
72+
assertFalse("File does not exist", testFile.exists());
73+
RandomAccessFile r = new RandomAccessFile(testFile, "rw");
74+
assertTrue("File exists", testFile.exists());
75+
76+
assertTrue("No files tracked", FileCleaner.getTrackCount() == 0);
77+
FileCleaner.track(path, r);
78+
assertTrue("One file tracked", FileCleaner.getTrackCount() == 1);
79+
80+
r.close();
81+
testFile = null;
82+
r = null;
83+
84+
while (FileCleaner.getTrackCount() != 0) {
85+
System.gc();
86+
}
87+
88+
assertTrue("No files tracked", FileCleaner.getTrackCount() == 0);
89+
assertFalse("File does not exist", new File(path).exists());
90+
}
91+
}

0 commit comments

Comments
 (0)