Skip to content

Commit 390a58b

Browse files
committed
Ignore FileNotFoundExceptions on Files#walk usages. Fixes: #666
1 parent a71abe3 commit 390a58b

File tree

6 files changed

+601
-4
lines changed

6 files changed

+601
-4
lines changed

core/src/main/java/de/bluecolored/bluemap/core/resources/pack/Pack.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import de.bluecolored.bluemap.core.resources.ResourcePath;
3131
import de.bluecolored.bluemap.core.resources.adapter.ResourcesGson;
3232
import de.bluecolored.bluemap.core.resources.pack.resourcepack.ResourcePack;
33+
import de.bluecolored.bluemap.core.util.FileHelper;
3334
import de.bluecolored.bluemap.core.util.Key;
3435
import lombok.Getter;
3536
import lombok.RequiredArgsConstructor;
@@ -182,7 +183,7 @@ protected static Stream<Path> walk(Path root) {
182183
if (!Files.exists(root)) return Stream.empty();
183184
if (Files.isRegularFile(root)) return Stream.of(root);
184185
try {
185-
return Files.walk(root);
186+
return FileHelper.walk(root);
186187
} catch (IOException ex) {
187188
throw new CompletionException(ex);
188189
}

core/src/main/java/de/bluecolored/bluemap/core/storage/file/FileGridStorage.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import de.bluecolored.bluemap.core.storage.ItemStorage;
2929
import de.bluecolored.bluemap.core.storage.compression.CompressedInputStream;
3030
import de.bluecolored.bluemap.core.storage.compression.Compression;
31+
import de.bluecolored.bluemap.core.util.FileHelper;
3132
import lombok.Getter;
3233
import lombok.RequiredArgsConstructor;
3334
import org.jetbrains.annotations.Nullable;
@@ -77,11 +78,10 @@ public ItemStorage cell(int x, int z) {
7778
return new FileItemStorage(getItemPath(x, z), compression, atomic);
7879
}
7980

80-
@SuppressWarnings("resource")
8181
@Override
8282
public Stream<Cell> stream() throws IOException {
8383
if (!Files.exists(root)) return Stream.empty();
84-
return Files.walk(root)
84+
return FileHelper.walk(root)
8585
.filter(Files::isRegularFile)
8686
.<Cell>map(itemPath -> {
8787
Path path = itemPath;

core/src/main/java/de/bluecolored/bluemap/core/storage/file/FileMapStorage.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import de.bluecolored.bluemap.core.storage.MapStorage;
3232
import de.bluecolored.bluemap.core.storage.compression.Compression;
3333
import de.bluecolored.bluemap.core.util.DeletingPathVisitor;
34+
import de.bluecolored.bluemap.core.util.FileHelper;
3435

3536
import java.io.IOException;
3637
import java.nio.file.Files;
@@ -154,7 +155,7 @@ public void delete(DoublePredicate onProgress) throws IOException {
154155
final LinkedList<Path> subFiles;
155156

156157
// collect sub-files to be able to provide progress-updates
157-
try (Stream<Path> pathStream = Files.walk(root, 3)) {
158+
try (Stream<Path> pathStream = FileHelper.walk(root, 3)) {
158159
subFiles = pathStream.collect(Collectors.toCollection(LinkedList::new));
159160
}
160161
subFilesCount = subFiles.size();

core/src/main/java/de/bluecolored/bluemap/core/util/FileHelper.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@
3434
import java.nio.file.WatchService;
3535
import java.nio.file.*;
3636
import java.nio.file.attribute.FileAttribute;
37+
import java.util.Spliterator;
38+
import java.util.Spliterators;
3739
import java.util.concurrent.TimeUnit;
40+
import java.util.stream.Stream;
41+
import java.util.stream.StreamSupport;
3842

3943
public class FileHelper {
4044

@@ -140,4 +144,30 @@ public static boolean awaitExistence(Path path, long timeout, TimeUnit unit) thr
140144
}
141145
}
142146

147+
/**
148+
* Adapted version of {@link Files#walk(Path, int, FileVisitOption...)}.
149+
* This version ignores NoSuchFileException if they occur while iterating the file-tree.
150+
*/
151+
public static Stream<Path> walk(Path start, int maxDepth, FileVisitOption... options) throws IOException {
152+
FileTreeIterator iterator = new FileTreeIterator(start, maxDepth, options);
153+
try {
154+
Spliterator<FileTreeWalker.Event> spliterator =
155+
Spliterators.spliteratorUnknownSize(iterator, Spliterator.DISTINCT);
156+
return StreamSupport.stream(spliterator, false)
157+
.onClose(iterator::close)
158+
.map(FileTreeWalker.Event::file);
159+
} catch (Error|RuntimeException e) {
160+
iterator.close();
161+
throw e;
162+
}
163+
}
164+
165+
/**
166+
* Adapted version of {@link Files#walk(Path, FileVisitOption...)} .
167+
* This version ignores NoSuchFileException if they occur while iterating the file-tree.
168+
*/
169+
public static Stream<Path> walk(Path start, FileVisitOption... options) throws IOException {
170+
return walk(start, Integer.MAX_VALUE, options);
171+
}
172+
143173
}
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
* This file is part of BlueMap, licensed under the MIT License (MIT).
3+
*
4+
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
5+
* Copyright (c) contributors
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
/*
26+
* Copyright (c) 2013, 2023, Oracle and/or its affiliates. All rights reserved.
27+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
28+
*
29+
* This code is free software; you can redistribute it and/or modify it
30+
* under the terms of the GNU General Public License version 2 only, as
31+
* published by the Free Software Foundation. Oracle designates this
32+
* particular file as subject to the "Classpath" exception as provided
33+
* by Oracle in the LICENSE file that accompanied this code.
34+
*
35+
* This code is distributed in the hope that it will be useful, but WITHOUT
36+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
37+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
38+
* version 2 for more details (a copy is included in the LICENSE file that
39+
* accompanied this code).
40+
*
41+
* You should have received a copy of the GNU General Public License version
42+
* 2 along with this work; if not, write to the Free Software Foundation,
43+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
44+
*
45+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
46+
* or visit www.oracle.com if you need additional information or have any
47+
* questions.
48+
*/
49+
50+
package de.bluecolored.bluemap.core.util;
51+
52+
import java.io.Closeable;
53+
import java.io.IOException;
54+
import java.io.UncheckedIOException;
55+
import java.nio.file.FileVisitOption;
56+
import java.nio.file.NoSuchFileException;
57+
import java.nio.file.Path;
58+
import java.util.Iterator;
59+
import java.util.List;
60+
import java.util.NoSuchElementException;
61+
62+
/**
63+
* Adapted version of java.nio.file.FileTreeIterator.
64+
* This version ignores NoSuchFileException if they occur while iterating the file-tree.
65+
* Required to implement FileHelper#walk
66+
*/
67+
class FileTreeIterator implements Iterator<FileTreeWalker.Event>, Closeable {
68+
private final FileTreeWalker walker;
69+
private FileTreeWalker.Event next;
70+
71+
/**
72+
* Creates a new iterator to walk the file tree starting at the given file.
73+
*
74+
* @throws IllegalArgumentException
75+
* if {@code maxDepth} is negative
76+
* @throws IOException
77+
* if an I/O errors occurs opening the starting file
78+
* @throws SecurityException
79+
* if the security manager denies access to the starting file
80+
* @throws NullPointerException
81+
* if {@code start} or {@code options} is {@code null} or
82+
* the options array contains a {@code null} element
83+
*/
84+
FileTreeIterator(Path start, int maxDepth, FileVisitOption... options)
85+
throws IOException
86+
{
87+
this.walker = new FileTreeWalker(List.of(options), maxDepth);
88+
this.next = walker.walk(start);
89+
assert next.type() == FileTreeWalker.EventType.ENTRY ||
90+
next.type() == FileTreeWalker.EventType.START_DIRECTORY;
91+
92+
// IOException if there is a problem accessing the starting file
93+
IOException ioe = next.ioeException();
94+
if (ioe != null)
95+
throw ioe;
96+
}
97+
98+
private void fetchNextIfNeeded() {
99+
if (next == null) {
100+
FileTreeWalker.Event ev = walker.next();
101+
while (ev != null) {
102+
IOException ioe = ev.ioeException();
103+
if (ioe != null) {
104+
if (ioe instanceof NoSuchFileException)
105+
continue; // ignore NoSuchFileExceptions, and just continue iterating
106+
throw new UncheckedIOException(ioe);
107+
}
108+
109+
// END_DIRECTORY events are ignored
110+
if (ev.type() != FileTreeWalker.EventType.END_DIRECTORY) {
111+
next = ev;
112+
return;
113+
}
114+
ev = walker.next();
115+
}
116+
}
117+
}
118+
119+
@Override
120+
public boolean hasNext() {
121+
if (!walker.isOpen())
122+
throw new IllegalStateException();
123+
fetchNextIfNeeded();
124+
return next != null;
125+
}
126+
127+
@Override
128+
public FileTreeWalker.Event next() {
129+
if (!walker.isOpen())
130+
throw new IllegalStateException();
131+
fetchNextIfNeeded();
132+
if (next == null)
133+
throw new NoSuchElementException();
134+
FileTreeWalker.Event result = next;
135+
next = null;
136+
return result;
137+
}
138+
139+
@Override
140+
public void close() {
141+
walker.close();
142+
}
143+
144+
}

0 commit comments

Comments
 (0)