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

cstamas pushed a commit to branch maven-resolver-1.9.x
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git


The following commit(s) were added to refs/heads/maven-resolver-1.9.x by this 
push:
     new 82766b0e2 [1.9.x] TrackingFileManager changes (#1695)
82766b0e2 is described below

commit 82766b0e211f8bfa0ca346bf7352d0de7a7d6706
Author: Tamas Cservenak <[email protected]>
AuthorDate: Fri Nov 28 15:45:34 2025 +0100

    [1.9.x] TrackingFileManager changes (#1695)
    
    Backport from master. Needs Maven changes as well. Aligned with master 
fully.
    
    Master PR: https://github.com/apache/maven-resolver/pull/1692
---
 .../internal/impl/DefaultTrackingFileManager.java  | 98 ++++++++++++----------
 .../internal/impl/DefaultUpdateCheckManager.java   |  2 +-
 .../aether/internal/impl/TrackingFileManager.java  |  7 ++
 .../impl/DefaultTrackingFileManagerTest.java       | 13 +++
 4 files changed, 77 insertions(+), 43 deletions(-)

diff --git 
a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManager.java
 
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManager.java
index 8f41f7d2f..7076f38f7 100644
--- 
a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManager.java
+++ 
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManager.java
@@ -21,17 +21,19 @@ package org.eclipse.aether.internal.impl;
 import javax.inject.Named;
 import javax.inject.Singleton;
 
-import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
-import java.io.RandomAccessFile;
 import java.io.UncheckedIOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.Channels;
 import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
 import java.nio.channels.OverlappingFileLockException;
 import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.nio.file.StandardOpenOption;
 import java.util.Map;
 import java.util.Properties;
 
@@ -45,6 +47,9 @@ import org.slf4j.LoggerFactory;
  * to back off two parallel implementations that coexist in Maven (this class 
and {@code maven-compat} one), as in
  * certain cases the two implementations may collide on properties files. This 
locking must remain in place for as long
  * as {@code maven-compat} code exists.
+ *
+ * <em>IMPORTANT:</em> This class is kept fully in sync with the master branch 
one (w/ simple change to convert File
+ * to Path instances).
  */
 @Singleton
 @Named
@@ -53,15 +58,18 @@ public final class DefaultTrackingFileManager implements 
TrackingFileManager {
 
     @Override
     public Properties read(File file) {
-        if (Files.isReadable(file.toPath())) {
-            synchronized (getMutex(file)) {
-                try (FileInputStream stream = new FileInputStream(file);
-                        FileLock unused = fileLock(stream.getChannel(), 
Math.max(1, file.length()), true)) {
+        Path path = file.toPath();
+        if (Files.isReadable(path)) {
+            synchronized (mutex(path)) {
+                try (FileChannel fileChannel = FileChannel.open(path, 
StandardOpenOption.READ);
+                        FileLock unused = fileLock(fileChannel, true)) {
                     Properties props = new Properties();
-                    props.load(stream);
+                    props.load(Channels.newInputStream(fileChannel));
                     return props;
+                } catch (NoSuchFileException e) {
+                    LOGGER.debug("No such file to read {}: {}", path, 
e.getMessage());
                 } catch (IOException e) {
-                    LOGGER.warn("Failed to read tracking file '{}'", file, e);
+                    LOGGER.warn("Failed to read tracking file '{}'", path, e);
                     throw new UncheckedIOException(e);
                 }
             }
@@ -71,22 +79,20 @@ public final class DefaultTrackingFileManager implements 
TrackingFileManager {
 
     @Override
     public Properties update(File file, Map<String, String> updates) {
-        Properties props = new Properties();
-
+        Path path = file.toPath();
         try {
-            Files.createDirectories(file.getParentFile().toPath());
+            Files.createDirectories(path.getParent());
         } catch (IOException e) {
-            LOGGER.warn("Failed to create tracking file parent '{}'", file, e);
+            LOGGER.warn("Failed to create tracking file parent '{}'", path, e);
             throw new UncheckedIOException(e);
         }
-
-        synchronized (getMutex(file)) {
-            try (RandomAccessFile raf = new RandomAccessFile(file, "rw");
-                    FileLock unused = fileLock(raf.getChannel(), Math.max(1, 
raf.length()), false)) {
-                if (raf.length() > 0) {
-                    byte[] buffer = new byte[(int) raf.length()];
-                    raf.readFully(buffer);
-                    props.load(new ByteArrayInputStream(buffer));
+        synchronized (mutex(path)) {
+            try (FileChannel fileChannel = FileChannel.open(
+                            path, StandardOpenOption.READ, 
StandardOpenOption.WRITE, StandardOpenOption.CREATE);
+                    FileLock unused = fileLock(fileChannel, false)) {
+                Properties props = new Properties();
+                if (fileChannel.size() > 0) {
+                    props.load(Channels.newInputStream(fileChannel));
                 }
 
                 for (Map.Entry<String, String> update : updates.entrySet()) {
@@ -97,46 +103,54 @@ public final class DefaultTrackingFileManager implements 
TrackingFileManager {
                     }
                 }
 
-                LOGGER.debug("Writing tracking file '{}'", file);
+                LOGGER.debug("Writing tracking file '{}'", path);
                 ByteArrayOutputStream stream = new ByteArrayOutputStream(1024 
* 2);
                 props.store(
                         stream,
                         "NOTE: This is a Maven Resolver internal 
implementation file"
                                 + ", its format can be changed without prior 
notice.");
-                raf.seek(0L);
-                raf.write(stream.toByteArray());
-                raf.setLength(raf.getFilePointer());
+                fileChannel.position(0);
+                int written = 
fileChannel.write(ByteBuffer.wrap(stream.toByteArray()));
+                fileChannel.truncate(written);
+                return props;
             } catch (IOException e) {
-                LOGGER.warn("Failed to write tracking file '{}'", file, e);
+                LOGGER.warn("Failed to write tracking file '{}'", path, e);
                 throw new UncheckedIOException(e);
             }
         }
+    }
 
-        return props;
+    @Override
+    public boolean delete(File file) {
+        Path path = file.toPath();
+        if (Files.isReadable(path)) {
+            synchronized (mutex(path)) {
+                try (FileChannel fileChannel = FileChannel.open(path, 
StandardOpenOption.WRITE);
+                        FileLock unused = fileLock(fileChannel, false)) {
+                    Files.delete(path);
+                    return true;
+                } catch (NoSuchFileException e) {
+                    LOGGER.debug("No such file to delete {}: {}", path, 
e.getMessage());
+                } catch (IOException e) {
+                    LOGGER.warn("Failed to delete tracking file '{}'", path, 
e);
+                    throw new UncheckedIOException(e);
+                }
+            }
+        }
+        return false;
     }
 
-    private Object getMutex(File file) {
+    private Object mutex(Path path) {
         // The interned string of path is (mis)used as mutex, to exclude 
different threads going for same file,
         // as JVM file locking happens on JVM not on Thread level. This is how 
original code did it  ¯\_(ツ)_/¯
-        /*
-         * NOTE: Locks held by one JVM must not overlap and using the 
canonical path is our best bet, still another
-         * piece of code might have locked the same file (unlikely though) or 
the canonical path fails to capture file
-         * identity sufficiently as is the case with Java 1.6 and symlinks on 
Windows.
-         */
-        try {
-            return file.getCanonicalPath().intern();
-        } catch (IOException e) {
-            LOGGER.warn("Failed to canonicalize path {}", file, e);
-            // TODO This is code smell and deprecated
-            return file.getAbsolutePath().intern();
-        }
+        return path.toAbsolutePath().normalize().toString().intern();
     }
 
-    private FileLock fileLock(FileChannel channel, long size, boolean shared) 
throws IOException {
+    private FileLock fileLock(FileChannel channel, boolean shared) throws 
IOException {
         FileLock lock = null;
         for (int attempts = 8; attempts >= 0; attempts--) {
             try {
-                lock = channel.lock(0, size, shared);
+                lock = channel.lock(0, Long.MAX_VALUE, shared);
                 break;
             } catch (OverlappingFileLockException e) {
                 if (attempts <= 0) {
diff --git 
a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultUpdateCheckManager.java
 
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultUpdateCheckManager.java
index 50a960bfa..7f64b200f 100644
--- 
a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultUpdateCheckManager.java
+++ 
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultUpdateCheckManager.java
@@ -476,7 +476,7 @@ public class DefaultUpdateCheckManager implements 
UpdateCheckManager, Service {
         Properties props = write(touchFile, dataKey, transferKey, 
check.getException());
 
         if (artifactFile.exists() && !hasErrors(props)) {
-            touchFile.delete();
+            trackingFileManager.delete(touchFile);
         }
     }
 
diff --git 
a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/TrackingFileManager.java
 
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/TrackingFileManager.java
index 5e922bf58..93639d708 100644
--- 
a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/TrackingFileManager.java
+++ 
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/TrackingFileManager.java
@@ -36,4 +36,11 @@ public interface TrackingFileManager {
      * as in updated file, never {@code null}.
      */
     Properties update(File file, Map<String, String> updates);
+
+    /**
+     * Deletes the specified properties file, if exists. If file existed and 
was deleted, returns {@code true}.
+     *
+     * @since 1.9.25
+     */
+    boolean delete(File file);
 }
diff --git 
a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManagerTest.java
 
b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManagerTest.java
index 7209ed798..035b96a1d 100644
--- 
a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManagerTest.java
+++ 
b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultTrackingFileManagerTest.java
@@ -20,6 +20,7 @@ package org.eclipse.aether.internal.impl;
 
 import java.io.File;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -31,6 +32,7 @@ import org.eclipse.aether.internal.test.util.TestFileUtils;
 import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
@@ -104,6 +106,17 @@ public class DefaultTrackingFileManagerTest {
         }
     }
 
+    @Test
+    public void testDeleteFileIsGone() throws Exception {
+        TrackingFileManager tfm = new DefaultTrackingFileManager();
+
+        for (int i = 0; i < 1000; i++) {
+            File propFile = 
TestFileUtils.createTempFile("#COMMENT\nkey1=value1\nkey2 : value2");
+            assertTrue(tfm.delete(propFile));
+            assertFalse("File is not gone", 
Files.isRegularFile(propFile.toPath()));
+        }
+    }
+
     @Test
     public void testLockingOnCanonicalPath() throws Exception {
         final TrackingFileManager tfm = new DefaultTrackingFileManager();

Reply via email to