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

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


The following commit(s) were added to refs/heads/master by this push:
     new 5bca4bbe6 Fix locking issues (#1660)
5bca4bbe6 is described below

commit 5bca4bbe69f57df906d278c8d0e5b98e59da9c66
Author: Tamas Cservenak <[email protected]>
AuthorDate: Thu Nov 20 16:16:40 2025 +0100

    Fix locking issues (#1660)
    
    Changes:
    * introduce dedicated ex on timeout
    * up default timeouts (from 30s to 900s)
    * improve message by listing all lock subjects
    * improve message by mentioning the property that user should use to 
increase timeouts (there is no one size fits all; we could go with "infinite" 
timeouts. but am unsure about that)
---
 .../main/java/org/eclipse/aether/SyncContext.java  | 29 +++++++++++-
 .../synccontext/named/NamedLockFactoryAdapter.java | 52 +++++++++++++++++++---
 src/site/markdown/configuration.md                 |  2 +-
 3 files changed, 74 insertions(+), 9 deletions(-)

diff --git 
a/maven-resolver-api/src/main/java/org/eclipse/aether/SyncContext.java 
b/maven-resolver-api/src/main/java/org/eclipse/aether/SyncContext.java
index 63e8394bf..d9d0bfd45 100644
--- a/maven-resolver-api/src/main/java/org/eclipse/aether/SyncContext.java
+++ b/maven-resolver-api/src/main/java/org/eclipse/aether/SyncContext.java
@@ -61,12 +61,39 @@ public interface SyncContext extends Closeable {
      *
      * @param artifacts The artifacts to acquire, may be {@code null} or empty 
if none.
      * @param metadatas The metadatas to acquire, may be {@code null} or empty 
if none.
+     * @throws FailedToAcquireLockException if method calls to acquire lock 
within configured time.
      */
-    void acquire(Collection<? extends Artifact> artifacts, Collection<? 
extends Metadata> metadatas);
+    void acquire(Collection<? extends Artifact> artifacts, Collection<? 
extends Metadata> metadatas)
+            throws FailedToAcquireLockException;
 
     /**
      * Releases all previously acquired artifacts/metadatas. If no resources 
have been acquired before or if this
      * synchronization context has already been closed, this method does 
nothing.
      */
+    @Override
     void close();
+
+    /**
+     * Specific exception thrown by {@link #acquire(Collection, Collection)} 
method when it cannot acquire the lock.
+     *
+     * @since 1.9.25
+     */
+    final class FailedToAcquireLockException extends IllegalStateException {
+        private final boolean shared;
+
+        /**
+         * Constructor.
+         */
+        public FailedToAcquireLockException(boolean shared, String message) {
+            super(message);
+            this.shared = shared;
+        }
+
+        /**
+         * Returns {@code true} for shared and {@code false} for exclusive 
sync contexts.
+         */
+        public boolean isShared() {
+            return shared;
+        }
+    }
 }
diff --git 
a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/NamedLockFactoryAdapter.java
 
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/NamedLockFactoryAdapter.java
index e96212f04..2a2b0bafb 100644
--- 
a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/NamedLockFactoryAdapter.java
+++ 
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/synccontext/named/NamedLockFactoryAdapter.java
@@ -22,6 +22,7 @@ import java.util.ArrayDeque;
 import java.util.Collection;
 import java.util.Deque;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 import org.eclipse.aether.ConfigurationProperties;
 import org.eclipse.aether.RepositorySystemSession;
@@ -33,6 +34,7 @@ import org.eclipse.aether.named.NamedLockFactory;
 import org.eclipse.aether.named.NamedLockKey;
 import org.eclipse.aether.named.providers.FileLockNamedLockFactory;
 import org.eclipse.aether.util.ConfigUtils;
+import org.eclipse.aether.util.artifact.ArtifactIdUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -54,7 +56,7 @@ public final class NamedLockFactoryAdapter {
      */
     public static final String CONFIG_PROP_TIME = CONFIG_PROPS_PREFIX + "time";
 
-    public static final long DEFAULT_TIME = 30L;
+    public static final long DEFAULT_TIME = 900L;
 
     /**
      * The unit of maximum time amount to be blocked to obtain lock. Use 
TimeUnit enum names.
@@ -161,7 +163,7 @@ public final class NamedLockFactoryAdapter {
             this.shared = shared;
             this.lockNaming = lockNaming;
             this.namedLockFactory = namedLockFactory;
-            this.time = getTime(session);
+            this.time = getTime(session, DEFAULT_TIME, CONFIG_PROP_TIME);
             this.timeUnit = getTimeUnit(session);
             this.retry = getRetry(session);
             this.retryWait = getRetryWait(session);
@@ -178,8 +180,8 @@ public final class NamedLockFactoryAdapter {
             }
         }
 
-        private long getTime(final RepositorySystemSession session) {
-            return ConfigUtils.getLong(session, DEFAULT_TIME, 
CONFIG_PROP_TIME);
+        private long getTime(final RepositorySystemSession session, long 
defaultValue, String... keys) {
+            return ConfigUtils.getLong(session, defaultValue, keys);
         }
 
         private TimeUnit getTimeUnit(final RepositorySystemSession session) {
@@ -255,12 +257,48 @@ public final class NamedLockFactoryAdapter {
             }
             // if we are here, means all attempts were unsuccessful: fail
             close();
-            IllegalStateException ex = new IllegalStateException("Could not 
acquire " + lockKind + " lock for "
-                    + namedLock.key().resources() + " using lock "
-                    + namedLock.key().name() + " in " + timeStr);
+            String message = "Could not acquire " + lockKind + " lock for "
+                    + lockSubjects(artifacts, metadatas) + " in " + timeStr
+                    + "; consider using '" + CONFIG_PROP_TIME
+                    + "' property to increase lock timeout to a value that 
fits your environment";
+            FailedToAcquireLockException ex = new 
FailedToAcquireLockException(shared, message);
             throw namedLockFactory.onFailure(ex);
         }
 
+        private String lockSubjects(
+                Collection<? extends Artifact> artifacts, Collection<? extends 
Metadata> metadatas) {
+            StringBuilder builder = new StringBuilder();
+            if (artifacts != null && !artifacts.isEmpty()) {
+                builder.append("artifacts: ")
+                        
.append(artifacts.stream().map(ArtifactIdUtils::toId).collect(Collectors.joining(",
 ")));
+            }
+            if (metadatas != null && !metadatas.isEmpty()) {
+                if (builder.length() != 0) {
+                    builder.append("; ");
+                }
+                builder.append("metadata: ")
+                        
.append(metadatas.stream().map(this::metadataSubjects).collect(Collectors.joining(",
 ")));
+            }
+            return builder.toString();
+        }
+
+        private String metadataSubjects(Metadata metadata) {
+            String name = "";
+            if (!metadata.getGroupId().isEmpty()) {
+                name += metadata.getGroupId();
+                if (!metadata.getArtifactId().isEmpty()) {
+                    name += ":" + metadata.getArtifactId();
+                    if (!metadata.getVersion().isEmpty()) {
+                        name += ":" + metadata.getVersion();
+                    }
+                }
+            }
+            if (!metadata.getType().isEmpty()) {
+                name += (name.isEmpty() ? "" : ":") + metadata.getType();
+            }
+            return name;
+        }
+
         @Override
         public void close() {
             while (!locks.isEmpty()) {
diff --git a/src/site/markdown/configuration.md 
b/src/site/markdown/configuration.md
index 7e1d84040..b9ad76bb2 100644
--- a/src/site/markdown/configuration.md
+++ b/src/site/markdown/configuration.md
@@ -118,7 +118,7 @@ To modify this file, edit the template and regenerate.
 | `"aether.syncContext.named.redisson.configFile"` | `String` | Path to a 
Redisson configuration file in YAML format. Read official documentation for 
details. |  -  | 1.7.0 |  No  | Java System Properties |
 | `"aether.syncContext.named.retry"` | `Integer` | The amount of retries on 
time-out. |  `1`  | 1.7.0 |  No  | Session Configuration |
 | `"aether.syncContext.named.retry.wait"` | `Long` | The amount of 
milliseconds to wait between retries on time-out. |  `200l`  | 1.7.0 |  No  | 
Session Configuration |
-| `"aether.syncContext.named.time"` | `Long` | The maximum of time amount to 
be blocked to obtain lock. |  `30l`  | 1.7.0 |  No  | Session Configuration |
+| `"aether.syncContext.named.time"` | `Long` | The maximum of time amount to 
be blocked to obtain lock. |  `900l`  | 1.7.0 |  No  | Session Configuration |
 | `"aether.syncContext.named.time.unit"` | `String` | The unit of maximum time 
amount to be blocked to obtain lock. Use TimeUnit enum names. |  `"SECONDS"`  | 
1.7.0 |  No  | Session Configuration |
 | `"aether.system.dependencyVisitor"` | `String` | A flag indicating which 
visitor should be used to "flatten" the dependency graph into list. In Maven 4 
the default is new "levelOrder", while Maven 3 used "preOrder". This property 
accepts values "preOrder", "postOrder" and "levelOrder". |  `"levelOrder"`  | 
2.0.0 |  No  | Session Configuration |
 | `"aether.transport.apache.followRedirects"` | `Boolean` | If enabled, Apache 
HttpClient will follow HTTP redirects. |  `true`  | 2.0.2 |  Yes  | Session 
Configuration |

Reply via email to