diff --git a/files-api/src/main/java/module-info.java b/files-api/src/main/java/module-info.java index 1d0afc8..911bafa 100644 --- a/files-api/src/main/java/module-info.java +++ b/files-api/src/main/java/module-info.java @@ -5,4 +5,5 @@ module org.xbib.files { exports org.xbib.files; uses FileServiceProvider; provides FileServiceProvider with DefaultFileServiceProvider; + requires java.logging; } diff --git a/files-api/src/main/java/org/xbib/files/FileService.java b/files-api/src/main/java/org/xbib/files/FileService.java index 6ef7dfa..8bae9b8 100644 --- a/files-api/src/main/java/org/xbib/files/FileService.java +++ b/files-api/src/main/java/org/xbib/files/FileService.java @@ -18,6 +18,8 @@ import java.util.Objects; import java.util.Optional; import java.util.ServiceLoader; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; import java.util.stream.Stream; public interface FileService { @@ -115,4 +117,29 @@ public interface FileService { Stream walk(String path, FileVisitOption... options) throws IOException; Stream walk(String path, int maxdepth, FileVisitOption... options) throws IOException; + + /** + * Replacement for Files.walk() without AccessDeniedException. + * @param p the path + * @return a stream of readable files under the path + */ + static Stream walk(Path p) { + if (Files.isReadable(p)) { + if (Files.isDirectory(p)) { + try (Stream stream = Files.list(p)) { + return stream.flatMap(FileService::walk); + } catch (IOException e) { + Logger.getLogger(FileService.class.getName()) + .log(Level.FINE,"skipped " + p + " because of " + e.getMessage()); + return Stream.empty(); + } + } else { + return Stream.of(p); + } + } else { + Logger.getLogger(FileService.class.getName()) + .log(Level.WARNING, "unreadable: " + p); + } + return Stream.empty(); + } } diff --git a/files-api/src/main/java/org/xbib/files/FileTreeIterator.java b/files-api/src/main/java/org/xbib/files/FileTreeIterator.java index 2691a96..9278298 100644 --- a/files-api/src/main/java/org/xbib/files/FileTreeIterator.java +++ b/files-api/src/main/java/org/xbib/files/FileTreeIterator.java @@ -12,7 +12,6 @@ import org.xbib.files.FileTreeWalker.Event; /** * This source is a copy taken from java.nio.file.FileTreeIterator which is inaccessible. - * * An {@code Iterator} to iterate over the nodes of a file tree. * *
{@code
@@ -27,7 +26,9 @@ import org.xbib.files.FileTreeWalker.Event;
  */
 
 class FileTreeIterator implements Iterator, Closeable {
+
     private final FileTreeWalker walker;
+
     private Event next;
 
     /**
@@ -37,21 +38,15 @@ class FileTreeIterator implements Iterator, Closeable {
      *          if {@code maxDepth} is negative
      * @throws  IOException
      *          if an I/O errors occurs opening the starting file
-     * @throws  SecurityException
-     *          if the security manager denies access to the starting file
      * @throws  NullPointerException
      *          if {@code start} or {@code options} is {@code null} or
      *          the options array contains a {@code null} element
      */
-    FileTreeIterator(Path start, int maxDepth, FileVisitOption... options)
-        throws IOException
-    {
+    FileTreeIterator(Path start, int maxDepth, FileVisitOption... options) throws IOException {
         this.walker = new FileTreeWalker(Arrays.asList(options), maxDepth);
         this.next = walker.walk(start);
         assert next.type() == FileTreeWalker.EventType.ENTRY ||
                next.type() == FileTreeWalker.EventType.START_DIRECTORY;
-
-        // IOException if there a problem accessing the starting file
         IOException ioe = next.ioeException();
         if (ioe != null)
             throw ioe;
@@ -62,10 +57,9 @@ class FileTreeIterator implements Iterator, Closeable {
             FileTreeWalker.Event ev = walker.next();
             while (ev != null) {
                 IOException ioe = ev.ioeException();
-                if (ioe != null)
+                if (ioe != null) {
                     throw new UncheckedIOException(ioe);
-
-                // END_DIRECTORY events are ignored
+                }
                 if (ev.type() != FileTreeWalker.EventType.END_DIRECTORY) {
                     next = ev;
                     return;
@@ -77,19 +71,22 @@ class FileTreeIterator implements Iterator, Closeable {
 
     @Override
     public boolean hasNext() {
-        if (!walker.isOpen())
+        if (!walker.isOpen()) {
             throw new IllegalStateException();
+        }
         fetchNextIfNeeded();
         return next != null;
     }
 
     @Override
     public Event next() {
-        if (!walker.isOpen())
+        if (!walker.isOpen()) {
             throw new IllegalStateException();
+        }
         fetchNextIfNeeded();
-        if (next == null)
+        if (next == null) {
             throw new NoSuchElementException();
+        }
         Event result = next;
         next = null;
         return result;
diff --git a/files-api/src/main/java/org/xbib/files/FileTreeWalker.java b/files-api/src/main/java/org/xbib/files/FileTreeWalker.java
index 02844e6..7240bfe 100644
--- a/files-api/src/main/java/org/xbib/files/FileTreeWalker.java
+++ b/files-api/src/main/java/org/xbib/files/FileTreeWalker.java
@@ -12,11 +12,12 @@ import java.io.Closeable;
 import java.io.IOException;
 import java.util.ArrayDeque;
 import java.util.Collection;
+import java.util.Deque;
 import java.util.Iterator;
+import java.util.Objects;
 
 /**
  * This source is a copy taken from java.nio.file.FileTreeWalker which is inaccessible.
- *
  * Walks a file tree, generating a sequence of events corresponding to the files
  * in the tree.
  *
@@ -38,10 +39,11 @@ import java.util.Iterator;
  */
 
 class FileTreeWalker implements Closeable {
+
     private final boolean followLinks;
     private final LinkOption[] linkOptions;
     private final int maxDepth;
-    private final ArrayDeque stack = new ArrayDeque<>();
+    private final Deque stack = new ArrayDeque<>();
     private boolean closed;
 
     /**
@@ -89,7 +91,7 @@ class FileTreeWalker implements Closeable {
     /**
      * The event types.
      */
-    static enum EventType {
+    enum EventType {
         /**
          * Start of a directory
          */
@@ -109,13 +111,13 @@ class FileTreeWalker implements Closeable {
      */
     static class Event {
         private final EventType type;
-        private final Path file;
+        private final Path path;
         private final BasicFileAttributes attrs;
         private final IOException ioe;
 
-        private Event(EventType type, Path file, BasicFileAttributes attrs, IOException ioe) {
+        private Event(EventType type, Path path, BasicFileAttributes attrs, IOException ioe) {
             this.type = type;
-            this.file = file;
+            this.path = path;
             this.attrs = attrs;
             this.ioe = ioe;
         }
@@ -132,8 +134,8 @@ class FileTreeWalker implements Closeable {
             return type;
         }
 
-        Path file() {
-            return file;
+        Path path() {
+            return path;
         }
 
         BasicFileAttributes attributes() {
@@ -161,18 +163,17 @@ class FileTreeWalker implements Closeable {
         boolean fl = false;
         for (FileVisitOption option: options) {
             // will throw NPE if options contains null
-            switch (option) {
-                case FOLLOW_LINKS : fl = true; break;
-                default:
-                    throw new AssertionError("Should not get here");
+            if (Objects.requireNonNull(option) == FileVisitOption.FOLLOW_LINKS) {
+                fl = true;
+            } else {
+                throw new AssertionError("Should not get here");
             }
         }
         if (maxDepth < 0)
             throw new IllegalArgumentException("'maxDepth' is negative");
 
         this.followLinks = fl;
-        this.linkOptions = (fl) ? new LinkOption[0] :
-            new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
+        this.linkOptions = (fl) ? new LinkOption[0] : new LinkOption[] { LinkOption.NOFOLLOW_LINKS };
         this.maxDepth = maxDepth;
     }
 
@@ -181,22 +182,18 @@ class FileTreeWalker implements Closeable {
      * the walk is following sym links is not. The {@code canUseCached}
      * argument determines whether this method can use cached attributes.
      */
-    private BasicFileAttributes getAttributes(Path file, boolean canUseCached)
-        throws IOException
-    {
+    private BasicFileAttributes getAttributes(Path file)
+        throws IOException {
         // attempt to get attributes of file. If fails and we are following
         // links then a link target might not exist so get attributes of link
         BasicFileAttributes attrs;
         try {
             attrs = Files.readAttributes(file, BasicFileAttributes.class, linkOptions);
         } catch (IOException ioe) {
-            if (!followLinks)
+            if (!followLinks) {
                 throw ioe;
-
-            // attempt to get attrmptes without following links
-            attrs = Files.readAttributes(file,
-                                         BasicFileAttributes.class,
-                                         LinkOption.NOFOLLOW_LINKS);
+            }
+            attrs = Files.readAttributes(file, BasicFileAttributes.class, LinkOption.NOFOLLOW_LINKS);
         }
         return attrs;
     }
@@ -232,70 +229,39 @@ class FileTreeWalker implements Closeable {
     /**
      * Visits the given file, returning the {@code Event} corresponding to that
      * visit.
-     *
-     * The {@code ignoreSecurityException} parameter determines whether
-     * any SecurityException should be ignored or not. If a SecurityException
-     * is thrown, and is ignored, then this method returns {@code null} to
-     * mean that there is no event corresponding to a visit to the file.
-     *
-     * The {@code canUseCached} parameter determines whether cached attributes
-     * for the file can be used or not.
      */
-    private Event visit(Path entry, boolean ignoreSecurityException, boolean canUseCached) {
-        // need the file attributes
+    private Event visit(Path entry) {
         BasicFileAttributes attrs;
         try {
-            attrs = getAttributes(entry, canUseCached);
+            attrs = getAttributes(entry);
         } catch (IOException ioe) {
             return new Event(EventType.ENTRY, entry, ioe);
-        } catch (SecurityException se) {
-            if (ignoreSecurityException)
-                return null;
-            throw se;
         }
-
-        // at maximum depth or file is not a directory
         int depth = stack.size();
         if (depth >= maxDepth || !attrs.isDirectory()) {
             return new Event(EventType.ENTRY, entry, attrs);
         }
-
-        // check for cycles when following links
         if (followLinks && wouldLoop(entry, attrs.fileKey())) {
-            return new Event(EventType.ENTRY, entry,
-                             new FileSystemLoopException(entry.toString()));
+            return new Event(EventType.ENTRY, entry, new FileSystemLoopException(entry.toString()));
         }
-
-        // file is a directory, attempt to open it
-        DirectoryStream stream = null;
+        DirectoryStream stream;
         try {
             stream = Files.newDirectoryStream(entry);
         } catch (IOException ioe) {
             return new Event(EventType.ENTRY, entry, ioe);
-        } catch (SecurityException se) {
-            if (ignoreSecurityException)
-                return null;
-            throw se;
         }
-
-        // push a directory node to the stack and return an event
         stack.push(new DirectoryNode(entry, attrs.fileKey(), stream));
         return new Event(EventType.START_DIRECTORY, entry, attrs);
     }
 
-
     /**
      * Start walking from the given file.
      */
     Event walk(Path file) {
-        if (closed)
+        if (closed) {
             throw new IllegalStateException("Closed");
-
-        Event ev = visit(file,
-                         false,   // ignoreSecurityException
-                         false);  // canUseCached
-        assert ev != null;
-        return ev;
+        }
+        return visit(file);
     }
 
     /**
@@ -304,51 +270,35 @@ class FileTreeWalker implements Closeable {
      */
     Event next() {
         DirectoryNode top = stack.peek();
-        if (top == null)
-            return null;      // stack is empty, we are done
-
-        // continue iteration of the directory at the top of the stack
-        Event ev;
-        do {
-            Path entry = null;
-            IOException ioe = null;
-
-            // get next entry in the directory
-            if (!top.skipped()) {
-                Iterator iterator = top.iterator();
-                try {
-                    if (iterator.hasNext()) {
-                        entry = iterator.next();
-                    }
-                } catch (DirectoryIteratorException x) {
-                    ioe = x.getCause();
+        if (top == null) {
+            return null;
+        }
+        Path entry = null;
+        IOException ioe = null;
+        if (!top.skipped()) {
+            Iterator iterator = top.iterator();
+            try {
+                if (iterator.hasNext()) {
+                    entry = iterator.next();
+                }
+            } catch (DirectoryIteratorException x) {
+                ioe = x.getCause();
+            }
+        }
+        if (entry == null) {
+            try {
+                top.stream().close();
+            } catch (IOException e) {
+                if (ioe == null) {
+                    ioe = e;
+                } else {
+                    ioe.addSuppressed(e);
                 }
             }
-
-            // no next entry so close and pop directory,
-            // creating corresponding event
-            if (entry == null) {
-                try {
-                    top.stream().close();
-                } catch (IOException e) {
-                    if (ioe == null) {
-                        ioe = e;
-                    } else {
-                        ioe.addSuppressed(e);
-                    }
-                }
-                stack.pop();
-                return new Event(EventType.END_DIRECTORY, top.directory(), ioe);
-            }
-
-            // visit the entry
-            ev = visit(entry,
-                       true,   // ignoreSecurityException
-                       true);  // canUseCached
-
-        } while (ev == null);
-
-        return ev;
+            stack.pop();
+            return new Event(EventType.END_DIRECTORY, top.directory(), ioe);
+        }
+        return visit(entry);
     }
 
     /**
@@ -362,17 +312,9 @@ class FileTreeWalker implements Closeable {
             DirectoryNode node = stack.pop();
             try {
                 node.stream().close();
-            } catch (IOException ignore) { }
-        }
-    }
-
-    /**
-     * Skips the remaining entries in the directory at the top of the stack.
-     * This method is a no-op if the stack is empty or the walker is closed.
-     */
-    void skipRemainingSiblings() {
-        if (!stack.isEmpty()) {
-            stack.peek().skip();
+            } catch (IOException ignore) {
+                // ignore
+            }
         }
     }
 
diff --git a/files-api/src/main/java/org/xbib/files/FileWalker.java b/files-api/src/main/java/org/xbib/files/FileWalker.java
index b94e858..3133533 100644
--- a/files-api/src/main/java/org/xbib/files/FileWalker.java
+++ b/files-api/src/main/java/org/xbib/files/FileWalker.java
@@ -7,7 +7,6 @@ import java.nio.file.DirectoryIteratorException;
 import java.nio.file.DirectoryStream;
 import java.nio.file.FileSystemLoopException;
 import java.nio.file.FileVisitOption;
-import java.nio.file.Files;
 import java.nio.file.NotDirectoryException;
 import java.nio.file.Path;
 import java.nio.file.attribute.BasicFileAttributes;
@@ -64,10 +63,6 @@ public class FileWalker {
      *          a directory (optional specific exception)
      * @throws  IOException
      *          if an I/O error occurs when opening the directory
-     * @throws  SecurityException
-     *          In the case of the default provider, and a security manager is
-     *          installed, the {@link SecurityManager#checkRead(String) checkRead}
-     *          method is invoked to check read access to the directory.
      */
     public static Stream list(DirectoryStream ds) throws IOException {
         try {
@@ -90,10 +85,8 @@ public class FileWalker {
                     }
                 }
             };
-            Spliterator spliterator =
-                    Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT);
-            return StreamSupport.stream(spliterator, false)
-                    .onClose(asUncheckedRunnable(ds));
+            Spliterator spliterator = Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT);
+            return StreamSupport.stream(spliterator, false).onClose(asUncheckedRunnable(ds));
         } catch (Error|RuntimeException e) {
             try {
                 ds.close();
@@ -179,11 +172,6 @@ public class FileWalker {
      *
      * @throws  IllegalArgumentException
      *          if the {@code maxDepth} parameter is negative
-     * @throws  SecurityException
-     *          If the security manager denies access to the starting file.
-     *          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 when accessing the starting file.
      */
@@ -204,7 +192,7 @@ public class FileWalker {
                             throw new UncheckedIOException(e);
                         }
                     })
-                    .map(FileTreeWalker.Event::file);
+                    .map(FileTreeWalker.Event::path);
         } catch (Error|RuntimeException e) {
             iterator.close();
             closeable.close();
@@ -213,8 +201,7 @@ public class FileWalker {
     }
 
     /**
-     * Convert a Closeable to a Runnable by converting checked IOException
-     * to UncheckedIOException
+     * Convert a Closeable to a Runnable by converting checked IOException to UncheckedIOException.
      */
     private static Runnable asUncheckedRunnable(Closeable c) {
         return () -> {
diff --git a/gradle.properties b/gradle.properties
index d88b328..f803b8b 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -1,5 +1,5 @@
 group = org.xbib
 name = files
-version = 4.1.0
+version = 4.2.0
 
 org.gradle.warning.mode = ALL