You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2022/07/19 12:49:38 UTC

[commons-io] branch master updated: Add PathUtils.getLastModifiedFileTime(*).

This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-io.git


The following commit(s) were added to refs/heads/master by this push:
     new cb6f3aab Add PathUtils.getLastModifiedFileTime(*).
cb6f3aab is described below

commit cb6f3aabcf872aeab3bb4c975d163f340b93f051
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Tue Jul 19 08:49:31 2022 -0400

    Add PathUtils.getLastModifiedFileTime(*).
---
 src/changes/changes.xml                            |  3 +
 .../java/org/apache/commons/io/file/PathUtils.java | 71 ++++++++++++++++++++++
 .../org/apache/commons/io/file/PathUtilsTest.java  | 51 ++++++++++++++--
 3 files changed, 120 insertions(+), 5 deletions(-)

diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 5fc2e954..83760b2f 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -403,6 +403,9 @@ The <action> type attribute can be add,update,fix,remove.
       <action dev="ggregory" type="add" due-to="Gary Gregory">
         Add XmlStreamWriter(OutputStream, Charset).
       </action>
+      <action dev="ggregory" type="add" due-to="Gary Gregory">
+        Add PathUtils.getLastModifiedFileTime(*).
+      </action>
       <!-- UPDATE -->
       <action dev="kinow" type="update" due-to="Dependabot, Gary Gregory">
         Bump actions/cache from 2.1.6 to 3.0.5 #307, #337.
diff --git a/src/main/java/org/apache/commons/io/file/PathUtils.java b/src/main/java/org/apache/commons/io/file/PathUtils.java
index 32292bb4..85b1d5be 100644
--- a/src/main/java/org/apache/commons/io/file/PathUtils.java
+++ b/src/main/java/org/apache/commons/io/file/PathUtils.java
@@ -24,6 +24,7 @@ import java.io.OutputStream;
 import java.io.UncheckedIOException;
 import java.math.BigInteger;
 import java.net.URI;
+import java.net.URISyntaxException;
 import java.net.URL;
 import java.nio.charset.Charset;
 import java.nio.file.AccessDeniedException;
@@ -812,6 +813,76 @@ public final class PathUtils {
         return Files.getFileAttributeView(path, DosFileAttributeView.class, options);
     }
 
+    /**
+     * Gets the file's last modified time or null if the file does not exist.
+     * <p>
+     * The method provides a workaround for bug <a href="https://bugs.openjdk.java.net/browse/JDK-8177809">JDK-8177809</a>
+     * where {@link File#lastModified()} looses milliseconds and always ends in 000. This bug is in OpenJDK 8 and 9, and
+     * fixed in 11.
+     * </p>
+     * 
+     * @param file the file to query.
+     * @return the file's last modified time.
+     * @throws IOException Thrown if an I/O error occurs.
+     * @since 2.12.0
+     */
+    public static FileTime getLastModifiedFileTime(final File file) throws IOException {
+        return getLastModifiedFileTime(file.toPath(), null, EMPTY_LINK_OPTION_ARRAY);
+    }
+
+    /**
+     * Gets the file's last modified time or null if the file does not exist.
+     *
+     * @param path the file to query.
+     * @param options options indicating how symbolic links are handled.
+     * @return the file's last modified time.
+     * @throws IOException Thrown if an I/O error occurs.
+     * @since 2.12.0
+     */
+    public static FileTime getLastModifiedFileTime(final Path path, final LinkOption... options) throws IOException {
+        return getLastModifiedFileTime(path, null, options);
+    }
+
+    /**
+     * Gets the file's last modified time or null if the file does not exist.
+     *
+     * @param path the file to query.
+     * @param defaultIfAbsent Returns this file time of the file does not exist, may be null.
+     * @param options options indicating how symbolic links are handled.
+     * @return the file's last modified time.
+     * @throws IOException Thrown if an I/O error occurs.
+     * @since 2.12.0
+     */
+    public static FileTime getLastModifiedFileTime(final Path path, final FileTime defaultIfAbsent, final LinkOption... options) throws IOException {
+        return Files.exists(path) ? getLastModifiedTime(path, options) : defaultIfAbsent;
+    }
+
+    /**
+     * Gets the file's last modified time or null if the file does not exist.
+     *
+     * @param uri the file to query.
+     * @return the file's last modified time.
+     * @throws IOException Thrown if an I/O error occurs.
+     * @since 2.12.0
+     */
+    public static FileTime getLastModifiedFileTime(final URI uri) throws IOException {
+        return getLastModifiedFileTime(Paths.get(uri), null, EMPTY_LINK_OPTION_ARRAY);
+    }
+
+    /**
+     * Gets the file's last modified time or null if the file does not exist.
+     *
+     * @param url the file to query.
+     * @return the file's last modified time.
+     * @throws IOException Thrown if an I/O error occurs.
+     * @throws URISyntaxException if the URL is not formatted strictly according to to RFC2396 and cannot be converted to a
+     *         URI.
+     * @since 2.12.0
+     */
+    public static FileTime getLastModifiedFileTime(final URL url) throws IOException, URISyntaxException {
+        return getLastModifiedFileTime(url.toURI());
+    }
+
     private static FileTime getLastModifiedTime(final Path path, final LinkOption... options) throws IOException {
         return Files.getLastModifiedTime(Objects.requireNonNull(path, "path"), options);
     }
