Add PathUtils.waitFor(Path, Duration, LinkOption...).
MODIFY
src/changes/changes.xml
src/changes/changes.xml
@@ -164,9 +164,12 @@ The <action> type attribute can be add,update,fix,remove.
Add FileTimes.
</action>
<action dev="ggregory" type="add" due-to="Gary Gregory">
- Update FileEntry to use FileTime instead of long for file time stamps.
+ Add PathUtils.waitFor(Path, Duration, LinkOption...).
</action>
<!-- UPDATE -->
+ <action dev="ggregory" type="add" due-to="Gary Gregory">
+ Update FileEntry to use FileTime instead of long for file time stamps.
+ </action>
<action dev="ggregory" type="update" due-to="Dependabot">
Bump Maven Javadoc plugin from 3.2.0 to 3.3.0.
</action>

MODIFY
src/main/java/org/apache/commons/io/FileUtils.java
src/main/java/org/apache/commons/io/FileUtils.java
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.FileTime;
+import java.time.Duration;
import java.time.Instant;
import java.time.LocalTime;
import java.time.ZoneId;
@@ -3175,28 +3176,7 @@ private static void validateMoveParameters(final File source, final File destina
*/
public static boolean waitFor(final File file, final int seconds) {
Objects.requireNonNull(file, "file");
- final long finishAtMillis = System.currentTimeMillis() + (seconds * 1000L);
- boolean wasInterrupted = false;
- try {
- while (!file.exists()) {
- final long remainingMillis = finishAtMillis - System.currentTimeMillis();
- if (remainingMillis < 0){
- return false;
- }
- try {
- Thread.sleep(Math.min(100, remainingMillis));
- } catch (final InterruptedException ignore) {
- wasInterrupted = true;
- } catch (final Exception ex) {
- break;
- }
- }
- } finally {
- if (wasInterrupted) {
- Thread.currentThread().interrupt();
- }
- }
- return true;
+ return PathUtils.waitFor(file.toPath(), Duration.ofSeconds(seconds), PathUtils.EMPTY_LINK_OPTION_ARRAY);
}
/**

MODIFY
src/main/java/org/apache/commons/io/file/PathUtils.java
src/main/java/org/apache/commons/io/file/PathUtils.java
import java.nio.file.attribute.PosixFileAttributeView;
import java.nio.file.attribute.PosixFileAttributes;
import java.nio.file.attribute.PosixFilePermission;
+import java.time.Duration;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
@@ -1068,6 +1069,46 @@ public static Path setReadOnly(final Path path, final boolean readOnly, final Li
return visitFileTree(visitor, Paths.get(uri));
}
+ /**
+ * Waits for the file system to propagate a file creation, with a timeout.
+ * <p>
+ * This method repeatedly tests {@link File#exists()} until it returns true up to the maximum time specified in seconds.
+ * </p>
+ *
+ * @param file the file to check, must not be {@code null}.
+ * @param duration the maximum time in seconds to wait.
+ * @param options options indicating how symbolic links are handled.
+ * @return true if file exists.
+ * @throws NullPointerException if the file is {@code null}.
+ * @since 2.12.0
+ */
+ public static boolean waitFor(final Path file, final Duration duration, LinkOption... options) {
+ Objects.requireNonNull(file, "file");
+ final Instant finishInstant = Instant.now().plus(duration);
+ boolean wasInterrupted = false;
+ final long minSleepMillis = 100;
+ try {
+ while (!Files.exists(file, options)) {
+ final long remainingMillis = finishInstant.minusMillis(System.currentTimeMillis()).toEpochMilli();
+ if (remainingMillis < 0) {
+ return false;
+ }
+ try {
+ Thread.sleep(Math.min(minSleepMillis, remainingMillis));
+ } catch (final InterruptedException ignore) {
+ wasInterrupted = true;
+ } catch (final Exception ex) {
+ break;
+ }
+ }
+ } finally {
+ if (wasInterrupted) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ return true;
+ }
+
/**
* Returns a stream of filtered paths.
*

Add PathUtils.waitFor(Path, Duration, LinkOption...).