LakiGeri
LakiGeri

Reputation: 2105

Java8 predicate with try catch

I'm a newbie of java8 streaming word, but I want to understand that. After making some simple stuff, I went deeper, so I like to list all the files in a folder. I got the idea from here.

    Files.walk(start)
        .filter(Files::isRegularFile)
        .forEach(System.out::println);

It works well, but I ran that a folder, where some spec file are located, and throws AccessDeniedException. I made some search, and I know its not a simple problem. I try to handle the exception like this:

private static Predicate<Path> fillAccessDenied() {
    return p -> {
        try {
            if(Files.isRegularFile(p)) {                    
                return true;                
            } else {
                return false;
            }
        } catch (UncheckedIOException e) {
            e.printStackTrace();
            return false;
        }
    };
}
  //....
    Files.walk(start)
        .filter(Utils.fillAccessDenied())
        .forEach(System.out::println);

But the problem remains, and I don't understand why. Can somebody give me some clue what is the problem here?

Thanks in advance!

EDIT:

I took the system out between a try catch, but the problem remains.

public static void println(Object x) {

    try {
        String s = String.valueOf(x);
        System.out.println(s);
    } catch (UncheckedIOException e) {
        e.printStackTrace();
    }
}

//...

    Files.walk(start)
        .filter(Utils.fillAccessDenied())
        .forEach(Utils::println);

My input path "/" and I run it on linux. Does it cause this problem?

stacktrace:

java.io.UncheckedIOException: java.nio.file.AccessDeniedException: /etc/sysconfig/network/providers
    at java.nio.file.FileTreeIterator.fetchNextIfNeeded(FileTreeIterator.java:88)
    at java.nio.file.FileTreeIterator.hasNext(FileTreeIterator.java:104)
    at java.util.Iterator.forEachRemaining(Iterator.java:115)
    at java.util.Spliterators$IteratorSpliterator.forEachRemaining(Spliterators.java:1801)
    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
    at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(ForEachOps.java:151)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(ForEachOps.java:174)
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
    at java.util.stream.ReferencePipeline.forEach(ReferencePipeline.java:418)
    at hu.gergelylakatos.jbackupper.JBackupper.main(JBackupper.java:45)
Caused by: java.nio.file.AccessDeniedException: /etc/sysconfig/network/providers
    at sun.nio.fs.UnixException.translateToIOException(UnixException.java:84)
    at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102)
    at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107)
    at sun.nio.fs.UnixFileSystemProvider.newDirectoryStream(UnixFileSystemProvider.java:427)
    at java.nio.file.Files.newDirectoryStream(Files.java:457)
    at java.nio.file.FileTreeWalker.visit(FileTreeWalker.java:300)
    at java.nio.file.FileTreeWalker.next(FileTreeWalker.java:372)
    at java.nio.file.FileTreeIterator.fetchNextIfNeeded(FileTreeIterator.java:95)
    ... 10 more

Upvotes: 2

Views: 2342

Answers (2)

Holger
Holger

Reputation: 298153

You can not catch the exception at Files.isRegularFile when it isn’t thrown at this place. Since the exception is thrown inside the code implementing iteration logic of Files.walk, you can only catch it at the forEach method, when it has already terminated the entire operation.

You may implement the iteration logic yourself to have more control. E.g.

public static Stream<Path> traverse(Path p, BiConsumer<Path,IOException> handler) {
    if(Files.isDirectory(p)) try {
        return Stream.concat(Stream.of(p),
            Files.list(p).flatMap(sub -> traverse(sub, handler)));
    } catch(IOException ex) {
        handler.accept(p,ex);
    }
    return Stream.of(p);
}

which you may use like

traverse(start, (p,ex) -> System.err.println(p+": "+ex))
    .filter(Files::isRegularFile)
    .forEach(System.out::println);

to print the exception and continue, or

traverse(start, (p,ex) -> { throw new UncheckedIOException(ex); })
    .filter(Files::isRegularFile)
    .forEach(System.out::println);

to bail out like Files.walk.


When you know that you are interested in regular files only, you can create a specialized method like

public static Stream<Path> traverseLeafs(Path p, BiConsumer<Path,IOException> handler) {
    if(Files.isDirectory(p)) try {
        return Files.list(p).flatMap(sub -> traverseLeafs(sub, handler));
    } catch(IOException ex) {
        handler.accept(p,ex);
        return Stream.empty();
    }
    return Stream.of(p);
}

which excludes directories right at the source. You may still use filter(Files::isRegularFile) though, to exclude special files which are neither directory nor regular file.

You may also use Files.isDirectory(p) && Files.isReadable(p) as condition here, to prevent the AccessDeniedException from occurring, at least as long as the access flags do not change between Files.isReadable(p) and Files.list(p).

Upvotes: 4

dustytrash
dustytrash

Reputation: 1590

I believe you need to change your filter. Checking if the file isRegular, doesn't mean you won't get the AccessException.

Ensure you're filtering out folders.

private static Predicate<Path> fillAccessDenied() {
    return p -> {
        try {
            if(Files.isReadable(p) && !Files.isDirectory(p)) {                    
                return true;                
            } else {
                return false;
            }
        } catch (UncheckedIOException e) {
            e.printStackTrace();
            return false;
        }
    };
}

Upvotes: 0

Related Questions