diff --git a/src/test/java/org/apache/commons/io/file/PathUtilsTest.java b/src/test/java/org/apache/commons/io/file/PathUtilsTest.java
index 503828fb..4f2100e1 100644
--- a/src/test/java/org/apache/commons/io/file/PathUtilsTest.java
+++ b/src/test/java/org/apache/commons/io/file/PathUtilsTest.java
@@ -21,6 +21,7 @@ import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertThrowsExactly;
@@ -31,6 +32,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.net.URI;
+import java.net.URISyntaxException;
 import java.nio.charset.StandardCharsets;
 import java.nio.file.DirectoryStream;
 import java.nio.file.FileAlreadyExistsException;
@@ -89,10 +91,18 @@ public class PathUtilsTest extends AbstractTempDirTest {
         return symlinkDir;
     }
 
+    private Path current() {
+        return PathUtils.current();
+    }
+
     private Long getLastModifiedMillis(final Path file) throws IOException {
         return Files.getLastModifiedTime(file).toMillis();
     }
 
+    private Path getNonExistantPath() {
+        return Paths.get("/does not exist/for/certain");
+    }
+
     private FileSystem openArchive(final Path p, final boolean createNew) throws IOException {
         if (createNew) {
             final Map<String, String> env = new HashMap<>();
@@ -227,6 +237,37 @@ public class PathUtilsTest extends AbstractTempDirTest {
         assertThrowsExactly(FileAlreadyExistsException.class, () -> PathUtils.createParentDirectories(symlinkedDir.resolve("child")));
     }
 
+    @Test
+    public void testGetLastModifiedFileTime_File_Present() throws IOException {
+        assertNotNull(PathUtils.getLastModifiedFileTime(current().toFile()));
+    }
+
+    @Test
+    public void testGetLastModifiedFileTime_Path_Absent() throws IOException {
+        assertNull(PathUtils.getLastModifiedFileTime(getNonExistantPath()));
+    }
+
+    @Test
+    public void testGetLastModifiedFileTime_Path_FileTime_Absent() throws IOException {
+        final FileTime fromMillis = FileTime.fromMillis(0);
+        assertEquals(fromMillis, PathUtils.getLastModifiedFileTime(getNonExistantPath(), fromMillis));
+    }
+
+    @Test
+    public void testGetLastModifiedFileTime_Path_Present() throws IOException {
+        assertNotNull(PathUtils.getLastModifiedFileTime(current()));
+    }
+
+    @Test
+    public void testGetLastModifiedFileTime_URI_Present() throws IOException {
+        assertNotNull(PathUtils.getLastModifiedFileTime(current().toUri()));
+    }
+
+    @Test
+    public void testGetLastModifiedFileTime_URL_Present() throws IOException, URISyntaxException {
+        assertNotNull(PathUtils.getLastModifiedFileTime(current().toUri().toURL()));
+    }
+
     @Test
     public void testGetTempDirectory() {
         final Path tempDirectory = Paths.get(System.getProperty("java.io.tmpdir"));
@@ -254,12 +295,12 @@ public class PathUtilsTest extends AbstractTempDirTest {
     public void testIsPosix() throws IOException {
         boolean isPosix;
         try {
-            Files.getPosixFilePermissions(PathUtils.current());
+            Files.getPosixFilePermissions(current());
             isPosix = true;
         } catch (final UnsupportedOperationException e) {
             isPosix = false;
         }
-        assertEquals(isPosix, PathUtils.isPosix(PathUtils.current()));
+        assertEquals(isPosix, PathUtils.isPosix(current()));
     }
 
     @Test
@@ -278,7 +319,7 @@ public class PathUtilsTest extends AbstractTempDirTest {
     @Test
     public void testNewDirectoryStream() throws Exception {
         final PathFilter pathFilter = new NameFileFilter(PATH_FIXTURE);
-        try (DirectoryStream<Path> stream = PathUtils.newDirectoryStream(PathUtils.current(), pathFilter)) {
+        try (DirectoryStream<Path> stream = PathUtils.newDirectoryStream(current(), pathFilter)) {
             final Iterator<Path> iterator = stream.iterator();
             final Path path = iterator.next();
             assertEquals(PATH_FIXTURE, path.getFileName().toString());
@@ -336,12 +377,12 @@ public class PathUtilsTest extends AbstractTempDirTest {
     public void testReadAttributesPosix() throws IOException {
         boolean isPosix;
         try {
-            Files.getPosixFilePermissions(PathUtils.current());
+            Files.getPosixFilePermissions(current());
             isPosix = true;
         } catch (final UnsupportedOperationException e) {
             isPosix = false;
         }
-        assertEquals(isPosix, PathUtils.readAttributes(PathUtils.current(), PosixFileAttributes.class) != null);
+        assertEquals(isPosix, PathUtils.readAttributes(current(), PosixFileAttributes.class) != null);
     }
 
     @Test