Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions api/maven-api-core/src/main/java/org/apache/maven/api/Session.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
Expand All @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -71,14 +72,27 @@ 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,
remoteRepositories,
remoteRepositories == null
? map(session.getRequest().getRemoteRepositories(), RepositoryUtils::toRepo)
: null,
lookup);
lookup,
context);
this.mavenSession = session;
this.mavenRepositorySystem = mavenRepositorySystem;
this.runtimeInformation = runtimeInformation;
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,21 +20,107 @@

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;
import org.eclipse.aether.RepositorySystemSession;
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);
Expand All @@ -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;
}
}
Loading
Loading