From b8b552933fa7b102ad96be5ac341014b652ab767 Mon Sep 17 00:00:00 2001 From: alanb Date: Sun, 3 Oct 2010 19:39:25 +0100 Subject: [PATCH] 6907737: (file) FileVisitor and Files.walkFileTree issues Reviewed-by: sherman --- .../nio/file/FileSystemLoopException.java | 50 ++++++++++ .../classes/java/nio/file/FileTreeWalker.java | 66 +++++-------- .../java/nio/file/FileVisitOption.java | 6 +- .../classes/java/nio/file/FileVisitor.java | 92 +++++++++---------- src/share/classes/java/nio/file/Files.java | 42 +++++---- .../java/nio/file/SimpleFileVisitor.java | 77 ++++++---------- src/share/sample/nio/file/Chmod.java | 8 +- src/share/sample/nio/file/Copy.java | 22 ++--- src/share/sample/nio/file/WatchDir.java | 10 +- test/java/nio/file/Files/MaxDepth.java | 67 ++++++++++++++ test/java/nio/file/Files/Misc.java | 21 +++-- test/java/nio/file/Files/PrintFileTree.java | 31 ++++--- test/java/nio/file/Files/SkipSiblings.java | 14 +-- test/java/nio/file/Files/TerminateWalk.java | 13 +-- .../java/nio/file/Files/WalkWithSecurity.java | 2 +- test/java/nio/file/Files/walk_file_tree.sh | 8 +- test/java/nio/file/TestUtil.java | 9 +- 17 files changed, 297 insertions(+), 241 deletions(-) create mode 100644 src/share/classes/java/nio/file/FileSystemLoopException.java create mode 100644 test/java/nio/file/Files/MaxDepth.java diff --git a/src/share/classes/java/nio/file/FileSystemLoopException.java b/src/share/classes/java/nio/file/FileSystemLoopException.java new file mode 100644 index 000000000..fe6beb5ad --- /dev/null +++ b/src/share/classes/java/nio/file/FileSystemLoopException.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.nio.file; + +/** + * Checked exception thrown when a file system loop, or cycle, is encountered. + * + * @since 1.7 + * @see Files#walkFileTree + */ + +public class FileSystemLoopException + extends FileSystemException +{ + private static final long serialVersionUID = 4843039591949217617L; + + /** + * Constructs an instance of this class. + * + * @param file + * a string identifying the file causing the cycle or {@code null} if + * not known + */ + public FileSystemLoopException(String file) { + super(file); + } +} diff --git a/src/share/classes/java/nio/file/FileTreeWalker.java b/src/share/classes/java/nio/file/FileTreeWalker.java index 8086012a9..cd45d7652 100644 --- a/src/share/classes/java/nio/file/FileTreeWalker.java +++ b/src/share/classes/java/nio/file/FileTreeWalker.java @@ -38,7 +38,6 @@ import sun.nio.fs.BasicFileAttributesHolder; class FileTreeWalker { private final boolean followLinks; - private final boolean detectCycles; private final LinkOption[] linkOptions; private final FileVisitor visitor; private final int maxDepth; @@ -48,17 +47,15 @@ class FileTreeWalker { int maxDepth) { boolean fl = false; - boolean dc = false; for (FileVisitOption option: options) { + // will throw NPE if options contains null switch (option) { - case FOLLOW_LINKS : fl = true; break; - case DETECT_CYCLES : dc = true; break; + case FOLLOW_LINKS : fl = true; break; default: throw new AssertionError("Should not get here"); } } this.followLinks = fl; - this.detectCycles = fl | dc; this.linkOptions = (fl) ? new LinkOption[0] : new LinkOption[] { LinkOption.NOFOLLOW_LINKS }; this.visitor = visitor; @@ -68,13 +65,11 @@ class FileTreeWalker { /** * Walk file tree starting at the given file */ - void walk(Path start) { + void walk(Path start) throws IOException { FileVisitResult result = walk(start, 0, new ArrayList()); - if (result == null) { - throw new NullPointerException("Visitor returned 'null'"); - } + Objects.nonNull(result, "FileVisitor returned null"); } /** @@ -88,11 +83,8 @@ class FileTreeWalker { private FileVisitResult walk(Path file, int depth, List ancestors) + throws IOException { - // depth check - if (depth > maxDepth) - return FileVisitResult.CONTINUE; - // if attributes are cached then use them if possible BasicFileAttributes attrs = null; if ((depth > 0) && @@ -137,13 +129,13 @@ class FileTreeWalker { return visitor.visitFileFailed(file, exc); } - // file is not a directory so invoke visitFile method - if (!attrs.isDirectory()) { + // at maximum depth or file is not a directory + if (depth >= maxDepth || !attrs.isDirectory()) { return visitor.visitFile(file, attrs); } - // check for cycles - if (detectCycles) { + // check for cycles when following links + if (followLinks) { Object key = attrs.fileKey(); // if this directory and ancestor has a file key then we compare @@ -153,19 +145,23 @@ class FileTreeWalker { if (key != null && ancestorKey != null) { if (key.equals(ancestorKey)) { // cycle detected - return visitor.visitFile(file, attrs); + return visitor.visitFileFailed(file, + new FileSystemLoopException(file.toString())); } } else { + boolean isSameFile = false; try { - if (file.isSameFile(ancestor.file())) { - // cycle detected - return visitor.visitFile(file, attrs); - } + isSameFile = file.isSameFile(ancestor.file()); } catch (IOException x) { // ignore } catch (SecurityException x) { // ignore } + if (isSameFile) { + // cycle detected + return visitor.visitFileFailed(file, + new FileSystemLoopException(file.toString())); + } } } @@ -181,7 +177,7 @@ class FileTreeWalker { try { stream = file.newDirectoryStream(); } catch (IOException x) { - return visitor.preVisitDirectoryFailed(file, x); + return visitor.visitFileFailed(file, x); } catch (SecurityException x) { // ignore, as per spec return FileVisitResult.CONTINUE; @@ -192,20 +188,14 @@ class FileTreeWalker { // invoke preVisitDirectory and then visit each entry try { - result = visitor.preVisitDirectory(file); + result = visitor.preVisitDirectory(file, attrs); if (result != FileVisitResult.CONTINUE) { return result; } - // if an I/O occurs during iteration then a CME is thrown. We - // need to distinguish this from a CME thrown by the visitor. - boolean inAction = false; - try { for (Path entry: stream) { - inAction = true; result = walk(entry, depth+1, ancestors); - inAction = false; // returning null will cause NPE to be thrown if (result == null || result == FileVisitResult.TERMINATE) @@ -215,17 +205,9 @@ class FileTreeWalker { if (result == FileVisitResult.SKIP_SIBLINGS) break; } - } catch (ConcurrentModificationException x) { - // if CME thrown because the iteration failed then remember - // the IOException so that it is notified to postVisitDirectory - if (!inAction) { - // iteration failed - Throwable t = x.getCause(); - if (t instanceof IOException) - ioe = (IOException)t; - } - if (ioe == null) - throw x; + } catch (DirectoryIteratorException e) { + // IOException will be notified to postVisitDirectory + ioe = e.getCause(); } } finally { try { @@ -238,7 +220,7 @@ class FileTreeWalker { } finally { // remove key from trail if doing cycle detection - if (detectCycles) { + if (followLinks) { ancestors.remove(ancestors.size()-1); } } diff --git a/src/share/classes/java/nio/file/FileVisitOption.java b/src/share/classes/java/nio/file/FileVisitOption.java index 7f1f14013..8941bd691 100644 --- a/src/share/classes/java/nio/file/FileVisitOption.java +++ b/src/share/classes/java/nio/file/FileVisitOption.java @@ -37,9 +37,5 @@ public enum FileVisitOption { /** * Follow symbolic links. */ - FOLLOW_LINKS, - /** - * Detect cycles in the file tree. - */ - DETECT_CYCLES; + FOLLOW_LINKS; } diff --git a/src/share/classes/java/nio/file/FileVisitor.java b/src/share/classes/java/nio/file/FileVisitor.java index aefc6a8c2..06ac0b870 100644 --- a/src/share/classes/java/nio/file/FileVisitor.java +++ b/src/share/classes/java/nio/file/FileVisitor.java @@ -40,33 +40,28 @@ import java.io.IOException; * Path start = ... * Files.walkFileTree(start, new SimpleFileVisitor<Path>() { * @Override - * public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { - * try { - * file.delete(); - * } catch (IOException exc) { - * // failed to delete, do error handling here - * } + * public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) + * throws IOException + * { + * file.delete(); * return FileVisitResult.CONTINUE; * } * @Override - * public FileVisitResult postVisitDirectory(Path dir, IOException e) { - * if (e == null) { - * try { - * dir.delete(); - * } catch (IOException exc) { - * // failed to delete, do error handling here - * } - * } else { + * public FileVisitResult postVisitDirectory(Path dir, IOException e) + * throws IOException + * { + * if (e != null) { * // directory iteration failed + * throw e; * } + * dir.delete(); * return FileVisitResult.CONTINUE; * } * }); * - *

Furthermore, suppose we want to copy a file tree rooted at a source - * directory to a target location. In that case, symbolic links should be - * followed and the target directory should be created before the entries in - * the directory are copied. + *

Furthermore, suppose we want to copy a file tree to a target location. + * In that case, symbolic links should be followed and the target directory + * should be created before the entries in the directory are copied. *

  *     final Path source = ...
  *     final Path target = ...
@@ -74,25 +69,21 @@ import java.io.IOException;
  *     Files.walkFileTree(source, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
  *         new SimpleFileVisitor<Path>() {
  *             @Override
- *             public FileVisitResult preVisitDirectory(Path dir) {
+ *             public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
+ *                 throws IOException
+ *             {
  *                 try {
  *                     dir.copyTo(target.resolve(source.relativize(dir)));
  *                 } catch (FileAlreadyExistsException e) {
  *                      // ignore
- *                 } catch (IOException e) {
- *                     // copy failed, do error handling here
- *                     // skip rest of directory and descendants
- *                     return SKIP_SUBTREE;
  *                 }
  *                 return CONTINUE;
  *             }
  *             @Override
- *             public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
- *                 try {
- *                     file.copyTo(target.resolve(source.relativize(file)));
- *                 } catch (IOException e) {
- *                     // copy failed, do error handling here
- *                 }
+ *             public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+ *                 throws IOException
+ *             {
+ *                 file.copyTo(target.resolve(source.relativize(file)));
  *                 return CONTINUE;
  *             }
  *         });
@@ -114,22 +105,16 @@ public interface FileVisitor {
      *
      * @param   dir
      *          a reference to the directory
+     * @param   attrs
+     *          the directory's basic attributes
      *
      * @return  the visit result
-     */
-    FileVisitResult preVisitDirectory(T dir);
-
-    /**
-     * Invoked for a directory that could not be opened.
      *
-     * @param   dir
-     *          a reference to the directory
-     * @param   exc
-     *          the I/O exception thrown from the attempt to open the directory
-     *
-     * @return  the visit result
+     * @throws  IOException
+     *          if an I/O error occurs
      */
-    FileVisitResult preVisitDirectoryFailed(T dir, IOException exc);
+    FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs)
+        throws IOException;
 
     /**
      * Invoked for a file in a directory.
@@ -140,21 +125,30 @@ public interface FileVisitor {
      *          the file's basic attributes
      *
      * @return  the visit result
+     *
+     * @throws  IOException
+     *          if an I/O error occurs
      */
-    FileVisitResult visitFile(T file, BasicFileAttributes attrs);
+    FileVisitResult visitFile(T file, BasicFileAttributes attrs)
+        throws IOException;
 
     /**
-     * Invoked for a file when its basic file attributes could not be read.
+     * Invoked for a file that could not be visited. This method is invoked
+     * if the file's attributes could not be read, the file is a directory
+     * that could not be opened, and other reasons.
      *
      * @param   file
      *          a reference to the file
      * @param   exc
-     *          the I/O exception thrown from the attempt to read the file
-     *          attributes
+     *          the I/O exception that prevented the file from being visited
      *
      * @return  the visit result
+     *
+     * @throws  IOException
+     *          if an I/O error occurs
      */
-    FileVisitResult visitFileFailed(T file, IOException exc);
+    FileVisitResult visitFileFailed(T file, IOException exc)
+        throws IOException;
 
     /**
      * Invoked for a directory after entries in the directory, and all of their
@@ -171,6 +165,10 @@ public interface FileVisitor {
      *          of the directory to complete prematurely
      *
      * @return  the visit result
+     *
+     * @throws  IOException
+     *          if an I/O error occurs
      */
-    FileVisitResult postVisitDirectory(T dir, IOException exc);
+    FileVisitResult postVisitDirectory(T dir, IOException exc)
+        throws IOException;
 }
diff --git a/src/share/classes/java/nio/file/Files.java b/src/share/classes/java/nio/file/Files.java
index 703a2a82c..7322016b4 100644
--- a/src/share/classes/java/nio/file/Files.java
+++ b/src/share/classes/java/nio/file/Files.java
@@ -135,9 +135,9 @@ public final class Files {
      * FileVisitor} invoked for each file encountered. File tree traversal
      * completes when all accessible files in the tree have been visited, or a
      * visit method returns a result of {@link FileVisitResult#TERMINATE
-     * TERMINATE}. Where a visit method terminates due an uncaught error or
-     * runtime exception then the traversal is terminated and the error or
-     * exception is propagated to the caller of this method.
+     * TERMINATE}. Where a visit method terminates due an {@code IOException},
+     * an uncaught error, or runtime exception, then the traversal is terminated
+     * and the error or exception is propagated to the caller of this method.
      *
      * 

For each file encountered this method attempts to gets its {@link * java.nio.file.attribute.BasicFileAttributes}. If the file is not a @@ -146,12 +146,10 @@ public final class Files { * due to an I/O exception, then the {@link FileVisitor#visitFileFailed * visitFileFailed} method is invoked with the I/O exception. * - *

Where the file is a directory, this method attempts to open it by - * invoking its {@link Path#newDirectoryStream newDirectoryStream} method. - * Where the directory could not be opened, due to an {@code IOException}, - * then the {@link FileVisitor#preVisitDirectoryFailed preVisitDirectoryFailed} - * method is invoked with the I/O exception, after which, the file tree walk - * continues, by default, at the next sibling of the directory. + *

Where the file is a directory, and the directory could not be opened, + * then the {@code visitFileFailed} method is invoked with the I/O exception, + * after which, the file tree walk continues, by default, at the next + * sibling of the directory. * *

Where the directory is opened successfully, then the entries in the * directory, and their descendants are visited. When all entries @@ -171,26 +169,25 @@ public final class Files { * method is invoked as specified above). * *

If the {@code options} parameter contains the {@link - * FileVisitOption#DETECT_CYCLES DETECT_CYCLES} or {@link - * FileVisitOption#FOLLOW_LINKS FOLLOW_LINKS} options then this method keeps + * FileVisitOption#FOLLOW_LINKS FOLLOW_LINKS} option then this method keeps * track of directories visited so that cycles can be detected. A cycle * arises when there is an entry in a directory that is an ancestor of the * directory. Cycle detection is done by recording the {@link * java.nio.file.attribute.BasicFileAttributes#fileKey file-key} of directories, * or if file keys are not available, by invoking the {@link Path#isSameFile * isSameFile} method to test if a directory is the same file as an - * ancestor. When a cycle is detected the {@link FileVisitor#visitFile - * visitFile} is invoked with the attributes of the directory. The {@link - * java.nio.file.attribute.BasicFileAttributes#isDirectory isDirectory} - * method may be used to test if the file is a directory and that a cycle is - * detected. The {@code preVisitDirectory} and {@code postVisitDirectory} - * methods are not invoked. + * ancestor. When a cycle is detected it is treated as an I/O error, and the + * {@link FileVisitor#visitFileFailed visitFileFailed} method is invoked with + * an instance of {@link FileSystemLoopException}. * *

The {@code maxDepth} parameter is the maximum number of levels of * directories to visit. A value of {@code 0} means that only the starting * file is visited, unless denied by the security manager. A value of * {@link Integer#MAX_VALUE MAX_VALUE} may be used to indicate that all - * levels should be visited. + * levels should be visited. The {@code visitFile} method is invoked for all + * files, including directories, encountered at {@code maxDepth}, unless the + * basic file attributes cannot be read, in which case the {@code + * visitFileFailed} method is invoked. * *

If a visitor returns a result of {@code null} then {@code * NullPointerException} is thrown. @@ -215,11 +212,14 @@ public final class Files { * In the case of the default provider, the {@link * SecurityManager#checkRead(String) checkRead} method is invoked * to check read access to the directory. + * @throws IOException + * If an I/O error is thrown by a visitor method */ public static void walkFileTree(Path start, Set options, int maxDepth, FileVisitor visitor) + throws IOException { if (maxDepth < 0) throw new IllegalArgumentException("'maxDepth' is negative"); @@ -245,8 +245,12 @@ public final class Files { * In the case of the default provider, the {@link * SecurityManager#checkRead(String) checkRead} method is invoked * to check read access to the directory. + * @throws IOException + * If an I/O error is thrown by a visitor method */ - public static void walkFileTree(Path start, FileVisitor visitor) { + public static void walkFileTree(Path start, FileVisitor visitor) + throws IOException + { walkFileTree(start, EnumSet.noneOf(FileVisitOption.class), Integer.MAX_VALUE, diff --git a/src/share/classes/java/nio/file/SimpleFileVisitor.java b/src/share/classes/java/nio/file/SimpleFileVisitor.java index 66a2a74f2..77fb516b1 100644 --- a/src/share/classes/java/nio/file/SimpleFileVisitor.java +++ b/src/share/classes/java/nio/file/SimpleFileVisitor.java @@ -27,7 +27,7 @@ package java.nio.file; import java.nio.file.attribute.BasicFileAttributes; import java.io.IOException; -import java.io.IOError; +import java.util.Objects; /** * A simple visitor of files with default behavior to visit all files and to @@ -47,14 +47,6 @@ public class SimpleFileVisitor implements FileVisitor { protected SimpleFileVisitor() { } - /** - * Throws NullPointerException if obj is null. - */ - private static void checkNotNull(Object obj) { - if (obj == null) - throw new NullPointerException(); - } - /** * Invoked for a directory before entries in the directory are visited. * @@ -62,28 +54,14 @@ public class SimpleFileVisitor implements FileVisitor { * CONTINUE}. */ @Override - public FileVisitResult preVisitDirectory(T dir) { - checkNotNull(dir); + public FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs) + throws IOException + { + Objects.nonNull(dir); + Objects.nonNull(attrs); return FileVisitResult.CONTINUE; } - /** - * Invoked for a directory that could not be opened. - * - *

Unless overridden, this method throws {@link IOError} with the I/O - * exception as cause. - * - * @throws IOError - * with the I/O exception thrown when the attempt to open the - * directory failed - */ - @Override - public FileVisitResult preVisitDirectoryFailed(T dir, IOException exc) { - checkNotNull(dir); - checkNotNull(exc); - throw new IOError(exc); - } - /** * Invoked for a file in a directory. * @@ -91,27 +69,26 @@ public class SimpleFileVisitor implements FileVisitor { * CONTINUE}. */ @Override - public FileVisitResult visitFile(T file, BasicFileAttributes attrs) { - checkNotNull(file); - checkNotNull(attrs); + public FileVisitResult visitFile(T file, BasicFileAttributes attrs) + throws IOException + { + Objects.nonNull(file); + Objects.nonNull(attrs); return FileVisitResult.CONTINUE; } /** - * Invoked for a file when its basic file attributes could not be read. - * - *

Unless overridden, this method throws {@link IOError} with the I/O - * exception as cause. + * Invoked for a file that could not be visited. * - * @throws IOError - * with the I/O exception thrown when the attempt to read the file - * attributes failed + *

Unless overridden, this method re-throws the I/O exception that prevented + * the file from being visited. */ @Override - public FileVisitResult visitFileFailed(T file, IOException exc) { - checkNotNull(file); - checkNotNull(exc); - throw new IOError(exc); + public FileVisitResult visitFileFailed(T file, IOException exc) + throws IOException + { + Objects.nonNull(file); + throw exc; } /** @@ -120,18 +97,16 @@ public class SimpleFileVisitor implements FileVisitor { * *

Unless overridden, this method returns {@link FileVisitResult#CONTINUE * CONTINUE} if the directory iteration completes without an I/O exception; - * otherwise this method throws {@link IOError} with the I/O exception as - * cause. - * - * @throws IOError - * with the I/O exception thrown when iteration of the directory - * completed prematurely due to an I/O error + * otherwise this method re-throws the I/O exception that caused the iteration + * of the directory to terminate prematurely. */ @Override - public FileVisitResult postVisitDirectory(T dir, IOException exc) { - checkNotNull(dir); + public FileVisitResult postVisitDirectory(T dir, IOException exc) + throws IOException + { + Objects.nonNull(dir); if (exc != null) - throw new IOError(exc); + throw exc; return FileVisitResult.CONTINUE; } } diff --git a/src/share/sample/nio/file/Chmod.java b/src/share/sample/nio/file/Chmod.java index 235d1ef1d..fd5cd9185 100644 --- a/src/share/sample/nio/file/Chmod.java +++ b/src/share/sample/nio/file/Chmod.java @@ -285,17 +285,11 @@ public class Chmod { } @Override - public FileVisitResult preVisitDirectory(FileRef dir) { + public FileVisitResult preVisitDirectory(FileRef dir, BasicFileAttributes attrs) { chmod(dir, changer); return CONTINUE; } - @Override - public FileVisitResult preVisitDirectoryFailed(FileRef dir, IOException exc) { - System.err.println("WARNING: " + exc); - return CONTINUE; - } - @Override public FileVisitResult visitFile(FileRef file, BasicFileAttributes attrs) { chmod(file, changer); diff --git a/src/share/sample/nio/file/Copy.java b/src/share/sample/nio/file/Copy.java index 2c087062a..5408e6a5c 100644 --- a/src/share/sample/nio/file/Copy.java +++ b/src/share/sample/nio/file/Copy.java @@ -85,7 +85,7 @@ public class Copy { } @Override - public FileVisitResult preVisitDirectory(Path dir) { + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { // before visiting entries in a directory we copy the directory // (okay if directory already exists). CopyOption[] options = (preserve) ? @@ -103,20 +103,10 @@ public class Copy { return CONTINUE; } - @Override - public FileVisitResult preVisitDirectoryFailed(Path dir, IOException exc) { - System.err.format("Unable to copy: %s: %s%n", dir, exc); - return CONTINUE; - } - @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { - if (attrs.isDirectory()) { - System.err.println("cycle detected: " + file); - } else { - copyFile(file, target.resolve(source.relativize(file)), - prompt, preserve); - } + copyFile(file, target.resolve(source.relativize(file)), + prompt, preserve); return CONTINUE; } @@ -137,7 +127,11 @@ public class Copy { @Override public FileVisitResult visitFileFailed(Path file, IOException exc) { - System.err.format("Unable to copy: %s: %s%n", file, exc); + if (exc instanceof FileSystemLoopException) { + System.err.println("cycle detected: " + file); + } else { + System.err.format("Unable to copy: %s: %s%n", file, exc); + } return CONTINUE; } } diff --git a/src/share/sample/nio/file/WatchDir.java b/src/share/sample/nio/file/WatchDir.java index 8a9b889ec..73b4b7052 100644 --- a/src/share/sample/nio/file/WatchDir.java +++ b/src/share/sample/nio/file/WatchDir.java @@ -78,12 +78,10 @@ public class WatchDir { // register directory and sub-directories Files.walkFileTree(start, new SimpleFileVisitor() { @Override - public FileVisitResult preVisitDirectory(Path dir) { - try { - register(dir); - } catch (IOException x) { - throw new IOError(x); - } + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) + throws IOException + { + register(dir); return FileVisitResult.CONTINUE; } }); diff --git a/test/java/nio/file/Files/MaxDepth.java b/test/java/nio/file/Files/MaxDepth.java new file mode 100644 index 000000000..a895238d5 --- /dev/null +++ b/test/java/nio/file/Files/MaxDepth.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.io.IOException; +import java.util.*; + +/** + * Unit test for Files.walkFileTree to test maxDepth parameter + */ + +public class MaxDepth { + public static void main(String[] args) throws Exception { + final Path top = Paths.get(args[0]); + + for (int i=0; i<5; i++) { + Set opts = Collections.emptySet(); + final int maxDepth = i; + Files.walkFileTree(top, opts, maxDepth, new SimpleFileVisitor() { + // compute depth based on relative path to top directory + private int depth(Path file) { + Path rp = file.relativize(top); + return (rp == null) ? 0 : rp.getNameCount(); + } + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { + int d = depth(dir); + if (d == maxDepth) + throw new RuntimeException("Should not open directories at maxDepth"); + if (d > maxDepth) + throw new RuntimeException("Too deep"); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + int d = depth(file); + if (d > maxDepth) + throw new RuntimeException("Too deep"); + return FileVisitResult.CONTINUE; + } + }); + } + } +} diff --git a/test/java/nio/file/Files/Misc.java b/test/java/nio/file/Files/Misc.java index d722f7245..89e7c251a 100644 --- a/test/java/nio/file/Files/Misc.java +++ b/test/java/nio/file/Files/Misc.java @@ -30,6 +30,7 @@ import java.nio.file.*; import java.nio.file.attribute.Attributes; +import java.nio.file.attribute.BasicFileAttributes; import java.io.IOException; import java.util.*; @@ -117,25 +118,25 @@ public class Misc { SimpleFileVisitor visitor = new SimpleFileVisitor() { }; boolean ranTheGauntlet = false; - try { visitor.preVisitDirectory(null); + BasicFileAttributes attrs = Attributes.readBasicFileAttributes(Paths.get(".")); + + try { visitor.preVisitDirectory(null, attrs); } catch (NullPointerException x0) { - try { visitor.preVisitDirectoryFailed(null, new IOException()); + try { visitor.preVisitDirectory(dir, null); } catch (NullPointerException x1) { - try { visitor.preVisitDirectoryFailed(dir, null); + try { visitor.visitFile(null, attrs); } catch (NullPointerException x2) { - try { visitor.visitFile(null, Attributes.readBasicFileAttributes(Paths.get("."))); - } catch (NullPointerException x3) { try { visitor.visitFile(dir, null); - } catch (NullPointerException x4) { + } catch (NullPointerException x3) { try { visitor.visitFileFailed(null, new IOException()); - } catch (NullPointerException x5) { + } catch (NullPointerException x4) { try { visitor.visitFileFailed(dir, null); - } catch (NullPointerException x6) { + } catch (NullPointerException x5) { try { visitor.postVisitDirectory(null, new IOException()); - } catch (NullPointerException x7) { + } catch (NullPointerException x6) { // if we get here then all visit* methods threw NPE as expected ranTheGauntlet = true; - }}}}}}}} + }}}}}}} if (!ranTheGauntlet) throw new RuntimeException("A visit method did not throw NPE"); } diff --git a/test/java/nio/file/Files/PrintFileTree.java b/test/java/nio/file/Files/PrintFileTree.java index 044da8fec..fdc17cdef 100644 --- a/test/java/nio/file/Files/PrintFileTree.java +++ b/test/java/nio/file/Files/PrintFileTree.java @@ -56,29 +56,34 @@ public class PrintFileTree { final boolean reportCycles = printCycles; Files.walkFileTree(dir, options, Integer.MAX_VALUE, new FileVisitor() { - public FileVisitResult preVisitDirectory(FileRef dir) { + @Override + public FileVisitResult preVisitDirectory(FileRef dir, BasicFileAttributes attrs) { System.out.println(dir); return FileVisitResult.CONTINUE; } - public FileVisitResult preVisitDirectoryFailed(FileRef dir, IOException exc) { - exc.printStackTrace(); - return FileVisitResult.CONTINUE; - } + @Override public FileVisitResult visitFile(FileRef file, BasicFileAttributes attrs) { if (!attrs.isDirectory() || reportCycles) System.out.println(file); return FileVisitResult.CONTINUE; } - public FileVisitResult postVisitDirectory(FileRef dir, IOException exc) { - if (exc != null) { - exc.printStackTrace(); - return FileVisitResult.TERMINATE; - } + @Override + public FileVisitResult postVisitDirectory(FileRef dir, IOException exc) + throws IOException + { + if (exc != null) + throw exc; return FileVisitResult.CONTINUE; } - public FileVisitResult visitFileFailed(FileRef file, IOException exc) { - exc.printStackTrace(); - return FileVisitResult.TERMINATE; + @Override + public FileVisitResult visitFileFailed(FileRef file, IOException exc) + throws IOException + { + if (reportCycles && (exc instanceof FileSystemLoopException)) { + System.out.println(file); + return FileVisitResult.CONTINUE; + } + throw exc; } }); } diff --git a/test/java/nio/file/Files/SkipSiblings.java b/test/java/nio/file/Files/SkipSiblings.java index 952fdea49..5cd7e34ac 100644 --- a/test/java/nio/file/Files/SkipSiblings.java +++ b/test/java/nio/file/Files/SkipSiblings.java @@ -54,32 +54,28 @@ public class SkipSiblings { public static void main(String[] args) throws Exception { Path dir = Paths.get(args[0]); - Files.walkFileTree(dir, new FileVisitor() { - public FileVisitResult preVisitDirectory(Path dir) { + Files.walkFileTree(dir, new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { check(dir); if (skip(dir)) return FileVisitResult.SKIP_SIBLINGS; return FileVisitResult.CONTINUE; } - public FileVisitResult preVisitDirectoryFailed(Path dir, IOException exc) { - throw new RuntimeException(exc); - } - + @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { check(file); if (skip(file)) return FileVisitResult.SKIP_SIBLINGS; return FileVisitResult.CONTINUE; } + @Override public FileVisitResult postVisitDirectory(Path dir, IOException x) { if (x != null) throw new RuntimeException(x); check(dir); return FileVisitResult.CONTINUE; } - public FileVisitResult visitFileFailed(Path file, IOException x) { - throw new RuntimeException(x); - } }); } } diff --git a/test/java/nio/file/Files/TerminateWalk.java b/test/java/nio/file/Files/TerminateWalk.java index 5299b8781..b2858c450 100644 --- a/test/java/nio/file/Files/TerminateWalk.java +++ b/test/java/nio/file/Files/TerminateWalk.java @@ -49,22 +49,19 @@ public class TerminateWalk { public static void main(String[] args) throws Exception { Path dir = Paths.get(args[0]); - Files.walkFileTree(dir, new FileVisitor() { - public FileVisitResult preVisitDirectory(Path dir) { - return maybeTerminate(); - } - public FileVisitResult preVisitDirectoryFailed(Path dir, IOException exc) { + Files.walkFileTree(dir, new SimpleFileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { return maybeTerminate(); } + @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { return maybeTerminate(); } + @Override public FileVisitResult postVisitDirectory(Path dir, IOException x) { return maybeTerminate(); } - public FileVisitResult visitFileFailed(Path file, IOException x) { - return maybeTerminate(); - } }); } } diff --git a/test/java/nio/file/Files/WalkWithSecurity.java b/test/java/nio/file/Files/WalkWithSecurity.java index f4386190c..9044398b6 100644 --- a/test/java/nio/file/Files/WalkWithSecurity.java +++ b/test/java/nio/file/Files/WalkWithSecurity.java @@ -116,7 +116,7 @@ public class WalkWithSecurity { } @Override - public FileVisitResult preVisitDirectory(Path dir) { + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { System.out.println(dir); count++; return FileVisitResult.CONTINUE; diff --git a/test/java/nio/file/Files/walk_file_tree.sh b/test/java/nio/file/Files/walk_file_tree.sh index 50d141f87..4f41d3cc0 100644 --- a/test/java/nio/file/Files/walk_file_tree.sh +++ b/test/java/nio/file/Files/walk_file_tree.sh @@ -22,9 +22,9 @@ # # @test -# @bug 4313887 +# @bug 4313887 6907737 # @summary Unit test for walkFileTree method -# @build CreateFileTree PrintFileTree SkipSiblings TerminateWalk +# @build CreateFileTree PrintFileTree SkipSiblings TerminateWalk MaxDepth # @run shell walk_file_tree.sh # if TESTJAVA isn't set then we assume an interactive run. @@ -84,6 +84,10 @@ if [ $? != 0 ]; then failures=`expr $failures + 1`; fi $JAVA TerminateWalk "$ROOT" if [ $? != 0 ]; then failures=`expr $failures + 1`; fi +# test maxDepth +$JAVA MaxDepth "$ROOT" +if [ $? != 0 ]; then failures=`expr $failures + 1`; fi + # clean-up rm -r "$ROOT" diff --git a/test/java/nio/file/TestUtil.java b/test/java/nio/file/TestUtil.java index 753a4cdd3..c6acccd1e 100644 --- a/test/java/nio/file/TestUtil.java +++ b/test/java/nio/file/TestUtil.java @@ -44,15 +44,10 @@ public class TestUtil { return createTemporaryDirectory(System.getProperty("java.io.tmpdir")); } - static void removeAll(Path dir) { + static void removeAll(Path dir) throws IOException { Files.walkFileTree(dir, new FileVisitor() { @Override - public FileVisitResult preVisitDirectory(Path dir) { - return FileVisitResult.CONTINUE; - } - @Override - public FileVisitResult preVisitDirectoryFailed(Path dir, IOException exc) { - System.err.format("Error occured accessing directory %s\n", dir, exc); + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) { return FileVisitResult.CONTINUE; } @Override -- GitLab