changeset 2971:81b0c1e3d5a7

6907737: (file) FileVisitor and Files.walkFileTree issues Reviewed-by: sherman
author alanb
date Sun, 03 Oct 2010 19:39:25 +0100
parents a6591c8b046d
children b8af3bab5dbf
files src/share/classes/java/nio/file/FileSystemLoopException.java src/share/classes/java/nio/file/FileTreeWalker.java src/share/classes/java/nio/file/FileVisitOption.java src/share/classes/java/nio/file/FileVisitor.java src/share/classes/java/nio/file/Files.java src/share/classes/java/nio/file/SimpleFileVisitor.java src/share/sample/nio/file/Chmod.java src/share/sample/nio/file/Copy.java src/share/sample/nio/file/WatchDir.java test/java/nio/file/Files/MaxDepth.java test/java/nio/file/Files/Misc.java test/java/nio/file/Files/PrintFileTree.java test/java/nio/file/Files/SkipSiblings.java test/java/nio/file/Files/TerminateWalk.java test/java/nio/file/Files/WalkWithSecurity.java test/java/nio/file/Files/walk_file_tree.sh test/java/nio/file/TestUtil.java
diffstat 17 files changed, 298 insertions(+), 242 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/java/nio/file/FileSystemLoopException.java	Sun Oct 03 19:39:25 2010 +0100
@@ -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);
+    }
+}
--- a/src/share/classes/java/nio/file/FileTreeWalker.java	Sat Oct 02 12:59:52 2010 +0100
+++ b/src/share/classes/java/nio/file/FileTreeWalker.java	Sun Oct 03 19:39:25 2010 +0100
@@ -38,7 +38,6 @@
 
 class FileTreeWalker {
     private final boolean followLinks;
-    private final boolean detectCycles;
     private final LinkOption[] linkOptions;
     private final FileVisitor<? super Path> visitor;
     private final int maxDepth;
@@ -48,17 +47,15 @@
                    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 @@
     /**
      * 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<AncestorDirectory>());
-        if (result == null) {
-            throw new NullPointerException("Visitor returned 'null'");
-        }
+        Objects.nonNull(result, "FileVisitor returned null");
     }
 
     /**
@@ -88,11 +83,8 @@
     private FileVisitResult walk(Path file,
                                  int depth,
                                  List<AncestorDirectory> 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 @@
             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 @@
                 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 @@
             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 @@
 
             // 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 @@
                         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 @@
 
         } finally {
             // remove key from trail if doing cycle detection
-            if (detectCycles) {
+            if (followLinks) {
                 ancestors.remove(ancestors.size()-1);
             }
         }
--- a/src/share/classes/java/nio/file/FileVisitOption.java	Sat Oct 02 12:59:52 2010 +0100
+++ b/src/share/classes/java/nio/file/FileVisitOption.java	Sun Oct 03 19:39:25 2010 +0100
@@ -37,9 +37,5 @@
     /**
      * Follow symbolic links.
      */
-    FOLLOW_LINKS,
-    /**
-     * Detect cycles in the file tree.
-     */
-    DETECT_CYCLES;
+    FOLLOW_LINKS;
 }
--- a/src/share/classes/java/nio/file/FileVisitor.java	Sat Oct 02 12:59:52 2010 +0100
+++ b/src/share/classes/java/nio/file/FileVisitor.java	Sun Oct 03 19:39:25 2010 +0100
@@ -40,33 +40,28 @@
  *     Path start = ...
  *     Files.walkFileTree(start, new SimpleFileVisitor&lt;Path&gt;() {
  *         &#64;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;
  *         }
  *         &#64;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;
  *         }
  *     });
  * </pre>
- * <p> 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.
+ * <p> 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.
  * <pre>
  *     final Path source = ...
  *     final Path target = ...
@@ -74,25 +69,21 @@
  *     Files.walkFileTree(source, EnumSet.of(FileVisitOption.FOLLOW_LINKS), Integer.MAX_VALUE,
  *         new SimpleFileVisitor&lt;Path&gt;() {
  *             &#64;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;
  *             }
  *             &#64;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 @@
      *
      * @param   dir
      *          a reference to the directory
+     * @param   attrs
+     *          the directory's basic attributes
      *
      * @return  the visit result
+     *
+     * @throws  IOException
+     *          if an I/O error occurs
      */
-    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
-     */
-    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 @@
      *          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 @@
      *          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;
 }
