Author: scolebourne Date: Thu Aug 31 16:34:31 2006 New Revision: 439102 URL: http://svn.apache.org/viewvc?rev=439102&view=rev Log: IO-56,IO-70 - FileDeleteStrategy / FileCleaner - FileDeleteStrategy is a strategy for handling file deletion and can be used as a calback in FileCleaner - Together these allow FileCleaner to do a forceDelete to kill directories and provide hooks for secure delete
Added: jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/FileDeleteStrategy.java (with props) Modified: jakarta/commons/proper/io/trunk/RELEASE-NOTES.txt jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/FileCleaner.java jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/FileCleanerTestCase.java jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/PackageTestSuite.java Modified: jakarta/commons/proper/io/trunk/RELEASE-NOTES.txt URL: http://svn.apache.org/viewvc/jakarta/commons/proper/io/trunk/RELEASE-NOTES.txt?rev=439102&r1=439101&r2=439102&view=diff ============================================================================== --- jakarta/commons/proper/io/trunk/RELEASE-NOTES.txt (original) +++ jakarta/commons/proper/io/trunk/RELEASE-NOTES.txt Thu Aug 31 16:34:31 2006 @@ -51,6 +51,9 @@ - FileSystemUtils.freeSpace [IO-91] - This is now documented not to work on SunOS 5 +- FileCleaner + - This now handles the situation where an error occurs when deleting the file + Enhancements from 1.2 --------------------- @@ -62,6 +65,19 @@ - wildcardMatch - new method that has IOCase as a parameter - equals - new method that has IOCase as a parameter +- FileUtils.isFileOlder + - add methods to check if a file is older (i.e. isFileOlder()) - counterparts + to the existing isFileNewer() methods. + +- FileUtils.checksum, FileUtils.checksumCRC32 + - add methods to create a checksum of a file + +- FileDeleteStrategy +- FileCleaner [IO-56,IO-70] + - FileDeleteStrategy is a strategy for handling file deletion + - This can be used as a calback in FileCleaner + - Together these allow FileCleaner to do a forceDelete to kill directories + - WildcardFileFilter - Replacement for WildcardFilter - Accepts both files and directories @@ -105,13 +121,6 @@ - FileFilterUtils - new sizeRangeFileFilter(long minimumSize, long maximumSize) method which creates a filter that accepts files within the specified size range. - -- FileUtils - - add methods to check if a file is older (i.e. isFileOlder()) - counterparts - to the existing isFileNewer() methods. - -- FileUtils.checksum, FileUtils.checksumCRC32 - - add methods to create a checksum of a file Feedback Modified: jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/FileCleaner.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/FileCleaner.java?rev=439102&r1=439101&r2=439102&view=diff ============================================================================== --- jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/FileCleaner.java (original) +++ jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/FileCleaner.java Thu Aug 31 16:34:31 2006 @@ -81,69 +81,104 @@ reaper.start(); } + //----------------------------------------------------------------------- /** * Track the specified file, using the provided marker, deleting the file * when the marker instance is garbage collected. + * The [EMAIL PROTECTED] FileDeleteStrategy#NORMAL normal} deletion strategy will be used. * - * @param file The file to be tracked. - * @param marker The marker object used to track the file. + * @param file the file to be tracked, not null + * @param marker the marker object used to track the file, not null + * @throws NullPointerException if the file is null */ public static void track(File file, Object marker) { - trackers.add(new Tracker(file, marker, q)); + track(file, marker, (FileDeleteStrategy) null); } /** * Track the specified file, using the provided marker, deleting the file * when the marker instance is garbage collected. + * The speified deletion strategy is used. * - * @param path The full path to the file to be tracked. - * @param marker The marker object used to track the file. + * @param file the file to be tracked, not null + * @param marker the marker object used to track the file, not null + * @param deleteStrategy the strategy to delete the file, null means normal + * @throws NullPointerException if the file is null + */ + public static void track(File file, Object marker, FileDeleteStrategy deleteStrategy) { + if (file == null) { + throw new NullPointerException("The file must not be null"); + } + trackers.add(new Tracker(file.getPath(), deleteStrategy, marker, q)); + } + + /** + * Track the specified file, using the provided marker, deleting the file + * when the marker instance is garbage collected. + * The [EMAIL PROTECTED] FileDeleteStrategy#NORMAL normal} deletion strategy will be used. + * + * @param path the full path to the file to be tracked, not null + * @param marker the marker object used to track the file, not null + * @throws NullPointerException if the path is null */ public static void track(String path, Object marker) { - trackers.add(new Tracker(path, marker, q)); + track(path, marker, (FileDeleteStrategy) null); + } + + /** + * Track the specified file, using the provided marker, deleting the file + * when the marker instance is garbage collected. + * The speified deletion strategy is used. + * + * @param path the full path to the file to be tracked, not null + * @param marker the marker object used to track the file, not null + * @param deleteStrategy the strategy to delete the file, null means normal + * @throws NullPointerException if the path is null + */ + public static void track(String path, Object marker, FileDeleteStrategy deleteStrategy) { + if (path == null) { + throw new NullPointerException("The path must not be null"); + } + trackers.add(new Tracker(path, deleteStrategy, marker, q)); } /** * Retrieve the number of files currently being tracked, and therefore * awaiting deletion. * - * @return the number of files being tracked. + * @return the number of files being tracked */ public static int getTrackCount() { return trackers.size(); } + //----------------------------------------------------------------------- /** * Inner class which acts as the reference for a file pending deletion. */ - private static class Tracker extends PhantomReference { + static class Tracker extends PhantomReference { /** * The full path to the file being tracked. */ - private String path; - + private final String path; /** - * Constructs an instance of this class from the supplied parameters. - * - * @param file The file to be tracked. - * @param marker The marker object used to track the file. - * @param queue The queue on to which the tracker will be pushed. + * The strategy for deleting files. */ - public Tracker(File file, Object marker, ReferenceQueue queue) { - this(file.getPath(), marker, queue); - } + private final FileDeleteStrategy deleteStrategy; /** * Constructs an instance of this class from the supplied parameters. * - * @param path The full path to the file to be tracked. - * @param marker The marker object used to track the file. - * @param queue The queue on to which the tracker will be pushed. + * @param path the full path to the file to be tracked, not null + * @param deleteStrategy the strategy to delete the file, null means normal + * @param marker the marker object used to track the file, not null + * @param queue the queue on to which the tracker will be pushed, not null */ - public Tracker(String path, Object marker, ReferenceQueue queue) { + Tracker(String path, FileDeleteStrategy deleteStrategy, Object marker, ReferenceQueue queue) { super(marker, queue); this.path = path; + this.deleteStrategy = (deleteStrategy == null ? FileDeleteStrategy.NORMAL : deleteStrategy); } /** @@ -153,7 +188,8 @@ * <code>false</code> otherwise. */ public boolean delete() { - return new File(path).delete(); + return deleteStrategy.deleteQuietly(new File(path)); } } + } Added: jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/FileDeleteStrategy.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/FileDeleteStrategy.java?rev=439102&view=auto ============================================================================== --- jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/FileDeleteStrategy.java (added) +++ jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/FileDeleteStrategy.java Thu Aug 31 16:34:31 2006 @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 + * + * http://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.apache.commons.io; + +import java.io.File; +import java.io.IOException; + +/** + * Strategy for deleting files. + * <p> + * There is more than one way to delete a file. + * You may want to limit access to certain directories, to only delete + * directories if they are empty, or maybe to force deletion. + * <p> + * This class captures the strategy to use and is designed for user subclassing. + * + * @author Stephen Colebourne + * @version $Id $ + * @since Commons IO 1.3 + */ +public class FileDeleteStrategy { + + /** + * The singleton instance for normal file deletion, which does not permit + * the deletion of directories that are not empty. + */ + public static final FileDeleteStrategy NORMAL = new FileDeleteStrategy("Normal"); + /** + * The singleton instance for forced file deletion, which always deletes, + * even if the file represents a non-empty directory. + */ + public static final FileDeleteStrategy FORCE = new ForceFileDeleteStrategy(); + + /** The name of the strategy. */ + private final String name; + + //----------------------------------------------------------------------- + /** + * Restricted constructor. + * + * @param name the name by which the strategy is known + */ + protected FileDeleteStrategy(String name) { + this.name = name; + } + + //----------------------------------------------------------------------- + /** + * Deletes the file object, which may be a file or a directory. + * All <code>IOException</code>s are caught and false returned instead. + * If the file does not exist or is null, true is returned. + * <p> + * Subclass writers should override [EMAIL PROTECTED] #doDelete(File)}, not this method. + * + * @param fileToDelete the file to delete, null returns true + * @return true if the file was deleted, or there was no such file + */ + public boolean deleteQuietly(File fileToDelete) { + if (fileToDelete == null) { + return true; + } + try { + return doDelete(fileToDelete); + } catch (IOException ex) { + return false; + } + } + + /** + * Deletes the file object, which may be a file or a directory. + * If the file does not exist, the method just returns. + * <p> + * Subclass writers should override [EMAIL PROTECTED] #doDelete(File)}, not this method. + * + * @param fileToDelete the file to delete, not null + * @throws NullPointerException if the file is null + * @throws IOException if an error occurs during file deletion + */ + public void delete(File fileToDelete) throws IOException { + if (doDelete(fileToDelete) == false) { + throw new IOException("Deletion failed: " + fileToDelete); + } + } + + /** + * Actually deletes the file object, which may be a file or a directory. + * <p> + * This method is designed for subclasses to override. + * The implementation may return either false or an <code>IOException</code> + * when deletion fails. The [EMAIL PROTECTED] #delete(File)} and [EMAIL PROTECTED] #deleteQuietly(File)} + * methods will handle either response appropriately. + * If the file does not exist, the implementation must return true. + * <p> + * This implementation uses [EMAIL PROTECTED] File#delete()}. + * + * @param fileToDelete the file to delete, not null + * @return true if the file was deleted, or there was no such file + * @throws NullPointerException if the file is null + * @throws IOException if an error occurs during file deletion + */ + protected boolean doDelete(File fileToDelete) throws IOException { + return fileToDelete.delete(); + } + + //----------------------------------------------------------------------- + /** + * Gets a string describing the delete strategy. + * + * @return a string describing the delete strategy + */ + public String toString() { + return "FileDelete[" + name + "]"; + } + + //----------------------------------------------------------------------- + /** + * Force file deletion strategy. + */ + static class ForceFileDeleteStrategy extends FileDeleteStrategy { + ForceFileDeleteStrategy() { + super("Force"); + } + protected boolean doDelete(File fileToDelete) throws IOException { + if (fileToDelete.exists()) { + FileUtils.forceDelete(fileToDelete); + } + return true; + } + } + +} Propchange: jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/FileDeleteStrategy.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/FileDeleteStrategy.java ------------------------------------------------------------------------------ svn:keywords = author date id revision Modified: jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/FileCleanerTestCase.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/FileCleanerTestCase.java?rev=439102&r1=439101&r2=439102&view=diff ============================================================================== --- jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/FileCleanerTestCase.java (original) +++ jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/FileCleanerTestCase.java Thu Aug 31 16:34:31 2006 @@ -64,29 +64,114 @@ FileUtils.deleteDirectory(getTestDirectory()); } - /** - * Test the FileCleaner implementation. - */ - public void testFileCleaner() throws Exception { + //----------------------------------------------------------------------- + public void testFileCleanerFile() throws Exception { String path = testFile.getPath(); - - assertFalse("File does not exist", testFile.exists()); + + assertEquals(false, testFile.exists()); RandomAccessFile r = new RandomAccessFile(testFile, "rw"); - assertTrue("File exists", testFile.exists()); - - assertTrue("No files tracked", FileCleaner.getTrackCount() == 0); + assertEquals(true, testFile.exists()); + + assertEquals(0, FileCleaner.getTrackCount()); FileCleaner.track(path, r); - assertTrue("One file tracked", FileCleaner.getTrackCount() == 1); - + assertEquals(1, FileCleaner.getTrackCount()); + r.close(); testFile = null; r = null; + while (FileCleaner.getTrackCount() != 0) { + System.gc(); + } + + assertEquals(0, FileCleaner.getTrackCount()); + assertEquals(false, new File(path).exists()); + } + public void testFileCleanerDirectory() throws Exception { + createFile(testFile, 100); + assertEquals(true, testFile.exists()); + assertEquals(true, getTestDirectory().exists()); + + Object obj = new Object(); + assertEquals(0, FileCleaner.getTrackCount()); + FileCleaner.track(getTestDirectory(), obj); + assertEquals(1, FileCleaner.getTrackCount()); + + obj = null; while (FileCleaner.getTrackCount() != 0) { System.gc(); } + + assertEquals(0, FileCleaner.getTrackCount()); + assertEquals(true, testFile.exists()); // not deleted, as dir not empty + assertEquals(true, testFile.getParentFile().exists()); // not deleted, as dir not empty + } - assertTrue("No files tracked", FileCleaner.getTrackCount() == 0); - assertFalse("File does not exist", new File(path).exists()); + public void testFileCleanerDirectory_NullStrategy() throws Exception { + createFile(testFile, 100); + assertEquals(true, testFile.exists()); + assertEquals(true, getTestDirectory().exists()); + + Object obj = new Object(); + assertEquals(0, FileCleaner.getTrackCount()); + FileCleaner.track(getTestDirectory(), obj, (FileDeleteStrategy) null); + assertEquals(1, FileCleaner.getTrackCount()); + + obj = null; + while (FileCleaner.getTrackCount() != 0) { + System.gc(); + } + + assertEquals(0, FileCleaner.getTrackCount()); + assertEquals(true, testFile.exists()); // not deleted, as dir not empty + assertEquals(true, testFile.getParentFile().exists()); // not deleted, as dir not empty } + + public void testFileCleanerDirectory_ForceStrategy() throws Exception { + createFile(testFile, 100); + assertEquals(true, testFile.exists()); + assertEquals(true, getTestDirectory().exists()); + + Object obj = new Object(); + assertEquals(0, FileCleaner.getTrackCount()); + FileCleaner.track(getTestDirectory(), obj, FileDeleteStrategy.FORCE); + assertEquals(1, FileCleaner.getTrackCount()); + + obj = null; + while (FileCleaner.getTrackCount() != 0) { + System.gc(); + } + + assertEquals(0, FileCleaner.getTrackCount()); + assertEquals(false, testFile.exists()); + assertEquals(false, testFile.getParentFile().exists()); + } + + public void testFileCleanerNull() throws Exception { + try { + FileCleaner.track((File) null, new Object()); + fail(); + } catch (NullPointerException ex) { + // expected + } + try { + FileCleaner.track((File) null, new Object(), FileDeleteStrategy.NORMAL); + fail(); + } catch (NullPointerException ex) { + // expected + } + try { + FileCleaner.track((String) null, new Object()); + fail(); + } catch (NullPointerException ex) { + // expected + } + try { + FileCleaner.track((String) null, new Object(), FileDeleteStrategy.NORMAL); + fail(); + } catch (NullPointerException ex) { + // expected + } + } + } Modified: jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/PackageTestSuite.java URL: http://svn.apache.org/viewvc/jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/PackageTestSuite.java?rev=439102&r1=439101&r2=439102&view=diff ============================================================================== --- jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/PackageTestSuite.java (original) +++ jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/PackageTestSuite.java Thu Aug 31 16:34:31 2006 @@ -38,6 +38,7 @@ suite.addTest(new TestSuite(CopyUtilsTest.class)); suite.addTest(new TestSuite(DemuxTestCase.class)); suite.addTest(new TestSuite(EndianUtilsTest.class)); + suite.addTest(new TestSuite(FileCleanerTestCase.class)); suite.addTest(new TestSuite(FilenameUtilsTestCase.class)); suite.addTest(new TestSuite(FilenameUtilsWildcardTestCase.class)); suite.addTest(new TestSuite(FileSystemUtilsTestCase.class)); --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]