diff --git a/api/maven-api-core/pom.xml b/api/maven-api-core/pom.xml index 6c1bb03326f0..4d97cb9d7fbb 100644 --- a/api/maven-api-core/pom.xml +++ b/api/maven-api-core/pom.xml @@ -65,6 +65,11 @@ junit-jupiter-api test + + org.mockito + mockito-core + test + 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 39ae21a0cc15..d9899dcc4ca0 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 @@ -38,9 +38,6 @@ /** * The session to install / deploy / resolve artifacts and dependencies. * - * TODO: add request trace so that requests can be linked together and through the resolver - * TODO: add a Request interface holding session + parent request - * * @since 4.0.0 */ @Experimental diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactCoordinatesFactoryRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactCoordinatesFactoryRequest.java index cf96d490fdbf..074c9bcebd2d 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactCoordinatesFactoryRequest.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactCoordinatesFactoryRequest.java @@ -34,10 +34,7 @@ */ @Experimental @Immutable -public interface ArtifactCoordinatesFactoryRequest { - - @Nonnull - Session getSession(); +public interface ArtifactCoordinatesFactoryRequest extends Request { String getGroupId(); @@ -112,6 +109,7 @@ static ArtifactFactoryRequestBuilder builder() { @NotThreadSafe class ArtifactFactoryRequestBuilder { private Session session; + private RequestTrace trace; private String groupId; private String artifactId; private String version; @@ -127,6 +125,11 @@ public ArtifactFactoryRequestBuilder session(Session session) { return this; } + public ArtifactFactoryRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + public ArtifactFactoryRequestBuilder groupId(String groupId) { this.groupId = groupId; return this; @@ -164,7 +167,7 @@ public ArtifactFactoryRequestBuilder coordinateString(String coordinateString) { public ArtifactCoordinatesFactoryRequest build() { return new DefaultArtifactFactoryRequestArtifact( - session, groupId, artifactId, version, classifier, extension, type, coordinateString); + session, trace, groupId, artifactId, version, classifier, extension, type, coordinateString); } private static class DefaultArtifactFactoryRequestArtifact extends BaseRequest @@ -180,6 +183,7 @@ private static class DefaultArtifactFactoryRequestArtifact extends BaseRequest { @Nonnull RemoteRepository getRepository(); @@ -69,6 +67,7 @@ static ArtifactDeployerRequest build( class ArtifactDeployerRequestBuilder { Session session; + RequestTrace trace; RemoteRepository repository; Collection artifacts; int retryFailedDeploymentCount; @@ -81,6 +80,12 @@ public ArtifactDeployerRequestBuilder session(Session session) { return this; } + @Nonnull + public ArtifactDeployerRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + @Nonnull public ArtifactDeployerRequestBuilder repository(RemoteRepository repository) { this.repository = repository; @@ -99,7 +104,8 @@ public ArtifactDeployerRequestBuilder retryFailedDeploymentCount(int retryFailed @Nonnull public ArtifactDeployerRequest build() { - return new DefaultArtifactDeployerRequest(session, repository, artifacts, retryFailedDeploymentCount); + return new DefaultArtifactDeployerRequest( + session, trace, repository, artifacts, retryFailedDeploymentCount); } private static class DefaultArtifactDeployerRequest extends BaseRequest @@ -111,10 +117,11 @@ private static class DefaultArtifactDeployerRequest extends BaseRequest DefaultArtifactDeployerRequest( @Nonnull Session session, + @Nullable RequestTrace trace, @Nonnull RemoteRepository repository, @Nonnull Collection artifacts, int retryFailedDeploymentCount) { - super(session); + super(session, trace); this.repository = requireNonNull(repository, "repository cannot be null"); this.artifacts = List.copyOf(requireNonNull(artifacts, "artifacts cannot be null")); this.retryFailedDeploymentCount = retryFailedDeploymentCount; @@ -136,6 +143,14 @@ public Collection getArtifacts() { public int getRetryFailedDeploymentCount() { return retryFailedDeploymentCount; } + + @Override + public String toString() { + return "ArtifactDeployerRequest[" + "repository=" + + repository + ", artifacts=" + + artifacts + ", retryFailedDeploymentCount=" + + retryFailedDeploymentCount + ']'; + } } } } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactFactoryRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactFactoryRequest.java index 378eed160836..c162f42093dd 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactFactoryRequest.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactFactoryRequest.java @@ -23,6 +23,7 @@ import org.apache.maven.api.annotations.Immutable; import org.apache.maven.api.annotations.Nonnull; import org.apache.maven.api.annotations.NotThreadSafe; +import org.apache.maven.api.annotations.Nullable; import static java.util.Objects.requireNonNull; @@ -33,10 +34,7 @@ */ @Experimental @Immutable -public interface ArtifactFactoryRequest { - - @Nonnull - Session getSession(); +public interface ArtifactFactoryRequest extends Request { String getGroupId(); @@ -87,6 +85,7 @@ static ArtifactFactoryRequestBuilder builder() { @NotThreadSafe class ArtifactFactoryRequestBuilder { private Session session; + private RequestTrace trace; private String groupId; private String artifactId; private String version; @@ -101,6 +100,11 @@ public ArtifactFactoryRequestBuilder session(Session session) { return this; } + public ArtifactFactoryRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + public ArtifactFactoryRequestBuilder groupId(String groupId) { this.groupId = groupId; return this; @@ -133,7 +137,7 @@ public ArtifactFactoryRequestBuilder type(String type) { public ArtifactFactoryRequest build() { return new DefaultArtifactFactoryRequest( - session, groupId, artifactId, version, classifier, extension, type); + session, trace, groupId, artifactId, version, classifier, extension, type); } private static class DefaultArtifactFactoryRequest extends BaseRequest @@ -145,15 +149,17 @@ private static class DefaultArtifactFactoryRequest extends BaseRequest private final String extension; private final String type; + @SuppressWarnings("checkstyle:ParameterNumber") DefaultArtifactFactoryRequest( @Nonnull Session session, + @Nullable RequestTrace trace, String groupId, String artifactId, String version, String classifier, String extension, String type) { - super(session); + super(session, trace); this.groupId = groupId; this.artifactId = artifactId; this.version = version; @@ -191,6 +197,17 @@ public String getExtension() { public String getType() { return type; } + + @Override + public String toString() { + return "ArtifactFactoryRequest[" + "groupId='" + + groupId + '\'' + ", artifactId='" + + artifactId + '\'' + ", version='" + + version + '\'' + ", classifier='" + + classifier + '\'' + ", extension='" + + extension + '\'' + ", type='" + + type + '\'' + ']'; + } } } } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactInstallerRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactInstallerRequest.java index badf60170d61..7c3130aced7c 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactInstallerRequest.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactInstallerRequest.java @@ -39,10 +39,7 @@ */ @Experimental @Immutable -public interface ArtifactInstallerRequest { - - @Nonnull - Session getSession(); +public interface ArtifactInstallerRequest extends Request { @Nonnull Collection getArtifacts(); @@ -63,6 +60,7 @@ static ArtifactInstallerRequest build(Session session, Collection artifacts = Collections.emptyList(); ArtifactInstallerRequestBuilder() {} @@ -73,6 +71,12 @@ public ArtifactInstallerRequestBuilder session(@Nonnull Session session) { return this; } + @Nonnull + public ArtifactInstallerRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + @Nonnull public ArtifactInstallerRequestBuilder artifacts(@Nullable Collection artifacts) { this.artifacts = artifacts != null ? artifacts : Collections.emptyList(); @@ -81,15 +85,18 @@ public ArtifactInstallerRequestBuilder artifacts(@Nullable Collection implements ArtifactInstallerRequest { private final Collection artifacts; - DefaultArtifactInstallerRequest(@Nonnull Session session, @Nonnull Collection artifacts) { - super(session); + DefaultArtifactInstallerRequest( + @Nonnull Session session, + @Nullable RequestTrace trace, + @Nonnull Collection artifacts) { + super(session, trace); this.artifacts = List.copyOf(requireNonNull(artifacts, "artifacts cannot be null")); } @@ -98,6 +105,11 @@ static class DefaultArtifactInstallerRequest extends BaseRequest implem public Collection getArtifacts() { return artifacts; } + + @Override + public String toString() { + return "ArtifactInstallerRequest[" + "artifacts=" + artifacts + ']'; + } } } } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverRequest.java index ae937914e661..4651a07332f9 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverRequest.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverRequest.java @@ -39,9 +39,7 @@ */ @Experimental @Immutable -public interface ArtifactResolverRequest { - @Nonnull - Session getSession(); +public interface ArtifactResolverRequest extends Request { @Nonnull Collection getCoordinates(); @@ -78,6 +76,7 @@ static ArtifactResolverRequest build( @NotThreadSafe class ArtifactResolverRequestBuilder { Session session; + RequestTrace trace; Collection coordinates; List repositories; @@ -89,6 +88,12 @@ public ArtifactResolverRequestBuilder session(Session session) { return this; } + @Nonnull + public ArtifactResolverRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + @Nonnull public ArtifactResolverRequestBuilder coordinates(Collection coordinates) { this.coordinates = coordinates; @@ -103,7 +108,7 @@ public ArtifactResolverRequestBuilder repositories(List reposi @Nonnull public ArtifactResolverRequest build() { - return new DefaultArtifactResolverRequest(session, coordinates, repositories); + return new DefaultArtifactResolverRequest(session, trace, coordinates, repositories); } private static class DefaultArtifactResolverRequest extends BaseRequest @@ -116,9 +121,10 @@ private static class DefaultArtifactResolverRequest extends BaseRequest DefaultArtifactResolverRequest( @Nonnull Session session, + @Nullable RequestTrace trace, @Nonnull Collection coordinates, @Nonnull List repositories) { - super(session); + super(session, trace); this.coordinates = List.copyOf(requireNonNull(coordinates, "coordinates cannot be null")); this.repositories = repositories; } @@ -134,6 +140,13 @@ public Collection getCoordinates() { public List getRepositories() { return repositories; } + + @Override + public String toString() { + return "ArtifactResolverRequest[" + "coordinates=" + + coordinates + ", repositories=" + + repositories + ']'; + } } } } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverResult.java index 74d6cacb5faa..f00f7b2337cd 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverResult.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ArtifactResolverResult.java @@ -33,7 +33,7 @@ * @since 4.0.0 */ @Experimental -public interface ArtifactResolverResult { +public interface ArtifactResolverResult extends Result { /** * @return {@link Artifact} */ diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/BaseRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/BaseRequest.java index 43bbf6e0b30c..6bf2c31b223b 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/BaseRequest.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/BaseRequest.java @@ -30,16 +30,26 @@ * @since 4.0.0 */ @Experimental -abstract class BaseRequest { +abstract class BaseRequest implements Request { private final S session; + private final RequestTrace trace; protected BaseRequest(@Nonnull S session) { + this(session, null); + } + + protected BaseRequest(@Nonnull S session, RequestTrace trace) { this.session = requireNonNull(session, "session cannot be null"); + this.trace = trace; } @Nonnull public S getSession() { return session; } + + public RequestTrace getTrace() { + return trace; + } } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyCoordinatesFactoryRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyCoordinatesFactoryRequest.java index 7618aaa9921c..5bcf124711f4 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyCoordinatesFactoryRequest.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyCoordinatesFactoryRequest.java @@ -30,6 +30,7 @@ import org.apache.maven.api.annotations.Immutable; import org.apache.maven.api.annotations.Nonnull; import org.apache.maven.api.annotations.NotThreadSafe; +import org.apache.maven.api.annotations.Nullable; import static java.util.Objects.requireNonNull; @@ -105,6 +106,7 @@ static DependencyCoordinatesFactoryRequestBuilder builder() { @NotThreadSafe class DependencyCoordinatesFactoryRequestBuilder { private Session session; + private RequestTrace trace; private String groupId; private String artifactId; private String version; @@ -123,6 +125,11 @@ public DependencyCoordinatesFactoryRequestBuilder session(Session session) { return this; } + public DependencyCoordinatesFactoryRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + public DependencyCoordinatesFactoryRequestBuilder groupId(String groupId) { this.groupId = groupId; return this; @@ -191,6 +198,7 @@ public DependencyCoordinatesFactoryRequestBuilder exclusion(Exclusion exclusion) public DependencyCoordinatesFactoryRequest build() { return new DefaultDependencyCoordinatesFactoryRequest( session, + trace, groupId, artifactId, version, @@ -219,6 +227,7 @@ private static class DefaultDependencyCoordinatesFactoryRequest extends BaseRequ @SuppressWarnings("checkstyle:ParameterNumber") private DefaultDependencyCoordinatesFactoryRequest( @Nonnull Session session, + @Nullable RequestTrace trace, String groupId, String artifactId, String version, @@ -229,7 +238,7 @@ private DefaultDependencyCoordinatesFactoryRequest( String scope, boolean optional, Collection exclusions) { - super(session); + super(session, trace); this.groupId = groupId; this.artifactId = artifactId; this.version = version; @@ -291,6 +300,21 @@ public boolean isOptional() { public Collection getExclusions() { return exclusions; } + + @Override + public String toString() { + return "DependencyCoordinatesFactoryRequest[" + "groupId='" + + groupId + '\'' + ", artifactId='" + + artifactId + '\'' + ", version='" + + version + '\'' + ", classifier='" + + classifier + '\'' + ", extension='" + + extension + '\'' + ", type='" + + type + '\'' + ", coordinateString='" + + coordinateString + '\'' + ", scope='" + + scope + '\'' + ", optional=" + + optional + ", exclusions=" + + exclusions + ']'; + } } } } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java index 1a8d3529f1fb..532a9b6551af 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverRequest.java @@ -52,7 +52,7 @@ */ @Experimental @Immutable -public interface DependencyResolverRequest { +public interface DependencyResolverRequest extends Request { enum RequestType { COLLECT, @@ -60,9 +60,6 @@ enum RequestType { RESOLVE } - @Nonnull - Session getSession(); - @Nonnull RequestType getRequestType(); @@ -173,6 +170,7 @@ static DependencyResolverRequest build( class DependencyResolverRequestBuilder { Session session; + RequestTrace trace; RequestType requestType; Project project; Artifact rootArtifact; @@ -192,6 +190,12 @@ public DependencyResolverRequestBuilder session(@Nonnull Session session) { return this; } + @Nonnull + public DependencyResolverRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + @Nonnull public DependencyResolverRequestBuilder requestType(@Nonnull RequestType requestType) { this.requestType = requestType; @@ -350,6 +354,7 @@ public DependencyResolverRequestBuilder repositories(@Nonnull List implements DependencyResolverRequest { + + static final class AlwaysTrueFilter implements Predicate { + @Override + public boolean test(PathType pathType) { + return true; + } + + @Override + public boolean equals(Object obj) { + return obj instanceof AlwaysTrueFilter; + } + + @Override + public int hashCode() { + return AlwaysTrueFilter.class.hashCode(); + } + + @Override + public String toString() { + return "AlwaysTrueFilter[]"; + } + } + + private static final Predicate DEFAULT_FILTER = new AlwaysTrueFilter(); + private final RequestType requestType; private final Project project; private final Artifact rootArtifact; @@ -385,6 +415,7 @@ static class DefaultDependencyResolverRequest extends BaseRequest @SuppressWarnings("checkstyle:ParameterNumber") DefaultDependencyResolverRequest( @Nonnull Session session, + @Nullable RequestTrace trace, @Nonnull RequestType requestType, @Nullable Project project, @Nullable Artifact rootArtifact, @@ -395,7 +426,7 @@ static class DefaultDependencyResolverRequest extends BaseRequest @Nullable PathScope pathScope, @Nullable Predicate pathTypeFilter, @Nullable List repositories) { - super(session); + super(session, trace); this.requestType = requireNonNull(requestType, "requestType cannot be null"); this.project = project; this.rootArtifact = rootArtifact; @@ -405,7 +436,7 @@ static class DefaultDependencyResolverRequest extends BaseRequest List.copyOf(requireNonNull(managedDependencies, "managedDependencies cannot be null")); this.verbose = verbose; this.pathScope = requireNonNull(pathScope, "pathScope cannot be null"); - this.pathTypeFilter = (pathTypeFilter != null) ? pathTypeFilter : (t) -> true; + this.pathTypeFilter = (pathTypeFilter != null) ? pathTypeFilter : DEFAULT_FILTER; this.repositories = repositories; if (verbose && requestType != RequestType.COLLECT) { throw new IllegalArgumentException("verbose cannot only be true when collecting dependencies"); @@ -468,10 +499,19 @@ public List getRepositories() { return repositories; } - @Nonnull @Override public String toString() { - return getRoot() + " -> " + getDependencies(); + return "DependencyResolverRequest[" + "requestType=" + + requestType + ", project=" + + project + ", rootArtifact=" + + rootArtifact + ", root=" + + root + ", dependencies=" + + dependencies + ", managedDependencies=" + + managedDependencies + ", verbose=" + + verbose + ", pathScope=" + + pathScope + ", pathTypeFilter=" + + pathTypeFilter + ", repositories=" + + repositories + ']'; } } } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverResult.java index 7b7f92046928..af53d280e2c6 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverResult.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/DependencyResolverResult.java @@ -41,7 +41,7 @@ * @see DependencyResolver#resolve(DependencyResolverRequest) */ @Experimental -public interface DependencyResolverResult { +public interface DependencyResolverResult extends Result { /** * Gets the exceptions that occurred while building the dependency graph. diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderRequest.java index 0b80828bc967..99d384fc9327 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderRequest.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderRequest.java @@ -42,7 +42,7 @@ */ @Experimental @Immutable -public interface ModelBuilderRequest { +public interface ModelBuilderRequest extends Request { /** * The possible request types for building a model. @@ -87,9 +87,6 @@ enum RepositoryMerging { REQUEST_DOMINANT, } - @Nonnull - Session getSession(); - @Nonnull ModelSource getSource(); @@ -177,6 +174,7 @@ static ModelBuilderRequestBuilder builder(ModelBuilderRequest request) { @NotThreadSafe class ModelBuilderRequestBuilder { Session session; + RequestTrace trace; RequestType requestType; boolean locationTracking; boolean recursive; @@ -194,6 +192,7 @@ class ModelBuilderRequestBuilder { ModelBuilderRequestBuilder(ModelBuilderRequest request) { this.session = request.getSession(); + this.trace = request.getTrace(); this.requestType = request.getRequestType(); this.locationTracking = request.isLocationTracking(); this.recursive = request.isRecursive(); @@ -213,6 +212,11 @@ public ModelBuilderRequestBuilder session(Session session) { return this; } + public ModelBuilderRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + public ModelBuilderRequestBuilder requestType(RequestType requestType) { this.requestType = requestType; return this; @@ -276,6 +280,7 @@ public ModelBuilderRequestBuilder lifecycleBindingsInjector(ModelTransformer lif public ModelBuilderRequest build() { return new DefaultModelBuilderRequest( session, + trace, requestType, locationTracking, recursive, @@ -307,6 +312,7 @@ private static class DefaultModelBuilderRequest extends BaseRequest imp @SuppressWarnings("checkstyle:ParameterNumber") DefaultModelBuilderRequest( @Nonnull Session session, + @Nullable RequestTrace trace, @Nonnull RequestType requestType, boolean locationTracking, boolean recursive, @@ -319,7 +325,7 @@ private static class DefaultModelBuilderRequest extends BaseRequest imp RepositoryMerging repositoryMerging, List repositories, ModelTransformer lifecycleBindingsInjector) { - super(session); + super(session, trace); this.requestType = requireNonNull(requestType, "requestType cannot be null"); this.locationTracking = locationTracking; this.recursive = recursive; @@ -395,6 +401,23 @@ public List getRepositories() { public ModelTransformer getLifecycleBindingsInjector() { return lifecycleBindingsInjector; } + + @Override + public String toString() { + return "ModelBuilderRequest[" + "requestType=" + + requestType + ", locationTracking=" + + locationTracking + ", recursive=" + + recursive + ", source=" + + source + ", profiles=" + + profiles + ", activeProfileIds=" + + activeProfileIds + ", inactiveProfileIds=" + + inactiveProfileIds + ", systemProperties=" + + systemProperties + ", userProperties=" + + userProperties + ", repositoryMerging=" + + repositoryMerging + ", repositories=" + + repositories + ", lifecycleBindingsInjector=" + + lifecycleBindingsInjector + ']'; + } } } } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderResult.java index df38a5a62dc7..e4bec3a4d50d 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderResult.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ModelBuilderResult.java @@ -31,7 +31,7 @@ * @since 4.0.0 */ @Experimental -public interface ModelBuilderResult { +public interface ModelBuilderResult extends Result { /** * Gets the source from which the model was read. diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderRequest.java index feedd5103bf0..e0e9eee5b533 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderRequest.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderRequest.java @@ -42,10 +42,7 @@ */ @Experimental @Immutable -public interface ProjectBuilderRequest { - - @Nonnull - Session getSession(); +public interface ProjectBuilderRequest extends Request { @Nonnull Optional getPath(); @@ -86,6 +83,7 @@ static ProjectBuilderRequestBuilder builder() { @NotThreadSafe class ProjectBuilderRequestBuilder { Session session; + RequestTrace trace; Path path; Source source; boolean allowStubModel; @@ -100,6 +98,11 @@ public ProjectBuilderRequestBuilder session(Session session) { return this; } + public ProjectBuilderRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + public ProjectBuilderRequestBuilder path(Path path) { this.path = path; return this; @@ -122,7 +125,7 @@ public ProjectBuilderRequestBuilder repositories(List reposito public ProjectBuilderRequest build() { return new DefaultProjectBuilderRequest( - session, path, source, allowStubModel, recursive, processPlugins, repositories); + session, trace, path, source, allowStubModel, recursive, processPlugins, repositories); } private static class DefaultProjectBuilderRequest extends BaseRequest @@ -137,13 +140,14 @@ private static class DefaultProjectBuilderRequest extends BaseRequest @SuppressWarnings("checkstyle:ParameterNumber") DefaultProjectBuilderRequest( @Nonnull Session session, + @Nullable RequestTrace trace, @Nullable Path path, @Nullable Source source, boolean allowStubModel, boolean recursive, boolean processPlugins, @Nullable List repositories) { - super(session); + super(session, trace); this.path = path; this.source = source; this.allowStubModel = allowStubModel; @@ -183,6 +187,17 @@ public boolean isProcessPlugins() { public List getRepositories() { return repositories; } + + @Override + public String toString() { + return "ProjectBuilderRequest[" + "path=" + + path + ", source=" + + source + ", allowStubModel=" + + allowStubModel + ", recursive=" + + recursive + ", processPlugins=" + + processPlugins + ", repositories=" + + repositories + ']'; + } } } } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderResult.java index 2247aa7b2edf..1cc43440e940 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderResult.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ProjectBuilderResult.java @@ -32,7 +32,7 @@ * @since 4.0.0 */ @Experimental -public interface ProjectBuilderResult { +public interface ProjectBuilderResult extends Result { /** * Gets the identifier of the project that could not be built. The general format of the identifier is {@code diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/Request.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Request.java new file mode 100644 index 000000000000..28eecc94d8f8 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Request.java @@ -0,0 +1,91 @@ +/* + * 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.api.services; + +import org.apache.maven.api.ProtoSession; +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; + +/** + * Base interface for service requests in Maven. This interface defines the common contract + * for all request types within the Maven service layer, providing access to session context + * and request tracing capabilities. + * + *

Each request is associated with a {@link ProtoSession} that contains the configuration + * and context necessary for request processing, including: + *

    + *
  • User and system properties for interpolation
  • + *
  • Session start time information
  • + *
  • Project directory structures
  • + *
+ * + *

Requests can optionally carry trace information through {@link RequestTrace} to support: + *

    + *
  • Debugging and troubleshooting of request flows
  • + *
  • Audit logging of operations
  • + *
  • Performance monitoring of nested operations
  • + *
+ * + *

This interface is designed to be extended by specific request types that handle + * different Maven operations. All implementations must be immutable to ensure thread safety + * and predictable behavior in concurrent environments. + * + * @param the type of ProtoSession associated with this request, allowing for + * type-safe session handling in specific request implementations + * + * @see ProtoSession + * @see RequestTrace + * @see Result + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface Request { + + /** + * Returns the session associated with this request. + * + * @return the session instance, never {@code null} + */ + @Nonnull + S getSession(); + + /** + * Returns the trace information associated with this request, if any. + * The trace provides context about the request's position in the operation + * hierarchy and can carry additional diagnostic information. + * + * @return the request trace, or {@code null} if no trace information is available + */ + @Nullable + RequestTrace getTrace(); + + /** + * Returns a string representation of this request, used for debugging and logging purposes. + * The format should include the request type and any significant attributes that define the + * request's state. + * + * @return a string representation of this request, never {@code null} + */ + @Override + @Nonnull + String toString(); +} 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 new file mode 100644 index 000000000000..6dafc3aeaf57 --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/RequestTrace.java @@ -0,0 +1,62 @@ +/* + * 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.api.services; + +import org.apache.maven.api.annotations.Nullable; + +/** + * 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 + * supports the following key features: + * + *

    + *
  • Maintains parent-child relationships between requests to track operation nesting
  • + *
  • Carries contextual data describing the current request or operation
  • + *
  • Supports both internal session operations and client-provided trace information
  • + *
+ * + *

For internal session operations, the trace typically contains {@code *Request} objects + * that represent the current processing state. Client code can also create traces with + * application-specific data to provide context when invoking session methods.

+ * + *

This trace information is particularly useful for:

+ *
    + *
  • Debugging and troubleshooting request flows
  • + *
  • Audit logging of session operations
  • + *
  • Performance monitoring of nested operations
  • + *
+ * + * @param context The context identifier for this request trace, helping to identify the scope or purpose + * of the request. May be null if no specific context is needed. + * @param parent The parent request trace that led to this request, establishing the chain of nested + * operations. May be null for top-level requests. + * @param data Additional data associated with this request trace, typically containing the actual request + * object being processed or any application-specific state information. May be null if no + * additional data is needed. + */ +public record RequestTrace(@Nullable String context, @Nullable RequestTrace parent, @Nullable Object data) { + + public static final String CONTEXT_PLUGIN = "plugin"; + public static final String CONTEXT_PROJECT = "project"; + public static final String CONTEXT_BOOTSTRAP = "bootstrap"; + + public RequestTrace(RequestTrace parent, Object data) { + this(parent != null ? parent.context() : null, parent, data); + } +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/Result.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Result.java new file mode 100644 index 000000000000..65bdf97a91ab --- /dev/null +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/Result.java @@ -0,0 +1,53 @@ +/* + * 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.api.services; + +import org.apache.maven.api.annotations.Experimental; +import org.apache.maven.api.annotations.Immutable; +import org.apache.maven.api.annotations.Nonnull; + +/** + * Base interface for service operation results in Maven. This interface defines the common contract + * for operation results, providing access to the original request that generated this result. + * + *

Each result is linked to its originating {@link Request}, allowing for: + *

    + *
  • Traceability between requests and their outcomes
  • + *
  • Access to the session context used during processing
  • + *
  • Correlation of results with their initiating parameters
  • + *
+ * + * @param the type of Request that produced this result, ensuring type-safe + * access to the original request parameters + * + * @see Request + * @since 4.0.0 + */ +@Experimental +@Immutable +public interface Result> { + + /** + * Returns the request that produced this result. + * + * @return the originating request instance, never {@code null} + */ + @Nonnull + REQ getRequest(); +} diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderRequest.java index 311f92236239..57fe9cace4ec 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderRequest.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderRequest.java @@ -37,10 +37,7 @@ */ @Experimental @Immutable -public interface SettingsBuilderRequest { - - @Nonnull - ProtoSession getSession(); +public interface SettingsBuilderRequest extends Request { /** * Gets the installation settings source. @@ -133,6 +130,7 @@ static SettingsBuilderRequestBuilder builder() { @NotThreadSafe class SettingsBuilderRequestBuilder { ProtoSession session; + RequestTrace trace; Source installationSettingsSource; Source projectSettingsSource; Source userSettingsSource; @@ -143,6 +141,11 @@ public SettingsBuilderRequestBuilder session(ProtoSession session) { return this; } + public SettingsBuilderRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + public SettingsBuilderRequestBuilder installationSettingsSource(Source installationSettingsSource) { this.installationSettingsSource = installationSettingsSource; return this; @@ -166,6 +169,7 @@ public SettingsBuilderRequestBuilder interpolationSource(UnaryOperator i public SettingsBuilderRequest build() { return new DefaultSettingsBuilderRequest( session, + trace, installationSettingsSource, projectSettingsSource, userSettingsSource, @@ -182,11 +186,12 @@ private static class DefaultSettingsBuilderRequest extends BaseRequest interpolationSource) { - super(session); + super(session, trace); this.installationSettingsSource = installationSettingsSource; this.projectSettingsSource = projectSettingsSource; this.userSettingsSource = userSettingsSource; @@ -216,6 +221,15 @@ public Optional getUserSettingsSource() { public Optional> getInterpolationSource() { return Optional.ofNullable(interpolationSource); } + + @Override + public String toString() { + return "SettingsBuilderRequest[" + "installationSettingsSource=" + + installationSettingsSource + ", projectSettingsSource=" + + projectSettingsSource + ", userSettingsSource=" + + userSettingsSource + ", interpolationSource=" + + interpolationSource + ']'; + } } } } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderResult.java index c05e244767fa..37b6213da287 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderResult.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/SettingsBuilderResult.java @@ -27,7 +27,7 @@ * @since 4.0.0 */ @Experimental -public interface SettingsBuilderResult { +public interface SettingsBuilderResult extends Result { /** * Gets the assembled settings. diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderRequest.java index ea7c5ab912a5..9eca5b131069 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderRequest.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderRequest.java @@ -35,9 +35,7 @@ * @since 4.0.0 */ @Experimental -public interface ToolchainsBuilderRequest { - @Nonnull - ProtoSession getSession(); +public interface ToolchainsBuilderRequest extends Request { /** * Gets the installation Toolchains source. @@ -93,6 +91,7 @@ static ToolchainsBuilderRequestBuilder builder() { @NotThreadSafe class ToolchainsBuilderRequestBuilder { ProtoSession session; + RequestTrace trace; Source installationToolchainsSource; Source userToolchainsSource; @@ -101,6 +100,11 @@ public ToolchainsBuilderRequestBuilder session(ProtoSession session) { return this; } + public ToolchainsBuilderRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + public ToolchainsBuilderRequestBuilder installationToolchainsSource(Source installationToolchainsSource) { this.installationToolchainsSource = installationToolchainsSource; return this; @@ -113,7 +117,7 @@ public ToolchainsBuilderRequestBuilder userToolchainsSource(Source userToolchain public ToolchainsBuilderRequest build() { return new ToolchainsBuilderRequestBuilder.DefaultToolchainsBuilderRequest( - session, installationToolchainsSource, userToolchainsSource); + session, trace, installationToolchainsSource, userToolchainsSource); } private static class DefaultToolchainsBuilderRequest extends BaseRequest @@ -124,9 +128,10 @@ private static class DefaultToolchainsBuilderRequest extends BaseRequest getInstallationToolchainsSource() { public Optional getUserToolchainsSource() { return Optional.ofNullable(userToolchainsSource); } + + @Override + public String toString() { + return "ToolchainsBuilderRequest[" + "installationToolchainsSource=" + + installationToolchainsSource + ", userToolchainsSource=" + + userToolchainsSource + ']'; + } } } } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderResult.java index d48d45bd799e..3cf06a7397c5 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderResult.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/ToolchainsBuilderResult.java @@ -27,7 +27,7 @@ * @since 4.0.0 */ @Experimental -public interface ToolchainsBuilderResult { +public interface ToolchainsBuilderResult extends Result { /** * Gets the assembled toolchains. * diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverRequest.java index d563091dbebd..3ab8a89b1eda 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverRequest.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverRequest.java @@ -35,10 +35,7 @@ * @since 4.0.0 */ @Experimental -public interface VersionRangeResolverRequest { - - @Nonnull - Session getSession(); +public interface VersionRangeResolverRequest extends Request { @Nonnull ArtifactCoordinates getArtifactCoordinates(); @@ -72,6 +69,7 @@ static VersionResolverRequestBuilder builder() { @NotThreadSafe class VersionResolverRequestBuilder { Session session; + RequestTrace trace; ArtifactCoordinates artifactCoordinates; List repositories; @@ -80,6 +78,11 @@ public VersionResolverRequestBuilder session(Session session) { return this; } + public VersionResolverRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + public VersionResolverRequestBuilder artifactCoordinates(ArtifactCoordinates artifactCoordinates) { this.artifactCoordinates = artifactCoordinates; return this; @@ -91,7 +94,7 @@ public VersionResolverRequestBuilder repositories(List reposit } public VersionRangeResolverRequest build() { - return new DefaultVersionResolverRequest(session, artifactCoordinates, repositories); + return new DefaultVersionResolverRequest(session, trace, artifactCoordinates, repositories); } private static class DefaultVersionResolverRequest extends BaseRequest @@ -102,9 +105,10 @@ private static class DefaultVersionResolverRequest extends BaseRequest @SuppressWarnings("checkstyle:ParameterNumber") DefaultVersionResolverRequest( @Nonnull Session session, + @Nullable RequestTrace trace, @Nonnull ArtifactCoordinates artifactCoordinates, @Nullable List repositories) { - super(session); + super(session, trace); this.artifactCoordinates = artifactCoordinates; this.repositories = repositories; } @@ -120,6 +124,13 @@ public ArtifactCoordinates getArtifactCoordinates() { public List getRepositories() { return repositories; } + + @Override + public String toString() { + return "VersionResolverRequest[" + "artifactCoordinates=" + + artifactCoordinates + ", repositories=" + + repositories + ']'; + } } } } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverResult.java index e0a5f8348593..798190c0a08e 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverResult.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionRangeResolverResult.java @@ -36,7 +36,7 @@ * @since 4.0.0 */ @Experimental -public interface VersionRangeResolverResult { +public interface VersionRangeResolverResult extends Result { /** * Gets the exceptions that occurred while resolving the version range. diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverRequest.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverRequest.java index 71a9a558eb76..f2751d0c0cad 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverRequest.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverRequest.java @@ -35,10 +35,7 @@ * @since 4.0.0 */ @Experimental -public interface VersionResolverRequest { - - @Nonnull - Session getSession(); +public interface VersionResolverRequest extends Request { @Nonnull ArtifactCoordinates getArtifactCoordinates(); @@ -74,6 +71,7 @@ static VersionResolverRequestBuilder builder() { @NotThreadSafe class VersionResolverRequestBuilder { Session session; + RequestTrace trace; ArtifactCoordinates artifactCoordinates; List repositories; @@ -82,6 +80,11 @@ public VersionResolverRequestBuilder session(Session session) { return this; } + public VersionResolverRequestBuilder trace(RequestTrace trace) { + this.trace = trace; + return this; + } + public VersionResolverRequestBuilder artifactCoordinates(ArtifactCoordinates artifactCoordinates) { this.artifactCoordinates = artifactCoordinates; return this; @@ -93,7 +96,7 @@ public VersionResolverRequestBuilder repositories(List reposit } public VersionResolverRequest build() { - return new DefaultVersionResolverRequest(session, artifactCoordinates, repositories); + return new DefaultVersionResolverRequest(session, trace, artifactCoordinates, repositories); } private static class DefaultVersionResolverRequest extends BaseRequest @@ -104,9 +107,10 @@ private static class DefaultVersionResolverRequest extends BaseRequest @SuppressWarnings("checkstyle:ParameterNumber") DefaultVersionResolverRequest( @Nonnull Session session, + @Nullable RequestTrace trace, @Nonnull ArtifactCoordinates artifactCoordinates, @Nullable List repositories) { - super(session); + super(session, trace); this.artifactCoordinates = artifactCoordinates; this.repositories = repositories; } @@ -122,6 +126,13 @@ public ArtifactCoordinates getArtifactCoordinates() { public List getRepositories() { return repositories; } + + @Override + public String toString() { + return "VersionResolverRequest[" + "artifactCoordinates=" + + artifactCoordinates + ", repositories=" + + repositories + ']'; + } } } } diff --git a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverResult.java b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverResult.java index 2897183e8c19..5b9e61b40d01 100644 --- a/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverResult.java +++ b/api/maven-api-core/src/main/java/org/apache/maven/api/services/VersionResolverResult.java @@ -31,7 +31,7 @@ * @since 4.0.0 */ @Experimental -public interface VersionResolverResult { +public interface VersionResolverResult extends Result { @Nonnull List getExceptions(); diff --git a/api/maven-api-core/src/test/java/org/apache/maven/api/services/RequestImplementationTest.java b/api/maven-api-core/src/test/java/org/apache/maven/api/services/RequestImplementationTest.java new file mode 100644 index 000000000000..61dc77c2e923 --- /dev/null +++ b/api/maven-api-core/src/test/java/org/apache/maven/api/services/RequestImplementationTest.java @@ -0,0 +1,79 @@ +/* + * 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.api.services; + +import java.util.Arrays; +import java.util.List; + +import org.apache.maven.api.ArtifactCoordinates; +import org.apache.maven.api.RemoteRepository; +import org.apache.maven.api.Session; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; + +class RequestImplementationTest { + + @Test + void testArtifactResolverRequestEquality() { + Session session = mock(Session.class); + ArtifactCoordinates coords1 = mock(ArtifactCoordinates.class); + ArtifactCoordinates coords2 = mock(ArtifactCoordinates.class); + RemoteRepository repo1 = mock(RemoteRepository.class); + RemoteRepository repo2 = mock(RemoteRepository.class); + + List repositories1 = Arrays.asList(repo1, repo2); + List repositories2 = Arrays.asList(repo1, repo2); + + ArtifactResolverRequest.ArtifactResolverRequestBuilder builder = ArtifactResolverRequest.builder(); + + ArtifactResolverRequest request1 = builder.session(session) + .coordinates(Arrays.asList(coords1, coords2)) + .repositories(repositories1) + .build(); + + ArtifactResolverRequest request2 = builder.session(session) + .coordinates(Arrays.asList(coords1, coords2)) + .repositories(repositories2) + .build(); + + ArtifactResolverRequest request3 = builder.session(session) + .coordinates(Arrays.asList(coords2, coords1)) // Different order + .repositories(repositories1) + .build(); + + // Test toString + String toString = request1.toString(); + assertTrue(toString.contains("coordinates=")); + assertTrue(toString.contains("repositories=")); + } + + @Test + void testRequestTraceIntegration() { + Session session = mock(Session.class); + RequestTrace trace = new RequestTrace("test-context", null, "test-data"); + + ArtifactInstallerRequest request = + ArtifactInstallerRequest.builder().session(session).trace(trace).build(); + + assertEquals(trace, request.getTrace()); + assertEquals(session, request.getSession()); + } +} 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 new file mode 100644 index 000000000000..4172a15d573d --- /dev/null +++ b/api/maven-api-core/src/test/java/org/apache/maven/api/services/RequestTraceTest.java @@ -0,0 +1,85 @@ +/* + * 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.api.services; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class RequestTraceTest { + + @Test + void testRequestTraceCreation() { + RequestTrace parentTrace = new RequestTrace("parent-context", null, "parent-data"); + RequestTrace childTrace = new RequestTrace("child-context", parentTrace, "child-data"); + + assertEquals("parent-context", parentTrace.context()); + assertNull(parentTrace.parent()); + assertEquals("parent-data", parentTrace.data()); + + assertEquals("child-context", childTrace.context()); + assertSame(parentTrace, childTrace.parent()); + assertEquals("child-data", childTrace.data()); + } + + @Test + void testRequestTraceWithParentContextInheritance() { + RequestTrace parentTrace = new RequestTrace("parent-context", null, "parent-data"); + RequestTrace childTrace = new RequestTrace(parentTrace, "child-data"); + + assertEquals("parent-context", parentTrace.context()); + assertEquals("parent-context", childTrace.context()); + assertEquals("child-data", childTrace.data()); + } + + @Test + void testPredefinedContexts() { + assertEquals("plugin", RequestTrace.CONTEXT_PLUGIN); + assertEquals("project", RequestTrace.CONTEXT_PROJECT); + assertEquals("bootstrap", RequestTrace.CONTEXT_BOOTSTRAP); + } + + @Test + void testNullValues() { + RequestTrace trace = new RequestTrace(null, null, null); + assertNull(trace.context()); + assertNull(trace.parent()); + assertNull(trace.data()); + } + + @Test + void testChainedTraces() { + RequestTrace root = new RequestTrace("root", null, "root-data"); + RequestTrace level1 = new RequestTrace("level1", root, "level1-data"); + RequestTrace level2 = new RequestTrace("level2", level1, "level2-data"); + RequestTrace level3 = new RequestTrace(level2, "level3-data"); + + // Verify the chain + assertNull(root.parent()); + assertEquals(root, level1.parent()); + assertEquals(level1, level2.parent()); + assertEquals(level2, level3.parent()); + + // Verify context inheritance + assertEquals("root", root.context()); + assertEquals("level1", level1.context()); + assertEquals("level2", level2.context()); + assertEquals("level2", level3.context()); // Inherited from parent + } +} diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/aether/ReverseTreeRepositoryListener.java b/impl/maven-core/src/main/java/org/apache/maven/internal/aether/ReverseTreeRepositoryListener.java index f1ae7295dcfd..a7379ae3cb9d 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/internal/aether/ReverseTreeRepositoryListener.java +++ b/impl/maven-core/src/main/java/org/apache/maven/internal/aether/ReverseTreeRepositoryListener.java @@ -29,8 +29,8 @@ import java.util.ListIterator; import java.util.Objects; -import org.apache.maven.model.InputLocation; -import org.apache.maven.model.Plugin; +import org.apache.maven.api.model.InputLocation; +import org.apache.maven.api.model.Plugin; import org.eclipse.aether.AbstractRepositoryListener; import org.eclipse.aether.RepositoryEvent; import org.eclipse.aether.RepositorySystemSession; @@ -78,6 +78,8 @@ public void artifactResolved(RepositoryEvent event) { artifactRequest = artifactRequestData; } else if (data instanceof Plugin pluginData) { plugin = pluginData; + } else if (data instanceof org.apache.maven.model.Plugin pluginData) { + plugin = pluginData.getDelegate(); } trace = trace.getParent(); } @@ -152,7 +154,7 @@ public void artifactResolved(RepositoryEvent event) { } if (trackingFile == null) { - return; + return; // parent or imported bom ? } try { Files.createDirectories(trackingDir); diff --git a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectBuilder.java b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectBuilder.java index f77536fc289f..06690ec1b5f5 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectBuilder.java +++ b/impl/maven-core/src/main/java/org/apache/maven/internal/impl/DefaultProjectBuilder.java @@ -43,6 +43,7 @@ import org.apache.maven.artifact.repository.ArtifactRepository; import org.apache.maven.impl.DefaultDependencyResolverResult; import org.apache.maven.impl.MappedCollection; +import org.apache.maven.impl.RequestTraceHelper; import org.apache.maven.model.building.ModelProblem; import org.apache.maven.model.building.ModelSource2; import org.apache.maven.project.DefaultProjectBuildingRequest; @@ -67,6 +68,7 @@ public DefaultProjectBuilder(org.apache.maven.project.ProjectBuilder builder) { public ProjectBuilderResult build(ProjectBuilderRequest request) throws ProjectBuilderException, IllegalArgumentException { InternalMavenSession session = InternalMavenSession.from(request.getSession()); + RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(request.getSession(), request); try { List repositories = session.toArtifactRepositories( request.getRepositories() != null ? request.getRepositories() : session.getRemoteRepositories()); @@ -87,6 +89,11 @@ public ProjectBuilderResult build(ProjectBuilderRequest request) throw new IllegalArgumentException("Invalid request"); } return new ProjectBuilderResult() { + @Override + public ProjectBuilderRequest getRequest() { + return request; + } + @Nonnull @Override public String getProjectId() { @@ -175,11 +182,14 @@ public Severity getSeverity() { public Optional getDependencyResolverResult() { return Optional.ofNullable(res.getDependencyResolutionResult()) .map(r -> new DefaultDependencyResolverResult( - null, r.getCollectionErrors(), session.getNode(r.getDependencyGraph()), 0)); + // TODO: this should not be null + null, null, r.getCollectionErrors(), session.getNode(r.getDependencyGraph()), 0)); } }; } catch (ProjectBuildingException e) { throw new ProjectBuilderException("Unable to build project", e); + } finally { + RequestTraceHelper.exit(trace); } } diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java index e40586fba549..16aee3db3130 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/internal/DefaultPluginDependenciesResolver.java @@ -30,6 +30,8 @@ import org.apache.maven.RepositoryUtils; import org.apache.maven.api.DependencyScope; +import org.apache.maven.impl.InternalSession; +import org.apache.maven.impl.RequestTraceHelper; import org.apache.maven.impl.resolver.RelocatedArtifact; import org.apache.maven.model.Dependency; import org.apache.maven.model.Plugin; @@ -70,7 +72,7 @@ @Named @Singleton public class DefaultPluginDependenciesResolver implements PluginDependenciesResolver { - private static final String REPOSITORY_CONTEXT = "plugin"; + private static final String REPOSITORY_CONTEXT = org.apache.maven.api.services.RequestTrace.CONTEXT_PLUGIN; private final Logger logger = LoggerFactory.getLogger(getClass()); @@ -187,7 +189,8 @@ private DependencyResult resolveInternal( List repositories, RepositorySystemSession session) throws PluginResolutionException { - RequestTrace trace = RequestTrace.newChild(null, plugin); + InternalSession iSession = InternalSession.from(session); + RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(iSession, plugin.getDelegate()); if (pluginArtifact == null) { pluginArtifact = toArtifact(plugin, session); @@ -217,9 +220,9 @@ private DependencyResult resolveInternal( } DependencyRequest depRequest = new DependencyRequest(request, resolutionFilter); - depRequest.setTrace(trace); + depRequest.setTrace(trace.trace()); - request.setTrace(RequestTrace.newChild(trace, depRequest)); + request.setTrace(RequestTrace.newChild(trace.trace(), depRequest)); node = repoSystem.collectDependencies(pluginSession, request).getRoot(); @@ -239,6 +242,8 @@ private DependencyResult resolveInternal( .flatMap(r -> r.getExceptions().stream())) .collect(Collectors.toList()); throw new PluginResolutionException(plugin, exceptions, e); + } finally { + RequestTraceHelper.exit(trace); } } } diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/prefix/internal/DefaultPluginPrefixResolver.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/prefix/internal/DefaultPluginPrefixResolver.java index 8bcea4968534..c616533cea25 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/prefix/internal/DefaultPluginPrefixResolver.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/prefix/internal/DefaultPluginPrefixResolver.java @@ -62,7 +62,7 @@ @Named @Singleton public class DefaultPluginPrefixResolver implements PluginPrefixResolver { - private static final String REPOSITORY_CONTEXT = "plugin"; + private static final String REPOSITORY_CONTEXT = org.apache.maven.api.services.RequestTrace.CONTEXT_PLUGIN; private final Logger logger = LoggerFactory.getLogger(getClass()); private final BuildPluginManager pluginManager; diff --git a/impl/maven-core/src/main/java/org/apache/maven/plugin/version/internal/DefaultPluginVersionResolver.java b/impl/maven-core/src/main/java/org/apache/maven/plugin/version/internal/DefaultPluginVersionResolver.java index 9082b30887d9..b2afbb073efb 100644 --- a/impl/maven-core/src/main/java/org/apache/maven/plugin/version/internal/DefaultPluginVersionResolver.java +++ b/impl/maven-core/src/main/java/org/apache/maven/plugin/version/internal/DefaultPluginVersionResolver.java @@ -72,7 +72,7 @@ @Named @Singleton public class DefaultPluginVersionResolver implements PluginVersionResolver { - private static final String REPOSITORY_CONTEXT = "plugin"; + private static final String REPOSITORY_CONTEXT = org.apache.maven.api.services.RequestTrace.CONTEXT_PLUGIN; private static final Object CACHE_KEY = new Object(); 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 ade3a7dde843..117b81fa4532 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 @@ -84,6 +84,7 @@ import org.apache.maven.api.services.PathScopeRegistry; import org.apache.maven.api.services.ProjectScopeRegistry; import org.apache.maven.api.services.RepositoryFactory; +import org.apache.maven.api.services.RequestTrace; import org.apache.maven.api.services.TypeRegistry; import org.apache.maven.api.services.VersionParser; import org.apache.maven.api.services.VersionRangeResolver; @@ -900,4 +901,20 @@ public DependencyScope requireDependencyScope(String id) { public PathScope requirePathScope(String id) { return getService(PathScopeRegistry.class).require(id); } + + @Override + public void setCurrentTrace(RequestTrace trace) { + getTraceHolder().set(trace); + } + + @Override + public RequestTrace getCurrentTrace() { + return getTraceHolder().get(); + } + + @SuppressWarnings("unchecked") + private ThreadLocal getTraceHolder() { + org.eclipse.aether.SessionData data = session.getData(); + return (ThreadLocal) data.computeIfAbsent(RequestTrace.class, ThreadLocal::new); + } } diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultArtifactResolver.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultArtifactResolver.java index a64be28b868a..b83d64920617 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultArtifactResolver.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultArtifactResolver.java @@ -51,6 +51,7 @@ public ArtifactResolverResult resolve(ArtifactResolverRequest request) throws ArtifactResolverException, IllegalArgumentException { nonNull(request, "request"); InternalSession session = InternalSession.from(request.getSession()); + RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(session, request); try { Map paths = new HashMap<>(); ArtifactManager artifactManager = session.getService(ArtifactManager.class); @@ -68,7 +69,8 @@ public ArtifactResolverResult resolve(ArtifactResolverRequest request) DownloadedArtifact resolved = session.getArtifact(DownloadedArtifact.class, aetherArtifact); paths.put(resolved, path); } else { - requests.add(new ArtifactRequest(aetherArtifact, repositories, null)); + requests.add( + new ArtifactRequest(aetherArtifact, repositories, trace.context()).setTrace(trace.trace())); } } if (!requests.isEmpty()) { @@ -80,19 +82,29 @@ public ArtifactResolverResult resolve(ArtifactResolverRequest request) paths.put(artifact, path); } } - return new DefaultArtifactResolverResult(paths); + return new DefaultArtifactResolverResult(request, paths); } catch (ArtifactResolutionException e) { throw new ArtifactResolverException("Unable to resolve artifact: " + e.getMessage(), e); + } finally { + RequestTraceHelper.exit(trace); } } static class DefaultArtifactResolverResult implements ArtifactResolverResult { + + final ArtifactResolverRequest request; final Map paths; - DefaultArtifactResolverResult(Map paths) { + DefaultArtifactResolverResult(ArtifactResolverRequest request, Map paths) { + this.request = request; this.paths = paths; } + @Override + public ArtifactResolverRequest getRequest() { + return request; + } + @Override public Collection getArtifacts() { return paths.keySet(); diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolver.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolver.java index 4b4fc43e512e..984c2c90a944 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolver.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolver.java @@ -75,58 +75,64 @@ public DependencyResolverResult collect(@Nonnull DependencyResolverRequest reque throws DependencyResolverException, IllegalArgumentException { nonNull(request, "request"); InternalSession session = InternalSession.from(request.getSession()); + RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(session, request); + try { + Artifact rootArtifact; + DependencyCoordinates root; + Collection dependencies; + Collection managedDependencies; + List remoteRepositories; + if (request.getProject().isPresent()) { + Project project = request.getProject().get(); + rootArtifact = project.getPomArtifact(); + root = null; + dependencies = project.getDependencies(); + managedDependencies = project.getManagedDependencies(); + remoteRepositories = request.getRepositories() != null + ? request.getRepositories() + : session.getService(ProjectManager.class).getRemoteProjectRepositories(project); + } else { + rootArtifact = request.getRootArtifact().orElse(null); + root = request.getRoot().orElse(null); + dependencies = request.getDependencies(); + managedDependencies = request.getManagedDependencies(); + remoteRepositories = + request.getRepositories() != null ? request.getRepositories() : session.getRemoteRepositories(); + } + ResolutionScope resolutionScope = null; + if (request.getPathScope() != null) { + resolutionScope = session.getSession() + .getScopeManager() + .getResolutionScope(request.getPathScope().id()) + .orElseThrow(); + } + CollectRequest collectRequest = new CollectRequest() + .setRootArtifact(rootArtifact != null ? session.toArtifact(rootArtifact) : null) + .setRoot(root != null ? session.toDependency(root, false) : null) + .setDependencies(session.toDependencies(dependencies, false)) + .setManagedDependencies(session.toDependencies(managedDependencies, true)) + .setRepositories(session.toRepositories(remoteRepositories)) + .setRequestContext(trace.context()) + .setTrace(trace.trace()); + collectRequest.setResolutionScope(resolutionScope); - Artifact rootArtifact; - DependencyCoordinates root; - Collection dependencies; - Collection managedDependencies; - List remoteRepositories; - if (request.getProject().isPresent()) { - Project project = request.getProject().get(); - rootArtifact = project.getPomArtifact(); - root = null; - dependencies = project.getDependencies(); - managedDependencies = project.getManagedDependencies(); - remoteRepositories = request.getRepositories() != null - ? request.getRepositories() - : session.getService(ProjectManager.class).getRemoteProjectRepositories(project); - } else { - rootArtifact = request.getRootArtifact().orElse(null); - root = request.getRoot().orElse(null); - dependencies = request.getDependencies(); - managedDependencies = request.getManagedDependencies(); - remoteRepositories = - request.getRepositories() != null ? request.getRepositories() : session.getRemoteRepositories(); - } - ResolutionScope resolutionScope = null; - if (request.getPathScope() != null) { - resolutionScope = session.getSession() - .getScopeManager() - .getResolutionScope(request.getPathScope().id()) - .orElseThrow(); - } - CollectRequest collectRequest = new CollectRequest() - .setRootArtifact(rootArtifact != null ? session.toArtifact(rootArtifact) : null) - .setRoot(root != null ? session.toDependency(root, false) : null) - .setDependencies(session.toDependencies(dependencies, false)) - .setManagedDependencies(session.toDependencies(managedDependencies, true)) - .setRepositories(session.toRepositories(remoteRepositories)); - collectRequest.setResolutionScope(resolutionScope); - - RepositorySystemSession systemSession = session.getSession(); - if (request.getVerbose()) { - systemSession = new DefaultRepositorySystemSession(systemSession) - .setConfigProperty(ConflictResolver.CONFIG_PROP_VERBOSE, true) - .setConfigProperty(DependencyManagerUtils.CONFIG_PROP_VERBOSE, true); - } + RepositorySystemSession systemSession = session.getSession(); + if (request.getVerbose()) { + systemSession = new DefaultRepositorySystemSession(systemSession) + .setConfigProperty(ConflictResolver.CONFIG_PROP_VERBOSE, true) + .setConfigProperty(DependencyManagerUtils.CONFIG_PROP_VERBOSE, true); + } - try { - final CollectResult result = - session.getRepositorySystem().collectDependencies(systemSession, collectRequest); - return new DefaultDependencyResolverResult( - null, result.getExceptions(), session.getNode(result.getRoot(), request.getVerbose()), 0); - } catch (DependencyCollectionException e) { - throw new DependencyResolverException("Unable to collect dependencies", e); + try { + final CollectResult result = + session.getRepositorySystem().collectDependencies(systemSession, collectRequest); + return new DefaultDependencyResolverResult( + null, null, result.getExceptions(), session.getNode(result.getRoot(), request.getVerbose()), 0); + } catch (DependencyCollectionException e) { + throw new DependencyResolverException("Unable to collect dependencies", e); + } + } finally { + RequestTraceHelper.exit(trace); } } @@ -162,54 +168,60 @@ private static DependencyFilter getScopeDependencyFilter(PathScope scope) { */ @Override public DependencyResolverResult resolve(DependencyResolverRequest request) - throws DependencyResolverException, DependencyResolverException, ArtifactResolverException { + throws DependencyResolverException, ArtifactResolverException { InternalSession session = InternalSession.from(nonNull(request, "request").getSession()); - DependencyResolverResult result; - DependencyResolverResult collectorResult = collect(request); - List repositories = request.getRepositories() != null - ? request.getRepositories() - : request.getProject().isPresent() - ? session.getService(ProjectManager.class) - .getRemoteProjectRepositories( - request.getProject().get()) - : session.getRemoteRepositories(); - if (request.getRequestType() == DependencyResolverRequest.RequestType.COLLECT) { - result = collectorResult; - } else { - List nodes = flatten(session, collectorResult.getRoot(), request.getPathScope()); - List coordinates = nodes.stream() - .map(Node::getDependency) - .filter(Objects::nonNull) - .map(Artifact::toCoordinates) - .collect(Collectors.toList()); - Predicate filter = request.getPathTypeFilter(); - if (request.getRequestType() == DependencyResolverRequest.RequestType.FLATTEN) { - DefaultDependencyResolverResult flattenResult = new DefaultDependencyResolverResult( - null, collectorResult.getExceptions(), collectorResult.getRoot(), nodes.size()); - for (Node node : nodes) { - flattenResult.addNode(node); - } - result = flattenResult; + RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(session, request); + try { + DependencyResolverResult result; + DependencyResolverResult collectorResult = collect(request); + List repositories = request.getRepositories() != null + ? request.getRepositories() + : request.getProject().isPresent() + ? session.getService(ProjectManager.class) + .getRemoteProjectRepositories( + request.getProject().get()) + : session.getRemoteRepositories(); + if (request.getRequestType() == DependencyResolverRequest.RequestType.COLLECT) { + result = collectorResult; } else { - PathModularizationCache cache = new PathModularizationCache(); // TODO: should be project-wide cache. - DefaultDependencyResolverResult resolverResult = new DefaultDependencyResolverResult( - cache, collectorResult.getExceptions(), collectorResult.getRoot(), nodes.size()); - ArtifactResolverResult artifactResolverResult = - session.getService(ArtifactResolver.class).resolve(session, coordinates, repositories); - for (Node node : nodes) { - Dependency d = node.getDependency(); - Path path = (d != null) ? artifactResolverResult.getPath(d) : null; - try { - resolverResult.addDependency(node, d, filter, path); - } catch (IOException e) { - throw cannotReadModuleInfo(path, e); + List nodes = flatten(session, collectorResult.getRoot(), request.getPathScope()); + List coordinates = nodes.stream() + .map(Node::getDependency) + .filter(Objects::nonNull) + .map(Artifact::toCoordinates) + .collect(Collectors.toList()); + Predicate filter = request.getPathTypeFilter(); + if (request.getRequestType() == DependencyResolverRequest.RequestType.FLATTEN) { + DefaultDependencyResolverResult flattenResult = new DefaultDependencyResolverResult( + null, null, collectorResult.getExceptions(), collectorResult.getRoot(), nodes.size()); + for (Node node : nodes) { + flattenResult.addNode(node); + } + result = flattenResult; + } else { + PathModularizationCache cache = + new PathModularizationCache(); // TODO: should be project-wide cache. + DefaultDependencyResolverResult resolverResult = new DefaultDependencyResolverResult( + null, cache, collectorResult.getExceptions(), collectorResult.getRoot(), nodes.size()); + ArtifactResolverResult artifactResolverResult = + session.getService(ArtifactResolver.class).resolve(session, coordinates, repositories); + for (Node node : nodes) { + Dependency d = node.getDependency(); + Path path = (d != null) ? artifactResolverResult.getPath(d) : null; + try { + resolverResult.addDependency(node, d, filter, path); + } catch (IOException e) { + throw cannotReadModuleInfo(path, e); + } } + result = resolverResult; } - result = resolverResult; } + return result; + } finally { + RequestTraceHelper.exit(trace); } - return result; } private static DependencyResolverException cannotReadModuleInfo(final Path path, final IOException cause) { diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolverResult.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolverResult.java index 9464ed7f11d5..fa7ace6e0d05 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolverResult.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultDependencyResolverResult.java @@ -52,6 +52,10 @@ * @see DefaultDependencyResolver#resolve(DependencyResolverRequest) */ public class DefaultDependencyResolverResult implements DependencyResolverResult { + /** + * The corresponding request. + */ + private final DependencyResolverRequest request; /** * The exceptions that occurred while building the dependency graph. */ @@ -97,13 +101,19 @@ public class DefaultDependencyResolverResult implements DependencyResolverResult * Creates an initially empty result. Callers should add path elements by calls * to {@link #addDependency(Node, Dependency, Predicate, Path)}. * + * @param request the corresponding request * @param cache cache of module information about each dependency * @param exceptions the exceptions that occurred while building the dependency graph * @param root the root node of the dependency graph * @param count estimated number of dependencies */ public DefaultDependencyResolverResult( - PathModularizationCache cache, List exceptions, Node root, int count) { + DependencyResolverRequest request, + PathModularizationCache cache, + List exceptions, + Node root, + int count) { + this.request = request; this.cache = cache; this.exceptions = exceptions; this.root = root; @@ -333,6 +343,11 @@ private Path findArtifactPath(String group, String artifact) throws IOException return null; } + @Override + public DependencyResolverRequest getRequest() { + return request; + } + @Override public List getExceptions() { return exceptions; diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSettingsBuilder.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSettingsBuilder.java index 0d63698b3577..0fd45bc6b5e3 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSettingsBuilder.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultSettingsBuilder.java @@ -143,7 +143,7 @@ public SettingsBuilderResult build(SettingsBuilderRequest request) throws Settin throw new SettingsBuilderException("Error building settings", problems); } - return new DefaultSettingsBuilderResult(effective, problems); + return new DefaultSettingsBuilderResult(request, effective, problems); } private Settings readSettings( @@ -338,15 +338,24 @@ public org.apache.maven.api.model.Profile convert(Profile profile) { */ static class DefaultSettingsBuilderResult implements SettingsBuilderResult { + private final SettingsBuilderRequest request; + private final Settings effectiveSettings; private final ProblemCollector problems; - DefaultSettingsBuilderResult(Settings effectiveSettings, ProblemCollector problems) { + DefaultSettingsBuilderResult( + SettingsBuilderRequest request, Settings effectiveSettings, ProblemCollector problems) { + this.request = request; this.effectiveSettings = effectiveSettings; this.problems = (problems != null) ? problems : ProblemCollector.empty(); } + @Override + public SettingsBuilderRequest getRequest() { + return request; + } + @Override public Settings getEffectiveSettings() { return effectiveSettings; diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultToolchainsBuilder.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultToolchainsBuilder.java index 0a5507763fa9..77aca76a41da 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultToolchainsBuilder.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultToolchainsBuilder.java @@ -83,7 +83,7 @@ public ToolchainsBuilderResult build(ToolchainsBuilderRequest request) throws To throw new ToolchainsBuilderException("Error building toolchains", problems); } - return new DefaultToolchainsBuilderResult(effective, problems); + return new DefaultToolchainsBuilderResult(request, effective, problems); } private PersistedToolchains readToolchains( @@ -167,16 +167,26 @@ private PersistedToolchains interpolate( */ static class DefaultToolchainsBuilderResult implements ToolchainsBuilderResult { + private final ToolchainsBuilderRequest request; + private final PersistedToolchains effectiveToolchains; private final ProblemCollector problems; DefaultToolchainsBuilderResult( - PersistedToolchains effectiveToolchains, ProblemCollector problems) { + ToolchainsBuilderRequest request, + PersistedToolchains effectiveToolchains, + ProblemCollector problems) { + this.request = request; this.effectiveToolchains = effectiveToolchains; this.problems = (problems != null) ? problems : ProblemCollector.empty(); } + @Override + public ToolchainsBuilderRequest getRequest() { + return request; + } + @Override public PersistedToolchains getEffectiveToolchains() { return effectiveToolchains; diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultVersionRangeResolver.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultVersionRangeResolver.java index 74c2f75b1337..a189d4328abe 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultVersionRangeResolver.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultVersionRangeResolver.java @@ -58,22 +58,29 @@ public VersionRangeResolverResult resolve(VersionRangeResolverRequest request) requireNonNull(request, "request"); InternalSession session = InternalSession.from(request.getSession()); + RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(session, request); try { VersionRangeResult res = repositorySystem.resolveVersionRange( session.getSession(), new VersionRangeRequest( - session.toArtifact(request.getArtifactCoordinates()), - session.toRepositories( - request.getRepositories() != null - ? request.getRepositories() - : session.getRemoteRepositories()), - null)); + session.toArtifact(request.getArtifactCoordinates()), + session.toRepositories( + request.getRepositories() != null + ? request.getRepositories() + : session.getRemoteRepositories()), + trace.context()) + .setTrace(trace.trace())); Map repos = res.getVersions().stream() .filter(v -> res.getRepository(v) != null) .collect(Collectors.toMap(v -> v.toString(), res::getRepository)); return new VersionRangeResolverResult() { + @Override + public VersionRangeResolverRequest getRequest() { + return request; + } + @Override public List getExceptions() { return res.getExceptions(); @@ -98,6 +105,8 @@ public Optional getRepository(Version version) { }; } catch (VersionRangeResolutionException e) { throw new VersionRangeResolverException("Unable to resolve version range", e); + } finally { + RequestTraceHelper.exit(trace); } } } diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultVersionResolver.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultVersionResolver.java index bc824cd256f2..a9b4ba4748f1 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultVersionResolver.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/DefaultVersionResolver.java @@ -53,18 +53,24 @@ public VersionResolverResult resolve(VersionResolverRequest request) throws Vers nonNull(request, "request"); InternalSession session = InternalSession.from(request.getSession()); + RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(session, request); try { - VersionResult res = repositorySystem.resolveVersion( - session.getSession(), - new VersionRequest( + VersionRequest req = new VersionRequest( session.toArtifact(request.getArtifactCoordinates()), session.toRepositories( request.getRepositories() != null ? request.getRepositories() : session.getRemoteRepositories()), - null)); + trace.context()) + .setTrace(trace.trace()); + VersionResult res = repositorySystem.resolveVersion(session.getSession(), req); return new VersionResolverResult() { + @Override + public VersionResolverRequest getRequest() { + return request; + } + @Override public List getExceptions() { return res.getExceptions(); @@ -89,6 +95,8 @@ public Optional getRepository() { }; } catch (VersionResolutionException e) { throw new VersionResolverException("Unable to resolve version", e); + } finally { + RequestTraceHelper.exit(trace); } } } diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/InternalSession.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/InternalSession.java index ce01aa50f935..fe8a44715b23 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/InternalSession.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/InternalSession.java @@ -30,6 +30,8 @@ import org.apache.maven.api.RemoteRepository; import org.apache.maven.api.Session; import org.apache.maven.api.annotations.Nonnull; +import org.apache.maven.api.annotations.Nullable; +import org.apache.maven.api.services.RequestTrace; import org.eclipse.aether.RepositorySystem; import org.eclipse.aether.RepositorySystemSession; @@ -86,4 +88,26 @@ List toDependencies( RepositorySystemSession getSession(); RepositorySystem getRepositorySystem(); + + /** + * Sets the current request trace for the session. + * The request trace provides contextual information about the current operation + * being performed and can be used for debugging and monitoring purposes. + * The trace is stored in thread-local storage, allowing for concurrent operations + * with different traces. + * + * @param trace the trace to set as current, may be null to clear the trace + * @see RequestTraceHelper#enter(Session, Object) For the recommended way to manage traces + */ + void setCurrentTrace(@Nullable RequestTrace trace); + + /** + * Gets the current request trace for the session from thread-local storage. + * Each thread maintains its own trace context, ensuring thread-safety for + * concurrent operations. + * + * @return the current request trace, or null if no trace is set + * @see RequestTraceHelper#enter(Session, Object) For the recommended way to manage traces + */ + RequestTrace getCurrentTrace(); } 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 new file mode 100644 index 000000000000..44b54003f25d --- /dev/null +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/RequestTraceHelper.java @@ -0,0 +1,138 @@ +/* + * 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.stream.Collectors; + +import org.apache.maven.api.Session; +import org.apache.maven.api.services.Request; +import org.eclipse.aether.RequestTrace; +import org.eclipse.aether.collection.CollectRequest; +import org.eclipse.aether.collection.CollectStepData; +import org.eclipse.aether.resolution.ArtifactDescriptorRequest; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.eclipse.aether.resolution.DependencyRequest; + +/** + * Helper class to manage request tracing for improved error logging in Maven's dependency resolution. + * This class provides utilities to: + * - Track request traces through Maven's dependency resolution process + * - Convert between Maven and Resolver trace formats + * - Generate human-readable interpretations of trace data + */ +public final class RequestTraceHelper { + + /** + * Represents a resolver trace containing both Maven and Resolver-specific trace information + * @param session The current Maven session + * @param context The trace context + * @param trace The Resolver-specific trace + * @param mvnTrace The Maven-specific trace + */ + public record ResolverTrace( + Session session, String context, RequestTrace trace, org.apache.maven.api.services.RequestTrace mvnTrace) {} + + /** + * Creates a new trace entry and updates the session's current trace + * @param session The current Maven session + * @param data The data object to associate with the trace + * @return A new ResolverTrace containing both Maven and Resolver trace information + */ + public static ResolverTrace enter(Session session, Object data) { + InternalSession iSession = InternalSession.from(session); + 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); + iSession.setCurrentTrace(trace); + return new ResolverTrace(session, trace.context(), toResolver(trace), trace); + } + + /** + * Restores the parent trace as the current trace in the session + * @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()); + } + + /** + * Converts a Resolver trace to a Maven trace + * @param context The context string for the new Maven trace + * @param trace The Resolver trace to convert + * @return A new Maven trace, or null if the input trace was null + */ + public static org.apache.maven.api.services.RequestTrace toMaven(String context, RequestTrace trace) { + if (trace != null) { + return new org.apache.maven.api.services.RequestTrace( + context, toMaven(context, trace.getParent()), trace.getData()); + } else { + return null; + } + } + + /** + * Converts a Maven trace to a Resolver trace + * @param trace The Maven trace to convert + * @return A new Resolver trace, or null if the input trace was null + */ + public static RequestTrace toResolver(org.apache.maven.api.services.RequestTrace trace) { + if (trace != null) { + return RequestTrace.newChild(toResolver(trace.parent()), trace.data()); + } else { + return null; + } + } + + /** + * Creates a human-readable interpretation of a request trace + * @param detailed If true, includes additional details such as dependency paths + * @param requestTrace The trace to interpret + * @return A string describing the trace context and relevant details + */ + public static String interpretTrace(boolean detailed, RequestTrace requestTrace) { + while (requestTrace != null) { + Object data = requestTrace.getData(); + if (data instanceof DependencyRequest request) { + return "dependency resolution for " + request; + } else if (data instanceof CollectRequest request) { + return "dependency collection for " + request; + } else if (data instanceof CollectStepData stepData) { + String msg = "dependency collection step for " + stepData.getContext(); + if (detailed) { + msg += ". Path to offending node from root:\n"; + msg += stepData.getPath().stream() + .map(n -> " -> " + n.toString()) + .collect(Collectors.joining("\n")); + msg += "\n => " + stepData.getNode(); + } + return msg; + } else if (data instanceof ArtifactDescriptorRequest request) { + return "artifact descriptor request for " + request.getArtifact(); + } else if (data instanceof ArtifactRequest request) { + return "artifact request for " + request.getArtifact(); + } else if (data instanceof org.apache.maven.api.model.Plugin plugin) { + return "plugin " + plugin.getId(); + } + requestTrace = requestTrace.getParent(); + } + + return "n/a"; + } +} diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java index e2c9d6164f6f..542f9c96caa8 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilder.java @@ -83,6 +83,7 @@ import org.apache.maven.api.services.ModelSource; import org.apache.maven.api.services.ProblemCollector; import org.apache.maven.api.services.RepositoryFactory; +import org.apache.maven.api.services.RequestTrace; import org.apache.maven.api.services.Source; import org.apache.maven.api.services.SuperPomProvider; import org.apache.maven.api.services.VersionParserException; @@ -110,6 +111,8 @@ import org.apache.maven.api.services.xml.XmlReaderRequest; import org.apache.maven.api.spi.ModelParserException; import org.apache.maven.api.spi.ModelTransformer; +import org.apache.maven.impl.InternalSession; +import org.apache.maven.impl.RequestTraceHelper; import org.apache.maven.impl.util.PhasingExecutor; import org.apache.maven.model.v4.MavenTransformer; import org.slf4j.Logger; @@ -219,24 +222,30 @@ protected class ModelBuilderSessionImpl implements ModelBuilderSession { */ @Override public ModelBuilderResult build(ModelBuilderRequest request) throws ModelBuilderException { - // Create or derive a session based on the request - ModelBuilderSessionState session; - if (mainSession == null) { - mainSession = new ModelBuilderSessionState(request); - session = mainSession; - } else { - session = mainSession.derive( - request, new DefaultModelBuilderResult(ProblemCollector.create(mainSession.session))); - } - // Build the request - if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_PROJECT) { - // build the build poms - session.buildBuildPom(); - } else { - // simply build the effective model - session.buildEffectiveModel(new LinkedHashSet<>()); + RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(request.getSession(), request); + try { + // Create or derive a session based on the request + ModelBuilderSessionState session; + if (mainSession == null) { + mainSession = new ModelBuilderSessionState(request); + session = mainSession; + } else { + session = mainSession.derive( + request, + new DefaultModelBuilderResult(request, ProblemCollector.create(mainSession.session))); + } + // Build the request + if (request.getRequestType() == ModelBuilderRequest.RequestType.BUILD_PROJECT) { + // build the build poms + session.buildBuildPom(); + } else { + // simply build the effective model + session.buildEffectiveModel(new LinkedHashSet<>()); + } + return session.result; + } finally { + RequestTraceHelper.exit(trace); } - return session.result; } } @@ -262,7 +271,7 @@ protected class ModelBuilderSessionState implements ModelProblemCollector { this( request.getSession(), request, - new DefaultModelBuilderResult(ProblemCollector.create(request.getSession())), + new DefaultModelBuilderResult(request, ProblemCollector.create(request.getSession())), request.getSession() .getData() .computeIfAbsent(SessionData.key(ModelCache.class), modelCacheFactory::newInstance), @@ -304,7 +313,7 @@ private ModelBuilderSessionState( } ModelBuilderSessionState derive(ModelSource source) { - return derive(source, new DefaultModelBuilderResult(ProblemCollector.create(session))); + return derive(source, new DefaultModelBuilderResult(request, ProblemCollector.create(session))); } ModelBuilderSessionState derive(ModelSource source, DefaultModelBuilderResult result) { @@ -315,7 +324,7 @@ ModelBuilderSessionState derive(ModelSource source, DefaultModelBuilderResult re * Creates a new session, sharing cached datas and propagating errors. */ ModelBuilderSessionState derive(ModelBuilderRequest request) { - return derive(request, new DefaultModelBuilderResult(ProblemCollector.create(session))); + return derive(request, new DefaultModelBuilderResult(request, ProblemCollector.create(session))); } ModelBuilderSessionState derive(ModelBuilderRequest request, DefaultModelBuilderResult result) { @@ -640,16 +649,21 @@ private void buildBuildPom() throws ModelBuilderException { // This is done through the phased executor var allResults = results(result).toList(); List exceptions = new CopyOnWriteArrayList<>(); + InternalSession session = InternalSession.from(this.session); + RequestTrace trace = session.getCurrentTrace(); try (PhasingExecutor executor = createExecutor()) { for (DefaultModelBuilderResult r : allResults) { executor.execute(() -> { ModelBuilderSessionState mbs = derive(r.getSource(), r); + session.setCurrentTrace(trace); try { mbs.buildEffectiveModel(new LinkedHashSet<>()); } catch (ModelBuilderException e) { // gathered with problem collector } catch (RuntimeException t) { exceptions.add(t); + } finally { + session.setCurrentTrace(null); } }); } @@ -683,7 +697,7 @@ private void loadFromRoot(Path root, Path top) { try (PhasingExecutor executor = createExecutor()) { DefaultModelBuilderResult r = Objects.equals(top, root) ? result - : new DefaultModelBuilderResult(ProblemCollector.create(session)); + : new DefaultModelBuilderResult(request, ProblemCollector.create(session)); loadFilePom(executor, top, root, Set.of(), r); } if (result.getFileModel() == null && !Objects.equals(top, root)) { @@ -757,12 +771,21 @@ private void loadFilePom( DefaultModelBuilderResult cr = Objects.equals(top, subprojectFile) ? result - : new DefaultModelBuilderResult(ProblemCollector.create(session)); + : new DefaultModelBuilderResult(request, ProblemCollector.create(session)); if (request.isRecursive()) { r.getChildren().add(cr); } - executor.execute(() -> loadFilePom(executor, top, subprojectFile, concat(parents, pom), cr)); + InternalSession session = InternalSession.from(this.session); + RequestTrace trace = session.getCurrentTrace(); + executor.execute(() -> { + session.setCurrentTrace(trace); + try { + loadFilePom(executor, top, subprojectFile, concat(parents, pom), cr); + } finally { + session.setCurrentTrace(null); + } + }); } } catch (ModelBuilderException e) { // gathered with problem collector @@ -1905,12 +1928,17 @@ private static List getSubprojects(Model activated) { @Override public Model buildRawModel(ModelBuilderRequest request) throws ModelBuilderException { - ModelBuilderSessionState build = new ModelBuilderSessionState(request); - Model model = build.readRawModel(); - if (build.hasErrors()) { - throw build.newModelBuilderException(); + RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(request.getSession(), request); + try { + ModelBuilderSessionState build = new ModelBuilderSessionState(request); + Model model = build.readRawModel(); + if (build.hasErrors()) { + throw build.newModelBuilderException(); + } + return model; + } finally { + RequestTraceHelper.exit(trace); } - return model; } static String getGroupId(Model model) { diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilderResult.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilderResult.java index 89f4b7ae3811..5f87b7d77996 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilderResult.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/model/DefaultModelBuilderResult.java @@ -25,6 +25,7 @@ import org.apache.maven.api.model.Model; import org.apache.maven.api.model.Profile; +import org.apache.maven.api.services.ModelBuilderRequest; import org.apache.maven.api.services.ModelBuilderResult; import org.apache.maven.api.services.ModelProblem; import org.apache.maven.api.services.ModelSource; @@ -34,6 +35,7 @@ * Collects the output of the model builder. */ class DefaultModelBuilderResult implements ModelBuilderResult { + private ModelBuilderRequest request; private ModelSource source; private Model fileModel; private Model rawModel; @@ -44,10 +46,16 @@ class DefaultModelBuilderResult implements ModelBuilderResult { private final ProblemCollector problemCollector; private final List children = new ArrayList<>(); - DefaultModelBuilderResult(ProblemCollector problemCollector) { + DefaultModelBuilderResult(ModelBuilderRequest request, ProblemCollector problemCollector) { + this.request = request; this.problemCollector = problemCollector; } + @Override + public ModelBuilderRequest getRequest() { + return request; + } + @Override public ModelSource getSource() { return source; diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/DefaultArtifactDescriptorReader.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/DefaultArtifactDescriptorReader.java index 3343c4678fb7..f3928494aab1 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/DefaultArtifactDescriptorReader.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/DefaultArtifactDescriptorReader.java @@ -38,6 +38,7 @@ import org.apache.maven.api.services.ProblemCollector; import org.apache.maven.api.services.model.ModelResolverException; import org.apache.maven.impl.InternalSession; +import org.apache.maven.impl.RequestTraceHelper; import org.apache.maven.impl.model.ModelProblemUtils; import org.eclipse.aether.RepositoryEvent; import org.eclipse.aether.RepositoryEvent.EventType; @@ -195,6 +196,7 @@ private Model loadPom( pomArtifact.getGroupId() + ":" + pomArtifact.getArtifactId() + ":" + pomArtifact.getVersion(); ModelBuilderRequest modelRequest = ModelBuilderRequest.builder() .session(iSession) + .trace(RequestTraceHelper.toMaven(request.getRequestContext(), trace)) .requestType(ModelBuilderRequest.RequestType.CONSUMER_DEPENDENCY) .source(ModelSource.fromPath(pomArtifact.getPath(), gav)) // This merge is on purpose because otherwise user properties would override model diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/RequestTraceHelper.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/RequestTraceHelper.java deleted file mode 100644 index 3ee54da7aa9f..000000000000 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/resolver/RequestTraceHelper.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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.resolver; - -import java.util.stream.Collectors; - -import org.eclipse.aether.RequestTrace; -import org.eclipse.aether.collection.CollectRequest; -import org.eclipse.aether.collection.CollectStepData; -import org.eclipse.aether.resolution.ArtifactDescriptorRequest; -import org.eclipse.aether.resolution.ArtifactRequest; -import org.eclipse.aether.resolution.DependencyRequest; - -/** - * Helper class to manage {@link RequestTrace} for better error logging. - */ -public final class RequestTraceHelper { - - /** - * Method that creates some informational string based on passed in {@link RequestTrace}. The contents of request - * trace can literally be anything, but this class tries to cover "most common" cases that are happening in Maven. - */ - public static String interpretTrace(boolean detailed, RequestTrace requestTrace) { - while (requestTrace != null) { - Object data = requestTrace.getData(); - if (data instanceof DependencyRequest request) { - return "dependency resolution for " + request; - } else if (data instanceof CollectRequest request) { - return "dependency collection for " + request; - } else if (data instanceof CollectStepData stepData) { - String msg = "dependency collection step for " + stepData.getContext(); - if (detailed) { - msg += ". Path to offending node from root:\n"; - msg += stepData.getPath().stream() - .map(n -> " -> " + n.toString()) - .collect(Collectors.joining("\n")); - msg += "\n => " + stepData.getNode(); - } - return msg; - } else if (data instanceof ArtifactDescriptorRequest request) { - return "artifact descriptor request for " + request.getArtifact(); - } else if (data instanceof ArtifactRequest request) { - return "artifact request for " + request.getArtifact(); - // TODO: this class is not reachable here! - // } else if (data instanceof org.apache.maven.model.Plugin plugin) { - // return "plugin " + plugin.getId(); - } - requestTrace = requestTrace.getParent(); - } - - return "n/a"; - } -} 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 new file mode 100644 index 000000000000..33294298ad1f --- /dev/null +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/RequestTraceHelperTest.java @@ -0,0 +1,87 @@ +/* + * 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 org.apache.maven.api.services.Request; +import org.eclipse.aether.RequestTrace; +import org.eclipse.aether.resolution.ArtifactRequest; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +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.verify; +import static org.mockito.Mockito.when; + +class RequestTraceHelperTest { + + @Test + void testEnterWithRequestData() { + InternalSession session = mock(InternalSession.class); + Request request = mock(Request.class); + org.apache.maven.api.services.RequestTrace existingTrace = + new org.apache.maven.api.services.RequestTrace(null, "test"); + + when(request.getTrace()).thenReturn(existingTrace); + + RequestTraceHelper.ResolverTrace result = RequestTraceHelper.enter(session, request); + + assertNotNull(result); + assertEquals(existingTrace, result.mvnTrace()); + verify(session).setCurrentTrace(existingTrace); + } + + @Test + void testInterpretTraceWithArtifactRequest() { + ArtifactRequest artifactRequest = mock(ArtifactRequest.class); + RequestTrace trace = RequestTrace.newChild(null, artifactRequest); + + String result = RequestTraceHelper.interpretTrace(false, trace); + + assertTrue(result.startsWith("artifact request for ")); + } + + @Test + void testToMavenWithNullTrace() { + assertNull(RequestTraceHelper.toMaven("test", null)); + } + + @Test + void testToResolverWithNullTrace() { + assertNull(RequestTraceHelper.toResolver(null)); + } + + @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 currentTrace = + new org.apache.maven.api.services.RequestTrace(parentTrace, "current"); + + RequestTraceHelper.ResolverTrace resolverTrace = + new RequestTraceHelper.ResolverTrace(session, "test", null, currentTrace); + + RequestTraceHelper.exit(resolverTrace); + + verify(session).setCurrentTrace(parentTrace); + } +} diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelBuilderResultTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelBuilderResultTest.java new file mode 100644 index 000000000000..beea6fa88b24 --- /dev/null +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/model/DefaultModelBuilderResultTest.java @@ -0,0 +1,109 @@ +/* + * 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.model; + +import org.apache.maven.api.model.Model; +import org.apache.maven.api.services.BuilderProblem; +import org.apache.maven.api.services.ModelBuilderRequest; +import org.apache.maven.api.services.ModelProblem; +import org.apache.maven.api.services.ModelSource; +import org.apache.maven.api.services.ProblemCollector; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.mock; + +class DefaultModelBuilderResultTest { + + private ModelBuilderRequest request; + private ProblemCollector problemCollector; + private DefaultModelBuilderResult result; + private ModelSource source; + private Model fileModel; + private Model rawModel; + private Model effectiveModel; + + @BeforeEach + void setUp() { + request = mock(ModelBuilderRequest.class); + problemCollector = ProblemCollector.create(10); + result = new DefaultModelBuilderResult(request, problemCollector); + + source = mock(ModelSource.class); + fileModel = mock(Model.class); + rawModel = mock(Model.class); + effectiveModel = mock(Model.class); + } + + @Test + void testModelLifecycle() { + // Test initial state + assertNull(result.getSource()); + assertNull(result.getFileModel()); + assertNull(result.getRawModel()); + assertNull(result.getEffectiveModel()); + assertEquals(0L, result.getProblemCollector().problems().count()); + + // Set and verify source + result.setSource(source); + assertSame(source, result.getSource()); + + // Set and verify file model + result.setFileModel(fileModel); + assertSame(fileModel, result.getFileModel()); + + // Set and verify raw model + result.setRawModel(rawModel); + assertSame(rawModel, result.getRawModel()); + + // Set and verify effective model + result.setEffectiveModel(effectiveModel); + assertSame(effectiveModel, result.getEffectiveModel()); + } + + @Test + void testProblemCollection() { + ModelProblem problem = mock(ModelProblem.class); + Mockito.when(problem.getSeverity()).thenReturn(BuilderProblem.Severity.ERROR); + problemCollector.reportProblem(problem); + + assertEquals(1, result.getProblemCollector().problems().count()); + assertSame(problem, result.getProblemCollector().problems().findFirst().get()); + } + + @Test + void testChildrenManagement() { + DefaultModelBuilderResult child1 = new DefaultModelBuilderResult(request, problemCollector); + DefaultModelBuilderResult child2 = new DefaultModelBuilderResult(request, problemCollector); + + result.getChildren().add(child1); + result.getChildren().add(child2); + + assertEquals(2, result.getChildren().size()); + assertTrue(result.getChildren().contains(child1)); + assertTrue(result.getChildren().contains(child2)); + } + + @Test + void testRequestAssociation() { + assertSame(request, result.getRequest()); + } +}