Skip to content

Commit 820c9aa

Browse files
committed
[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 <workwithgoutam@gmail.com>
1 parent 852e66a commit 820c9aa

15 files changed

Lines changed: 380 additions & 32 deletions

File tree

api/maven-api-core/src/main/java/org/apache/maven/api/Session.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import org.apache.maven.api.model.Repository;
3333
import org.apache.maven.api.services.ArtifactCoordinatesFactory;
3434
import org.apache.maven.api.services.DependencyCoordinatesFactory;
35+
import org.apache.maven.api.services.RequestTrace;
3536
import org.apache.maven.api.services.VersionResolverException;
3637
import org.apache.maven.api.settings.Settings;
3738
import org.apache.maven.api.toolchain.ToolchainModel;
@@ -179,6 +180,17 @@ default Map<String, String> getEffectiveProperties() {
179180
@Nonnull
180181
Session withRemoteRepositories(@Nonnull List<RemoteRepository> repositories);
181182

183+
/**
184+
* Creates a derived session using the given request trace as its context.
185+
* Requests made through the derived session inherit this trace unless they provide an explicit trace.
186+
*
187+
* @param trace the request trace to use as context
188+
* @return the derived session
189+
* @throws NullPointerException if {@code trace} or its context is null
190+
*/
191+
@Nonnull
192+
Session withContext(@Nonnull RequestTrace trace);
193+
182194
/**
183195
* Register the given listener which will receive all events.
184196
*

api/maven-api-core/src/main/java/org/apache/maven/api/services/RequestTrace.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@
1818
*/
1919
package org.apache.maven.api.services;
2020

21+
import org.apache.maven.api.annotations.Nonnull;
2122
import org.apache.maven.api.annotations.Nullable;
2223

24+
import static java.util.Objects.requireNonNull;
25+
2326
/**
2427
* Represents a hierarchical trace of nested requests within a session, enabling correlation between
2528
* session events and their originating operations in the application code. The trace structure
@@ -59,6 +62,10 @@ public record RequestTrace(
5962
public static final String CONTEXT_PROJECT = "project";
6063
public static final String CONTEXT_BOOTSTRAP = "bootstrap";
6164

65+
public RequestTrace(@Nonnull String context) {
66+
this(requireNonNull(context, "context cannot be null"), null, null);
67+
}
68+
6269
public RequestTrace(RequestTrace parent, Object data) {
6370
this(parent != null ? parent.context() : null, parent, data);
6471
}

api/maven-api-core/src/test/java/org/apache/maven/api/services/RequestTraceTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,20 @@
2323
import static org.junit.jupiter.api.Assertions.assertEquals;
2424
import static org.junit.jupiter.api.Assertions.assertNull;
2525
import static org.junit.jupiter.api.Assertions.assertSame;
26+
import static org.junit.jupiter.api.Assertions.assertThrows;
2627

2728
class RequestTraceTest {
2829

30+
@Test
31+
void testContextTraceCreation() {
32+
RequestTrace trace = new RequestTrace("context");
33+
34+
assertEquals("context", trace.context());
35+
assertNull(trace.parent());
36+
assertNull(trace.data());
37+
assertThrows(NullPointerException.class, () -> new RequestTrace((String) null));
38+
}
39+
2940
@Test
3041
void testRequestTraceCreation() {
3142
RequestTrace parentTrace = new RequestTrace("parent-context", null, "parent-data");

compat/maven-embedder/src/main/java/org/apache/maven/cli/internal/BootstrapCoreExtensionManager.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import org.apache.maven.api.services.Interpolator;
4242
import org.apache.maven.api.services.InterpolatorException;
4343
import org.apache.maven.api.services.RepositoryFactory;
44+
import org.apache.maven.api.services.RequestTrace;
4445
import org.apache.maven.api.services.VersionParser;
4546
import org.apache.maven.api.services.VersionRangeResolver;
4647
import org.apache.maven.execution.DefaultMavenExecutionResult;
@@ -250,13 +251,23 @@ static class SimpleSession extends DefaultSession {
250251
MavenSession session,
251252
RepositorySystem repositorySystem,
252253
List<org.apache.maven.api.RemoteRepository> repositories) {
253-
super(session, repositorySystem, repositories, null, null, null);
254+
this(session, repositorySystem, repositories, null);
255+
}
256+
257+
private SimpleSession(
258+
MavenSession session,
259+
RepositorySystem repositorySystem,
260+
List<org.apache.maven.api.RemoteRepository> repositories,
261+
RequestTrace context) {
262+
super(session, repositorySystem, repositories, null, null, null, context);
254263
}
255264

256265
@Override
257266
protected Session newSession(
258-
MavenSession mavenSession, List<org.apache.maven.api.RemoteRepository> repositories) {
259-
return new SimpleSession(mavenSession, getRepositorySystem(), repositories);
267+
MavenSession mavenSession,
268+
List<org.apache.maven.api.RemoteRepository> repositories,
269+
RequestTrace context) {
270+
return new SimpleSession(mavenSession, getRepositorySystem(), repositories, context);
260271
}
261272

262273
@Override

impl/maven-cli/src/main/java/org/apache/maven/cling/extensions/BootstrapCoreExtensionManager.java

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.apache.maven.api.services.Interpolator;
4343
import org.apache.maven.api.services.InterpolatorException;
4444
import org.apache.maven.api.services.RepositoryFactory;
45+
import org.apache.maven.api.services.RequestTrace;
4546
import org.apache.maven.api.services.VersionParser;
4647
import org.apache.maven.api.services.VersionRangeResolver;
4748
import org.apache.maven.cling.invoker.ProtoLookup;
@@ -254,6 +255,14 @@ static class SimpleSession extends DefaultSession {
254255
MavenSession session,
255256
RepositorySystem repositorySystem,
256257
List<org.apache.maven.api.RemoteRepository> repositories) {
258+
this(session, repositorySystem, repositories, null);
259+
}
260+
261+
private SimpleSession(
262+
MavenSession session,
263+
RepositorySystem repositorySystem,
264+
List<org.apache.maven.api.RemoteRepository> repositories,
265+
RequestTrace context) {
257266
super(
258267
session,
259268
repositorySystem,
@@ -262,13 +271,16 @@ static class SimpleSession extends DefaultSession {
262271
ProtoLookup.builder()
263272
.addMapping(RequestCacheFactory.class, new DefaultRequestCacheFactory())
264273
.build(),
265-
null);
274+
null,
275+
context);
266276
}
267277

268278
@Override
269279
protected Session newSession(
270-
MavenSession mavenSession, List<org.apache.maven.api.RemoteRepository> repositories) {
271-
return new SimpleSession(mavenSession, getRepositorySystem(), repositories);
280+
MavenSession mavenSession,
281+
List<org.apache.maven.api.RemoteRepository> repositories,
282+
RequestTrace context) {
283+
return new SimpleSession(mavenSession, getRepositorySystem(), repositories, context);
272284
}
273285

274286
@SuppressWarnings("unchecked")

impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultSession.java

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import org.apache.maven.api.services.Lookup;
3838
import org.apache.maven.api.services.LookupException;
3939
import org.apache.maven.api.services.MavenException;
40+
import org.apache.maven.api.services.RequestTrace;
4041
import org.apache.maven.api.settings.Settings;
4142
import org.apache.maven.api.toolchain.ToolchainModel;
4243
import org.apache.maven.artifact.repository.ArtifactRepository;
@@ -71,14 +72,27 @@ public DefaultSession(
7172
@Nonnull MavenRepositorySystem mavenRepositorySystem,
7273
@Nonnull Lookup lookup,
7374
@Nonnull RuntimeInformation runtimeInformation) {
75+
this(session, repositorySystem, remoteRepositories, mavenRepositorySystem, lookup, runtimeInformation, null);
76+
}
77+
78+
@SuppressWarnings("checkstyle:ParameterNumber")
79+
protected DefaultSession(
80+
@Nonnull MavenSession session,
81+
@Nonnull RepositorySystem repositorySystem,
82+
@Nullable List<RemoteRepository> remoteRepositories,
83+
@Nonnull MavenRepositorySystem mavenRepositorySystem,
84+
@Nonnull Lookup lookup,
85+
@Nonnull RuntimeInformation runtimeInformation,
86+
@Nullable RequestTrace context) {
7487
super(
7588
requireNonNull(session).getRepositorySession(),
7689
repositorySystem,
7790
remoteRepositories,
7891
remoteRepositories == null
7992
? map(session.getRequest().getRemoteRepositories(), RepositoryUtils::toRepo)
8093
: null,
81-
lookup);
94+
lookup,
95+
context);
8296
this.mavenSession = session;
8397
this.mavenRepositorySystem = mavenRepositorySystem;
8498
this.runtimeInformation = runtimeInformation;
@@ -195,7 +209,8 @@ public Map<String, Object> getPluginContext(Project project) {
195209
}
196210

197211
@Override
198-
protected Session newSession(RepositorySystemSession repoSession, List<RemoteRepository> repositories) {
212+
protected Session newSession(
213+
RepositorySystemSession repoSession, List<RemoteRepository> repositories, RequestTrace context) {
199214
MavenSession t = getMavenSession();
200215
final MavenSession ms = requireNonNull(t);
201216
final MavenSession mss;
@@ -204,17 +219,18 @@ protected Session newSession(RepositorySystemSession repoSession, List<RemoteRep
204219
} else {
205220
mss = ms;
206221
}
207-
return newSession(mss, repositories);
222+
return newSession(mss, repositories, context);
208223
}
209224

210-
protected Session newSession(MavenSession mavenSession, List<RemoteRepository> repositories) {
225+
protected Session newSession(MavenSession mavenSession, List<RemoteRepository> repositories, RequestTrace context) {
211226
return new DefaultSession(
212227
requireNonNull(mavenSession),
213228
getRepositorySystem(),
214229
repositories,
215230
mavenRepositorySystem,
216231
lookup,
217-
runtimeInformation);
232+
runtimeInformation,
233+
context);
218234
}
219235

220236
@Override

impl/maven-core/src/test/java/org/apache/maven/internal/impl/DefaultSessionTest.java

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,21 +20,107 @@
2020

2121
import java.nio.file.Paths;
2222
import java.util.Collections;
23+
import java.util.concurrent.CyclicBarrier;
24+
import java.util.concurrent.ExecutorService;
25+
import java.util.concurrent.Executors;
26+
import java.util.concurrent.Future;
2327

28+
import org.apache.maven.api.Session;
29+
import org.apache.maven.api.services.RequestTrace;
2430
import org.apache.maven.execution.DefaultMavenExecutionRequest;
2531
import org.apache.maven.execution.MavenSession;
32+
import org.apache.maven.impl.InternalSession;
33+
import org.apache.maven.impl.RequestTraceHelper;
2634
import org.apache.maven.model.root.RootLocator;
2735
import org.eclipse.aether.DefaultRepositorySystemSession;
2836
import org.eclipse.aether.RepositorySystem;
2937
import org.eclipse.aether.RepositorySystemSession;
3038
import org.junit.jupiter.api.Test;
3139

3240
import static org.junit.jupiter.api.Assertions.assertEquals;
41+
import static org.junit.jupiter.api.Assertions.assertNull;
42+
import static org.junit.jupiter.api.Assertions.assertSame;
3343
import static org.junit.jupiter.api.Assertions.assertThrows;
3444
import static org.mockito.Mockito.mock;
3545

3646
public class DefaultSessionTest {
3747

48+
@Test
49+
void testContextPropagationAndNesting() {
50+
DefaultSession session = newSession();
51+
RequestTrace context = new RequestTrace("context");
52+
InternalSession contextualSession = InternalSession.from(session.withContext(context));
53+
54+
assertNull(session.getCurrentTrace());
55+
assertSame(context, contextualSession.getCurrentTrace());
56+
57+
RequestTraceHelper.ResolverTrace nested = RequestTraceHelper.enter(contextualSession, "nested");
58+
assertSame(context, nested.mvnTrace().parent());
59+
assertEquals("context", nested.context());
60+
assertSame(nested.mvnTrace(), contextualSession.getCurrentTrace());
61+
62+
RequestTraceHelper.exit(nested);
63+
assertSame(context, contextualSession.getCurrentTrace());
64+
assertNull(session.getCurrentTrace());
65+
}
66+
67+
@Test
68+
void testContextValidation() {
69+
DefaultSession session = newSession();
70+
71+
assertThrows(NullPointerException.class, () -> session.withContext(null));
72+
assertThrows(NullPointerException.class, () -> session.withContext(new RequestTrace(null, null, null)));
73+
}
74+
75+
@Test
76+
void testContextIsIsolatedAcrossThreads() throws Exception {
77+
RequestTrace context = new RequestTrace("context");
78+
InternalSession session = InternalSession.from(newSession().withContext(context));
79+
CyclicBarrier barrier = new CyclicBarrier(2);
80+
ExecutorService executor = Executors.newFixedThreadPool(2);
81+
82+
try {
83+
Future<RequestTrace> first = executor.submit(() -> enterAndReadTrace(session, barrier, "first"));
84+
Future<RequestTrace> second = executor.submit(() -> enterAndReadTrace(session, barrier, "second"));
85+
86+
RequestTrace firstTrace = first.get();
87+
RequestTrace secondTrace = second.get();
88+
assertEquals("first", firstTrace.data());
89+
assertEquals("second", secondTrace.data());
90+
assertSame(context, firstTrace.parent());
91+
assertSame(context, secondTrace.parent());
92+
assertSame(context, session.getCurrentTrace());
93+
} finally {
94+
executor.shutdownNow();
95+
}
96+
}
97+
98+
@Test
99+
void testActiveTraceIsVisibleAcrossSessionsSharingResolverSession() {
100+
DefaultSession parent = newSession();
101+
InternalSession derived = InternalSession.from(parent.withContext(new RequestTrace("context")));
102+
RequestTraceHelper.ResolverTrace nested = RequestTraceHelper.enter(derived, "nested");
103+
104+
try {
105+
assertSame(nested.mvnTrace(), parent.getCurrentTrace());
106+
assertSame(
107+
nested.mvnTrace(), InternalSession.from(parent.getSession()).getCurrentTrace());
108+
} finally {
109+
RequestTraceHelper.exit(nested);
110+
}
111+
112+
assertNull(parent.getCurrentTrace());
113+
assertEquals("context", derived.getCurrentTrace().context());
114+
}
115+
116+
@Test
117+
void testRepositoryDerivationPreservesContext() {
118+
RequestTrace context = new RequestTrace("context");
119+
Session derived = newSession().withContext(context).withRemoteRepositories(Collections.emptyList());
120+
121+
assertSame(context, InternalSession.from(derived).getCurrentTrace());
122+
}
123+
38124
@Test
39125
void testRootDirectoryWithNull() {
40126
RepositorySystemSession rss = new DefaultRepositorySystemSession(h -> false);
@@ -60,4 +146,24 @@ void testRootDirectory() {
60146

61147
assertEquals(Paths.get("myRootDirectory"), session.getRootDirectory());
62148
}
149+
150+
private static RequestTrace enterAndReadTrace(InternalSession session, CyclicBarrier barrier, String data)
151+
throws Exception {
152+
RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(session, data);
153+
try {
154+
barrier.await();
155+
return session.getCurrentTrace();
156+
} finally {
157+
RequestTraceHelper.exit(trace);
158+
}
159+
}
160+
161+
private static DefaultSession newSession() {
162+
RepositorySystemSession rss = new DefaultRepositorySystemSession(h -> false);
163+
MavenSession mavenSession = new MavenSession(null, rss, new DefaultMavenExecutionRequest(), null);
164+
DefaultSession session = new DefaultSession(
165+
mavenSession, mock(RepositorySystem.class), Collections.emptyList(), null, null, null);
166+
InternalSession.associate(rss, session);
167+
return session;
168+
}
63169
}

0 commit comments

Comments
 (0)