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

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


The following commit(s) were added to refs/heads/maven-resolver-1.9.x by this 
push:
     new f181c06e2 [1.9.x] Fix locking issues (#1662)
f181c06e2 is described below

commit f181c06e2fc7b92abdf10328eeff21ee4766eb6a
Author: Tamas Cservenak <[email protected]>
AuthorDate: Mon Nov 17 20:38:56 2025 +0100

    [1.9.x] Fix locking issues (#1662)
    
    Crude and most obvious changes.
    
    Changes:
    * introduce dedicated ex (backport from 2)
    * 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 | 44 +++++++++++++++++++++-
 src/site/markdown/configuration.md                 |  2 +-
 3 files changed, 71 insertions(+), 4 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 7a608c95d..20d6132ec 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
@@ -23,6 +23,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Deque;
 import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
 
 import org.eclipse.aether.RepositorySystemSession;
 import org.eclipse.aether.SyncContext;
@@ -32,6 +33,7 @@ import org.eclipse.aether.named.NamedLock;
 import org.eclipse.aether.named.NamedLockFactory;
 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;
 
@@ -43,7 +45,7 @@ import static java.util.Objects.requireNonNull;
 public final class NamedLockFactoryAdapter {
     public static final String TIME_KEY = "aether.syncContext.named.time";
 
-    public static final long DEFAULT_TIME = 30L;
+    public static final long DEFAULT_TIME = 900L;
 
     public static final String TIME_UNIT_KEY = 
"aether.syncContext.named.time.unit";
 
@@ -216,12 +218,50 @@ public final class NamedLockFactoryAdapter {
                 }
             }
             if (!illegalStateExceptions.isEmpty()) {
-                IllegalStateException ex = new IllegalStateException("Could 
not acquire lock(s)");
+                String message = "Could not acquire " + (shared ? "shared" : 
"exclusive") + " lock for "
+                        + lockSubjects(artifacts, metadatas) + " in " + time + 
" " + timeUnit
+                        + "; consider using '" + TIME_KEY
+                        + "' property to increase lock timeout to a value that 
fits your environment";
+                FailedToAcquireLockException ex = new 
FailedToAcquireLockException(shared, message);
                 illegalStateExceptions.forEach(ex::addSuppressed);
                 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;
+        }
+
         private void closeAll() {
             if (locks.isEmpty()) {
                 return;
diff --git a/src/site/markdown/configuration.md 
b/src/site/markdown/configuration.md
index 83db98530..6669adba3 100644
--- a/src/site/markdown/configuration.md
+++ b/src/site/markdown/configuration.md
@@ -103,7 +103,7 @@ under the License.
 | `aether.syncContext.named.nameMapper`                                       
| String                | Name of name mapper implementing the 
`org.eclipse.aether.internal.impl.synccontext.named.NameMapper` interface.      
                                                                                
                                                                                
                                                                                
                                [...]
 | `aether.syncContext.named.retry`                                            
| int                   | Count of retries SyncContext adapter should perform, 
when obtaining locks.                                                           
                                                                                
                                                                                
                                                                                
                [...]
 | `aether.syncContext.named.retry.wait`                                       
| long                  | Amount of milliseconds a thread to wait between 
retries, when obtaining locks.                                                  
                                                                                
                                                                                
                                                                                
                     [...]
-| `aether.syncContext.named.time`                                             
| long                  | Amount of time a synchronization context shall wait 
to obtain a lock.                                                               
                                                                                
                                                                                
                                                                                
                 [...]
+| `aether.syncContext.named.time`                                             
| long                  | Amount of time a synchronization context shall wait 
to obtain a lock.                                                               
                                                                                
                                                                                
                                                                                
                 [...]
 | `aether.syncContext.named.time.unit`                                        
| long                  | Unit of the lock wait time.                           
                                                                                
                                                                                
                                                                                
                                                                                
               [...]
 | `aether.syncContext.named.discriminating.discriminator`                     
| String                | A discriminator name prefix identifying a Resolver 
instance.                                                                       
                                                                                
                                                                                
                                                                                
                  [...]
 | `aether.syncContext.named.discriminating.hostname`                          
| String                | The hostname to be used with discriminating mapper.   
                                                                                
                                                                                
                                                                                
                                                                                
               [...]

Reply via email to