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");
+ }
}