--- a/src/share/classes/java/nio/file/Files.java	Sat Oct 02 12:59:52 2010 +0100
+++ b/src/share/classes/java/nio/file/Files.java	Sun Oct 03 19:39:25 2010 +0100
@@ -135,9 +135,9 @@
      * 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.
      *
      * <p> 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 @@
      * due to an I/O exception, then the {@link FileVisitor#visitFileFailed
      * visitFileFailed} method is invoked with the I/O exception.
      *
-     * <p> 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 <em>sibling</em> of the directory.
+     * <p> 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
+     * <em>sibling</em> of the directory.
      *
      * <p> Where the directory is opened successfully, then the entries in the
      * directory, and their <em>descendants</em> are visited. When all entries
@@ -171,26 +169,25 @@
      * method is invoked as specified above).
      *
      * <p> 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}.
      *
      * <p> 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.
      *
      * <p> If a visitor returns a result of {@code null} then {@code
      * NullPointerException} is thrown.
@@ -215,11 +212,14 @@
      *          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<FileVisitOption> options,
                                     int maxDepth,
                                     FileVisitor<? super Path> visitor)
+        throws IOException
     {
         if (maxDepth < 0)
             throw new IllegalArgumentException("'maxDepth' is negative");
@@ -245,8 +245,12 @@
      *          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<? super Path> visitor) {
+    public static void walkFileTree(Path start, FileVisitor<? super Path> visitor)
+        throws IOException
+    {
         walkFileTree(start,
                      EnumSet.noneOf(FileVisitOption.class),
                      Integer.MAX_VALUE,
--- a/src/share/classes/java/nio/file/SimpleFileVisitor.java	Sat Oct 02 12:59:52 2010 +0100
+++ b/src/share/classes/java/nio/file/SimpleFileVisitor.java	Sun Oct 03 19:39:25 2010 +0100
@@ -27,7 +27,7 @@
 
 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
@@ -48,70 +48,47 @@
     }
 
     /**
-     * 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.
      *
      * <p> Unless overridden, this method returns {@link FileVisitResult#CONTINUE
      * 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.
-     *
-     * <p> 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.
      *
      * <p> Unless overridden, this method returns {@link FileVisitResult#CONTINUE
      * 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.
+     * Invoked for a file that could not be visited.
      *
-     * <p> 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 read the file
-     *          attributes failed
+     * <p> 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 @@
      *
      * <p> 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;
     }
 }
--- a/src/share/sample/nio/file/Chmod.java	Sat Oct 02 12:59:52 2010 +0100
+++ b/src/share/sample/nio/file/Chmod.java	Sun Oct 03 19:39:25 2010 +0100
@@ -285,18 +285,12 @@
         }
 
         @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);
             return CONTINUE;
--- a/src/share/sample/nio/file/Copy.java	Sat Oct 02 12:59:52 2010 +0100
+++ b/src/share/sample/nio/file/Copy.java	Sun Oct 03 19:39:25 2010 +0100
@@ -85,7 +85,7 @@
         }
 
         @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) ?
@@ -104,19 +104,9 @@
         }
 
         @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 @@
 
         @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;
         }
     }
--- a/src/share/sample/nio/file/WatchDir.java	Sat Oct 02 12:59:52 2010 +0100
+++ b/src/share/sample/nio/file/WatchDir.java	Sun Oct 03 19:39:25 2010 +0100
@@ -78,12 +78,10 @@
         // register directory and sub-directories
         Files.walkFileTree(start, new SimpleFileVisitor<Path>() {
             @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;
             }
         });
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/java/nio/file/Files/MaxDepth.java	Sun Oct 03 19:39:25 2010 +0100
@@ -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<FileVisitOption> opts = Collections.emptySet();
+            final int maxDepth = i;
+            Files.walkFileTree(top, opts, maxDepth, new SimpleFileVisitor<Path>() {
+                // 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;
+                }
+            });
+        }
+    }
+}
--- a/test/java/nio/file/Files/Misc.java	Sat Oct 02 12:59:52 2010 +0100
+++ b/test/java/nio/file/Files/Misc.java	Sun Oct 03 19:39:25 2010 +0100
@@ -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 @@
 
         SimpleFileVisitor<Path> visitor = new SimpleFileVisitor<Path>() { };
         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(".")));
+        try {  visitor.visitFile(dir, null);
         } catch (NullPointerException x3) {
-        try {  visitor.visitFile(dir, null);
+        try { visitor.visitFileFailed(null, new IOException());
         } catch (NullPointerException x4) {
-        try { visitor.visitFileFailed(null, new IOException());
+        try { visitor.visitFileFailed(dir, null);
         } catch (NullPointerException x5) {
-        try { visitor.visitFileFailed(dir, null);
+        try { visitor.postVisitDirectory(null, new IOException());
         } catch (NullPointerException x6) {
-        try { visitor.postVisitDirectory(null, new IOException());
-        } catch (NullPointerException x7) {
             // 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");
     }
--- a/test/java/nio/file/Files/PrintFileTree.java	Sat Oct 02 12:59:52 2010 +0100
+++ b/test/java/nio/file/Files/PrintFileTree.java	Sun Oct 03 19:39:25 2010 +0100
@@ -56,29 +56,34 @@
 
         final boolean reportCycles = printCycles;
         Files.walkFileTree(dir, options, Integer.MAX_VALUE, new FileVisitor<FileRef>() {
-            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;
             }
         });
     }
--- a/test/java/nio/file/Files/SkipSiblings.java	Sat Oct 02 12:59:52 2010 +0100
+++ b/test/java/nio/file/Files/SkipSiblings.java	Sun Oct 03 19:39:25 2010 +0100
@@ -54,32 +54,28 @@
     public static void main(String[] args) throws Exception {
         Path dir = Paths.get(args[0]);
 
-        Files.walkFileTree(dir, new FileVisitor<Path>() {
-            public FileVisitResult preVisitDirectory(Path dir) {
+        Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
+            @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);
-            }
         });
     }
 }
--- a/test/java/nio/file/Files/TerminateWalk.java	Sat Oct 02 12:59:52 2010 +0100
+++ b/test/java/nio/file/Files/TerminateWalk.java	Sun Oct 03 19:39:25 2010 +0100
@@ -49,22 +49,19 @@
     public static void main(String[] args) throws Exception {
         Path dir = Paths.get(args[0]);
 
-        Files.walkFileTree(dir, new FileVisitor<Path>() {
-            public FileVisitResult preVisitDirectory(Path dir) {
+        Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
                 return maybeTerminate();
             }
-            public FileVisitResult preVisitDirectoryFailed(Path dir, IOException exc) {
-                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();
-            }
         });
     }
 }
--- a/test/java/nio/file/Files/WalkWithSecurity.java	Sat Oct 02 12:59:52 2010 +0100
+++ b/test/java/nio/file/Files/WalkWithSecurity.java	Sun Oct 03 19:39:25 2010 +0100
@@ -116,7 +116,7 @@
         }
 
         @Override
-        public FileVisitResult preVisitDirectory(Path dir) {
+        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
             System.out.println(dir);
             count++;
             return FileVisitResult.CONTINUE;
--- a/test/java/nio/file/Files/walk_file_tree.sh	Sat Oct 02 12:59:52 2010 +0100
+++ b/test/java/nio/file/Files/walk_file_tree.sh	Sun Oct 03 19:39:25 2010 +0100
@@ -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 @@
 $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"
 
--- a/test/java/nio/file/TestUtil.java	Sat Oct 02 12:59:52 2010 +0100
+++ b/test/java/nio/file/TestUtil.java	Sun Oct 03 19:39:25 2010 +0100
@@ -44,15 +44,10 @@
         return createTemporaryDirectory(System.getProperty("java.io.tmpdir"));
     }
 
-    static void removeAll(Path dir) {
+    static void removeAll(Path dir) throws IOException {
         Files.walkFileTree(dir, new FileVisitor<Path>() {
             @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