gnodet commented on code in PR #11734:
URL: https://github.com/apache/maven/pull/11734#discussion_r3284349112


##########
compat/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java:
##########
@@ -902,4 +902,61 @@ void profileActivationPropertyWithProjectExpression() 
throws Exception {
                         + "${project.version} expressions are not supported 
during profile activation.",
                 result.getWarnings().get(1));
     }
+
+    /**
+     * Validates thread-safety of DefaultModelValidator during concurrent 
model validation.
+     *
+     * <p>This test addresses GitHub issue #11618 where concurrent access to a 
shared
+     * {@code HashSet} in {@code DefaultModelValidator} could cause {@code 
ClassCastException}.

Review Comment:
   Nit: the rest of the file uses top-level imports. These fully-qualified 
inline references work, but adding proper imports would be more consistent with 
the existing style:
   
   ```java
   import java.util.concurrent.CountDownLatch;
   import java.util.concurrent.TimeUnit;
   import java.util.concurrent.atomic.AtomicReference;
   ```



##########
compat/maven-model-builder/src/test/java/org/apache/maven/model/validation/DefaultModelValidatorTest.java:
##########
@@ -902,4 +902,61 @@ void profileActivationPropertyWithProjectExpression() 
throws Exception {
                         + "${project.version} expressions are not supported 
during profile activation.",
                 result.getWarnings().get(1));
     }
+
+    /**
+     * Validates thread-safety of DefaultModelValidator during concurrent 
model validation.
+     *
+     * <p>This test addresses GitHub issue #11618 where concurrent access to a 
shared
+     * {@code HashSet} in {@code DefaultModelValidator} could cause {@code 
ClassCastException}.
+     * The underlying issue occurs when multiple threads access a 
non-thread-safe {@code HashSet}
+     * (backed by {@code HashMap}) during internal restructuring operations.
+     *
+     * <p>The fix replaces {@code HashSet} with {@code 
ConcurrentHashMap.newKeySet()} to provide
+     * thread-safe concurrent access without external synchronization.
+     *
+     * @see <a href="https://github.com/apache/maven/issues/11618";>GitHub 
#11618</a>
+     */
+    @Test
+    void testConcurrentValidation() throws Exception {
+        int threadCount = 10;
+        int iterationsPerThread = 100;
+        java.util.concurrent.CountDownLatch startLatch = new 
java.util.concurrent.CountDownLatch(1);
+        java.util.concurrent.CountDownLatch doneLatch = new 
java.util.concurrent.CountDownLatch(threadCount);
+        java.util.concurrent.atomic.AtomicReference<Throwable> failure = new 
java.util.concurrent.atomic.AtomicReference<>();
+
+        // Create multiple threads that will validate models concurrently
+        for (int t = 0; t < threadCount; t++) {
+            final int threadId = t;
+            new Thread(() -> {
+                try {
+                    startLatch.await(); // Wait for all threads to be ready
+                    for (int i = 0; i < iterationsPerThread; i++) {
+                        Model model = new Model();

Review Comment:
   Minor: the thread is created with `new Thread(...).start()` but never joined 
or named. `doneLatch.await(30, ...)` handles termination, but if a thread 
hangs, the unnamed daemon-less threads will keep the JVM alive after the test 
times out. Consider `Thread.ofVirtual()` or at minimum setting the thread as 
daemon / giving it a name for debuggability. Not a blocker.



##########
compat/maven-model-builder/src/main/java/org/apache/maven/model/validation/DefaultModelValidator.java:
##########
@@ -89,7 +90,9 @@ public class DefaultModelValidator implements ModelValidator {
 
     private static final String EMPTY = "";
 
-    private final Set<String> validIds = new HashSet<>();
+    // Thread-safe set required because class is @Singleton and validIds is 
accessed concurrently
+    // See: https://github.com/apache/maven/issues/11618

Review Comment:
   The fix itself is correct. As a pre-existing observation (not introduced by 
this PR): `validIds` grows unboundedly across all validations for the lifetime 
of the singleton — every unique groupId/artifactId/version ever validated is 
cached forever. In a long-running embedded scenario (IDE, daemon), this is a 
slow memory leak. The `maven-impl` module has the same pattern. Might be worth 
a follow-up issue to scope the cache per-session or use a bounded/weak-ref 
structure, but that's out of scope here.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to