You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by gn...@apache.org on 2026/09/09 22:09:24 UTC
(maven) branch master updated: [MNG-8671] Add request trace context to derived sessions (#13009)
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.git
The following commit(s) were added to refs/heads/master by this push:
new 40c59633cb [MNG-8671] Add request trace context to derived sessions (#13009)
40c59633cb is described below
commit 40c59633cbb9c2bc4c915440f7198bad5cb60faf
Author: Goutam Adwant <86...@users.noreply.github.com>
AuthorDate: Wed Sep 9 15:09:18 2026 -0700
[MNG-8671] Add request trace context to derived sessions (#13009)
* [MNG-8671] Add request trace context to derived sessions
Add Session.withContext(RequestTrace) and propagate the scoped trace through derived sessions and Resolver operations.
Restore prior traces exactly and cover nesting, concurrency, repository derivation, install, and deploy paths.
Signed-off-by: goutamadwant <wo...@gmail.com>
* [MNG-8671] Clear null request traces
Remove the per-thread trace entry when restoring a null trace instead of retaining a null ThreadLocal value.
---------
Signed-off-by: goutamadwant <wo...@gmail.com>
---
.../main/java/org/apache/maven/api/Session.java | 12 +++
.../apache/maven/api/services/RequestTrace.java | 7 ++
.../maven/api/services/RequestTraceTest.java | 11 +++
.../internal/BootstrapCoreExtensionManager.java | 17 +++-
.../extensions/BootstrapCoreExtensionManager.java | 18 +++-
.../apache/maven/internal/impl/DefaultSession.java | 26 ++++-
.../maven/internal/impl/DefaultSessionTest.java | 106 +++++++++++++++++++++
.../org/apache/maven/impl/AbstractSession.java | 36 ++++++-
.../apache/maven/impl/DefaultArtifactDeployer.java | 6 +-
.../maven/impl/DefaultArtifactInstaller.java | 8 +-
.../org/apache/maven/impl/RequestTraceHelper.java | 16 +++-
.../apache/maven/impl/standalone/ApiRunner.java | 19 +++-
.../maven/impl/RepositoryRequestTraceTest.java | 93 ++++++++++++++++++
.../apache/maven/impl/RequestTraceHelperTest.java | 32 ++++++-
.../maven/testing/plugin/stubs/SessionStub.java | 6 ++
15 files changed, 381 insertions(+), 32 deletions(-)
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/Session.java b/api/maven-api-core/src/main/java/org/apache/maven/api/Session.java
index 8ef3802062..38e608021c 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/Session.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/Session.java
@@ -32,6 +32,7 @@
import org.apache.maven.api.model.Repository;
import org.apache.maven.api.services.ArtifactCoordinatesFactory;
import org.apache.maven.api.services.DependencyCoordinatesFactory;
+import org.apache.maven.api.services.RequestTrace;
import org.apache.maven.api.services.VersionResolverException;
import org.apache.maven.api.settings.Settings;
import org.apache.maven.api.toolchain.ToolchainModel;
@@ -179,6 +180,17 @@ default Map<String, String> getEffectiveProperties() {
@Nonnull
Session withRemoteRepositories(@Nonnull List<RemoteRepository> repositories);
+ /**
+ * Creates a derived session using the given request trace as its context.
+ * Requests made through the derived session inherit this trace unless they provide an explicit trace.
+ *
+ * @param trace the request trace to use as context
+ * @return the derived session
+ * @throws NullPointerException if {@code trace} or its context is null
+ */
+ @Nonnull
+ Session withContext(@Nonnull RequestTrace trace);
+
/**
* Register the given listener which will receive all events.
*
diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/RequestTrace.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/RequestTrace.java
index ac67cb6450..8a345059b4 100644
--- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/RequestTrace.java
+++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/RequestTrace.java
@@ -18,8 +18,11 @@
*/
package org.apache.maven.api.services;
+import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.annotations.Nullable;
+import static java.util.Objects.requireNonNull;
+
/**
* Represents a hierarchical trace of nested requests within a session, enabling correlation between
* session events and their originating operations in the application code. The trace structure
@@ -59,6 +62,10 @@ public record RequestTrace(
public static final String CONTEXT_PROJECT = "project";
public static final String CONTEXT_BOOTSTRAP = "bootstrap";
+ public RequestTrace(@Nonnull String context) {
+ this(requireNonNull(context, "context cannot be null"), null, null);
+ }
+
public RequestTrace(RequestTrace parent, Object data) {
this(parent != null ? parent.context() : null, parent, data);
}
diff --git a/api/maven-api-core/src/test/java/org/apache/maven/api/services/RequestTraceTest.java b/api/maven-api-core/src/test/java/org/apache/maven/api/services/RequestTraceTest.java
index 78534f9ae5..304d556175 100644
--- a/api/maven-api-core/src/test/java/org/apache/maven/api/services/RequestTraceTest.java
+++ b/api/maven-api-core/src/test/java/org/apache/maven/api/services/RequestTraceTest.java
@@ -23,9 +23,20 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
class RequestTraceTest {
+ @Test
+ void testContextTraceCreation() {
+ RequestTrace trace = new RequestTrace("context");
+
+ assertEquals("context", trace.context());
+ assertNull(trace.parent());
+ assertNull(trace.data());
+ assertThrows(NullPointerException.class, () -> new RequestTrace((String) null));
+ }
+
@Test
void testRequestTraceCreation() {
RequestTrace parentTrace = new RequestTrace("parent-context", null, "parent-data");
diff --git a/compat/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java b/compat/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java
index 06990ea303..ef8c082c10 100644
--- a/compat/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java
+++ b/compat/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java
@@ -41,6 +41,7 @@
import org.apache.maven.api.services.Interpolator;
import org.apache.maven.api.services.InterpolatorException;
import org.apache.maven.api.services.RepositoryFactory;
+import org.apache.maven.api.services.RequestTrace;
import org.apache.maven.api.services.VersionParser;
import org.apache.maven.api.services.VersionRangeResolver;
import org.apache.maven.execution.DefaultMavenExecutionResult;
@@ -250,13 +251,23 @@ static class SimpleSession extends DefaultSession {
MavenSession session,
RepositorySystem repositorySystem,
List<org.apache.maven.api.RemoteRepository> repositories) {
- super(session, repositorySystem, repositories, null, null, null);
+ this(session, repositorySystem, repositories, null);
+ }
+
+ private SimpleSession(
+ MavenSession session,
+ RepositorySystem repositorySystem,
+ List<org.apache.maven.api.RemoteRepository> repositories,
+ RequestTrace context) {
+ super(session, repositorySystem, repositories, null, null, null, context);
}
@Override
protected Session newSession(
- MavenSession mavenSession, List<org.apache.maven.api.RemoteRepository> repositories) {
- return new SimpleSession(mavenSession, getRepositorySystem(), repositories);
+ MavenSession mavenSession,
+ List<org.apache.maven.api.RemoteRepository> repositories,
+ RequestTrace context) {
+ return new SimpleSession(mavenSession, getRepositorySystem(), repositories, context);
}
@Override
diff --git a/impl/maven-cli/src/main/java/org/apache/maven/cling/extensions/BootstrapCoreExtensionManager.java b/impl/maven-cli/src/main/java/org/apache/maven/cling/extensions/BootstrapCoreExtensionManager.java
index d0735af889..5c436f3d04 100644
--- a/impl/maven-cli/src/main/java/org/apache/maven/cling/extensions/BootstrapCoreExtensionManager.java
+++ b/impl/maven-cli/src/main/java/org/apache/maven/cling/extensions/BootstrapCoreExtensionManager.java
@@ -42,6 +42,7 @@
import org.apache.maven.api.services.Interpolator;
import org.apache.maven.api.services.InterpolatorException;
import org.apache.maven.api.services.RepositoryFactory;
+import org.apache.maven.api.services.RequestTrace;
import org.apache.maven.api.services.VersionParser;
import org.apache.maven.api.services.VersionRangeResolver;
import org.apache.maven.cling.invoker.ProtoLookup;
@@ -254,6 +255,14 @@ static class SimpleSession extends DefaultSession {
MavenSession session,
RepositorySystem repositorySystem,
List<org.apache.maven.api.RemoteRepository> repositories) {
+ this(session, repositorySystem, repositories, null);
+ }
+
+ private SimpleSession(
+ MavenSession session,
+ RepositorySystem repositorySystem,
+ List<org.apache.maven.api.RemoteRepository> repositories,
+ RequestTrace context) {
super(
session,
repositorySystem,
@@ -262,13 +271,16 @@ static class SimpleSession extends DefaultSession {
ProtoLookup.builder()
.addMapping(RequestCacheFactory.class, new DefaultRequestCacheFactory())
.build(),
- null);
+ null,
+ context);
}
@Override
protected Session newSession(
- MavenSession mavenSession, List<org.apache.maven.api.RemoteRepository> repositories) {
- return new SimpleSession(mavenSession, getRepositorySystem(), repositories);
+ MavenSession mavenSession,
+ List<org.apache.maven.api.RemoteRepository> repositories,
+ RequestTrace context) {
+ return new SimpleSession(mavenSession, getRepositorySystem(), repositories, context);
}
@SuppressWarnings("unchecked")
diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultSession.java b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultSession.java
index 4e054d3a57..fbbd64fa2c 100644
--- a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultSession.java
+++ b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultSession.java
@@ -37,6 +37,7 @@
import org.apache.maven.api.services.Lookup;
import org.apache.maven.api.services.LookupException;
import org.apache.maven.api.services.MavenException;
+import org.apache.maven.api.services.RequestTrace;
import org.apache.maven.api.settings.Settings;
import org.apache.maven.api.toolchain.ToolchainModel;
import org.apache.maven.artifact.repository.ArtifactRepository;
@@ -71,6 +72,18 @@ public DefaultSession(
@Nonnull MavenRepositorySystem mavenRepositorySystem,
@Nonnull Lookup lookup,
@Nonnull RuntimeInformation runtimeInformation) {
+ this(session, repositorySystem, remoteRepositories, mavenRepositorySystem, lookup, runtimeInformation, null);
+ }
+
+ @SuppressWarnings("checkstyle:ParameterNumber")
+ protected DefaultSession(
+ @Nonnull MavenSession session,
+ @Nonnull RepositorySystem repositorySystem,
+ @Nullable List<RemoteRepository> remoteRepositories,
+ @Nonnull MavenRepositorySystem mavenRepositorySystem,
+ @Nonnull Lookup lookup,
+ @Nonnull RuntimeInformation runtimeInformation,
+ @Nullable RequestTrace context) {
super(
requireNonNull(session).getRepositorySession(),
repositorySystem,
@@ -78,7 +91,8 @@ public DefaultSession(
remoteRepositories == null
? map(session.getRequest().getRemoteRepositories(), RepositoryUtils::toRepo)
: null,
- lookup);
+ lookup,
+ context);
this.mavenSession = session;
this.mavenRepositorySystem = mavenRepositorySystem;
this.runtimeInformation = runtimeInformation;
@@ -195,7 +209,8 @@ public Map<String, Object> getPluginContext(Project project) {
}
@Override
- protected Session newSession(RepositorySystemSession repoSession, List<RemoteRepository> repositories) {
+ protected Session newSession(
+ RepositorySystemSession repoSession, List<RemoteRepository> repositories, RequestTrace context) {
MavenSession t = getMavenSession();
final MavenSession ms = requireNonNull(t);
final MavenSession mss;
@@ -204,17 +219,18 @@ protected Session newSession(RepositorySystemSession repoSession, List<RemoteRep
} else {
mss = ms;
}
- return newSession(mss, repositories);
+ return newSession(mss, repositories, context);
}
- protected Session newSession(MavenSession mavenSession, List<RemoteRepository> repositories) {
+ protected Session newSession(MavenSession mavenSession, List<RemoteRepository> repositories, RequestTrace context) {
return new DefaultSession(
requireNonNull(mavenSession),
getRepositorySystem(),
repositories,
mavenRepositorySystem,
lookup,
- runtimeInformation);
+ runtimeInformation,
+ context);
}
@Override
diff --git a/impl/maven-core/src/test/java/org/apache/maven/internal/impl/DefaultSessionTest.java b/impl/maven-core/src/test/java/org/apache/maven/internal/impl/DefaultSessionTest.java
index a25276b22e..fc4d0c324f 100644
--- a/impl/maven-core/src/test/java/org/apache/maven/internal/impl/DefaultSessionTest.java
+++ b/impl/maven-core/src/test/java/org/apache/maven/internal/impl/DefaultSessionTest.java
@@ -20,9 +20,17 @@
import java.nio.file.Paths;
import java.util.Collections;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import org.apache.maven.api.Session;
+import org.apache.maven.api.services.RequestTrace;
import org.apache.maven.execution.DefaultMavenExecutionRequest;
import org.apache.maven.execution.MavenSession;
+import org.apache.maven.impl.InternalSession;
+import org.apache.maven.impl.RequestTraceHelper;
import org.apache.maven.model.root.RootLocator;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
@@ -30,11 +38,89 @@
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.mock;
public class DefaultSessionTest {
+ @Test
+ void testContextPropagationAndNesting() {
+ DefaultSession session = newSession();
+ RequestTrace context = new RequestTrace("context");
+ InternalSession contextualSession = InternalSession.from(session.withContext(context));
+
+ assertNull(session.getCurrentTrace());
+ assertSame(context, contextualSession.getCurrentTrace());
+
+ RequestTraceHelper.ResolverTrace nested = RequestTraceHelper.enter(contextualSession, "nested");
+ assertSame(context, nested.mvnTrace().parent());
+ assertEquals("context", nested.context());
+ assertSame(nested.mvnTrace(), contextualSession.getCurrentTrace());
+
+ RequestTraceHelper.exit(nested);
+ assertSame(context, contextualSession.getCurrentTrace());
+ assertNull(session.getCurrentTrace());
+ }
+
+ @Test
+ void testContextValidation() {
+ DefaultSession session = newSession();
+
+ assertThrows(NullPointerException.class, () -> session.withContext(null));
+ assertThrows(NullPointerException.class, () -> session.withContext(new RequestTrace(null, null, null)));
+ }
+
+ @Test
+ void testContextIsIsolatedAcrossThreads() throws Exception {
+ RequestTrace context = new RequestTrace("context");
+ InternalSession session = InternalSession.from(newSession().withContext(context));
+ CyclicBarrier barrier = new CyclicBarrier(2);
+ ExecutorService executor = Executors.newFixedThreadPool(2);
+
+ try {
+ Future<RequestTrace> first = executor.submit(() -> enterAndReadTrace(session, barrier, "first"));
+ Future<RequestTrace> second = executor.submit(() -> enterAndReadTrace(session, barrier, "second"));
+
+ RequestTrace firstTrace = first.get();
+ RequestTrace secondTrace = second.get();
+ assertEquals("first", firstTrace.data());
+ assertEquals("second", secondTrace.data());
+ assertSame(context, firstTrace.parent());
+ assertSame(context, secondTrace.parent());
+ assertSame(context, session.getCurrentTrace());
+ } finally {
+ executor.shutdownNow();
+ }
+ }
+
+ @Test
+ void testActiveTraceIsVisibleAcrossSessionsSharingResolverSession() {
+ DefaultSession parent = newSession();
+ InternalSession derived = InternalSession.from(parent.withContext(new RequestTrace("context")));
+ RequestTraceHelper.ResolverTrace nested = RequestTraceHelper.enter(derived, "nested");
+
+ try {
+ assertSame(nested.mvnTrace(), parent.getCurrentTrace());
+ assertSame(
+ nested.mvnTrace(), InternalSession.from(parent.getSession()).getCurrentTrace());
+ } finally {
+ RequestTraceHelper.exit(nested);
+ }
+
+ assertNull(parent.getCurrentTrace());
+ assertEquals("context", derived.getCurrentTrace().context());
+ }
+
+ @Test
+ void testRepositoryDerivationPreservesContext() {
+ RequestTrace context = new RequestTrace("context");
+ Session derived = newSession().withContext(context).withRemoteRepositories(Collections.emptyList());
+
+ assertSame(context, InternalSession.from(derived).getCurrentTrace());
+ }
+
@Test
void testRootDirectoryWithNull() {
RepositorySystemSession rss = new DefaultRepositorySystemSession(h -> false);
@@ -60,4 +146,24 @@ void testRootDirectory() {
assertEquals(Paths.get("myRootDirectory"), session.getRootDirectory());
}
+
+ private static RequestTrace enterAndReadTrace(InternalSession session, CyclicBarrier barrier, String data)
+ throws Exception {
+ RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(session, data);
+ try {
+ barrier.await();
+ return session.getCurrentTrace();
+ } finally {
+ RequestTraceHelper.exit(trace);
+ }
+ }
+
+ private static DefaultSession newSession() {
+ RepositorySystemSession rss = new DefaultRepositorySystemSession(h -> false);
+ MavenSession mavenSession = new MavenSession(null, rss, new DefaultMavenExecutionRequest(), null);
+ DefaultSession session = new DefaultSession(
+ mavenSession, mock(RepositorySystem.class), Collections.emptyList(), null, null, null);
+ InternalSession.associate(rss, session);
+ return session;
+ }
}
diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/AbstractSession.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/AbstractSession.java
index 60c8198bcb..512174a465 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/impl/AbstractSession.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/AbstractSession.java
@@ -131,6 +131,7 @@ public abstract class AbstractSession implements InternalSession {
Cache.newCache(Cache.ReferenceType.WEAK, "AbstractSession-Repositories");
private final Cache<org.eclipse.aether.graph.Dependency, Dependency> allDependencies =
Cache.newCache(Cache.ReferenceType.WEAK, "AbstractSession-Dependencies");
+ private final RequestTrace context;
private volatile RequestCache requestCache;
static {
@@ -143,11 +144,22 @@ public AbstractSession(
List<RemoteRepository> repositories,
List<org.eclipse.aether.repository.RemoteRepository> resolverRepositories,
Lookup lookup) {
+ this(session, repositorySystem, repositories, resolverRepositories, lookup, null);
+ }
+
+ protected AbstractSession(
+ RepositorySystemSession session,
+ RepositorySystem repositorySystem,
+ List<RemoteRepository> repositories,
+ List<org.eclipse.aether.repository.RemoteRepository> resolverRepositories,
+ Lookup lookup,
+ RequestTrace context) {
this.session = requireNonNull(session, "session");
this.repositorySystem = repositorySystem;
this.repositories = getRepositories(repositories, resolverRepositories);
this.lookup = lookup;
this.injector = lookup != null ? lookup.lookupOptional(Injector.class).orElse(null) : null;
+ this.context = context;
}
@SuppressWarnings("unchecked")
@@ -383,16 +395,25 @@ public Session withLocalRepository(@Nonnull LocalRepository localRepository) {
RepositorySystemSession repoSession =
new DefaultRepositorySystemSession(session).setLocalRepositoryManager(localRepositoryManager);
- return newSession(repoSession, repositories);
+ return newSession(repoSession, repositories, getCurrentTrace());
}
@Nonnull
@Override
public Session withRemoteRepositories(@Nonnull List<RemoteRepository> repositories) {
- return newSession(session, repositories);
+ return newSession(session, repositories, getCurrentTrace());
}
- protected abstract Session newSession(RepositorySystemSession session, List<RemoteRepository> repositories);
+ @Nonnull
+ @Override
+ public Session withContext(@Nonnull RequestTrace trace) {
+ requireNonNull(trace, "trace");
+ requireNonNull(trace.context(), "trace context");
+ return newSession(session, repositories, trace);
+ }
+
+ protected abstract Session newSession(
+ RepositorySystemSession session, List<RemoteRepository> repositories, RequestTrace context);
@Nonnull
@Override
@@ -1037,12 +1058,17 @@ public PathScope requirePathScope(String id) {
@Override
public void setCurrentTrace(RequestTrace trace) {
- getTraceHolder().set(trace);
+ if (trace == null || trace == context) {
+ getTraceHolder().remove();
+ } else {
+ getTraceHolder().set(trace);
+ }
}
@Override
public RequestTrace getCurrentTrace() {
- return getTraceHolder().get();
+ RequestTrace trace = getTraceHolder().get();
+ return trace != null ? trace : context;
}
@SuppressWarnings("unchecked")
diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultArtifactDeployer.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultArtifactDeployer.java
index fb6291bd74..004ab64579 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultArtifactDeployer.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultArtifactDeployer.java
@@ -46,14 +46,18 @@ public void deploy(@Nonnull ArtifactDeployerRequest request) {
InternalSession session = InternalSession.from(request.getSession());
Collection<ProducedArtifact> artifacts = requireNonNull(request.getArtifacts(), "request.artifacts");
RemoteRepository repository = requireNonNull(request.getRepository(), "request.repository");
+ RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(session, request);
try {
DeployRequest deployRequest = new DeployRequest()
.setRepository(session.toRepository(repository))
- .setArtifacts(session.toArtifacts(artifacts));
+ .setArtifacts(session.toArtifacts(artifacts))
+ .setTrace(trace.trace());
session.getRepositorySystem().deploy(session.getSession(), deployRequest);
} catch (DeploymentException e) {
throw new ArtifactDeployerException("Unable to deploy artifacts", e);
+ } finally {
+ RequestTraceHelper.exit(trace);
}
}
}
diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultArtifactInstaller.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultArtifactInstaller.java
index 368d2f839c..4be87a23df 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultArtifactInstaller.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultArtifactInstaller.java
@@ -46,13 +46,17 @@ public class DefaultArtifactInstaller implements ArtifactInstaller {
public void install(ArtifactInstallerRequest request) throws ArtifactInstallerException, IllegalArgumentException {
requireNonNull(request, "request");
InternalSession session = InternalSession.from(request.getSession());
+ RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(session, request);
try {
- InstallRequest installRequest =
- new InstallRequest().setArtifacts(session.toArtifacts(request.getArtifacts()));
+ InstallRequest installRequest = new InstallRequest()
+ .setArtifacts(session.toArtifacts(request.getArtifacts()))
+ .setTrace(trace.trace());
repositorySystem.install(session.getSession(), installRequest);
} catch (InstallationException e) {
throw new ArtifactInstallerException(e.getMessage(), e);
+ } finally {
+ RequestTraceHelper.exit(trace);
}
}
}
diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/RequestTraceHelper.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/RequestTraceHelper.java
index 44b54003f2..f488c2953f 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/impl/RequestTraceHelper.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/RequestTraceHelper.java
@@ -44,9 +44,14 @@ public final class RequestTraceHelper {
* @param context The trace context
* @param trace The Resolver-specific trace
* @param mvnTrace The Maven-specific trace
+ * @param previousMvnTrace The Maven trace to restore when leaving this scope
*/
public record ResolverTrace(
- Session session, String context, RequestTrace trace, org.apache.maven.api.services.RequestTrace mvnTrace) {}
+ Session session,
+ String context,
+ RequestTrace trace,
+ org.apache.maven.api.services.RequestTrace mvnTrace,
+ org.apache.maven.api.services.RequestTrace previousMvnTrace) {}
/**
* Creates a new trace entry and updates the session's current trace
@@ -56,20 +61,21 @@ public record ResolverTrace(
*/
public static ResolverTrace enter(Session session, Object data) {
InternalSession iSession = InternalSession.from(session);
+ org.apache.maven.api.services.RequestTrace previousTrace = iSession.getCurrentTrace();
org.apache.maven.api.services.RequestTrace trace = data instanceof Request<?> req && req.getTrace() != null
? req.getTrace()
- : new org.apache.maven.api.services.RequestTrace(iSession.getCurrentTrace(), data);
+ : new org.apache.maven.api.services.RequestTrace(previousTrace, data);
iSession.setCurrentTrace(trace);
- return new ResolverTrace(session, trace.context(), toResolver(trace), trace);
+ return new ResolverTrace(session, trace.context(), toResolver(trace), trace, previousTrace);
}
/**
- * Restores the parent trace as the current trace in the session
+ * Restores the trace that was current before entering this scope.
* @param trace The current resolver trace to exit from
*/
public static void exit(ResolverTrace trace) {
InternalSession iSession = InternalSession.from(trace.session());
- iSession.setCurrentTrace(trace.mvnTrace().parent());
+ iSession.setCurrentTrace(trace.previousMvnTrace());
}
/**
diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/standalone/ApiRunner.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/standalone/ApiRunner.java
index b64dafa869..fa3faa2e66 100644
--- a/impl/maven-impl/src/main/java/org/apache/maven/impl/standalone/ApiRunner.java
+++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/standalone/ApiRunner.java
@@ -62,6 +62,7 @@
import org.apache.maven.api.services.MavenException;
import org.apache.maven.api.services.PackagingRegistry;
import org.apache.maven.api.services.RepositoryFactory;
+import org.apache.maven.api.services.RequestTrace;
import org.apache.maven.api.services.SettingsBuilder;
import org.apache.maven.api.services.TypeRegistry;
import org.apache.maven.api.services.VersionParser;
@@ -298,15 +299,27 @@ protected DefaultSession(
List<RemoteRepository> repositories,
List<org.eclipse.aether.repository.RemoteRepository> resolverRepositories,
Lookup lookup) {
- super(session, repositorySystem, repositories, resolverRepositories, lookup);
+ this(session, repositorySystem, repositories, resolverRepositories, lookup, null);
+ }
+
+ protected DefaultSession(
+ RepositorySystemSession session,
+ RepositorySystem repositorySystem,
+ List<RemoteRepository> repositories,
+ List<org.eclipse.aether.repository.RemoteRepository> resolverRepositories,
+ Lookup lookup,
+ RequestTrace context) {
+ super(session, repositorySystem, repositories, resolverRepositories, lookup, context);
systemProperties = System.getenv().entrySet().stream()
.collect(Collectors.toMap(e -> "env." + e.getKey(), Map.Entry::getValue));
System.getProperties().forEach((k, v) -> systemProperties.put(k.toString(), v.toString()));
}
@Override
- protected Session newSession(RepositorySystemSession session, List<RemoteRepository> repositories) {
- DefaultSession newSession = new DefaultSession(session, repositorySystem, repositories, null, lookup);
+ protected Session newSession(
+ RepositorySystemSession session, List<RemoteRepository> repositories, RequestTrace context) {
+ DefaultSession newSession =
+ new DefaultSession(session, repositorySystem, repositories, null, lookup, context);
newSession.settings = this.settings;
newSession.mavenVersion = this.mavenVersion;
newSession.userProperties = this.userProperties;
diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/RepositoryRequestTraceTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/RepositoryRequestTraceTest.java
new file mode 100644
index 0000000000..67f57b4982
--- /dev/null
+++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/RepositoryRequestTraceTest.java
@@ -0,0 +1,93 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.maven.impl;
+
+import java.util.List;
+
+import org.apache.maven.api.RemoteRepository;
+import org.apache.maven.api.services.ArtifactDeployerRequest;
+import org.apache.maven.api.services.ArtifactInstallerRequest;
+import org.apache.maven.api.services.RequestTrace;
+import org.eclipse.aether.RepositorySystem;
+import org.eclipse.aether.RepositorySystemSession;
+import org.eclipse.aether.deployment.DeployRequest;
+import org.eclipse.aether.installation.InstallRequest;
+import org.junit.jupiter.api.Test;
+import org.mockito.ArgumentCaptor;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+class RepositoryRequestTraceTest {
+
+ @Test
+ void installerPropagatesAndRestoresRequestTrace() throws Exception {
+ RepositorySystem repositorySystem = mock(RepositorySystem.class);
+ RepositorySystemSession resolverSession = mock(RepositorySystemSession.class);
+ InternalSession session = mock(InternalSession.class);
+ ArtifactInstallerRequest request = mock(ArtifactInstallerRequest.class);
+ RequestTrace context = new RequestTrace("context");
+ RequestTrace explicitTrace = new RequestTrace("install", context, "install-request");
+ when(request.getSession()).thenReturn(session);
+ when(request.getTrace()).thenReturn(explicitTrace);
+ when(request.getArtifacts()).thenReturn(List.of());
+ when(session.getCurrentTrace()).thenReturn(context);
+ when(session.getSession()).thenReturn(resolverSession);
+ when(session.toArtifacts(List.of())).thenReturn(List.of());
+
+ new DefaultArtifactInstaller(repositorySystem).install(request);
+
+ ArgumentCaptor<InstallRequest> captor = ArgumentCaptor.forClass(InstallRequest.class);
+ verify(repositorySystem).install(eq(resolverSession), captor.capture());
+ assertEquals("install-request", captor.getValue().getTrace().getData());
+ verify(session).setCurrentTrace(explicitTrace);
+ verify(session).setCurrentTrace(context);
+ }
+
+ @Test
+ void deployerPropagatesAndRestoresInheritedTrace() throws Exception {
+ RepositorySystem repositorySystem = mock(RepositorySystem.class);
+ RepositorySystemSession resolverSession = mock(RepositorySystemSession.class);
+ InternalSession session = mock(InternalSession.class);
+ ArtifactDeployerRequest request = mock(ArtifactDeployerRequest.class);
+ RemoteRepository repository = mock(RemoteRepository.class);
+ org.eclipse.aether.repository.RemoteRepository resolverRepository =
+ new org.eclipse.aether.repository.RemoteRepository.Builder("test", "default", "https://repo.example")
+ .build();
+ RequestTrace context = new RequestTrace("context");
+ when(request.getSession()).thenReturn(session);
+ when(request.getArtifacts()).thenReturn(List.of());
+ when(request.getRepository()).thenReturn(repository);
+ when(session.getCurrentTrace()).thenReturn(context);
+ when(session.getSession()).thenReturn(resolverSession);
+ when(session.getRepositorySystem()).thenReturn(repositorySystem);
+ when(session.toRepository(repository)).thenReturn(resolverRepository);
+ when(session.toArtifacts(List.of())).thenReturn(List.of());
+
+ new DefaultArtifactDeployer().deploy(request);
+
+ ArgumentCaptor<DeployRequest> captor = ArgumentCaptor.forClass(DeployRequest.class);
+ verify(repositorySystem).deploy(eq(resolverSession), captor.capture());
+ assertEquals(request, captor.getValue().getTrace().getData());
+ verify(session).setCurrentTrace(context);
+ }
+}
diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/RequestTraceHelperTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/RequestTraceHelperTest.java
index 94acf3848c..26ad25fb45 100644
--- a/impl/maven-impl/src/test/java/org/apache/maven/impl/RequestTraceHelperTest.java
+++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/RequestTraceHelperTest.java
@@ -28,6 +28,7 @@
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -74,16 +75,37 @@ void testToResolverWithNullTrace() {
@Test
void testExitResetsParentTrace() {
InternalSession session = mock(InternalSession.class);
- org.apache.maven.api.services.RequestTrace parentTrace =
- new org.apache.maven.api.services.RequestTrace(null, "parent");
+ org.apache.maven.api.services.RequestTrace previousTrace =
+ new org.apache.maven.api.services.RequestTrace(null, "previous");
org.apache.maven.api.services.RequestTrace currentTrace =
- new org.apache.maven.api.services.RequestTrace(parentTrace, "current");
+ new org.apache.maven.api.services.RequestTrace(null, "current");
RequestTraceHelper.ResolverTrace resolverTrace =
- new RequestTraceHelper.ResolverTrace(session, "test", null, currentTrace);
+ new RequestTraceHelper.ResolverTrace(session, "test", null, currentTrace, previousTrace);
RequestTraceHelper.exit(resolverTrace);
- verify(session).setCurrentTrace(parentTrace);
+ verify(session).setCurrentTrace(previousTrace);
+ }
+
+ @Test
+ void testExplicitRequestTraceRestoresPreviousSessionTrace() {
+ InternalSession session = mock(InternalSession.class);
+ Request<?> request = mock(Request.class);
+ org.apache.maven.api.services.RequestTrace previousTrace =
+ new org.apache.maven.api.services.RequestTrace("context");
+ org.apache.maven.api.services.RequestTrace explicitParent =
+ new org.apache.maven.api.services.RequestTrace("explicit-parent");
+ org.apache.maven.api.services.RequestTrace explicitTrace =
+ new org.apache.maven.api.services.RequestTrace(explicitParent, "explicit");
+ when(session.getCurrentTrace()).thenReturn(previousTrace);
+ when(request.getTrace()).thenReturn(explicitTrace);
+
+ RequestTraceHelper.ResolverTrace resolverTrace = RequestTraceHelper.enter(session, request);
+ RequestTraceHelper.exit(resolverTrace);
+
+ verify(session).setCurrentTrace(explicitTrace);
+ verify(session).setCurrentTrace(previousTrace);
+ verify(session, times(2)).setCurrentTrace(org.mockito.ArgumentMatchers.any());
}
}
diff --git a/impl/maven-testing/src/main/java/org/apache/maven/testing/plugin/stubs/SessionStub.java b/impl/maven-testing/src/main/java/org/apache/maven/testing/plugin/stubs/SessionStub.java
index 51ca49b6e7..41f9394c86 100644
--- a/impl/maven-testing/src/main/java/org/apache/maven/testing/plugin/stubs/SessionStub.java
+++ b/impl/maven-testing/src/main/java/org/apache/maven/testing/plugin/stubs/SessionStub.java
@@ -53,6 +53,7 @@
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.annotations.Nullable;
import org.apache.maven.api.model.Repository;
+import org.apache.maven.api.services.RequestTrace;
import org.apache.maven.api.settings.Settings;
import org.apache.maven.api.toolchain.ToolchainModel;
@@ -192,6 +193,11 @@ public Session withRemoteRepositories(List<RemoteRepository> repositories) {
return null;
}
+ @Override
+ public Session withContext(RequestTrace trace) {
+ return null;
+ }
+
@Override
public void registerListener(Listener listener) {}