You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by sc...@apache.org on 2006/12/03 19:05:41 UTC
svn commit: r481847 - in /jakarta/commons/proper/io/trunk/src:
java/org/apache/commons/io/DirectoryWalker.java
test/org/apache/commons/io/DirectoryWalkerTestCase.java
Author: scolebourne
Date: Sun Dec 3 10:05:37 2006
New Revision: 481847
URL: http://svn.apache.org/viewvc?view=rev&rev=481847
Log:
Add additional cancellation support, including checkIsCancelled() and handleIsCancelled()
Modified:
jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/DirectoryWalker.java
jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/DirectoryWalkerTestCase.java
Modified: jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/DirectoryWalker.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/DirectoryWalker.java?view=diff&rev=481847&r1=481846&r2=481847
==============================================================================
--- jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/DirectoryWalker.java (original)
+++ jakarta/commons/proper/io/trunk/src/java/org/apache/commons/io/DirectoryWalker.java Sun Dec 3 10:05:37 2006
@@ -184,13 +184,15 @@
* <a name="external"></a>
* <h4>3.1 External / Multi-threaded</h4>
*
- * This example provides a <code>cancel()</code> method for external processes to
- * indcate that processing must stop. Calling this method sets a
- * <a href="http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#36930">volatile</a>
- * flag to (hopefully) ensure it will work properly in
- * a multi-threaded environment. In this implementation the flag is checked in two
- * of the lifecycle methods using a convenience <code>checkIfCancelled()</code> method
- * which throws a {@link CancelException} if cancellation has been requested.
+ * This example provides a public <code>cancel()</code> method that can be
+ * called by another thread to stop the processing. A typical example use-case
+ * would be a cancel button on a GUI. Calling this method sets a
+ * <a href="http://java.sun.com/docs/books/jls/second_edition/html/classes.doc.html#36930">
+ * volatile</a> flag to ensure it will work properly in a multi-threaded environment.
+ * The flag is returned by the <code>handleIsCancelled()</code> method, which
+ * will cause the walk to stop immediately. The <code>handleCancelled()</code>
+ * method will be the next, and last, callback method received once cancellation
+ * has occurred.
*
* <pre>
* public class FooDirectoryWalker extends DirectoryWalker {
@@ -201,24 +203,12 @@
* cancelled = true;
* }
*
- * protected boolean handleDirectory(File directory, int depth, Collection results) throws IOException {
- * checkIfCancelled(directory, depth); // Cancel Check
- * return true;
- * }
- *
- * protected void handleFile(File file, int depth, Collection results) throws IOException {
- * checkIfCancelled(file, depth); // Cancel Check
- * results.add(file);
- * }
- *
- * private void checkIfCancelled(File file, int depth) throws CancelException {
- * if (cancelled) {
- * throw new CancelException(file, depth);
- * }
+ * private void handleIsCancelled(File file, int depth, Collection results) {
+ * return cancelled;
* }
*
* protected void handleCancelled(File startDirectory, Collection results, CancelException cancel) {
- * // implement cancel processing here
+ * // implement processing required when a cancellation occurs
* }
* }
* </pre>
@@ -250,7 +240,7 @@
* }
*
* protected void handleCancelled(File startDirectory, Collection results, CancelException cancel) {
- * // implement cancel processing here
+ * // implement processing required when a cancellation occurs
* }
* }
* </pre>
@@ -324,7 +314,7 @@
/**
* Internal method that walks the directory hierarchy in a depth-first manner.
* <p>
- * Most users of this class do not need to call this method. This method will
+ * Users of this class do not need to call this method. This method will
* be called automatically by another (public) method on the specific subclass.
* <p>
* Writers of subclasses should call this method to start the directory walk.
@@ -336,7 +326,7 @@
* @throws NullPointerException if the start directory is null
* @throws IOException if an I/O Error occurs
*/
- protected void walk(File startDirectory, Collection results) throws IOException {
+ protected final void walk(File startDirectory, Collection results) throws IOException {
if (startDirectory == null) {
throw new NullPointerException("Start Directory is null");
}
@@ -358,10 +348,12 @@
* @throws IOException if an I/O Error occurs
*/
private void walk(File directory, int depth, Collection results) throws IOException {
+ checkIfCancelled(directory, depth, results);
if (handleDirectory(directory, depth, results)) {
handleDirectoryStart(directory, depth, results);
int childDepth = depth + 1;
if (depthLimit < 0 || childDepth <= depthLimit) {
+ checkIfCancelled(directory, depth, results);
File[] childFiles = (filter == null ? directory.listFiles() : directory.listFiles(filter));
if (childFiles == null) {
handleRestricted(directory, childDepth, results);
@@ -371,13 +363,97 @@
if (childFile.isDirectory()) {
walk(childFile, childDepth, results);
} else {
+ checkIfCancelled(childFile, childDepth, results);
handleFile(childFile, childDepth, results);
+ checkIfCancelled(childFile, childDepth, results);
}
}
}
}
handleDirectoryEnd(directory, depth, results);
}
+ checkIfCancelled(directory, depth, results);
+ }
+
+ //-----------------------------------------------------------------------
+ /**
+ * Checks whether the walk has been cancelled by calling {@link #handleIsCancelled},
+ * throwing a <code>CancelException</code> if it has.
+ * <p>
+ * Writers of subclasses should not normally call this method as it is called
+ * automatically by the walk of the tree. However, sometimes a single method,
+ * typically {@link #handleFile}, may take a long time to run. In that case,
+ * you may wish to check for cancellation by calling this method.
+ *
+ * @param file the current file being processed
+ * @param depth the current file level (starting directory = 0)
+ * @param results the collection of result objects, may be updated
+ * @return true to process this directory, false to skip this directory
+ * @throws IOException if an I/O Error occurs
+ */
+ protected final void checkIfCancelled(File file, int depth, Collection results) throws IOException {
+ if (handleIsCancelled(file, depth, results)) {
+ throw new CancelException(file, depth);
+ }
+ }
+
+ /**
+ * Overridable callback method invoked to determine if the entire walk
+ * operation should be immediately cancelled.
+ * <p>
+ * This method should be implemented by those subclasses that want to
+ * provide a public <code>cancel()</code> method available from another
+ * thread. The design pattern for the subclass should be as follows:
+ * <pre>
+ * public class FooDirectoryWalker extends DirectoryWalker {
+ * private volatile boolean cancelled = false;
+ *
+ * public void cancel() {
+ * cancelled = true;
+ * }
+ * private void handleIsCancelled(File file, int depth, Collection results) {
+ * return cancelled;
+ * }
+ * protected void handleCancelled(File startDirectory,
+ * Collection results, CancelException cancel) {
+ * // implement processing required when a cancellation occurs
+ * }
+ * }
+ * </pre>
+ * <p>
+ * If this method returns true, then the directory walk is immediately
+ * cancelled. The next callback method will be {@link #handleCancelled}.
+ * <p>
+ * This implementation returns false.
+ *
+ * @param file the file or directory being processed
+ * @param depth the current directory level (starting directory = 0)
+ * @return true if the walk has been cancelled
+ * @throws IOException if an I/O Error occurs
+ */
+ protected boolean handleIsCancelled(
+ File file, int depth, Collection results) throws IOException {
+ // do nothing - overridable by subclass
+ return false; // not cancelled
+ }
+
+ /**
+ * Overridable callback method invoked when the operation is cancelled.
+ * The file being processed when the cancellation occurred can be
+ * obtained from the exception.
+ * <p>
+ * This implementation just re-throws the {@link CancelException}.
+ *
+ * @param startDirectory the directory that the walk started from
+ * @param results the collection of result objects, may be updated
+ * @param cancel the exception throw to cancel further processing
+ * containing details at the point of cancellation.
+ * @throws IOException if an I/O Error occurs
+ */
+ protected void handleCancelled(File startDirectory, Collection results,
+ CancelException cancel) throws IOException {
+ // re-throw exception - overridable by subclass
+ throw cancel;
}
//-----------------------------------------------------------------------
@@ -480,25 +556,6 @@
*/
protected void handleEnd(Collection results) throws IOException {
// do nothing - overridable by subclass
- }
-
- /**
- * Overridable callback method invoked when the operation is cancelled.
- * The file being processed when the cancellation occurred can be
- * obtained from the exception.
- * <p>
- * This implementation just re-throws the {@link CancelException}.
- *
- * @param startDirectory the directory to start from
- * @param results the collection of result objects, may be updated
- * @param cancel the exception throw to cancel further processing
- * containing details at the point of cancellation.
- * @throws IOException if an I/O Error occurs
- */
- protected void handleCancelled(File startDirectory, Collection results,
- CancelException cancel) throws IOException {
- // re-throw exception - overridable by subclass
- throw cancel;
}
//-----------------------------------------------------------------------
Modified: jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/DirectoryWalkerTestCase.java
URL: http://svn.apache.org/viewvc/jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/DirectoryWalkerTestCase.java?view=diff&rev=481847&r1=481846&r2=481847
==============================================================================
--- jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/DirectoryWalkerTestCase.java (original)
+++ jakarta/commons/proper/io/trunk/src/test/org/apache/commons/io/DirectoryWalkerTestCase.java Sun Dec 3 10:05:37 2006
@@ -288,7 +288,7 @@
// Cancel on a file
try {
cancelName = "DirectoryWalker.java";
- List results = new TestCancelWalker(cancelName, false).find(javaDir);
+ new TestCancelWalker(cancelName, false).find(javaDir);
fail("CancelException not thrown for '" + cancelName + "'");
} catch (DirectoryWalker.CancelException cancel) {
assertEquals("File: " + cancelName, cancelName, cancel.getFile().getName());
@@ -300,7 +300,7 @@
// Cancel on a directory
try {
cancelName = "commons";
- List results = new TestCancelWalker(cancelName, false).find(javaDir);
+ new TestCancelWalker(cancelName, false).find(javaDir);
fail("CancelException not thrown for '" + cancelName + "'");
} catch (DirectoryWalker.CancelException cancel) {
assertEquals("File: " + cancelName, cancelName, cancel.getFile().getName());
@@ -320,6 +320,51 @@
}
+ /**
+ * Test Cancel
+ */
+ public void testMultiThreadCancel() {
+ String cancelName = null;
+ TestMultiThreadCancelWalker walker = null;
+ // Cancel on a file
+ try {
+ cancelName = "DirectoryWalker.java";
+ walker = new TestMultiThreadCancelWalker(cancelName, false);
+ walker.find(javaDir);
+ fail("CancelException not thrown for '" + cancelName + "'");
+ } catch (DirectoryWalker.CancelException cancel) {
+ File last = (File) walker.results.get(walker.results.size() - 1);
+ assertEquals(cancelName, last.getName());
+ assertEquals("Depth: " + cancelName, 5, cancel.getDepth());
+ } catch(IOException ex) {
+ fail("IOException: " + cancelName + " " + ex);
+ }
+
+ // Cancel on a directory
+ try {
+ cancelName = "commons";
+ walker = new TestMultiThreadCancelWalker(cancelName, false);
+ walker.find(javaDir);
+ fail("CancelException not thrown for '" + cancelName + "'");
+ } catch (DirectoryWalker.CancelException cancel) {
+ assertEquals("File: " + cancelName, cancelName, cancel.getFile().getName());
+ assertEquals("Depth: " + cancelName, 3, cancel.getDepth());
+ } catch(IOException ex) {
+ fail("IOException: " + cancelName + " " + ex);
+ }
+
+ // Suppress CancelException (use same file name as preceeding test)
+ try {
+ walker = new TestMultiThreadCancelWalker(cancelName, true);
+ List results = walker.find(javaDir);
+ File lastFile = (File) results.get(results.size() - 1);
+ assertEquals("Suppress: " + cancelName, cancelName, lastFile.getName());
+ } catch(IOException ex) {
+ fail("Suppress threw " + ex);
+ }
+
+ }
+
// ------------ Test DirectoryWalker implementation --------------------------
/**
@@ -413,6 +458,61 @@
if (cancelFileName.equals(file.getName())) {
throw new CancelException(file, depth);
}
+ }
+
+ /** Handles Cancel. */
+ protected void handleCancelled(File startDirectory, Collection results,
+ CancelException cancel) throws IOException {
+ if (!suppressCancel) {
+ super.handleCancelled(startDirectory, results, cancel);
+ }
+ }
+ }
+
+ /**
+ * Test DirectoryWalker implementation that finds files in a directory hierarchy
+ * applying a file filter.
+ */
+ static class TestMultiThreadCancelWalker extends DirectoryWalker {
+ private String cancelFileName;
+ private boolean suppressCancel;
+ private boolean cancelled;
+ public List results;
+
+ TestMultiThreadCancelWalker(String cancelFileName, boolean suppressCancel) {
+ super();
+ this.cancelFileName = cancelFileName;
+ this.suppressCancel = suppressCancel;
+ }
+
+ /** find files. */
+ protected List find(File startDirectory) throws IOException {
+ results = new ArrayList();
+ walk(startDirectory, results);
+ return results;
+ }
+
+ /** Handles a directory end by adding the File to the result set. */
+ protected void handleDirectoryEnd(File directory, int depth, Collection results) throws IOException {
+ results.add(directory);
+ assertEquals(false, cancelled);
+ if (cancelFileName.equals(directory.getName())) {
+ cancelled = true;
+ }
+ }
+
+ /** Handles a file by adding the File to the result set. */
+ protected void handleFile(File file, int depth, Collection results) throws IOException {
+ results.add(file);
+ assertEquals(false, cancelled);
+ if (cancelFileName.equals(file.getName())) {
+ cancelled = true;
+ }
+ }
+
+ /** Handles Cancelled. */
+ protected boolean handleIsCancelled(File file, int depth, Collection results) throws IOException {
+ return cancelled;
}
/** Handles Cancel. */
---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org