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


The following commit(s) were added to refs/heads/master by this push:
     new 1e5d22aee Fix: preserve original trace data when stamping re-entrancy 
marker (#1980)
1e5d22aee is described below

commit 1e5d22aeeadebe2942fb011f2c47ba84d5eced12
Author: Guillaume Nodet <[email protected]>
AuthorDate: Mon Jul 20 08:16:38 2026 +0200

    Fix: preserve original trace data when stamping re-entrancy marker (#1980)
    
    * Fix: preserve original trace data when stamping re-entrancy marker
    
    The re-entrancy detection added in e29d0cda stamps a marker object
    into the RequestTrace chain. However, it placed the marker as the
    trace tip data, replacing whatever was there before (typically an
    Artifact set by Maven core). Plugins that walk the trace chain and
    cast getData() to Artifact (e.g. pgpverify-maven-plugin) get a
    ClassCastException when they hit the marker object.
    
    This fix inserts the marker one level deeper in the trace chain and
    re-attaches the original tip data on top, so existing code that
    reads getData() continues to find the expected object. The
    isReentrant() method is unaffected because it walks the full chain
    and will still find the marker.
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    
    * Address review: assert re-entrancy marker identity in parent trace
    
    Co-Authored-By: Claude Opus 4.6 <[email protected]>
    
    ---------
    
    Co-authored-by: Claude Opus 4.6 <[email protected]>
---
 .../internal/impl/DefaultRepositorySystem.java     | 31 +++++++++----
 .../DefaultRepositorySystemReentrancyTest.java     | 52 ++++++++++++++++++++++
 2 files changed, 75 insertions(+), 8 deletions(-)

diff --git 
a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java
 
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java
index aecdd697d..915df50f6 100644
--- 
a/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java
+++ 
b/maven-resolver-impl/src/main/java/org/eclipse/aether/internal/impl/DefaultRepositorySystem.java
@@ -223,7 +223,7 @@ public class DefaultRepositorySystem implements 
RepositorySystem {
         if (!isReentrant(request.getTrace())) {
             validateSession(session);
             repositorySystemValidator.validateVersionRequest(session, request);
-            request.setTrace(RequestTrace.newChild(request.getTrace(), 
REPOSITORY_SYSTEM_CALL));
+            request.setTrace(stampReentrancyMarker(request.getTrace()));
         }
         return versionResolver.resolveVersion(session, request);
     }
@@ -235,7 +235,7 @@ public class DefaultRepositorySystem implements 
RepositorySystem {
         if (!isReentrant(request.getTrace())) {
             validateSession(session);
             repositorySystemValidator.validateVersionRangeRequest(session, 
request);
-            request.setTrace(RequestTrace.newChild(request.getTrace(), 
REPOSITORY_SYSTEM_CALL));
+            request.setTrace(stampReentrancyMarker(request.getTrace()));
         }
         return versionRangeResolver.resolveVersionRange(session, request);
     }
@@ -248,7 +248,7 @@ public class DefaultRepositorySystem implements 
RepositorySystem {
         if (outermost) {
             validateSession(session);
             
repositorySystemValidator.validateArtifactDescriptorRequest(session, request);
-            request.setTrace(RequestTrace.newChild(request.getTrace(), 
REPOSITORY_SYSTEM_CALL));
+            request.setTrace(stampReentrancyMarker(request.getTrace()));
         }
         ArtifactDescriptorResult descriptorResult = 
artifactDescriptorReader.readArtifactDescriptor(session, request);
         if (outermost) {
@@ -266,7 +266,7 @@ public class DefaultRepositorySystem implements 
RepositorySystem {
         if (!isReentrant(request.getTrace())) {
             validateSession(session);
             repositorySystemValidator.validateArtifactRequests(session, 
Collections.singleton(request));
-            request.setTrace(RequestTrace.newChild(request.getTrace(), 
REPOSITORY_SYSTEM_CALL));
+            request.setTrace(stampReentrancyMarker(request.getTrace()));
         }
         return artifactResolver.resolveArtifact(session, request);
     }
@@ -286,7 +286,7 @@ public class DefaultRepositorySystem implements 
RepositorySystem {
             validateSession(session);
             repositorySystemValidator.validateArtifactRequests(session, 
requests);
             for (ArtifactRequest request : requests) {
-                request.setTrace(RequestTrace.newChild(request.getTrace(), 
REPOSITORY_SYSTEM_CALL));
+                request.setTrace(stampReentrancyMarker(request.getTrace()));
             }
         }
         return artifactResolver.resolveArtifacts(session, requests);
@@ -306,7 +306,7 @@ public class DefaultRepositorySystem implements 
RepositorySystem {
             validateSession(session);
             repositorySystemValidator.validateMetadataRequests(session, 
requests);
             for (MetadataRequest request : requests) {
-                request.setTrace(RequestTrace.newChild(request.getTrace(), 
REPOSITORY_SYSTEM_CALL));
+                request.setTrace(stampReentrancyMarker(request.getTrace()));
             }
         }
         return metadataResolver.resolveMetadata(session, requests);
@@ -319,7 +319,7 @@ public class DefaultRepositorySystem implements 
RepositorySystem {
         if (!isReentrant(request.getTrace())) {
             validateSession(session);
             repositorySystemValidator.validateCollectRequest(session, request);
-            request.setTrace(RequestTrace.newChild(request.getTrace(), 
REPOSITORY_SYSTEM_CALL));
+            request.setTrace(stampReentrancyMarker(request.getTrace()));
         }
         return dependencyCollector.collectDependencies(session, request);
     }
@@ -331,7 +331,7 @@ public class DefaultRepositorySystem implements 
RepositorySystem {
         if (!isReentrant(request.getTrace())) {
             validateSession(session);
             repositorySystemValidator.validateDependencyRequest(session, 
request);
-            request.setTrace(RequestTrace.newChild(request.getTrace(), 
REPOSITORY_SYSTEM_CALL));
+            request.setTrace(stampReentrancyMarker(request.getTrace()));
         }
         RequestTrace trace = RequestTrace.newChild(request.getTrace(), 
request);
 
@@ -557,6 +557,21 @@ public class DefaultRepositorySystem implements 
RepositorySystem {
         }
     }
 
+    /**
+     * Stamps the {@link #REPOSITORY_SYSTEM_CALL} re-entrancy marker into the 
trace chain
+     * while preserving the original trace tip data. The marker is inserted 
<em>below</em>
+     * the tip so that code walking the trace and casting {@code getData()} to 
its expected
+     * type (e.g. {@code org.apache.maven.artifact.Artifact}) still finds the 
original data
+     * at the tip rather than the anonymous marker object.
+     *
+     * @param currentTrace the current request trace (may be {@code null})
+     * @return a new trace with the marker inserted and original tip data 
preserved
+     */
+    private static RequestTrace stampReentrancyMarker(RequestTrace 
currentTrace) {
+        RequestTrace markerTrace = RequestTrace.newChild(currentTrace, 
REPOSITORY_SYSTEM_CALL);
+        return currentTrace != null ? RequestTrace.newChild(markerTrace, 
currentTrace.getData()) : markerTrace;
+    }
+
     /**
      * Checks whether the given {@link RequestTrace} indicates a re-entrant 
call by looking
      * for the {@link #REPOSITORY_SYSTEM_CALL} marker in the trace ancestry.
diff --git 
a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultRepositorySystemReentrancyTest.java
 
b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultRepositorySystemReentrancyTest.java
index 3d1f7e288..6aa9e10de 100644
--- 
a/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultRepositorySystemReentrancyTest.java
+++ 
b/maven-resolver-impl/src/test/java/org/eclipse/aether/internal/impl/DefaultRepositorySystemReentrancyTest.java
@@ -36,6 +36,7 @@ import org.eclipse.aether.impl.StubArtifactDescriptorReader;
 import org.eclipse.aether.internal.test.util.TestUtils;
 import org.eclipse.aether.resolution.ArtifactDescriptorRequest;
 import org.eclipse.aether.resolution.VersionRangeRequest;
+import org.eclipse.aether.resolution.VersionRequest;
 import org.eclipse.aether.spi.artifact.decorator.ArtifactDecorator;
 import org.eclipse.aether.spi.artifact.decorator.ArtifactDecoratorFactory;
 import org.eclipse.aether.spi.validator.Validator;
@@ -251,4 +252,55 @@ public class DefaultRepositorySystemReentrancyTest {
 
         assertEquals(2, validationCount.get(), "Two independent calls should 
each validate");
     }
+
+    @Test
+    void outerCallPreservesOriginalTraceData() throws Exception {
+        // Plugins (e.g. pgpverify-maven-plugin) walk the RequestTrace chain 
and cast
+        // getData() to Artifact without an instanceof check. The re-entrancy 
marker
+        // must NOT replace the original tip data — it should be inserted 
below it.
+        Object originalData = new Object();
+        RequestTrace originalTrace = RequestTrace.newChild(null, originalData);
+
+        VersionRequest request = new VersionRequest();
+        request.setArtifact(new DefaultArtifact("g:a:1"));
+        request.setRepositories(Collections.emptyList());
+        request.setTrace(originalTrace);
+
+        system.resolveVersion(session, request);
+
+        // After the call, the trace tip should still expose the original data
+        RequestTrace resultTrace = request.getTrace();
+        assertNotNull(resultTrace, "Request should have a trace after the 
call");
+        assertSame(
+                originalData,
+                resultTrace.getData(),
+                "Trace tip data should be the original data, not the 
re-entrancy marker");
+
+        // The marker should be present deeper in the chain (parent of the tip)
+        RequestTrace parent = resultTrace.getParent();
+        assertNotNull(parent, "Trace should have a parent containing the 
re-entrancy marker");
+        assertEquals(
+                "RepositorySystem", parent.getData().toString(), "Parent trace 
data should be the re-entrancy marker");
+    }
+
+    @Test
+    void outerCallWithNullTraceStillStampsMarker() throws Exception {
+        // When the original trace is null, the marker should still be stamped
+        // (as the tip, since there is no original data to preserve)
+        VersionRangeRequest request =
+                new VersionRangeRequest(new DefaultArtifact("g:a:1.0"), 
Collections.emptyList(), null);
+        assertNull(request.getTrace(), "Request should start with null trace");
+
+        system.resolveVersionRange(session, request);
+
+        RequestTrace resultTrace = request.getTrace();
+        assertNotNull(resultTrace, "Request should have a trace after the 
call");
+        // Re-entrant check should detect the marker
+        VersionRangeRequest innerRequest =
+                new VersionRangeRequest(new DefaultArtifact("g:b:2.0"), 
Collections.emptyList(), null);
+        innerRequest.setTrace(RequestTrace.newChild(resultTrace, 
"ModelResolver"));
+        int countBefore = validationCount.get();
+        system.resolveVersionRange(session, innerRequest);
+        assertEquals(countBefore, validationCount.get(), "Re-entrant call 
should skip validation");
+    }
 }

Reply via email to