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 8ef3802062ea..38e608021cba 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 getEffectiveProperties() { @Nonnull Session withRemoteRepositories(@Nonnull List 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 ac67cb64509e..8a345059b4a5 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 78534f9ae5f5..304d55617547 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 06990ea303bc..ef8c082c10f4 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 repositories) { - super(session, repositorySystem, repositories, null, null, null); + this(session, repositorySystem, repositories, null); + } + + private SimpleSession( + MavenSession session, + RepositorySystem repositorySystem, + List repositories, + RequestTrace context) { + super(session, repositorySystem, repositories, null, null, null, context); } @Override protected Session newSession( - MavenSession mavenSession, List repositories) { - return new SimpleSession(mavenSession, getRepositorySystem(), repositories); + MavenSession mavenSession, + List 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 d0735af889a6..5c436f3d0444 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 repositories) { + this(session, repositorySystem, repositories, null); + } + + private SimpleSession( + MavenSession session, + RepositorySystem repositorySystem, + List 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 repositories) { - return new SimpleSession(mavenSession, getRepositorySystem(), repositories); + MavenSession mavenSession, + List 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 4e054d3a57da..fbbd64fa2cac 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 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 getPluginContext(Project project) { } @Override - protected Session newSession(RepositorySystemSession repoSession, List repositories) { + protected Session newSession( + RepositorySystemSession repoSession, List repositories, RequestTrace context) { MavenSession t = getMavenSession(); final MavenSession ms = requireNonNull(t); final MavenSession mss; @@ -204,17 +219,18 @@ protected Session newSession(RepositorySystemSession repoSession, List repositories) { + protected Session newSession(MavenSession mavenSession, List 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 a25276b22ec3..fc4d0c324f1f 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 first = executor.submit(() -> enterAndReadTrace(session, barrier, "first")); + Future 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 60c8198bcb59..512174a46590 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 allDependencies = Cache.newCache(Cache.ReferenceType.WEAK, "AbstractSession-Dependencies"); + private final RequestTrace context; private volatile RequestCache requestCache; static { @@ -143,11 +144,22 @@ public AbstractSession( List repositories, List resolverRepositories, Lookup lookup) { + this(session, repositorySystem, repositories, resolverRepositories, lookup, null); + } + + protected AbstractSession( + RepositorySystemSession session, + RepositorySystem repositorySystem, + List repositories, + List 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 repositories) { - return newSession(session, repositories); + return newSession(session, repositories, getCurrentTrace()); } - protected abstract Session newSession(RepositorySystemSession session, List 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 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 fb6291bd74a5..004ab6457930 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 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 368d2f839ce7..4be87a23df22 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 44b54003f25d..f488c2953f26 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 b64dafa86944..fa3faa2e6656 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 repositories, List resolverRepositories, Lookup lookup) { - super(session, repositorySystem, repositories, resolverRepositories, lookup); + this(session, repositorySystem, repositories, resolverRepositories, lookup, null); + } + + protected DefaultSession( + RepositorySystemSession session, + RepositorySystem repositorySystem, + List repositories, + List 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 repositories) { - DefaultSession newSession = new DefaultSession(session, repositorySystem, repositories, null, lookup); + protected Session newSession( + RepositorySystemSession session, List 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 000000000000..67f57b4982d0 --- /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 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 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 94acf3848c3a..26ad25fb4546 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 51ca49b6e7c4..41f9394c868b 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 repositories) { return null; } + @Override + public Session withContext(RequestTrace trace) { + return null; + } + @Override public void registerListener(Listener listener) {}