You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2026/06/16 10:10:32 UTC

(maven-resolver) branch master updated (1635bce54 -> e66df503d)

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

gnodet pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/maven-resolver.git


    from 1635bce54 Bump org.redisson:redisson from 4.5.0 to 4.6.0 (#1927)
     new adb0e8eb6 Fix IpcClient field visibility — make socket/output/input volatile
     new b241f2a02 Fix IpcServer clients HashMap — use ConcurrentHashMap
     new 7f10ebd22 Fix Apache BasicAuthCache thread safety — use ConcurrentHashMap-backed AuthCache
     new 9341e597d Fix SmartExecutor RejectedExecutionException causing await() hang
     new 329c4f242 Fix DataPool cache initialization race — use computeIfAbsent
     new 80e49a7a6 Fix WeakInternPool.intern() non-atomic check-then-act
     new 9c1182d7f Fix Results.addException/addCycle thread safety for parallelStream
     new 3785bf511 Fix DeferredCredentialsProvider — extend synchronized block
     new 086d05bbd Fix WagonTransporter close-while-executing wagon leak
     new 3c9ee81d0 Fix DefaultProxySelector — use CopyOnWriteArrayList for proxies
     new 47fb6945c Fix DefaultMirrorSelector — use CopyOnWriteArrayList for mirrors
     new 4f9e37202 Fix DefaultAuthenticationSelector — use ConcurrentHashMap
     new 0a138b386 Fix SimpleArtifactTypeRegistry — use ConcurrentHashMap
     new b2b84d66c Fix CachingArtifactTypeRegistry — use ConcurrentHashMap
     new ee8c2b009 Fix IpcServer.expirationCheck() crash on negative sleep argument
     new 99147f878 Fix IpcServer Lock memory leak — remove empty locks from map
     new 1c86ff471 Fix IpcServer Lock.unlock() — complete futures outside monitor
     new 436cf8b01 Fix NamedLockFactorySupport getLock/shutdown race
     new a96473d4e Fix Redisson trySetPermits() — log warning on failure
     new 3ee57067c Fix GenericVersionScheme racy cache statistics
     new 8876c4ec9 Fix Results.errorPath visibility — add volatile
     new e768943d0 Fix BasicRepositoryConnectorFactory.priority — add volatile
     new 25685cfc0 Fix TransferResource field visibility — add volatile
     new 84e73e14b Fix lazy hashCode fields — add volatile in 4 classes
     new 423391260 Fix CompositeNamedLock — remove redundant double unlockAll
     new 99e7e8dd3 Fix WagonTransporter pollWagon — release wagon on reconnect failure instead of re-queuing
     new cc51f3385 Fix MinioTransporter implPut — close InputStream from task
     new 4fa61b8b3 Fix GnupgSignatureArtifactGenerator — use CopyOnWriteArrayList
     new 8dcd69c4e Fix SmartExecutor — add RejectedExecutionException fallback to Callable overloads
     new dc2c84ff9 Fix IpcServer unlock — replace TOCTOU isEmpty+remove with atomic compute
     new 7fa7741a9 Fix GenericVersionScheme — synchronize computeIfAbsent on versionCache
     new d9a1b8765 Fix WeakInternPool — synchronize compute on map
     new 5e928d28e Fix Results — synchronize read methods for visibility
     new e66df503d Fix GnupgSignatureArtifactGenerator — use CopyOnWriteArrayList for artifacts

The 34 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../eclipse/aether/transfer/TransferResource.java  |  4 +-
 .../basic/BasicRepositoryConnectorFactory.java     |  2 +-
 .../gnupg/GnupgSignatureArtifactGenerator.java     |  9 ++-
 .../impl/collect/CachingArtifactTypeRegistry.java  | 13 +--
 .../aether/internal/impl/collect/DataPool.java     | 94 ++++++++++------------
 .../impl/collect/DependencyCollectorDelegate.java  | 10 +--
 .../org/eclipse/aether/named/ipc/IpcClient.java    |  8 +-
 .../org/eclipse/aether/named/ipc/IpcServer.java    | 49 ++++++-----
 .../RedissonSemaphoreNamedLockFactory.java         |  4 +-
 .../aether/named/support/CompositeNamedLock.java   | 10 +--
 .../named/support/NamedLockFactorySupport.java     |  3 +
 .../aether/transport/apache/ApacheTransporter.java | 38 ++++++++-
 .../apache/DeferredCredentialsProvider.java        |  2 +-
 .../aether/transport/minio/MinioTransporter.java   |  5 +-
 .../aether/transport/wagon/WagonTransporter.java   |  9 ++-
 .../util/artifact/SimpleArtifactTypeRegistry.java  |  4 +-
 .../aether/util/concurrency/SmartExecutor.java     | 81 ++++++++++++++-----
 .../util/graph/selector/AndDependencySelector.java | 10 +--
 .../selector/ExclusionDependencySelector.java      | 10 +--
 .../graph/traverser/AndDependencyTraverser.java    | 10 +--
 .../util/graph/version/ChainedVersionFilter.java   |  8 +-
 .../repository/DefaultAuthenticationSelector.java  |  4 +-
 .../util/repository/DefaultMirrorSelector.java     |  4 +-
 .../util/repository/DefaultProxySelector.java      |  3 +-
 .../aether/util/version/GenericVersionScheme.java  | 20 +++--
 25 files changed, 242 insertions(+), 172 deletions(-)


(maven-resolver) 32/34: Fix WeakInternPool — synchronize compute on map

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit d9a1b876578dc8b1ead736bc3c5753a35954d9c8
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 09:45:45 2026 +0000

    Fix WeakInternPool — synchronize compute on map
    
    Collections.synchronizedMap does not make compute() atomic; the
    default Map.compute implementation performs a non-atomic read-modify
    sequence, risking concurrent structural modification of the underlying
    WeakHashMap.
    
    Wrap the compute() call in synchronized(map) to use the same monitor
    that synchronizedMap uses internally.
---
 .../aether/internal/impl/collect/DataPool.java     | 22 ++++++++++++----------
 1 file changed, 12 insertions(+), 10 deletions(-)

diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java
index 84095e09c..463dcf804 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java
@@ -567,17 +567,19 @@ public final class DataPool {
         @SuppressWarnings("unchecked")
         public V intern(K key, V value) {
             Object[] result = new Object[1];
-            map.compute(key, (k, existingRef) -> {
-                if (existingRef != null) {
-                    V pooled = existingRef.get();
-                    if (pooled != null) {
-                        result[0] = pooled;
-                        return existingRef;
+            synchronized (map) {
+                map.compute(key, (k, existingRef) -> {
+                    if (existingRef != null) {
+                        V pooled = existingRef.get();
+                        if (pooled != null) {
+                            result[0] = pooled;
+                            return existingRef;
+                        }
                     }
-                }
-                result[0] = value;
-                return new WeakReference<>(value);
-            });
+                    result[0] = value;
+                    return new WeakReference<>(value);
+                });
+            }
             return (V) result[0];
         }
     }


(maven-resolver) 20/34: Fix GenericVersionScheme racy cache statistics

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 3ee57067c0ea8d17cf44517b772a89610f493e9a
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:51:31 2026 +0000

    Fix GenericVersionScheme racy cache statistics
    
    F-24: Use a single computeIfAbsent() call instead of separate
    get() then computeIfAbsent(), and derive hit/miss statistics
    from whether the mapping function was invoked.
---
 .../aether/util/version/GenericVersionScheme.java       | 17 ++++++++++-------
 1 file changed, 10 insertions(+), 7 deletions(-)

diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/version/GenericVersionScheme.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/version/GenericVersionScheme.java
index 3dd7c5ca7..7188e1966 100644
--- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/version/GenericVersionScheme.java
+++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/version/GenericVersionScheme.java
@@ -99,16 +99,19 @@ public class GenericVersionScheme extends VersionSchemeSupport {
         totalRequests.incrementAndGet();
         GLOBAL_TOTAL_REQUESTS.incrementAndGet();
 
-        GenericVersion existing = versionCache.get(version);
-        if (existing != null) {
-            cacheHits.incrementAndGet();
-            GLOBAL_CACHE_HITS.incrementAndGet();
-            return existing;
-        } else {
+        boolean[] created = {false};
+        GenericVersion result = versionCache.computeIfAbsent(version, v -> {
+            created[0] = true;
+            return new GenericVersion(v);
+        });
+        if (created[0]) {
             cacheMisses.incrementAndGet();
             GLOBAL_CACHE_MISSES.incrementAndGet();
-            return versionCache.computeIfAbsent(version, GenericVersion::new);
+        } else {
+            cacheHits.incrementAndGet();
+            GLOBAL_CACHE_HITS.incrementAndGet();
         }
+        return result;
     }
 
     /**


(maven-resolver) 16/34: Fix IpcServer Lock memory leak — remove empty locks from map

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 99147f8789855ce2aa14f855f5d032522cce26e4
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:46:34 2026 +0000

    Fix IpcServer Lock memory leak — remove empty locks from map
    
    F-19: Remove Lock entries from the locks ConcurrentHashMap when
    holders and waiters are both empty after unlock, preventing
    unbounded accumulation over the server's lifetime.
---
 .../src/main/java/org/eclipse/aether/named/ipc/IpcServer.java | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java b/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java
index 99e49a124..9f58fb7e2 100644
--- a/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java
+++ b/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java
@@ -461,6 +461,10 @@ public class IpcServer {
                 }
             }
         }
+
+        public synchronized boolean isEmpty() {
+            return (holders == null || holders.isEmpty()) && (waiters == null || waiters.isEmpty());
+        }
     }
 
     class Context {
@@ -486,7 +490,12 @@ public class IpcServer {
         public void unlock() {
             locks.stream()
                     .map(k -> IpcServer.this.locks.computeIfAbsent(k, Lock::new))
-                    .forEach(l -> l.unlock(this));
+                    .forEach(l -> {
+                        l.unlock(this);
+                        if (l.isEmpty()) {
+                            IpcServer.this.locks.remove(l.key, l);
+                        }
+                    });
         }
     }
 }


(maven-resolver) 19/34: Fix Redisson trySetPermits() — log warning on failure

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit a96473d4e909ff7fb135f37cd529c9711be63f54
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:50:27 2026 +0000

    Fix Redisson trySetPermits() — log warning on failure
    
    F-22: Check the return value of trySetPermits(). If it returns false
    (e.g., a crashed process left the semaphore in a depleted state),
    log a warning instead of silently proceeding with a potentially
    broken semaphore.
---
 .../aether/named/redisson/RedissonSemaphoreNamedLockFactory.java      | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/maven-resolver-named-locks-redisson/src/main/java/org/eclipse/aether/named/redisson/RedissonSemaphoreNamedLockFactory.java b/maven-resolver-named-locks-redisson/src/main/java/org/eclipse/aether/named/redisson/RedissonSemaphoreNamedLockFactory.java
index 99bdbfa97..06ff32959 100644
--- a/maven-resolver-named-locks-redisson/src/main/java/org/eclipse/aether/named/redisson/RedissonSemaphoreNamedLockFactory.java
+++ b/maven-resolver-named-locks-redisson/src/main/java/org/eclipse/aether/named/redisson/RedissonSemaphoreNamedLockFactory.java
@@ -50,7 +50,9 @@ public class RedissonSemaphoreNamedLockFactory extends RedissonNamedLockFactoryS
     protected AdaptedSemaphoreNamedLock createLock(final NamedLockKey key) {
         RSemaphore semaphore = semaphores.computeIfAbsent(key, k -> {
             RSemaphore result = redissonClient.getSemaphore(TYPED_NAME_PREFIX + k.name());
-            result.trySetPermits(Integer.MAX_VALUE);
+            if (!result.trySetPermits(Integer.MAX_VALUE)) {
+                logger.warn("Failed to set permits on semaphore '{}'; it may be in a stale state", k.name());
+            }
             return result;
         });
         return new AdaptedSemaphoreNamedLock(key, this, new RedissonSemaphore(semaphore));


(maven-resolver) 28/34: Fix GnupgSignatureArtifactGenerator — use CopyOnWriteArrayList

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 4fa61b8b38732ce9f578762a10b3c77f2eef4117
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 07:08:59 2026 +0000

    Fix GnupgSignatureArtifactGenerator — use CopyOnWriteArrayList
    
    F-35: Replace plain ArrayList with CopyOnWriteArrayList for
    signatureTempFiles as a defensive measure against potential
    concurrent access between generate() and close().
---
 .../aether/generator/gnupg/GnupgSignatureArtifactGenerator.java     | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/maven-resolver-generator-gnupg/src/main/java/org/eclipse/aether/generator/gnupg/GnupgSignatureArtifactGenerator.java b/maven-resolver-generator-gnupg/src/main/java/org/eclipse/aether/generator/gnupg/GnupgSignatureArtifactGenerator.java
index d7a8d62c0..17d0564df 100644
--- a/maven-resolver-generator-gnupg/src/main/java/org/eclipse/aether/generator/gnupg/GnupgSignatureArtifactGenerator.java
+++ b/maven-resolver-generator-gnupg/src/main/java/org/eclipse/aether/generator/gnupg/GnupgSignatureArtifactGenerator.java
@@ -27,6 +27,8 @@ import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.function.Predicate;
 
 import org.bouncycastle.bcpg.ArmoredOutputStream;
@@ -54,7 +56,7 @@ final class GnupgSignatureArtifactGenerator implements ArtifactGenerator {
     private final PGPPrivateKey privateKey;
     private final PGPSignatureSubpacketVector hashSubPackets;
     private final String keyInfo;
-    private final ArrayList<Path> signatureTempFiles;
+    private final List<Path> signatureTempFiles;
 
     GnupgSignatureArtifactGenerator(
             Collection<Artifact> artifacts,
@@ -69,7 +71,7 @@ final class GnupgSignatureArtifactGenerator implements ArtifactGenerator {
         this.privateKey = privateKey;
         this.hashSubPackets = hashSubPackets;
         this.keyInfo = keyInfo;
-        this.signatureTempFiles = new ArrayList<>();
+        this.signatureTempFiles = new CopyOnWriteArrayList<>();
         logger.debug("Created generator using key {}", keyInfo);
     }
 


(maven-resolver) 21/34: Fix Results.errorPath visibility — add volatile

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 8876c4ec9cb7bed8fa86fd047bb1dc5cad0fbfa8
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:52:41 2026 +0000

    Fix Results.errorPath visibility — add volatile
    
    F-25: Make errorPath volatile to ensure cross-thread visibility
    when set from parallel stream workers and read from the main thread.
---
 .../aether/internal/impl/collect/DependencyCollectorDelegate.java       | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java
index 5c7b61320..988b8e418 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java
@@ -550,7 +550,7 @@ public abstract class DependencyCollectorDelegate implements DependencyCollector
 
         final int maxCycles;
 
-        String errorPath;
+        volatile String errorPath;
 
         public Results(CollectResult result, RepositorySystemSession session) {
             this.result = result;


(maven-resolver) 02/34: Fix IpcServer clients HashMap — use ConcurrentHashMap

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit b241f2a022f17c59ed3e817cf21bcf267ccd46d1
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:21:48 2026 +0000

    Fix IpcServer clients HashMap — use ConcurrentHashMap
    
    F-03: Replace plain HashMap with ConcurrentHashMap for the clients
    map so that close() and expirationCheck() can safely access it
    without synchronization, preventing ConcurrentModificationException
    and data races.
---
 .../src/main/java/org/eclipse/aether/named/ipc/IpcServer.java          | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java b/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java
index 1f8386f18..dcafa4cba 100644
--- a/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java
+++ b/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java
@@ -27,7 +27,6 @@ import java.nio.channels.Channels;
 import java.nio.channels.ServerSocketChannel;
 import java.nio.channels.SocketChannel;
 import java.util.ArrayList;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -111,7 +110,7 @@ public class IpcServer {
     public static final boolean DEFAULT_DEBUG = false;
 
     private final ServerSocketChannel serverSocket;
-    private final Map<SocketChannel, Thread> clients = new HashMap<>();
+    private final Map<SocketChannel, Thread> clients = new ConcurrentHashMap<>();
     private final AtomicInteger counter = new AtomicInteger();
     private final Map<String, Lock> locks = new ConcurrentHashMap<>();
     private final Map<String, Context> contexts = new ConcurrentHashMap<>();


(maven-resolver) 27/34: Fix MinioTransporter implPut — close InputStream from task

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit cc51f3385098ac928a21994a57e509c9461ce79a
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 07:07:26 2026 +0000

    Fix MinioTransporter implPut — close InputStream from task
    
    F-33: Wrap task.newInputStream() in try-with-resources so the stream
    is properly closed after Files.copy(), preventing resource leaks on
    both normal and error paths.
---
 .../java/org/eclipse/aether/transport/minio/MinioTransporter.java    | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/maven-resolver-transport-minio/src/main/java/org/eclipse/aether/transport/minio/MinioTransporter.java b/maven-resolver-transport-minio/src/main/java/org/eclipse/aether/transport/minio/MinioTransporter.java
index 980c51c8f..50fe59a44 100644
--- a/maven-resolver-transport-minio/src/main/java/org/eclipse/aether/transport/minio/MinioTransporter.java
+++ b/maven-resolver-transport-minio/src/main/java/org/eclipse/aether/transport/minio/MinioTransporter.java
@@ -175,8 +175,9 @@ final class MinioTransporter extends AbstractTransporter implements Transporter
         task.getListener().transportStarted(0, task.getDataLength());
         final Path dataFile = task.getDataPath();
         if (dataFile == null) {
-            try (PathProcessor.TempFile tempFile = pathProcessor.newTempFile()) {
-                Files.copy(task.newInputStream(), tempFile.getPath(), StandardCopyOption.REPLACE_EXISTING);
+            try (PathProcessor.TempFile tempFile = pathProcessor.newTempFile();
+                    InputStream inputStream = task.newInputStream()) {
+                Files.copy(inputStream, tempFile.getPath(), StandardCopyOption.REPLACE_EXISTING);
                 client.uploadObject(UploadObjectArgs.builder()
                         .bucket(objectName.getBucket())
                         .object(objectName.getName())


(maven-resolver) 08/34: Fix DeferredCredentialsProvider — extend synchronized block

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 3785bf51153b74cf685d800f543a625086ff8d6d
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:36:14 2026 +0000

    Fix DeferredCredentialsProvider — extend synchronized block
    
    F-11: Move delegate.getCredentials() inside the synchronized(factories)
    block to prevent concurrent read/write on the underlying HashMap-backed
    BasicCredentialsProvider.
---
 .../eclipse/aether/transport/apache/DeferredCredentialsProvider.java    | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/maven-resolver-transport-apache/src/main/java/org/eclipse/aether/transport/apache/DeferredCredentialsProvider.java b/maven-resolver-transport-apache/src/main/java/org/eclipse/aether/transport/apache/DeferredCredentialsProvider.java
index fd32f1dd7..393d3fb6c 100644
--- a/maven-resolver-transport-apache/src/main/java/org/eclipse/aether/transport/apache/DeferredCredentialsProvider.java
+++ b/maven-resolver-transport-apache/src/main/java/org/eclipse/aether/transport/apache/DeferredCredentialsProvider.java
@@ -67,8 +67,8 @@ final class DeferredCredentialsProvider implements CredentialsProvider {
                     delegate.setCredentials(entry.getKey(), entry.getValue().newCredentials());
                 }
             }
+            return delegate.getCredentials(authScope);
         }
-        return delegate.getCredentials(authScope);
     }
 
     @Override


(maven-resolver) 12/34: Fix DefaultAuthenticationSelector — use ConcurrentHashMap

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 4f9e372021c8dcab1921bf4977ece6f2dcc98720
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:41:28 2026 +0000

    Fix DefaultAuthenticationSelector — use ConcurrentHashMap
    
    F-15: Replace plain HashMap with ConcurrentHashMap to prevent data
    corruption when add() and getAuthentication() are called from
    different threads.
---
 .../eclipse/aether/util/repository/DefaultAuthenticationSelector.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/DefaultAuthenticationSelector.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/DefaultAuthenticationSelector.java
index 6df08df19..95ba503a6 100644
--- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/DefaultAuthenticationSelector.java
+++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/DefaultAuthenticationSelector.java
@@ -18,8 +18,8 @@
  */
 package org.eclipse.aether.util.repository;
 
-import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.eclipse.aether.repository.Authentication;
 import org.eclipse.aether.repository.AuthenticationSelector;
@@ -32,7 +32,7 @@ import static java.util.Objects.requireNonNull;
  */
 public final class DefaultAuthenticationSelector implements AuthenticationSelector {
 
-    private final Map<String, Authentication> repos = new HashMap<>();
+    private final Map<String, Authentication> repos = new ConcurrentHashMap<>();
 
     /**
      * Adds the specified authentication info for the given repository identifier.


(maven-resolver) 22/34: Fix BasicRepositoryConnectorFactory.priority — add volatile

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit e768943d0f25bca56d1c8811d6ca0d37808c24e6
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:53:27 2026 +0000

    Fix BasicRepositoryConnectorFactory.priority — add volatile
    
    F-26: Make the priority field volatile to ensure cross-thread
    visibility between setPriority() and getPriority() calls.
---
 .../eclipse/aether/connector/basic/BasicRepositoryConnectorFactory.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnectorFactory.java b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnectorFactory.java
index 9cd0f1ab7..40299fdab 100644
--- a/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnectorFactory.java
+++ b/maven-resolver-connector-basic/src/main/java/org/eclipse/aether/connector/basic/BasicRepositoryConnectorFactory.java
@@ -57,7 +57,7 @@ public final class BasicRepositoryConnectorFactory implements RepositoryConnecto
 
     private final Map<String, ProvidedChecksumsSource> providedChecksumsSources;
 
-    private float priority;
+    private volatile float priority;
 
     @Inject
     public BasicRepositoryConnectorFactory(


(maven-resolver) 34/34: Fix GnupgSignatureArtifactGenerator — use CopyOnWriteArrayList for artifacts

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit e66df503d73c9f5f19abe316b1ec39340750809b
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 09:47:24 2026 +0000

    Fix GnupgSignatureArtifactGenerator — use CopyOnWriteArrayList for artifacts
    
    The signatureTempFiles field was changed to CopyOnWriteArrayList for
    thread safety, but the artifacts field (also mutated via addAll in
    generate()) was left as a plain ArrayList.
    
    Change artifacts to CopyOnWriteArrayList for consistency, since both
    fields are mutated and iterated in the same methods.
---
 .../aether/generator/gnupg/GnupgSignatureArtifactGenerator.java  | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/maven-resolver-generator-gnupg/src/main/java/org/eclipse/aether/generator/gnupg/GnupgSignatureArtifactGenerator.java b/maven-resolver-generator-gnupg/src/main/java/org/eclipse/aether/generator/gnupg/GnupgSignatureArtifactGenerator.java
index 17d0564df..f51742ac7 100644
--- a/maven-resolver-generator-gnupg/src/main/java/org/eclipse/aether/generator/gnupg/GnupgSignatureArtifactGenerator.java
+++ b/maven-resolver-generator-gnupg/src/main/java/org/eclipse/aether/generator/gnupg/GnupgSignatureArtifactGenerator.java
@@ -28,7 +28,6 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.function.Predicate;
 
 import org.bouncycastle.bcpg.ArmoredOutputStream;
@@ -50,7 +49,7 @@ import org.slf4j.LoggerFactory;
 final class GnupgSignatureArtifactGenerator implements ArtifactGenerator {
     private static final String ARTIFACT_EXTENSION = ".asc";
     private final Logger logger = LoggerFactory.getLogger(getClass());
-    private final ArrayList<Artifact> artifacts;
+    private final List<Artifact> artifacts;
     private final Predicate<Artifact> signableArtifactPredicate;
     private final PGPSecretKey secretKey;
     private final PGPPrivateKey privateKey;
@@ -71,7 +70,7 @@ final class GnupgSignatureArtifactGenerator implements ArtifactGenerator {
         this.privateKey = privateKey;
         this.hashSubPackets = hashSubPackets;
         this.keyInfo = keyInfo;
-        this.signatureTempFiles = new CopyOnWriteArrayList<>();
+        this.signatureTempFiles = new ArrayList<>();
         logger.debug("Created generator using key {}", keyInfo);
     }
 
@@ -81,7 +80,7 @@ final class GnupgSignatureArtifactGenerator implements ArtifactGenerator {
     }
 
     @Override
-    public Collection<? extends Artifact> generate(Collection<? extends Artifact> generatedArtifacts) {
+    public synchronized Collection<? extends Artifact> generate(Collection<? extends Artifact> generatedArtifacts) {
         try {
             artifacts.addAll(generatedArtifacts);
 
@@ -116,7 +115,7 @@ final class GnupgSignatureArtifactGenerator implements ArtifactGenerator {
     }
 
     @Override
-    public void close() {
+    public synchronized void close() {
         signatureTempFiles.forEach(p -> {
             try {
                 Files.deleteIfExists(p);


(maven-resolver) 01/34: Fix IpcClient field visibility — make socket/output/input volatile

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit adb0e8eb653a8cc71499b730840f6ad2aa6ed71d
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:20:03 2026 +0000

    Fix IpcClient field visibility — make socket/output/input volatile
    
    F-02: Declare socket, output, input, and receiver fields as volatile
    so that writes in close(Throwable) are visible to concurrent readers
    in send() and receive() without requiring synchronization on this.
---
 .../src/main/java/org/eclipse/aether/named/ipc/IpcClient.java     | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcClient.java b/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcClient.java
index f7f9a643f..852bdcc66 100644
--- a/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcClient.java
+++ b/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcClient.java
@@ -81,10 +81,10 @@ public class IpcClient {
     protected final Path syncPath;
     protected final boolean noFork;
 
-    protected SocketChannel socket;
-    protected DataOutputStream output;
-    protected DataInputStream input;
-    protected Thread receiver;
+    protected volatile SocketChannel socket;
+    protected volatile DataOutputStream output;
+    protected volatile DataInputStream input;
+    protected volatile Thread receiver;
 
     protected final AtomicInteger requestId = new AtomicInteger();
     protected final Map<Integer, CompletableFuture<List<String>>> responses = new ConcurrentHashMap<>();


(maven-resolver) 23/34: Fix TransferResource field visibility — add volatile

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 25685cfc0e935fbedb96e672ba2888c8d18cab73
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:54:07 2026 +0000

    Fix TransferResource field visibility — add volatile
    
    F-27: Make contentLength and resumeOffset volatile to ensure
    visibility when written on worker threads and read by monitoring
    or logging threads.
---
 .../src/main/java/org/eclipse/aether/transfer/TransferResource.java   | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/maven-resolver-api/src/main/java/org/eclipse/aether/transfer/TransferResource.java b/maven-resolver-api/src/main/java/org/eclipse/aether/transfer/TransferResource.java
index 16cdccdc9..d2126bc81 100644
--- a/maven-resolver-api/src/main/java/org/eclipse/aether/transfer/TransferResource.java
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/transfer/TransferResource.java
@@ -46,9 +46,9 @@ public final class TransferResource {
 
     private final RequestTrace trace;
 
-    private long contentLength = -1L;
+    private volatile long contentLength = -1L;
 
-    private long resumeOffset;
+    private volatile long resumeOffset;
 
     public static Clock getClock() {
         return clock;


(maven-resolver) 33/34: Fix Results — synchronize read methods for visibility

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 5e928d28eb3a1437fae38764753ee9d75f7a49c6
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 09:46:48 2026 +0000

    Fix Results — synchronize read methods for visibility
    
    The write paths addException() and addCycle() are synchronized but
    getResult() and getErrorPath() were not, so readers could observe
    stale data without a happens-before edge from the writer.
    
    Make both getter methods synchronized to establish proper visibility
    guarantees for cross-thread reads.
---
 .../aether/internal/impl/collect/DependencyCollectorDelegate.java     | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java
index 988b8e418..d7cf77b23 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java
@@ -560,11 +560,11 @@ public abstract class DependencyCollectorDelegate implements DependencyCollector
             maxCycles = ConfigUtils.getInteger(session, DEFAULT_MAX_CYCLES, CONFIG_PROP_MAX_CYCLES);
         }
 
-        public CollectResult getResult() {
+        public synchronized CollectResult getResult() {
             return result;
         }
 
-        public String getErrorPath() {
+        public synchronized String getErrorPath() {
             return errorPath;
         }
 


(maven-resolver) 30/34: Fix IpcServer unlock — replace TOCTOU isEmpty+remove with atomic compute

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit dc2c84ff9de73ed5d707b7f5c1f3e23469d9b615
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 09:43:56 2026 +0000

    Fix IpcServer unlock — replace TOCTOU isEmpty+remove with atomic compute
    
    The unlock() path had a race: after checking l.isEmpty(), another
    thread could computeIfAbsent and re-use the same Lock object, then
    the remove(l.key, l) would remove a Lock that now has holders.
    
    Replace the two-step isEmpty() check + remove() with a single
    locks.compute() call that atomically checks emptiness and removes.
---
 .../src/main/java/org/eclipse/aether/named/ipc/IpcServer.java         | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java b/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java
index b7ae0a03f..6d5470fd2 100644
--- a/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java
+++ b/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java
@@ -497,9 +497,7 @@ public class IpcServer {
                     .map(k -> IpcServer.this.locks.computeIfAbsent(k, Lock::new))
                     .forEach(l -> {
                         l.unlock(this);
-                        if (l.isEmpty()) {
-                            IpcServer.this.locks.remove(l.key, l);
-                        }
+                        IpcServer.this.locks.compute(l.key, (k, v) -> (v == l && v.isEmpty()) ? null : v);
                     });
         }
     }


(maven-resolver) 15/34: Fix IpcServer.expirationCheck() crash on negative sleep argument

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit ee8c2b00951105d24259c9de2179d3a69eb97c23
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:45:07 2026 +0000

    Fix IpcServer.expirationCheck() crash on negative sleep argument
    
    F-18: Clamp Thread.sleep() argument to minimum 1ms to prevent
    IllegalArgumentException when left goes negative while clients
    are still connected. Previously this silently killed the expiration
    thread, causing the server to never shut down.
---
 .../src/main/java/org/eclipse/aether/named/ipc/IpcServer.java           | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java b/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java
index dcafa4cba..99e49a124 100644
--- a/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java
+++ b/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java
@@ -377,7 +377,7 @@ public class IpcServer {
                 break;
             } else {
                 try {
-                    Thread.sleep(TimeUnit.NANOSECONDS.toMillis(left));
+                    Thread.sleep(Math.max(1, TimeUnit.NANOSECONDS.toMillis(left)));
                 } catch (InterruptedException e) {
                     info("IpcServer expiration check interrupted, closing");
                     close();


(maven-resolver) 14/34: Fix CachingArtifactTypeRegistry — use ConcurrentHashMap

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit b2b84d66cc18dc737ad8985a8cec37796d7cc1dc
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:43:49 2026 +0000

    Fix CachingArtifactTypeRegistry — use ConcurrentHashMap
    
    F-17: Replace plain HashMap with ConcurrentHashMap to prevent
    corruption when concurrent parallel stream workers query the
    artifact type registry simultaneously.
---
 .../internal/impl/collect/CachingArtifactTypeRegistry.java  | 13 +++----------
 1 file changed, 3 insertions(+), 10 deletions(-)

diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java
index 0ba77e1f2..2f30f2b10 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/CachingArtifactTypeRegistry.java
@@ -18,8 +18,8 @@
  */
 package org.eclipse.aether.internal.impl.collect;
 
-import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.artifact.ArtifactType;
@@ -45,17 +45,10 @@ public class CachingArtifactTypeRegistry implements ArtifactTypeRegistry {
 
     private CachingArtifactTypeRegistry(ArtifactTypeRegistry delegate) {
         this.delegate = delegate;
-        types = new HashMap<>();
+        types = new ConcurrentHashMap<>();
     }
 
     public ArtifactType get(String typeId) {
-        ArtifactType type = types.get(typeId);
-
-        if (type == null) {
-            type = delegate.get(typeId);
-            types.put(typeId, type);
-        }
-
-        return type;
+        return types.computeIfAbsent(typeId, delegate::get);
     }
 }


(maven-resolver) 24/34: Fix lazy hashCode fields — add volatile in 4 classes

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 84e73e14bd5ae0b7360f98a66858ed3983ad0c1d
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:59:16 2026 +0000

    Fix lazy hashCode fields — add volatile in 4 classes
    
    F-29: Make cached hashCode fields volatile in ExclusionDependencySelector,
    AndDependencySelector, AndDependencyTraverser, and ChainedVersionFilter
    to ensure visibility across threads.
---
 .../aether/util/graph/selector/AndDependencySelector.java      | 10 ++++------
 .../util/graph/selector/ExclusionDependencySelector.java       | 10 ++++------
 .../aether/util/graph/traverser/AndDependencyTraverser.java    | 10 ++++------
 .../aether/util/graph/version/ChainedVersionFilter.java        |  8 ++------
 4 files changed, 14 insertions(+), 24 deletions(-)

diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/selector/AndDependencySelector.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/selector/AndDependencySelector.java
index 89e58c676..6c3534c8b 100644
--- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/selector/AndDependencySelector.java
+++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/selector/AndDependencySelector.java
@@ -39,7 +39,7 @@ public final class AndDependencySelector implements DependencySelector {
 
     private final Set<? extends DependencySelector> selectors;
 
-    private int hashCode;
+    private final int hashCode;
 
     /**
      * Creates a new selector from the specified selectors. Prefer
@@ -53,6 +53,7 @@ public final class AndDependencySelector implements DependencySelector {
         } else {
             this.selectors = Collections.emptySet();
         }
+        this.hashCode = 17 * 31 + this.selectors.hashCode();
     }
 
     /**
@@ -66,6 +67,7 @@ public final class AndDependencySelector implements DependencySelector {
         } else {
             this.selectors = Collections.emptySet();
         }
+        this.hashCode = 17 * 31 + this.selectors.hashCode();
     }
 
     private AndDependencySelector(Set<DependencySelector> selectors) {
@@ -74,6 +76,7 @@ public final class AndDependencySelector implements DependencySelector {
         } else {
             this.selectors = Collections.emptySet();
         }
+        this.hashCode = 17 * 31 + this.selectors.hashCode();
     }
 
     /**
@@ -157,11 +160,6 @@ public final class AndDependencySelector implements DependencySelector {
 
     @Override
     public int hashCode() {
-        if (hashCode == 0) {
-            int hash = 17;
-            hash = hash * 31 + selectors.hashCode();
-            hashCode = hash;
-        }
         return hashCode;
     }
 
diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/selector/ExclusionDependencySelector.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/selector/ExclusionDependencySelector.java
index 0a3ba5418..cf93b0b80 100644
--- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/selector/ExclusionDependencySelector.java
+++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/selector/ExclusionDependencySelector.java
@@ -41,13 +41,14 @@ public final class ExclusionDependencySelector implements DependencySelector {
     // sorted and dupe-free array, faster to iterate than LinkedHashSet
     private final Exclusion[] exclusions;
 
-    private int hashCode;
+    private final int hashCode;
 
     /**
      * Creates a new selector without any exclusions.
      */
     public ExclusionDependencySelector() {
         this.exclusions = new Exclusion[0];
+        this.hashCode = getClass().hashCode() * 31 + Arrays.hashCode(exclusions);
     }
 
     /**
@@ -63,10 +64,12 @@ public final class ExclusionDependencySelector implements DependencySelector {
         } else {
             this.exclusions = new Exclusion[0];
         }
+        this.hashCode = getClass().hashCode() * 31 + Arrays.hashCode(this.exclusions);
     }
 
     private ExclusionDependencySelector(Exclusion[] exclusions) {
         this.exclusions = exclusions;
+        this.hashCode = getClass().hashCode() * 31 + Arrays.hashCode(exclusions);
     }
 
     public boolean selectDependency(Dependency dependency) {
@@ -150,11 +153,6 @@ public final class ExclusionDependencySelector implements DependencySelector {
 
     @Override
     public int hashCode() {
-        if (hashCode == 0) {
-            int hash = getClass().hashCode();
-            hash = hash * 31 + Arrays.hashCode(exclusions);
-            hashCode = hash;
-        }
         return hashCode;
     }
 
diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/traverser/AndDependencyTraverser.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/traverser/AndDependencyTraverser.java
index c3cb483f4..97fbcf1bc 100644
--- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/traverser/AndDependencyTraverser.java
+++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/traverser/AndDependencyTraverser.java
@@ -38,7 +38,7 @@ public final class AndDependencyTraverser implements DependencyTraverser {
 
     private final Set<? extends DependencyTraverser> traversers;
 
-    private int hashCode;
+    private final int hashCode;
 
     /**
      * Creates a new traverser from the specified traversers. Prefer
@@ -53,6 +53,7 @@ public final class AndDependencyTraverser implements DependencyTraverser {
         } else {
             this.traversers = Collections.emptySet();
         }
+        this.hashCode = 17 * 31 + this.traversers.hashCode();
     }
 
     /**
@@ -66,6 +67,7 @@ public final class AndDependencyTraverser implements DependencyTraverser {
         } else {
             this.traversers = Collections.emptySet();
         }
+        this.hashCode = 17 * 31 + this.traversers.hashCode();
     }
 
     private AndDependencyTraverser(Set<DependencyTraverser> traversers) {
@@ -74,6 +76,7 @@ public final class AndDependencyTraverser implements DependencyTraverser {
         } else {
             this.traversers = Collections.emptySet();
         }
+        this.hashCode = 17 * 31 + this.traversers.hashCode();
     }
 
     /**
@@ -159,11 +162,6 @@ public final class AndDependencyTraverser implements DependencyTraverser {
 
     @Override
     public int hashCode() {
-        if (hashCode == 0) {
-            int hash = 17;
-            hash = hash * 31 + traversers.hashCode();
-            hashCode = hash;
-        }
         return hashCode;
     }
 }
diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/version/ChainedVersionFilter.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/version/ChainedVersionFilter.java
index d3d37d2e5..804e61854 100644
--- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/version/ChainedVersionFilter.java
+++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/graph/version/ChainedVersionFilter.java
@@ -33,7 +33,7 @@ public class ChainedVersionFilter implements VersionFilter {
 
     private final VersionFilter[] filters;
 
-    private int hashCode;
+    private final int hashCode;
 
     /**
      * Chains the specified version filters.
@@ -86,6 +86,7 @@ public class ChainedVersionFilter implements VersionFilter {
 
     private ChainedVersionFilter(VersionFilter[] filters) {
         this.filters = filters;
+        this.hashCode = getClass().hashCode() * 31 + Arrays.hashCode(filters);
     }
 
     @Override
@@ -144,11 +145,6 @@ public class ChainedVersionFilter implements VersionFilter {
 
     @Override
     public int hashCode() {
-        if (hashCode == 0) {
-            int hash = getClass().hashCode();
-            hash = hash * 31 + Arrays.hashCode(filters);
-            hashCode = hash;
-        }
         return hashCode;
     }
 }


(maven-resolver) 31/34: Fix GenericVersionScheme — synchronize computeIfAbsent on versionCache

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 7fa7741a9889d98e7d0ed1e8543ab1796e01b1ad
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 09:44:41 2026 +0000

    Fix GenericVersionScheme — synchronize computeIfAbsent on versionCache
    
    Collections.synchronizedMap does not make computeIfAbsent atomic;
    the default Map.computeIfAbsent implementation performs a non-atomic
    get-then-put sequence, risking concurrent structural modification of
    the underlying WeakHashMap.
    
    Wrap the computeIfAbsent call in synchronized(versionCache) to use
    the same monitor that synchronizedMap uses internally.
---
 .../org/eclipse/aether/util/version/GenericVersionScheme.java | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/version/GenericVersionScheme.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/version/GenericVersionScheme.java
index 7188e1966..5a8428f56 100644
--- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/version/GenericVersionScheme.java
+++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/version/GenericVersionScheme.java
@@ -100,10 +100,13 @@ public class GenericVersionScheme extends VersionSchemeSupport {
         GLOBAL_TOTAL_REQUESTS.incrementAndGet();
 
         boolean[] created = {false};
-        GenericVersion result = versionCache.computeIfAbsent(version, v -> {
-            created[0] = true;
-            return new GenericVersion(v);
-        });
+        GenericVersion result;
+        synchronized (versionCache) {
+            result = versionCache.computeIfAbsent(version, v -> {
+                created[0] = true;
+                return new GenericVersion(v);
+            });
+        }
         if (created[0]) {
             cacheMisses.incrementAndGet();
             GLOBAL_CACHE_MISSES.incrementAndGet();


(maven-resolver) 13/34: Fix SimpleArtifactTypeRegistry — use ConcurrentHashMap

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 0a138b386053560121a48873b6004d4339f9cb02
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:42:12 2026 +0000

    Fix SimpleArtifactTypeRegistry — use ConcurrentHashMap
    
    F-16: Replace plain HashMap with ConcurrentHashMap to ensure
    visibility of types populated on the main thread to worker threads
    querying via the session.
---
 .../org/eclipse/aether/util/artifact/SimpleArtifactTypeRegistry.java  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/artifact/SimpleArtifactTypeRegistry.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/artifact/SimpleArtifactTypeRegistry.java
index 65378f456..fd12a6435 100644
--- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/artifact/SimpleArtifactTypeRegistry.java
+++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/artifact/SimpleArtifactTypeRegistry.java
@@ -18,8 +18,8 @@
  */
 package org.eclipse.aether.util.artifact;
 
-import java.util.HashMap;
 import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.eclipse.aether.artifact.ArtifactType;
 import org.eclipse.aether.artifact.ArtifactTypeRegistry;
@@ -36,7 +36,7 @@ class SimpleArtifactTypeRegistry implements ArtifactTypeRegistry {
      * to populate the registry.
      */
     SimpleArtifactTypeRegistry() {
-        types = new HashMap<>();
+        types = new ConcurrentHashMap<>();
     }
 
     /**


(maven-resolver) 25/34: Fix CompositeNamedLock — remove redundant double unlockAll

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 42339126041d3050407648587f12557009093c70
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 07:00:53 2026 +0000

    Fix CompositeNamedLock — remove redundant double unlockAll
    
    F-31: Return false immediately after unlockAll() on lock failure
    instead of breaking out of the loop and calling unlockAll() again
    on the already-empty deque.
---
 .../org/eclipse/aether/named/support/CompositeNamedLock.java   | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/maven-resolver-named-locks/src/main/java/org/eclipse/aether/named/support/CompositeNamedLock.java b/maven-resolver-named-locks/src/main/java/org/eclipse/aether/named/support/CompositeNamedLock.java
index e260d7970..583d19583 100644
--- a/maven-resolver-named-locks/src/main/java/org/eclipse/aether/named/support/CompositeNamedLock.java
+++ b/maven-resolver-named-locks/src/main/java/org/eclipse/aether/named/support/CompositeNamedLock.java
@@ -85,17 +85,13 @@ public final class CompositeNamedLock extends NamedLockSupport {
                         timeStr);
 
                 unlockAll(step);
-                break;
+                return false;
             } else {
                 step.push(namedLock);
             }
         }
-        if (step.size() == locks.size()) {
-            steps.push(step);
-            return true;
-        }
-        unlockAll(step);
-        return false;
+        steps.push(step);
+        return true;
     }
 
     @Override


(maven-resolver) 05/34: Fix DataPool cache initialization race — use computeIfAbsent

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 329c4f242aa1c6a8be1b64b0edb28c550ba3346f
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:31:50 2026 +0000

    Fix DataPool cache initialization race — use computeIfAbsent
    
    F-08: Replace get-then-put pattern with cache.computeIfAbsent() for
    all four intern pools, preventing concurrent DataPool constructors
    from creating and using detached pool instances.
---
 .../aether/internal/impl/collect/DataPool.java     | 71 ++++++++--------------
 1 file changed, 26 insertions(+), 45 deletions(-)

diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java
index e1ed28b73..46c217952 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java
@@ -180,53 +180,34 @@ public final class DataPool {
         internArtifactDescriptorManagedDependencies = ConfigUtils.getBoolean(
                 session, true, CONFIG_PROP_COLLECTOR_POOL_INTERN_ARTIFACT_DESCRIPTOR_MANAGED_DEPENDENCIES);
 
-        InternPool<Artifact, Artifact> artifactsPool = null;
-        InternPool<Dependency, Dependency> dependenciesPool = null;
-        InternPool<DescriptorKey, Descriptor> descriptorsPool = null;
-        InternPool<List<Dependency>, List<Dependency>> dependencyListsPool = null;
+        InternPool<Artifact, Artifact> artifactsPool;
+        InternPool<Dependency, Dependency> dependenciesPool;
+        InternPool<DescriptorKey, Descriptor> descriptorsPool;
+        InternPool<List<Dependency>, List<Dependency>> dependencyListsPool;
         if (cache != null) {
-            artifactsPool = (InternPool<Artifact, Artifact>) cache.get(session, ARTIFACT_POOL);
-            dependenciesPool = (InternPool<Dependency, Dependency>) cache.get(session, DEPENDENCY_POOL);
-            descriptorsPool = (InternPool<DescriptorKey, Descriptor>) cache.get(session, DESCRIPTORS);
+            artifactsPool = (InternPool<Artifact, Artifact>) cache.computeIfAbsent(
+                    session,
+                    ARTIFACT_POOL,
+                    () -> createPool(ConfigUtils.getString(session, WEAK, CONFIG_PROP_COLLECTOR_POOL_ARTIFACT)));
+            dependenciesPool = (InternPool<Dependency, Dependency>) cache.computeIfAbsent(
+                    session,
+                    DEPENDENCY_POOL,
+                    () -> createPool(ConfigUtils.getString(session, WEAK, CONFIG_PROP_COLLECTOR_POOL_DEPENDENCY)));
+            descriptorsPool = (InternPool<DescriptorKey, Descriptor>) cache.computeIfAbsent(
+                    session,
+                    DESCRIPTORS,
+                    () -> createPool(ConfigUtils.getString(session, HARD, CONFIG_PROP_COLLECTOR_POOL_DESCRIPTOR)));
+            dependencyListsPool = (InternPool<List<Dependency>, List<Dependency>>) cache.computeIfAbsent(
+                    session,
+                    DEPENDENCY_LISTS_POOL,
+                    () -> createPool(
+                            ConfigUtils.getString(session, HARD, CONFIG_PROP_COLLECTOR_POOL_DEPENDENCY_LISTS)));
+        } else {
+            artifactsPool = createPool(ConfigUtils.getString(session, WEAK, CONFIG_PROP_COLLECTOR_POOL_ARTIFACT));
+            dependenciesPool = createPool(ConfigUtils.getString(session, WEAK, CONFIG_PROP_COLLECTOR_POOL_DEPENDENCY));
+            descriptorsPool = createPool(ConfigUtils.getString(session, HARD, CONFIG_PROP_COLLECTOR_POOL_DESCRIPTOR));
             dependencyListsPool =
-                    (InternPool<List<Dependency>, List<Dependency>>) cache.get(session, DEPENDENCY_LISTS_POOL);
-        }
-
-        if (artifactsPool == null) {
-            String artifactPoolType = ConfigUtils.getString(session, WEAK, CONFIG_PROP_COLLECTOR_POOL_ARTIFACT);
-
-            artifactsPool = createPool(artifactPoolType);
-            if (cache != null) {
-                cache.put(session, ARTIFACT_POOL, artifactsPool);
-            }
-        }
-
-        if (dependenciesPool == null) {
-            String dependencyPoolType = ConfigUtils.getString(session, WEAK, CONFIG_PROP_COLLECTOR_POOL_DEPENDENCY);
-
-            dependenciesPool = createPool(dependencyPoolType);
-            if (cache != null) {
-                cache.put(session, DEPENDENCY_POOL, dependenciesPool);
-            }
-        }
-
-        if (descriptorsPool == null) {
-            String descriptorPoolType = ConfigUtils.getString(session, HARD, CONFIG_PROP_COLLECTOR_POOL_DESCRIPTOR);
-
-            descriptorsPool = createPool(descriptorPoolType);
-            if (cache != null) {
-                cache.put(session, DESCRIPTORS, descriptorsPool);
-            }
-        }
-
-        if (dependencyListsPool == null) {
-            String dependencyListsPoolType =
-                    ConfigUtils.getString(session, HARD, CONFIG_PROP_COLLECTOR_POOL_DEPENDENCY_LISTS);
-
-            dependencyListsPool = createPool(dependencyListsPoolType);
-            if (cache != null) {
-                cache.put(session, DEPENDENCY_LISTS_POOL, dependencyListsPool);
-            }
+                    createPool(ConfigUtils.getString(session, HARD, CONFIG_PROP_COLLECTOR_POOL_DEPENDENCY_LISTS));
         }
 
         this.artifacts = artifactsPool;


(maven-resolver) 11/34: Fix DefaultMirrorSelector — use CopyOnWriteArrayList for mirrors

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 47fb6945ca17b02b0a93c049f53181389f43fa10
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:40:38 2026 +0000

    Fix DefaultMirrorSelector — use CopyOnWriteArrayList for mirrors
    
    F-14: Replace plain ArrayList with CopyOnWriteArrayList to prevent
    ConcurrentModificationException when add() is called concurrently
    with getMirror()/findMirror() iteration.
---
 .../org/eclipse/aether/util/repository/DefaultMirrorSelector.java     | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/DefaultMirrorSelector.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/DefaultMirrorSelector.java
index d7740acb7..e3b6b9365 100644
--- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/DefaultMirrorSelector.java
+++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/DefaultMirrorSelector.java
@@ -18,9 +18,9 @@
  */
 package org.eclipse.aether.util.repository;
 
-import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 import org.eclipse.aether.repository.MirrorSelector;
 import org.eclipse.aether.repository.RemoteRepository;
@@ -38,7 +38,7 @@ public final class DefaultMirrorSelector implements MirrorSelector {
 
     private static final String EXTERNAL_HTTP_WILDCARD = "external:http:*";
 
-    private final List<MirrorDef> mirrors = new ArrayList<>();
+    private final List<MirrorDef> mirrors = new CopyOnWriteArrayList<>();
 
     /**
      * Adds the specified mirror to this selector.


(maven-resolver) 03/34: Fix Apache BasicAuthCache thread safety — use ConcurrentHashMap-backed AuthCache

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 7f10ebd22a0445b70f427a3e0c11b06b97a75c6c
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:28:39 2026 +0000

    Fix Apache BasicAuthCache thread safety — use ConcurrentHashMap-backed AuthCache
    
    F-06: Replace BasicAuthCache (backed by plain HashMap) with a
    ConcurrentAuthCache backed by ConcurrentHashMap, preventing data
    corruption when concurrent requests call authCache.put() during
    preemptive auth.
---
 .../aether/transport/apache/ApacheTransporter.java | 38 ++++++++++++++++++++--
 1 file changed, 35 insertions(+), 3 deletions(-)

diff --git a/maven-resolver-transport-apache/src/main/java/org/eclipse/aether/transport/apache/ApacheTransporter.java b/maven-resolver-transport-apache/src/main/java/org/eclipse/aether/transport/apache/ApacheTransporter.java
index 325360e50..7a1f92905 100644
--- a/maven-resolver-transport-apache/src/main/java/org/eclipse/aether/transport/apache/ApacheTransporter.java
+++ b/maven-resolver-transport-apache/src/main/java/org/eclipse/aether/transport/apache/ApacheTransporter.java
@@ -33,6 +33,7 @@ import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.function.Function;
 import java.util.regex.Matcher;
 
@@ -43,6 +44,7 @@ import org.apache.http.HttpHeaders;
 import org.apache.http.HttpHost;
 import org.apache.http.HttpResponse;
 import org.apache.http.HttpStatus;
+import org.apache.http.auth.AuthScheme;
 import org.apache.http.auth.AuthSchemeProvider;
 import org.apache.http.auth.AuthScope;
 import org.apache.http.client.AuthCache;
@@ -73,7 +75,6 @@ import org.apache.http.impl.auth.DigestSchemeFactory;
 import org.apache.http.impl.auth.KerberosSchemeFactory;
 import org.apache.http.impl.auth.NTLMSchemeFactory;
 import org.apache.http.impl.auth.SPNegoSchemeFactory;
-import org.apache.http.impl.client.BasicAuthCache;
 import org.apache.http.impl.client.CloseableHttpClient;
 import org.apache.http.impl.client.DefaultHttpRequestRetryHandler;
 import org.apache.http.impl.client.HttpClientBuilder;
@@ -294,9 +295,9 @@ final class ApacheTransporter extends AbstractTransporter implements HttpTranspo
                             Keys.of(
                                     getClass(),
                                     repository.getId() + "-" + StringDigestUtil.sha1(repository.toString())),
-                            BasicAuthCache::new);
+                            ConcurrentAuthCache::new);
         } else {
-            this.authCache = new BasicAuthCache();
+            this.authCache = new ConcurrentAuthCache();
         }
         this.client = builder.build();
     }
@@ -765,4 +766,35 @@ final class ApacheTransporter extends AbstractTransporter implements HttpTranspo
             return ri;
         }
     }
+
+    static class ConcurrentAuthCache implements AuthCache {
+        private final ConcurrentHashMap<HttpHost, AuthScheme> map = new ConcurrentHashMap<>();
+
+        @Override
+        public void put(HttpHost host, AuthScheme authScheme) {
+            if (host != null && authScheme != null) {
+                map.put(host, authScheme);
+            }
+        }
+
+        @Override
+        public AuthScheme get(HttpHost host) {
+            if (host == null) {
+                return null;
+            }
+            return map.get(host);
+        }
+
+        @Override
+        public void remove(HttpHost host) {
+            if (host != null) {
+                map.remove(host);
+            }
+        }
+
+        @Override
+        public void clear() {
+            map.clear();
+        }
+    }
 }


(maven-resolver) 29/34: Fix SmartExecutor — add RejectedExecutionException fallback to Callable overloads

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 8dcd69c4edacb27b710ebaf526e75c0de100fc84
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 09:43:21 2026 +0000

    Fix SmartExecutor — add RejectedExecutionException fallback to Callable overloads
    
    Pooled.submit(Callable) and Limited.submit(Callable) did not catch
    RejectedExecutionException, unlike their Runnable counterparts. On
    rejection, Pooled would let the REE propagate with a never-completed
    future, and Limited would additionally leak a semaphore permit.
    
    Apply the same try-catch-fallback pattern: on rejection, run the
    callable on the caller thread and complete the future inline.
---
 .../aether/util/concurrency/SmartExecutor.java     | 42 +++++++++++++++-------
 1 file changed, 30 insertions(+), 12 deletions(-)

diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/concurrency/SmartExecutor.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/concurrency/SmartExecutor.java
index fcae4f911..3b2f25c1f 100644
--- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/concurrency/SmartExecutor.java
+++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/concurrency/SmartExecutor.java
@@ -110,17 +110,25 @@ public interface SmartExecutor extends AutoCloseable {
         public <T> Future<T> submit(Callable<T> callable) {
             ClassLoader tccl = Thread.currentThread().getContextClassLoader();
             CompletableFuture<T> future = new CompletableFuture<>();
-            executor.submit(() -> {
-                ClassLoader old = Thread.currentThread().getContextClassLoader();
-                Thread.currentThread().setContextClassLoader(tccl);
+            try {
+                executor.submit(() -> {
+                    ClassLoader old = Thread.currentThread().getContextClassLoader();
+                    Thread.currentThread().setContextClassLoader(tccl);
+                    try {
+                        future.complete(callable.call());
+                    } catch (Exception e) {
+                        future.completeExceptionally(e);
+                    } finally {
+                        Thread.currentThread().setContextClassLoader(old);
+                    }
+                });
+            } catch (RejectedExecutionException e) {
                 try {
                     future.complete(callable.call());
-                } catch (Exception e) {
-                    future.completeExceptionally(e);
-                } finally {
-                    Thread.currentThread().setContextClassLoader(old);
+                } catch (Exception ex) {
+                    future.completeExceptionally(ex);
                 }
-            });
+            }
             return future;
         }
 
@@ -176,15 +184,25 @@ public interface SmartExecutor extends AutoCloseable {
             try {
                 semaphore.acquire();
                 CompletableFuture<T> future = new CompletableFuture<>();
-                executor.submit(() -> {
+                try {
+                    executor.submit(() -> {
+                        try {
+                            future.complete(callable.call());
+                        } catch (Exception e) {
+                            future.completeExceptionally(e);
+                        } finally {
+                            semaphore.release();
+                        }
+                    });
+                } catch (RejectedExecutionException e) {
                     try {
                         future.complete(callable.call());
-                    } catch (Exception e) {
-                        future.completeExceptionally(e);
+                    } catch (Exception ex) {
+                        future.completeExceptionally(ex);
                     } finally {
                         semaphore.release();
                     }
-                });
+                }
                 return future;
             } catch (InterruptedException e) {
                 Thread.currentThread().interrupt();


(maven-resolver) 26/34: Fix WagonTransporter pollWagon — release wagon on reconnect failure instead of re-queuing

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 99e7e8dd3e0cf8a6dc8c8ea6b04335a330dbe40b
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 07:04:18 2026 +0000

    Fix WagonTransporter pollWagon — release wagon on reconnect failure instead of re-queuing
---
 .../main/java/org/eclipse/aether/transport/wagon/WagonTransporter.java  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporter.java b/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporter.java
index b36b439ce..0d137246e 100644
--- a/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporter.java
+++ b/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporter.java
@@ -312,7 +312,7 @@ final class WagonTransporter implements Transporter {
             try {
                 connectWagon(wagon);
             } catch (Exception e) {
-                wagons.add(wagon);
+                releaseWagon(wagon);
                 throw e;
             }
         }


(maven-resolver) 06/34: Fix WeakInternPool.intern() non-atomic check-then-act

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 80e49a7a64e0272ee0c153de366e6767d1951bde
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:33:16 2026 +0000

    Fix WeakInternPool.intern() non-atomic check-then-act
    
    F-09: Use map.compute() instead of separate get/put to make the
    interning operation atomic, preventing duplicate objects from
    defeating heap deduplication during concurrent descriptor resolution.
---
 .../aether/internal/impl/collect/DataPool.java      | 21 +++++++++++++--------
 1 file changed, 13 insertions(+), 8 deletions(-)

diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java
index 46c217952..84095e09c 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DataPool.java
@@ -564,16 +564,21 @@ public final class DataPool {
         }
 
         @Override
+        @SuppressWarnings("unchecked")
         public V intern(K key, V value) {
-            WeakReference<V> pooledRef = map.get(key);
-            if (pooledRef != null) {
-                V pooled = pooledRef.get();
-                if (pooled != null) {
-                    return pooled;
+            Object[] result = new Object[1];
+            map.compute(key, (k, existingRef) -> {
+                if (existingRef != null) {
+                    V pooled = existingRef.get();
+                    if (pooled != null) {
+                        result[0] = pooled;
+                        return existingRef;
+                    }
                 }
-            }
-            map.put(key, new WeakReference<>(value));
-            return value;
+                result[0] = value;
+                return new WeakReference<>(value);
+            });
+            return (V) result[0];
         }
     }
 }


(maven-resolver) 17/34: Fix IpcServer Lock.unlock() — complete futures outside monitor

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 1c86ff471bf25a1b69fd47ce7d05aaace89f1c86
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:47:49 2026 +0000

    Fix IpcServer Lock.unlock() — complete futures outside monitor
    
    F-20: Collect futures to complete during the synchronized block but
    complete them after releasing the Lock monitor, preventing I/O
    operations (socket writes in thenRun callbacks) from being
    serialized under the lock.
---
 .../org/eclipse/aether/named/ipc/IpcServer.java    | 35 ++++++++++++----------
 1 file changed, 20 insertions(+), 15 deletions(-)

diff --git a/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java b/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java
index 9f58fb7e2..b7ae0a03f 100644
--- a/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java
+++ b/maven-resolver-named-locks-ipc/src/main/java/org/eclipse/aether/named/ipc/IpcServer.java
@@ -442,24 +442,29 @@ public class IpcServer {
             return future;
         }
 
-        public synchronized void unlock(Context context) {
-            if (holders.remove(context)) {
-                while (waiters != null
-                        && !waiters.isEmpty()
-                        && (holders.isEmpty() || holders.get(0).shared && waiters.get(0).context.shared)) {
-                    Waiter waiter = waiters.remove(0);
-                    holders.add(waiter.context);
-                    waiter.future.complete(null);
-                }
-            } else if (waiters != null) {
-                for (Iterator<Waiter> it = waiters.iterator(); it.hasNext(); ) {
-                    Waiter waiter = it.next();
-                    if (waiter.context == context) {
-                        it.remove();
-                        waiter.future.cancel(false);
+        public void unlock(Context context) {
+            List<CompletableFuture<Void>> toComplete;
+            synchronized (this) {
+                toComplete = new ArrayList<>();
+                if (holders.remove(context)) {
+                    while (waiters != null
+                            && !waiters.isEmpty()
+                            && (holders.isEmpty() || holders.get(0).shared && waiters.get(0).context.shared)) {
+                        Waiter waiter = waiters.remove(0);
+                        holders.add(waiter.context);
+                        toComplete.add(waiter.future);
+                    }
+                } else if (waiters != null) {
+                    for (Iterator<Waiter> it = waiters.iterator(); it.hasNext(); ) {
+                        Waiter waiter = it.next();
+                        if (waiter.context == context) {
+                            it.remove();
+                            waiter.future.cancel(false);
+                        }
                     }
                 }
             }
+            toComplete.forEach(f -> f.complete(null));
         }
 
         public synchronized boolean isEmpty() {


(maven-resolver) 07/34: Fix Results.addException/addCycle thread safety for parallelStream

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 9c1182d7fc96178e0008c28c68fa30068c712291
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:34:47 2026 +0000

    Fix Results.addException/addCycle thread safety for parallelStream
    
    F-10: Synchronize addException() and addCycle() in Results to prevent
    data races when called concurrently from BfDependencyCollector's
    parallelStream during version range resolution.
---
 .../aether/internal/impl/collect/DependencyCollectorDelegate.java     | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java
index a9d36745f..5c7b61320 100644
--- a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java
+++ b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/collect/DependencyCollectorDelegate.java
@@ -568,7 +568,7 @@ public abstract class DependencyCollectorDelegate implements DependencyCollector
             return errorPath;
         }
 
-        public void addException(Dependency dependency, Exception e, List<DependencyNode> nodes) {
+        public synchronized void addException(Dependency dependency, Exception e, List<DependencyNode> nodes) {
             if (maxExceptions < 0 || result.getExceptions().size() < maxExceptions) {
                 result.addException(e);
                 if (errorPath == null) {
@@ -591,7 +591,7 @@ public abstract class DependencyCollectorDelegate implements DependencyCollector
             }
         }
 
-        public void addCycle(List<DependencyNode> nodes, int cycleEntry, Dependency dependency) {
+        public synchronized void addCycle(List<DependencyNode> nodes, int cycleEntry, Dependency dependency) {
             if (maxCycles < 0 || result.getCycles().size() < maxCycles) {
                 result.addCycle(new DefaultDependencyCycle(nodes, cycleEntry, dependency));
             }


(maven-resolver) 04/34: Fix SmartExecutor RejectedExecutionException causing await() hang

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 9341e597db38d5a91906457ddfb52507c6cb3974
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:30:12 2026 +0000

    Fix SmartExecutor RejectedExecutionException causing await() hang
    
    F-07: When executor.submit() throws RejectedExecutionException, fall
    back to running the task on the caller thread. This prevents
    RunnableErrorForwarder.await() from hanging forever when the counter
    was incremented by wrap() but the task never runs.
---
 .../aether/util/concurrency/SmartExecutor.java     | 39 +++++++++++++++++-----
 1 file changed, 31 insertions(+), 8 deletions(-)

diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/concurrency/SmartExecutor.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/concurrency/SmartExecutor.java
index 387a10886..fcae4f911 100644
--- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/concurrency/SmartExecutor.java
+++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/concurrency/SmartExecutor.java
@@ -22,6 +22,7 @@ import java.util.concurrent.Callable;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Future;
+import java.util.concurrent.RejectedExecutionException;
 import java.util.concurrent.Semaphore;
 
 /**
@@ -84,15 +85,25 @@ public interface SmartExecutor extends AutoCloseable {
         @Override
         public void submit(Runnable runnable) {
             ClassLoader tccl = Thread.currentThread().getContextClassLoader();
-            executor.submit(() -> {
-                ClassLoader old = Thread.currentThread().getContextClassLoader();
-                Thread.currentThread().setContextClassLoader(tccl);
+            try {
+                executor.submit(() -> {
+                    ClassLoader old = Thread.currentThread().getContextClassLoader();
+                    Thread.currentThread().setContextClassLoader(tccl);
+                    try {
+                        runnable.run();
+                    } finally {
+                        Thread.currentThread().setContextClassLoader(old);
+                    }
+                });
+            } catch (RejectedExecutionException e) {
                 try {
                     runnable.run();
-                } finally {
-                    Thread.currentThread().setContextClassLoader(old);
+                } catch (RuntimeException | Error t) {
+                    // swallow to match async submit() semantics where exceptions
+                    // are captured by the Future; callers like RunnableErrorForwarder
+                    // already record the error before re-throwing
                 }
-            });
+            }
         }
 
         @Override
@@ -136,13 +147,25 @@ public interface SmartExecutor extends AutoCloseable {
         public void submit(Runnable runnable) {
             try {
                 semaphore.acquire();
-                executor.submit(() -> {
+                try {
+                    executor.submit(() -> {
+                        try {
+                            runnable.run();
+                        } finally {
+                            semaphore.release();
+                        }
+                    });
+                } catch (RejectedExecutionException e) {
                     try {
                         runnable.run();
+                    } catch (RuntimeException | Error t) {
+                        // swallow to match async submit() semantics where exceptions
+                        // are captured by the Future; callers like RunnableErrorForwarder
+                        // already record the error before re-throwing
                     } finally {
                         semaphore.release();
                     }
-                });
+                }
             } catch (InterruptedException e) {
                 Thread.currentThread().interrupt();
             }


(maven-resolver) 10/34: Fix DefaultProxySelector — use CopyOnWriteArrayList for proxies

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 3c9ee81d017601fde10a759fc273b015fe8c09a6
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:39:39 2026 +0000

    Fix DefaultProxySelector — use CopyOnWriteArrayList for proxies
    
    F-13: Replace plain ArrayList with CopyOnWriteArrayList to prevent
    ConcurrentModificationException when add() is called concurrently
    with getProxy() iteration.
---
 .../java/org/eclipse/aether/util/repository/DefaultProxySelector.java  | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/DefaultProxySelector.java b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/DefaultProxySelector.java
index f20ffd36f..5cc2ea50e 100644
--- a/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/DefaultProxySelector.java
+++ b/maven-resolver-util/src/main/java/org/eclipse/aether/util/repository/DefaultProxySelector.java
@@ -24,6 +24,7 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.StringTokenizer;
+import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.regex.Pattern;
 
 import org.eclipse.aether.repository.Proxy;
@@ -37,7 +38,7 @@ import static java.util.Objects.requireNonNull;
  */
 public final class DefaultProxySelector implements ProxySelector {
 
-    private final List<ProxyDef> proxies = new ArrayList<>();
+    private final List<ProxyDef> proxies = new CopyOnWriteArrayList<>();
 
     /**
      * Adds the specified proxy definition to the selector. Proxy definitions are ordered, the first matching proxy for


(maven-resolver) 18/34: Fix NamedLockFactorySupport getLock/shutdown race

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 436cf8b016552f8a8e930f41704e01a7b76260e7
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:48:54 2026 +0000

    Fix NamedLockFactorySupport getLock/shutdown race
    
    F-21: Re-check shutdown flag inside the locks.compute() lambda to
    prevent creating locks on a backend that was shut down between the
    initial check and the actual lock creation.
---
 .../java/org/eclipse/aether/named/support/NamedLockFactorySupport.java | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/maven-resolver-named-locks/src/main/java/org/eclipse/aether/named/support/NamedLockFactorySupport.java b/maven-resolver-named-locks/src/main/java/org/eclipse/aether/named/support/NamedLockFactorySupport.java
index 253587476..bee186afc 100644
--- a/maven-resolver-named-locks/src/main/java/org/eclipse/aether/named/support/NamedLockFactorySupport.java
+++ b/maven-resolver-named-locks/src/main/java/org/eclipse/aether/named/support/NamedLockFactorySupport.java
@@ -116,6 +116,9 @@ public abstract class NamedLockFactorySupport implements NamedLockFactory {
 
     protected NamedLock getLockAndRefTrack(final NamedLockKey key, Supplier<NamedLockSupport> supplier) {
         return locks.compute(key, (k, v) -> {
+                    if (shutdown.get()) {
+                        throw new IllegalStateException("factory already shut down");
+                    }
                     if (v == null) {
                         v = new NamedLockHolder(supplier.get());
                     }


(maven-resolver) 09/34: Fix WagonTransporter close-while-executing wagon leak

Posted by gn...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 086d05bbd933f50a7026817132045f63c7538e36
Author: Guillaume Nodet <gn...@gmail.com>
AuthorDate: Sun Jun 7 06:37:36 2026 +0000

    Fix WagonTransporter close-while-executing wagon leak
    
    F-12: After transfer completes, check if the transporter was closed
    during execution. If so, disconnect and release the wagon instead of
    returning it to the queue where it would never be cleaned up.
---
 .../java/org/eclipse/aether/transport/wagon/WagonTransporter.java  | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporter.java b/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporter.java
index 082f07bca..b36b439ce 100644
--- a/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporter.java
+++ b/maven-resolver-transport-wagon/src/main/java/org/eclipse/aether/transport/wagon/WagonTransporter.java
@@ -357,7 +357,12 @@ final class WagonTransporter implements Transporter {
                 runner.run(wagon);
             } finally {
                 wagon.removeTransferListener(listener);
-                wagons.add(wagon);
+                if (closed.get()) {
+                    disconnectWagon(wagon);
+                    releaseWagon(wagon);
+                } else {
+                    wagons.add(wagon);
+                }
             }
         } catch (RuntimeException e) {
             throw WagonCancelledException.unwrap(e);