[MNG-8151] Merge DependencyCollector into DependencyResolver

This commit is contained in:
Guillaume Nodet 2024-06-06 16:24:08 +02:00
parent 7161cfa189
commit 5d139bdd12
18 changed files with 640 additions and 856 deletions

View File

@ -33,7 +33,7 @@
* Represents a dependency node within a Maven project's dependency collector. * Represents a dependency node within a Maven project's dependency collector.
* *
* @since 4.0.0 * @since 4.0.0
* @see org.apache.maven.api.services.DependencyCollectorResult#getRoot() * @see org.apache.maven.api.services.DependencyResolverResult#getRoot()
*/ */
@Experimental @Experimental
@Immutable @Immutable

View File

@ -496,25 +496,25 @@ Artifact createArtifact(
boolean isVersionSnapshot(@Nonnull String version); boolean isVersionSnapshot(@Nonnull String version);
/** /**
* Shortcut for {@code getService(DependencyCollector.class).collect(...)} * Shortcut for {@code getService(DependencyResolver.class).collect(...)}
* *
* @param artifact artifact for which to get the dependencies, including transitive ones * @param artifact artifact for which to get the dependencies, including transitive ones
* @return root node of the dependency graph for the given artifact * @return root node of the dependency graph for the given artifact
* *
* @see org.apache.maven.api.services.DependencyCollector#collect(Session, Artifact) * @see org.apache.maven.api.services.DependencyResolver#collect(Session, Artifact)
* @throws org.apache.maven.api.services.DependencyCollectorException if the dependency collection failed * @throws org.apache.maven.api.services.DependencyResolverException if the dependency collection failed
*/ */
@Nonnull @Nonnull
Node collectDependencies(@Nonnull Artifact artifact); Node collectDependencies(@Nonnull Artifact artifact);
/** /**
* Shortcut for {@code getService(DependencyCollector.class).collect(...)} * Shortcut for {@code getService(DependencyResolver.class).collect(...)}
* *
* @param project project for which to get the dependencies, including transitive ones * @param project project for which to get the dependencies, including transitive ones
* @return root node of the dependency graph for the given project * @return root node of the dependency graph for the given project
* *
* @see org.apache.maven.api.services.DependencyCollector#collect(Session, Project) * @see org.apache.maven.api.services.DependencyResolver#collect(Session, Project)
* @throws org.apache.maven.api.services.DependencyCollectorException if the dependency collection failed * @throws org.apache.maven.api.services.DependencyResolverException if the dependency collection failed
*/ */
@Nonnull @Nonnull
Node collectDependencies(@Nonnull Project project); Node collectDependencies(@Nonnull Project project);
@ -524,13 +524,13 @@ Artifact createArtifact(
* only concerned about determining the coordinates of the transitive dependencies and does not actually resolve the * only concerned about determining the coordinates of the transitive dependencies and does not actually resolve the
* artifact files. * artifact files.
* <p> * <p>
* Shortcut for {@code getService(DependencyCollector.class).resolve(...)} * Shortcut for {@code getService(DependencyResolver.class).resolve(...)}
* *
* @param dependency dependency for which to get transitive dependencies * @param dependency dependency for which to get transitive dependencies
* @return root node of the dependency graph for the given artifact * @return root node of the dependency graph for the given artifact
* *
* @see org.apache.maven.api.services.DependencyCollector#collect(Session, DependencyCoordinate) * @see org.apache.maven.api.services.DependencyResolver#collect(Session, DependencyCoordinate)
* @throws org.apache.maven.api.services.DependencyCollectorException if the dependency collection failed * @throws org.apache.maven.api.services.DependencyResolverException if the dependency collection failed
*/ */
@Nonnull @Nonnull
Node collectDependencies(@Nonnull DependencyCoordinate dependency); Node collectDependencies(@Nonnull DependencyCoordinate dependency);

View File

@ -1,107 +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.api.services;
import org.apache.maven.api.Artifact;
import org.apache.maven.api.DependencyCoordinate;
import org.apache.maven.api.Project;
import org.apache.maven.api.Service;
import org.apache.maven.api.Session;
import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Nonnull;
/**
* The DependencyCollector service can be used to collect dependencies
* for a given artifact and builds a graph of them.
* The dependencies collection mechanism will not download any artifacts,
* and only the pom files will be downloaded.
*
* @since 4.0.0
*/
@Experimental
public interface DependencyCollector extends Service {
/**
* Collects the transitive dependencies and builds a dependency graph.
* Note that this operation is only concerned about determining the coordinates of the
* transitive dependencies and does not actually resolve the artifact files.
*
* @param request the dependency collection request, must not be {@code null}
* @return the collection result, never {@code null}
* @throws DependencyCollectorException if the dependency tree could not be built
* @throws IllegalArgumentException if an argument is null or invalid
*
* @see DependencyCollector#collect(Session, Project)
* @see DependencyCollector#collect(Session, DependencyCoordinate)
* @see DependencyCollector#collect(Session, Artifact)
*/
@Nonnull
DependencyCollectorResult collect(@Nonnull DependencyCollectorRequest request);
/**
* Collects the transitive dependencies of some artifacts and builds a dependency graph. Note that this operation is
* only concerned about determining the coordinates of the transitive dependencies and does not actually resolve the
* artifact files.
*
* @param session the {@link Session}, must not be {@code null}
* @param root the Maven Dependency, must not be {@code null}
* @return the collection result, never {@code null}
* @throws DependencyCollectorException if the dependency tree could not be built
* @throws IllegalArgumentException if an argument is null or invalid
* @see #collect(DependencyCollectorRequest)
*/
@Nonnull
default DependencyCollectorResult collect(@Nonnull Session session, @Nonnull DependencyCoordinate root) {
return collect(DependencyCollectorRequest.build(session, root));
}
/**
* Collects the transitive dependencies of some artifacts and builds a dependency graph. Note that this operation is
* only concerned about determining the coordinates of the transitive dependencies and does not actually resolve the
* artifact files.
*
* @param session the {@link Session}, must not be {@code null}
* @param project the {@link Project}, must not be {@code null}
* @return the collection result, never {@code null}
* @throws DependencyCollectorException if the dependency tree could not be built
* @throws IllegalArgumentException if an argument is null or invalid
* @see #collect(DependencyCollectorRequest)
*/
@Nonnull
default DependencyCollectorResult collect(@Nonnull Session session, @Nonnull Project project) {
return collect(DependencyCollectorRequest.build(session, project));
}
/**
* Collects the transitive dependencies of some artifacts and builds a dependency graph. Note that this operation is
* only concerned about determining the coordinates of the transitive dependencies and does not actually resolve the
* artifact files.
*
* @param session the {@link Session}, must not be {@code null}
* @param artifact the {@link Artifact}, must not be {@code null}
* @return the collection result, never {@code null}
* @throws DependencyCollectorException if the dependency tree could not be built
* @throws IllegalArgumentException if an argument is null or invalid
* @see #collect(DependencyCollectorRequest)
*/
@Nonnull
default DependencyCollectorResult collect(@Nonnull Session session, @Nonnull Artifact artifact) {
return collect(DependencyCollectorRequest.build(session, artifact));
}
}

View File

@ -1,43 +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.api.services;
import org.apache.maven.api.annotations.Experimental;
/**
* Thrown in case of bad artifact descriptors, version ranges or other
* issues encountered during calculation of the dependency graph.
*
* @since 4.0.0
*/
@Experimental
public class DependencyCollectorException extends MavenException {
/**
*
*/
private static final long serialVersionUID = -3134726259840210686L;
/**
* @param message the message you would give for the exception
* @param cause the cause which is related to the message
*/
public DependencyCollectorException(String message, Throwable cause) {
super(message, cause);
}
}

View File

@ -1,310 +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.api.services;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.apache.maven.api.Artifact;
import org.apache.maven.api.DependencyCoordinate;
import org.apache.maven.api.Project;
import org.apache.maven.api.Session;
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.NotThreadSafe;
import org.apache.maven.api.annotations.Nullable;
import static org.apache.maven.api.services.BaseRequest.nonNull;
/**
* A request to collect the transitive dependencies and to build a dependency graph from them. There are three ways to
* create a dependency graph. First, only the root dependency can be given. Second, a root dependency and direct
* dependencies can be specified in which case the specified direct dependencies are merged with the direct dependencies
* retrieved from the artifact descriptor of the root dependency. And last, only direct dependencies can be specified in
* which case the root node of the resulting graph has no associated dependency.
*
* @since 4.0.0
* @see DependencyCollector#collect(DependencyCollectorRequest)
*/
@Experimental
@Immutable
public interface DependencyCollectorRequest {
@Nonnull
Session getSession();
Optional<Project> getProject();
@Nonnull
Optional<Artifact> getRootArtifact();
@Nonnull
Optional<DependencyCoordinate> getRoot();
@Nonnull
Collection<DependencyCoordinate> getDependencies();
@Nonnull
Collection<DependencyCoordinate> getManagedDependencies();
boolean getVerbose();
@Nonnull
static DependencyCollectorRequest build(@Nonnull Session session, Artifact root) {
return builder()
.session(nonNull(session, "session cannot be null"))
.rootArtifact(nonNull(root, "root cannot be null"))
.build();
}
@Nonnull
static DependencyCollectorRequest build(@Nonnull Session session, @Nonnull DependencyCoordinate root) {
return builder()
.session(nonNull(session, "session cannot be null"))
.root(nonNull(root, "root cannot be null"))
.build();
}
@Nonnull
static DependencyCollectorRequest build(@Nonnull Session session, @Nonnull Project project) {
return builder()
.session(nonNull(session, "session cannot be null"))
.project(nonNull(project, "project cannot be null"))
.build();
}
@Nonnull
static DependencyCollectorRequestBuilder builder() {
return new DependencyCollectorRequestBuilder();
}
@NotThreadSafe
class DependencyCollectorRequestBuilder {
Session session;
Project project;
Artifact rootArtifact;
DependencyCoordinate root;
List<DependencyCoordinate> dependencies = Collections.emptyList();
List<DependencyCoordinate> managedDependencies = Collections.emptyList();
boolean verbose;
DependencyCollectorRequestBuilder() {}
@Nonnull
public DependencyCollectorRequestBuilder session(@Nonnull Session session) {
this.session = session;
return this;
}
@Nonnull
public DependencyCollectorRequestBuilder project(@Nullable Project project) {
this.project = project;
return this;
}
/**
* Sets the root artifact for the dependency graph.
* This must not be confused with {@link #root(DependencyCoordinate)}: The root <em>dependency</em>, like any
* other specified dependency, will be subject to dependency collection/resolution, i.e. should have an artifact
* descriptor and a corresponding artifact file. The root <em>artifact</em> on the other hand is only used
* as a label for the root node of the graph in case no root dependency was specified. As such, the configured
* root artifact is ignored if {@link #root(DependencyCoordinate)} has been set.
*
* @param rootArtifact the root artifact for the dependency graph, may be {@code null}
* @return this request for chaining, never {@code null}
*/
@Nonnull
public DependencyCollectorRequestBuilder rootArtifact(@Nullable Artifact rootArtifact) {
this.rootArtifact = rootArtifact;
return this;
}
/**
* @param root The root dependency
* @return this request for chaining, never {@code null}
*/
@Nonnull
public DependencyCollectorRequestBuilder root(@Nonnull DependencyCoordinate root) {
this.root = root;
return this;
}
/**
* Sets the direct dependencies. If both a root dependency and direct dependencies are given in the request, the
* direct dependencies from the request will be merged with the direct dependencies from the root dependency's
* artifact descriptor, giving higher priority to the dependencies from the request.
*
* @param dependencies the direct dependencies, may be {@code null}
* @return this request for chaining, never {@code null}
*/
@Nonnull
public DependencyCollectorRequestBuilder dependencies(@Nullable List<DependencyCoordinate> dependencies) {
this.dependencies = (dependencies != null) ? dependencies : Collections.emptyList();
return this;
}
/**
* Adds the specified direct dependency.
*
* @param dependency the dependency to add, may be {@code null}
* @return this request for chaining, never {@code null}
*/
@Nonnull
public DependencyCollectorRequestBuilder dependency(@Nullable DependencyCoordinate dependency) {
if (dependency != null) {
if (this.dependencies.isEmpty()) {
this.dependencies = new ArrayList<>();
}
this.dependencies.add(dependency);
}
return this;
}
/**
* Sets the dependency management to apply to transitive dependencies. To clarify, this management does not
* apply to
* the direct dependencies of the root node.
*
* @param managedDependencies the dependency management, may be {@code null}
* @return this request for chaining, never {@code null}
*/
@Nonnull
public DependencyCollectorRequestBuilder managedDependencies(
@Nullable List<DependencyCoordinate> managedDependencies) {
this.managedDependencies = (managedDependencies != null) ? managedDependencies : Collections.emptyList();
return this;
}
/**
* Adds the specified managed dependency.
*
* @param managedDependency The managed dependency to add, may be {@code null} in which case the call
* will have no effect.
* @return this request for chaining, never {@code null}
*/
@Nonnull
public DependencyCollectorRequestBuilder managedDependency(@Nullable DependencyCoordinate managedDependency) {
if (managedDependency != null) {
if (this.managedDependencies.isEmpty()) {
this.managedDependencies = new ArrayList<>();
}
this.managedDependencies.add(managedDependency);
}
return this;
}
/**
* Specifies that the collection should be verbose.
*
* @param verbose whether the collection should be verbose or not
* @return this request for chaining, never {@code null}
*/
@Nonnull
public DependencyCollectorRequestBuilder verbose(boolean verbose) {
this.verbose = verbose;
return this;
}
@Nonnull
public DependencyCollectorRequest build() {
return new DefaultDependencyCollectorRequest(
session, project, rootArtifact, root, dependencies, managedDependencies, verbose);
}
static class DefaultDependencyCollectorRequest extends BaseRequest implements DependencyCollectorRequest {
private final Project project;
private final Artifact rootArtifact;
private final DependencyCoordinate root;
private final Collection<DependencyCoordinate> dependencies;
private final Collection<DependencyCoordinate> managedDependencies;
private final boolean verbose;
/**
* Creates a request with the specified properties.
*
* @param session {@link Session}
* @param rootArtifact The root dependency whose transitive dependencies should be collected, may be {@code
* null}.
*/
DefaultDependencyCollectorRequest(
@Nonnull Session session,
@Nullable Project project,
@Nullable Artifact rootArtifact,
@Nullable DependencyCoordinate root,
@Nonnull Collection<DependencyCoordinate> dependencies,
@Nonnull Collection<DependencyCoordinate> managedDependencies,
boolean verbose) {
super(session);
this.project = project;
this.rootArtifact = rootArtifact;
this.root = root;
this.dependencies = unmodifiable(nonNull(dependencies, "dependencies cannot be null"));
this.managedDependencies =
unmodifiable(nonNull(managedDependencies, "managedDependencies cannot be null"));
this.verbose = verbose;
}
@Nonnull
@Override
public Optional<Project> getProject() {
return Optional.ofNullable(project);
}
@Nonnull
@Override
public Optional<Artifact> getRootArtifact() {
return Optional.ofNullable(rootArtifact);
}
@Nonnull
@Override
public Optional<DependencyCoordinate> getRoot() {
return Optional.ofNullable(root);
}
@Nonnull
@Override
public Collection<DependencyCoordinate> getDependencies() {
return dependencies;
}
@Nonnull
@Override
public Collection<DependencyCoordinate> getManagedDependencies() {
return managedDependencies;
}
@Override
public boolean getVerbose() {
return verbose;
}
@Nonnull
@Override
public String toString() {
return getRoot() + " -> " + getDependencies();
}
}
}
}

View File

@ -1,51 +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.api.services;
import java.util.List;
import org.apache.maven.api.Node;
import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.annotations.Nullable;
/**
* The result of a dependency collection request.
*
* @since 4.0.0
* @see DependencyCollector#collect(DependencyCollectorRequest)
*/
@Experimental
public interface DependencyCollectorResult {
/**
* Gets the exceptions that occurred while building the dependency graph.
*
* @return the exceptions that occurred, never {@code null}
*/
@Nonnull
List<Exception> getExceptions();
/**
* Gets the root node of the dependency graph.
*
* @return the root node of the dependency graph or {@code null} if none
*/
@Nullable
Node getRoot();
}

View File

@ -20,6 +20,7 @@
import java.util.List; import java.util.List;
import org.apache.maven.api.Artifact;
import org.apache.maven.api.DependencyCoordinate; import org.apache.maven.api.DependencyCoordinate;
import org.apache.maven.api.Node; import org.apache.maven.api.Node;
import org.apache.maven.api.PathScope; import org.apache.maven.api.PathScope;
@ -35,55 +36,165 @@
@Experimental @Experimental
public interface DependencyResolver extends Service { public interface DependencyResolver extends Service {
/**
* Collects the transitive dependencies of some artifacts and builds a dependency graph. Note that this operation is
* only concerned about determining the coordinates of the transitive dependencies and does not actually resolve the
* artifact files.
*
* @param session the {@link Session}, must not be {@code null}
* @param root the Maven Dependency, must not be {@code null}
* @return the collection result, never {@code null}
* @throws DependencyResolverException if the dependency tree could not be built
* @throws IllegalArgumentException if an argument is null or invalid
* @see #collect(DependencyResolverRequest)
*/
@Nonnull
default DependencyResolverResult collect(@Nonnull Session session, @Nonnull DependencyCoordinate root) {
return collect(DependencyResolverRequest.build(session, DependencyResolverRequest.RequestType.COLLECT, root));
}
/**
* Collects the transitive dependencies of some artifacts and builds a dependency graph. Note that this operation is
* only concerned about determining the coordinates of the transitive dependencies and does not actually resolve the
* artifact files.
*
* @param session the {@link Session}, must not be {@code null}
* @param project the {@link Project}, must not be {@code null}
* @return the collection result, never {@code null}
* @throws DependencyResolverException if the dependency tree could not be built
* @throws IllegalArgumentException if an argument is null or invalid
* @see #collect(DependencyResolverRequest)
*/
@Nonnull
default DependencyResolverResult collect(@Nonnull Session session, @Nonnull Project project) {
return collect(
DependencyResolverRequest.build(session, DependencyResolverRequest.RequestType.COLLECT, project));
}
/**
* Collects the transitive dependencies of some artifacts and builds a dependency graph. Note that this operation is
* only concerned about determining the coordinates of the transitive dependencies and does not actually resolve the
* artifact files.
*
* @param session the {@link Session}, must not be {@code null}
* @param artifact the {@link Artifact}, must not be {@code null}
* @return the collection result, never {@code null}
* @throws DependencyResolverException if the dependency tree could not be built
* @throws IllegalArgumentException if an argument is null or invalid
* @see #collect(DependencyResolverRequest)
*/
@Nonnull
default DependencyResolverResult collect(@Nonnull Session session, @Nonnull Artifact artifact) {
return collect(
DependencyResolverRequest.build(session, DependencyResolverRequest.RequestType.COLLECT, artifact));
}
/**
* Collects the transitive dependencies and builds a dependency graph.
* Note that this operation is only concerned about determining the coordinates of the
* transitive dependencies and does not actually resolve the artifact files.
*
* @param request the dependency collection request, must not be {@code null}
* @return the collection result, never {@code null}
* @throws DependencyResolverException if the dependency tree could not be built
* @throws IllegalArgumentException if an argument is null or invalid
*
* @see DependencyResolver#collect(Session, Project)
* @see DependencyResolver#collect(Session, DependencyCoordinate)
* @see DependencyResolver#collect(Session, Artifact)
*/
@Nonnull
default DependencyResolverResult collect(@Nonnull DependencyResolverRequest request) {
if (request.getRequestType() != DependencyResolverRequest.RequestType.COLLECT) {
throw new IllegalArgumentException("requestType should be COLLECT when calling collect()");
}
return resolve(request);
}
/**
* Flattens a list of nodes.
*
* @param session
* @param node
* @param scope
* @return
* @throws DependencyResolverException
*/
List<Node> flatten(Session session, Node node, PathScope scope) throws DependencyResolverException; List<Node> flatten(Session session, Node node, PathScope scope) throws DependencyResolverException;
@Nonnull
default DependencyResolverResult flatten(@Nonnull Session session, @Nonnull Project project) {
return flatten(
DependencyResolverRequest.build(session, DependencyResolverRequest.RequestType.FLATTEN, project));
}
@Nonnull
default DependencyResolverResult flatten(
@Nonnull Session session, @Nonnull Project project, @Nonnull PathScope scope) {
return flatten(DependencyResolverRequest.build(
session, DependencyResolverRequest.RequestType.FLATTEN, project, scope));
}
@Nonnull
default DependencyResolverResult flatten(@Nonnull DependencyResolverRequest request) {
if (request.getRequestType() != DependencyResolverRequest.RequestType.FLATTEN) {
throw new IllegalArgumentException("requestType should be FLATTEN when calling flatten()");
}
return resolve(request);
}
@Nonnull
default DependencyResolverResult resolve(@Nonnull Session session, @Nonnull Project project) {
return resolve(
DependencyResolverRequest.build(session, DependencyResolverRequest.RequestType.RESOLVE, project));
}
@Nonnull
default DependencyResolverResult resolve(
@Nonnull Session session, @Nonnull Project project, @Nonnull PathScope scope) {
return resolve(DependencyResolverRequest.build(
session, DependencyResolverRequest.RequestType.RESOLVE, project, scope));
}
@Nonnull
default DependencyResolverResult resolve(@Nonnull Session session, @Nonnull DependencyCoordinate dependency) {
return resolve(
DependencyResolverRequest.build(session, DependencyResolverRequest.RequestType.RESOLVE, dependency));
}
@Nonnull
default DependencyResolverResult resolve(
@Nonnull Session session, @Nonnull DependencyCoordinate dependency, @Nonnull PathScope scope) {
return resolve(DependencyResolverRequest.build(
session, DependencyResolverRequest.RequestType.RESOLVE, dependency, scope));
}
@Nonnull
default DependencyResolverResult resolve(
@Nonnull Session session, @Nonnull List<DependencyCoordinate> dependencies) {
return resolve(
DependencyResolverRequest.build(session, DependencyResolverRequest.RequestType.RESOLVE, dependencies));
}
@Nonnull
default DependencyResolverResult resolve(
@Nonnull Session session, @Nonnull List<DependencyCoordinate> dependencies, @Nonnull PathScope scope) {
return resolve(DependencyResolverRequest.build(
session, DependencyResolverRequest.RequestType.RESOLVE, dependencies, scope));
}
/** /**
* This method collects, flattens and resolves the dependencies. * This method collects, flattens and resolves the dependencies.
* *
* @param request the request to resolve * @param request the request to resolve
* @return the result of the resolution * @return the result of the resolution
* @throws DependencyCollectorException
* @throws DependencyResolverException * @throws DependencyResolverException
* @throws ArtifactResolverException * @throws ArtifactResolverException
* *
* @see DependencyCollector#collect(DependencyCollectorRequest) * @see DependencyResolver#collect(DependencyResolverRequest)
* @see #flatten(Session, Node, PathScope) * @see #flatten(Session, Node, PathScope)
* @see ArtifactResolver#resolve(ArtifactResolverRequest) * @see ArtifactResolver#resolve(ArtifactResolverRequest)
*/ */
DependencyResolverResult resolve(DependencyResolverRequest request) DependencyResolverResult resolve(DependencyResolverRequest request)
throws DependencyCollectorException, DependencyResolverException, ArtifactResolverException; throws DependencyResolverException, ArtifactResolverException;
@Nonnull
default DependencyResolverResult resolve(@Nonnull Session session, @Nonnull Project project) {
return resolve(DependencyResolverRequest.build(session, project));
}
@Nonnull
default DependencyResolverResult resolve(
@Nonnull Session session, @Nonnull Project project, @Nonnull PathScope scope) {
return resolve(DependencyResolverRequest.build(session, project, scope));
}
@Nonnull
default DependencyResolverResult resolve(@Nonnull Session session, @Nonnull DependencyCoordinate dependency) {
return resolve(DependencyResolverRequest.build(session, dependency));
}
@Nonnull
default DependencyResolverResult resolve(
@Nonnull Session session, @Nonnull DependencyCoordinate dependency, @Nonnull PathScope scope) {
return resolve(DependencyResolverRequest.build(session, dependency, scope));
}
@Nonnull
default DependencyResolverResult resolve(
@Nonnull Session session, @Nonnull List<DependencyCoordinate> dependencies) {
return resolve(DependencyResolverRequest.build(session, dependencies));
}
@Nonnull
default DependencyResolverResult resolve(
@Nonnull Session session, @Nonnull List<DependencyCoordinate> dependencies, @Nonnull PathScope scope) {
return resolve(DependencyResolverRequest.build(session, dependencies, scope));
}
} }

View File

@ -18,8 +18,11 @@
*/ */
package org.apache.maven.api.services; package org.apache.maven.api.services;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.function.Predicate; import java.util.function.Predicate;
import org.apache.maven.api.Artifact; import org.apache.maven.api.Artifact;
@ -30,14 +33,54 @@
import org.apache.maven.api.Project; import org.apache.maven.api.Project;
import org.apache.maven.api.Session; import org.apache.maven.api.Session;
import org.apache.maven.api.annotations.Experimental; 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.Nonnull;
import org.apache.maven.api.annotations.NotThreadSafe; import org.apache.maven.api.annotations.NotThreadSafe;
import org.apache.maven.api.annotations.Nullable; import org.apache.maven.api.annotations.Nullable;
/**
* A request to collect the transitive dependencies and to build a dependency graph from them. There are three ways to
* create a dependency graph. First, only the root dependency can be given. Second, a root dependency and direct
* dependencies can be specified in which case the specified direct dependencies are merged with the direct dependencies
* retrieved from the artifact descriptor of the root dependency. And last, only direct dependencies can be specified in
* which case the root node of the resulting graph has no associated dependency.
*
* @since 4.0.0
*/
@Experimental @Experimental
public interface DependencyResolverRequest extends DependencyCollectorRequest { @Immutable
public interface DependencyResolverRequest {
enum RequestType {
COLLECT,
FLATTEN,
RESOLVE
}
@Nonnull @Nonnull
Session getSession();
@Nonnull
RequestType getRequestType();
@Nonnull
Optional<Project> getProject();
@Nonnull
Optional<Artifact> getRootArtifact();
@Nonnull
Optional<DependencyCoordinate> getRoot();
@Nonnull
Collection<DependencyCoordinate> getDependencies();
@Nonnull
Collection<DependencyCoordinate> getManagedDependencies();
boolean getVerbose();
@Nullable
PathScope getPathScope(); PathScope getPathScope();
/** /**
@ -48,6 +91,7 @@ public interface DependencyResolverRequest extends DependencyCollectorRequest {
* *
* @return a filter for the types of path (class-path, module-path, ) accepted by the tool * @return a filter for the types of path (class-path, module-path, ) accepted by the tool
*/ */
@Nullable
Predicate<PathType> getPathTypeFilter(); Predicate<PathType> getPathTypeFilter();
@Nonnull @Nonnull
@ -56,119 +100,202 @@ static DependencyResolverRequestBuilder builder() {
} }
@Nonnull @Nonnull
static DependencyResolverRequest build(Session session, Project project) { static DependencyResolverRequest build(Session session, RequestType requestType, Artifact rootArtifact) {
return build(session, project, PathScope.MAIN_RUNTIME); return new DependencyResolverRequestBuilder()
.session(session)
.requestType(requestType)
.rootArtifact(rootArtifact)
.pathScope(PathScope.MAIN_RUNTIME)
.build();
} }
@Nonnull @Nonnull
static DependencyResolverRequest build(Session session, Project project, PathScope scope) { static DependencyResolverRequest build(Session session, RequestType requestType, Project project) {
return build(session, requestType, project, PathScope.MAIN_RUNTIME);
}
@Nonnull
static DependencyResolverRequest build(Session session, RequestType requestType, Project project, PathScope scope) {
return new DependencyResolverRequestBuilder() return new DependencyResolverRequestBuilder()
.session(session) .session(session)
.requestType(requestType)
.project(project) .project(project)
.pathScope(scope) .pathScope(scope)
.build(); .build();
} }
@Nonnull @Nonnull
static DependencyResolverRequest build(Session session, DependencyCoordinate dependency) { static DependencyResolverRequest build(Session session, RequestType requestType, DependencyCoordinate dependency) {
return build(session, dependency, PathScope.MAIN_RUNTIME); return build(session, requestType, dependency, PathScope.MAIN_RUNTIME);
} }
@Nonnull @Nonnull
static DependencyResolverRequest build(Session session, DependencyCoordinate dependency, PathScope scope) { static DependencyResolverRequest build(
Session session, RequestType requestType, DependencyCoordinate dependency, PathScope scope) {
return new DependencyResolverRequestBuilder() return new DependencyResolverRequestBuilder()
.session(session) .session(session)
.requestType(requestType)
.dependency(dependency) .dependency(dependency)
.pathScope(scope) .pathScope(scope)
.build(); .build();
} }
@Nonnull @Nonnull
static DependencyResolverRequest build(Session session, List<DependencyCoordinate> dependencies) { static DependencyResolverRequest build(
return build(session, dependencies, PathScope.MAIN_RUNTIME); Session session, RequestType requestType, List<DependencyCoordinate> dependencies) {
return build(session, requestType, dependencies, PathScope.MAIN_RUNTIME);
} }
@Nonnull @Nonnull
static DependencyResolverRequest build(Session session, List<DependencyCoordinate> dependencies, PathScope scope) { static DependencyResolverRequest build(
Session session, RequestType requestType, List<DependencyCoordinate> dependencies, PathScope scope) {
return new DependencyResolverRequestBuilder() return new DependencyResolverRequestBuilder()
.session(session) .session(session)
.requestType(requestType)
.dependencies(dependencies) .dependencies(dependencies)
.pathScope(scope) .pathScope(scope)
.build(); .build();
} }
@NotThreadSafe @NotThreadSafe
class DependencyResolverRequestBuilder extends DependencyCollectorRequestBuilder { class DependencyResolverRequestBuilder {
PathScope pathScope;
Session session;
RequestType requestType;
Project project;
Artifact rootArtifact;
DependencyCoordinate root;
List<DependencyCoordinate> dependencies = Collections.emptyList();
List<DependencyCoordinate> managedDependencies = Collections.emptyList();
boolean verbose;
PathScope pathScope;
Predicate<PathType> pathTypeFilter; Predicate<PathType> pathTypeFilter;
DependencyResolverRequestBuilder() {}
@Nonnull @Nonnull
@Override
public DependencyResolverRequestBuilder session(@Nonnull Session session) { public DependencyResolverRequestBuilder session(@Nonnull Session session) {
super.session(session); this.session = session;
return this;
}
@Nonnull
public DependencyResolverRequestBuilder requestType(@Nonnull RequestType requestType) {
this.requestType = requestType;
return this; return this;
} }
@Nonnull @Nonnull
@Override
public DependencyResolverRequestBuilder project(@Nullable Project project) { public DependencyResolverRequestBuilder project(@Nullable Project project) {
super.project(project); this.project = project;
return this; return this;
} }
/**
* Sets the root artifact for the dependency graph.
* This must not be confused with {@link #root(DependencyCoordinate)}: The root <em>dependency</em>, like any
* other specified dependency, will be subject to dependency collection/resolution, i.e. should have an artifact
* descriptor and a corresponding artifact file. The root <em>artifact</em> on the other hand is only used
* as a label for the root node of the graph in case no root dependency was specified. As such, the configured
* root artifact is ignored if {@link #root(DependencyCoordinate)} has been set.
*
* @param rootArtifact the root artifact for the dependency graph, may be {@code null}
* @return this request for chaining, never {@code null}
*/
@Nonnull @Nonnull
@Override
public DependencyResolverRequestBuilder rootArtifact(@Nullable Artifact rootArtifact) { public DependencyResolverRequestBuilder rootArtifact(@Nullable Artifact rootArtifact) {
super.rootArtifact(rootArtifact); this.rootArtifact = rootArtifact;
return this; return this;
} }
/**
* @param root The root dependency
* @return this request for chaining, never {@code null}
*/
@Nonnull @Nonnull
@Override public DependencyResolverRequestBuilder root(@Nonnull DependencyCoordinate root) {
public DependencyResolverRequestBuilder root(@Nullable DependencyCoordinate root) { this.root = root;
super.root(root);
return this; return this;
} }
/**
* Sets the direct dependencies. If both a root dependency and direct dependencies are given in the request, the
* direct dependencies from the request will be merged with the direct dependencies from the root dependency's
* artifact descriptor, giving higher priority to the dependencies from the request.
*
* @param dependencies the direct dependencies, may be {@code null}
* @return this request for chaining, never {@code null}
*/
@Nonnull @Nonnull
@Override
public DependencyResolverRequestBuilder dependencies(@Nullable List<DependencyCoordinate> dependencies) { public DependencyResolverRequestBuilder dependencies(@Nullable List<DependencyCoordinate> dependencies) {
super.dependencies(dependencies); this.dependencies = (dependencies != null) ? dependencies : Collections.emptyList();
return this; return this;
} }
/**
* Adds the specified direct dependency.
*
* @param dependency the dependency to add, may be {@code null}
* @return this request for chaining, never {@code null}
*/
@Nonnull @Nonnull
@Override
public DependencyResolverRequestBuilder dependency(@Nullable DependencyCoordinate dependency) { public DependencyResolverRequestBuilder dependency(@Nullable DependencyCoordinate dependency) {
super.dependency(dependency); if (dependency != null) {
if (this.dependencies.isEmpty()) {
this.dependencies = new ArrayList<>();
}
this.dependencies.add(dependency);
}
return this; return this;
} }
/**
* Sets the dependency management to apply to transitive dependencies. To clarify, this management does not
* apply to
* the direct dependencies of the root node.
*
* @param managedDependencies the dependency management, may be {@code null}
* @return this request for chaining, never {@code null}
*/
@Nonnull @Nonnull
@Override
public DependencyResolverRequestBuilder managedDependencies( public DependencyResolverRequestBuilder managedDependencies(
@Nullable List<DependencyCoordinate> managedDependencies) { @Nullable List<DependencyCoordinate> managedDependencies) {
super.managedDependencies(managedDependencies); this.managedDependencies = (managedDependencies != null) ? managedDependencies : Collections.emptyList();
return this; return this;
} }
/**
* Adds the specified managed dependency.
*
* @param managedDependency The managed dependency to add, may be {@code null} in which case the call
* will have no effect.
* @return this request for chaining, never {@code null}
*/
@Nonnull @Nonnull
@Override
public DependencyResolverRequestBuilder managedDependency(@Nullable DependencyCoordinate managedDependency) { public DependencyResolverRequestBuilder managedDependency(@Nullable DependencyCoordinate managedDependency) {
super.managedDependency(managedDependency); if (managedDependency != null) {
if (this.managedDependencies.isEmpty()) {
this.managedDependencies = new ArrayList<>();
}
this.managedDependencies.add(managedDependency);
}
return this; return this;
} }
/**
* Specifies that the collection should be verbose.
*
* @param verbose whether the collection should be verbose or not
* @return this request for chaining, never {@code null}
*/
@Nonnull @Nonnull
@Override
public DependencyResolverRequestBuilder verbose(boolean verbose) { public DependencyResolverRequestBuilder verbose(boolean verbose) {
super.verbose(verbose); this.verbose = verbose;
return this; return this;
} }
@Nonnull @Nonnull
public DependencyResolverRequestBuilder pathScope(@Nonnull PathScope pathScope) { public DependencyResolverRequestBuilder pathScope(@Nullable PathScope pathScope) {
this.pathScope = pathScope; this.pathScope = pathScope;
return this; return this;
} }
@ -200,10 +327,11 @@ public DependencyResolverRequestBuilder pathTypeFilter(@Nonnull Collection<PathT
return pathTypeFilter(desiredTypes::contains); return pathTypeFilter(desiredTypes::contains);
} }
@Override @Nonnull
public DependencyResolverRequest build() { public DependencyResolverRequest build() {
return new DefaultDependencyResolverRequest( return new DefaultDependencyResolverRequest(
session, session,
requestType,
project, project,
rootArtifact, rootArtifact,
root, root,
@ -214,31 +342,92 @@ public DependencyResolverRequest build() {
pathTypeFilter); pathTypeFilter);
} }
static class DefaultDependencyResolverRequest extends DefaultDependencyCollectorRequest static class DefaultDependencyResolverRequest extends BaseRequest implements DependencyResolverRequest {
implements DependencyResolverRequest { private final RequestType requestType;
private final Project project;
private final Artifact rootArtifact;
private final DependencyCoordinate root;
private final Collection<DependencyCoordinate> dependencies;
private final Collection<DependencyCoordinate> managedDependencies;
private final boolean verbose;
private final PathScope pathScope; private final PathScope pathScope;
private final Predicate<PathType> pathTypeFilter; private final Predicate<PathType> pathTypeFilter;
/**
* Creates a request with the specified properties.
*
* @param session {@link Session}
* @param rootArtifact The root dependency whose transitive dependencies should be collected, may be {@code
* null}.
*/
DefaultDependencyResolverRequest( DefaultDependencyResolverRequest(
Session session, @Nonnull Session session,
Project project, @Nonnull RequestType requestType,
Artifact rootArtifact, @Nullable Project project,
DependencyCoordinate root, @Nullable Artifact rootArtifact,
Collection<DependencyCoordinate> dependencies, @Nullable DependencyCoordinate root,
Collection<DependencyCoordinate> managedDependencies, @Nonnull Collection<DependencyCoordinate> dependencies,
@Nonnull Collection<DependencyCoordinate> managedDependencies,
boolean verbose, boolean verbose,
PathScope pathScope, @Nullable PathScope pathScope,
Predicate<PathType> pathTypeFilter) { @Nullable Predicate<PathType> pathTypeFilter) {
super(session, project, rootArtifact, root, dependencies, managedDependencies, verbose); super(session);
this.pathScope = nonNull(pathScope, "pathScope cannot be null"); this.requestType = nonNull(requestType, "requestType cannot be null");
this.project = project;
this.rootArtifact = rootArtifact;
this.root = root;
this.dependencies = unmodifiable(nonNull(dependencies, "dependencies cannot be null"));
this.managedDependencies =
unmodifiable(nonNull(managedDependencies, "managedDependencies cannot be null"));
this.verbose = verbose;
this.pathScope = pathScope;
this.pathTypeFilter = (pathTypeFilter != null) ? pathTypeFilter : (t) -> true; this.pathTypeFilter = (pathTypeFilter != null) ? pathTypeFilter : (t) -> true;
if (verbose) { if (verbose && requestType != RequestType.COLLECT) {
throw new IllegalArgumentException("verbose cannot be true for resolving dependencies"); throw new IllegalArgumentException("verbose cannot only be true when collecting dependencies");
} }
} }
@Nonnull @Nonnull
@Override
public RequestType getRequestType() {
return requestType;
}
@Nonnull
@Override
public Optional<Project> getProject() {
return Optional.ofNullable(project);
}
@Nonnull
@Override
public Optional<Artifact> getRootArtifact() {
return Optional.ofNullable(rootArtifact);
}
@Nonnull
@Override
public Optional<DependencyCoordinate> getRoot() {
return Optional.ofNullable(root);
}
@Nonnull
@Override
public Collection<DependencyCoordinate> getDependencies() {
return dependencies;
}
@Nonnull
@Override
public Collection<DependencyCoordinate> getManagedDependencies() {
return managedDependencies;
}
@Override
public boolean getVerbose() {
return verbose;
}
@Override @Override
public PathScope getPathScope() { public PathScope getPathScope() {
return pathScope; return pathScope;
@ -248,6 +437,12 @@ public PathScope getPathScope() {
public Predicate<PathType> getPathTypeFilter() { public Predicate<PathType> getPathTypeFilter() {
return pathTypeFilter; return pathTypeFilter;
} }
@Nonnull
@Override
public String toString() {
return getRoot() + " -> " + getDependencies();
}
} }
} }
} }

View File

@ -29,9 +29,32 @@
import org.apache.maven.api.PathType; import org.apache.maven.api.PathType;
import org.apache.maven.api.annotations.Experimental; import org.apache.maven.api.annotations.Experimental;
import org.apache.maven.api.annotations.Nonnull; import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.annotations.Nullable;
/**
* The result of a dependency resolution request.
*
* @since 4.0.0
* @see DependencyResolver#resolve(DependencyResolverRequest)
*/
@Experimental @Experimental
public interface DependencyResolverResult extends DependencyCollectorResult { public interface DependencyResolverResult {
/**
* Gets the exceptions that occurred while building the dependency graph.
*
* @return the exceptions that occurred, never {@code null}
*/
@Nonnull
List<Exception> getExceptions();
/**
* Gets the root node of the dependency graph.
*
* @return the root node of the dependency graph or {@code null} if none
*/
@Nullable
Node getRoot();
/** /**
* The ordered list of the flattened dependency nodes. * The ordered list of the flattened dependency nodes.

View File

@ -75,5 +75,5 @@ public interface ProjectBuilderResult {
* @return the result of the dependency resolution for the project * @return the result of the dependency resolution for the project
*/ */
@Nonnull @Nonnull
Optional<DependencyCollectorResult> getDependencyResolverResult(); Optional<DependencyResolverResult> getDependencyResolverResult();
} }

View File

@ -596,39 +596,41 @@ public DependencyCoordinate createDependencyCoordinate(@Nonnull Dependency depen
} }
/** /**
* Shortcut for <code>getService(DependencyCollector.class).collect(...)</code> * Shortcut for <code>getService(DependencyResolver.class).collect(...)</code>
* *
* @throws DependencyCollectorException if the dependency collection failed * @throws DependencyResolverException if the dependency collection failed
* @see DependencyCollector#collect(Session, Artifact) * @see DependencyResolver#collect(Session, Artifact)
*/ */
@Nonnull @Nonnull
@Override @Override
public Node collectDependencies(@Nonnull Artifact artifact) { public Node collectDependencies(@Nonnull Artifact artifact) {
return getService(DependencyCollector.class).collect(this, artifact).getRoot(); return getService(DependencyResolver.class).collect(this, artifact).getRoot();
} }
/** /**
* Shortcut for <code>getService(DependencyCollector.class).collect(...)</code> * Shortcut for <code>getService(DependencyResolver.class).collect(...)</code>
* *
* @throws DependencyCollectorException if the dependency collection failed * @throws DependencyResolverException if the dependency collection failed
* @see DependencyCollector#collect(Session, Project) * @see DependencyResolver#collect(Session, Project)
*/ */
@Nonnull @Nonnull
@Override @Override
public Node collectDependencies(@Nonnull Project project) { public Node collectDependencies(@Nonnull Project project) {
return getService(DependencyCollector.class).collect(this, project).getRoot(); return getService(DependencyResolver.class).collect(this, project).getRoot();
} }
/** /**
* Shortcut for <code>getService(DependencyCollector.class).collect(...)</code> * Shortcut for <code>getService(DependencyResolver.class).collect(...)</code>
* *
* @throws DependencyCollectorException if the dependency collection failed * @throws DependencyResolverException if the dependency collection failed
* @see DependencyCollector#collect(Session, DependencyCoordinate) * @see DependencyResolver#collect(Session, DependencyCoordinate)
*/ */
@Nonnull @Nonnull
@Override @Override
public Node collectDependencies(@Nonnull DependencyCoordinate dependency) { public Node collectDependencies(@Nonnull DependencyCoordinate dependency) {
return getService(DependencyCollector.class).collect(this, dependency).getRoot(); Node root =
getService(DependencyResolver.class).collect(this, dependency).getRoot();
return root.getChildren().iterator().next();
} }
@Nonnull @Nonnull
@ -662,6 +664,7 @@ public Map<PathType, List<Path>> resolveDependencies(
return getService(DependencyResolver.class) return getService(DependencyResolver.class)
.resolve(DependencyResolverRequest.builder() .resolve(DependencyResolverRequest.builder()
.session(this) .session(this)
.requestType(DependencyResolverRequest.RequestType.RESOLVE)
.dependency(dependency) .dependency(dependency)
.pathScope(scope) .pathScope(scope)
.pathTypeFilter(desiredTypes) .pathTypeFilter(desiredTypes)
@ -675,6 +678,7 @@ public Map<PathType, List<Path>> resolveDependencies(
return getService(DependencyResolver.class) return getService(DependencyResolver.class)
.resolve(DependencyResolverRequest.builder() .resolve(DependencyResolverRequest.builder()
.session(this) .session(this)
.requestType(DependencyResolverRequest.RequestType.RESOLVE)
.project(project) .project(project)
.pathScope(scope) .pathScope(scope)
.pathTypeFilter(desiredTypes) .pathTypeFilter(desiredTypes)

View File

@ -1,109 +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.internal.impl;
import java.util.Collection;
import java.util.List;
import org.apache.maven.api.Artifact;
import org.apache.maven.api.DependencyCoordinate;
import org.apache.maven.api.Node;
import org.apache.maven.api.Project;
import org.apache.maven.api.RemoteRepository;
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.di.Named;
import org.apache.maven.api.di.Singleton;
import org.apache.maven.api.services.DependencyCollector;
import org.apache.maven.api.services.DependencyCollectorException;
import org.apache.maven.api.services.DependencyCollectorRequest;
import org.apache.maven.api.services.DependencyCollectorResult;
import org.apache.maven.api.services.ProjectManager;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.collection.CollectResult;
import org.eclipse.aether.collection.DependencyCollectionException;
import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
import org.eclipse.aether.util.graph.transformer.ConflictResolver;
import static org.apache.maven.internal.impl.Utils.nonNull;
@Named
@Singleton
public class DefaultDependencyCollector implements DependencyCollector {
@Nonnull
@Override
public DependencyCollectorResult collect(@Nonnull DependencyCollectorRequest request)
throws DependencyCollectorException, IllegalArgumentException {
nonNull(request, "request");
InternalSession session = InternalSession.from(request.getSession());
Artifact rootArtifact;
DependencyCoordinate root;
Collection<DependencyCoordinate> dependencies;
Collection<DependencyCoordinate> managedDependencies;
List<RemoteRepository> remoteRepositories;
if (request.getProject().isPresent()) {
Project project = request.getProject().get();
rootArtifact = project.getPomArtifact();
root = null;
dependencies = project.getDependencies();
managedDependencies = project.getManagedDependencies();
remoteRepositories = session.getService(ProjectManager.class).getRemoteProjectRepositories(project);
} else {
rootArtifact = request.getRootArtifact().orElse(null);
root = request.getRoot().orElse(null);
dependencies = request.getDependencies();
managedDependencies = request.getManagedDependencies();
remoteRepositories = session.getRemoteRepositories();
}
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));
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 DependencyCollectorResult() {
@Override
public List<Exception> getExceptions() {
return result.getExceptions();
}
@Override
public Node getRoot() {
return session.getNode(result.getRoot(), request.getVerbose());
}
};
} catch (DependencyCollectionException e) {
throw new DependencyCollectorException("Unable to collect dependencies", e);
}
}
}

View File

@ -0,0 +1,182 @@
/*
* 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.internal.impl;
import java.io.IOException;
import java.nio.file.Path;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.apache.maven.api.*;
import org.apache.maven.api.Artifact;
import org.apache.maven.api.ArtifactCoordinate;
import org.apache.maven.api.Dependency;
import org.apache.maven.api.Node;
import org.apache.maven.api.PathType;
import org.apache.maven.api.Session;
import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.di.Named;
import org.apache.maven.api.di.Singleton;
import org.apache.maven.api.services.*;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystemSession;
import org.eclipse.aether.collection.CollectRequest;
import org.eclipse.aether.collection.CollectResult;
import org.eclipse.aether.collection.DependencyCollectionException;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.graph.DependencyNode;
import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
import org.eclipse.aether.util.graph.transformer.ConflictResolver;
import static org.apache.maven.internal.impl.Utils.cast;
import static org.apache.maven.internal.impl.Utils.map;
import static org.apache.maven.internal.impl.Utils.nonNull;
@Named
@Singleton
public class DefaultDependencyResolver implements DependencyResolver {
@Nonnull
@Override
public DependencyResolverResult collect(@Nonnull DependencyResolverRequest request)
throws DependencyResolverException, IllegalArgumentException {
nonNull(request, "request");
InternalSession session = InternalSession.from(request.getSession());
Artifact rootArtifact;
DependencyCoordinate root;
Collection<DependencyCoordinate> dependencies;
Collection<DependencyCoordinate> managedDependencies;
List<RemoteRepository> remoteRepositories;
if (request.getProject().isPresent()) {
Project project = request.getProject().get();
rootArtifact = project.getPomArtifact();
root = null;
dependencies = project.getDependencies();
managedDependencies = project.getManagedDependencies();
remoteRepositories = session.getService(ProjectManager.class).getRemoteProjectRepositories(project);
} else {
rootArtifact = request.getRootArtifact().orElse(null);
root = request.getRoot().orElse(null);
dependencies = request.getDependencies();
managedDependencies = request.getManagedDependencies();
remoteRepositories = session.getRemoteRepositories();
}
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));
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);
}
}
@Override
public List<Node> flatten(Session s, Node node, PathScope scope) throws DependencyResolverException {
InternalSession session = InternalSession.from(s);
DependencyNode root = cast(AbstractNode.class, node, "node").getDependencyNode();
List<DependencyNode> dependencies = session.getRepositorySystem()
.flattenDependencyNodes(session.getSession(), root, getScopeDependencyFilter(scope));
dependencies.remove(root);
return map(dependencies, session::getNode);
}
private static DependencyFilter getScopeDependencyFilter(PathScope scope) {
Set<String> scopes =
scope.dependencyScopes().stream().map(DependencyScope::id).collect(Collectors.toSet());
return (n, p) -> {
org.eclipse.aether.graph.Dependency d = n.getDependency();
return d == null || scopes.contains(d.getScope());
};
}
/**
* Collects, flattens and resolves the dependencies.
*
* @param request the request to resolve
* @return the result of the resolution
*/
@Override
public DependencyResolverResult resolve(DependencyResolverRequest request)
throws DependencyResolverException, DependencyResolverException, ArtifactResolverException {
InternalSession session =
InternalSession.from(nonNull(request, "request").getSession());
DependencyResolverResult result;
DependencyResolverResult collectorResult = collect(request);
if (request.getRequestType() == DependencyResolverRequest.RequestType.COLLECT) {
result = collectorResult;
} else {
List<Node> nodes = flatten(session, collectorResult.getRoot(), request.getPathScope());
List<ArtifactCoordinate> coordinates = nodes.stream()
.map(Node::getDependency)
.filter(Objects::nonNull)
.map(Artifact::toCoordinate)
.collect(Collectors.toList());
Predicate<PathType> 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;
} else {
PathModularizationCache cache = new PathModularizationCache(); // TODO: should be project-wide cache.
DefaultDependencyResolverResult resolverResult = new DefaultDependencyResolverResult(
cache, collectorResult.getExceptions(), collectorResult.getRoot(), nodes.size());
Map<Artifact, Path> artifacts = session.resolveArtifacts(coordinates);
for (Node node : nodes) {
Dependency d = node.getDependency();
Path path = (d != null) ? artifacts.get(d) : null;
try {
resolverResult.addDependency(node, d, filter, path);
} catch (IOException e) {
throw cannotReadModuleInfo(path, e);
}
}
result = resolverResult;
}
}
return result;
}
private static DependencyResolverException cannotReadModuleInfo(final Path path, final IOException cause) {
return new DependencyResolverException("Cannot read module information of " + path, cause);
}
}

View File

@ -94,7 +94,7 @@ class DefaultDependencyResolverResult implements DependencyResolverResult {
/** /**
* Creates an initially empty result. Callers should add path elements by calls * Creates an initially empty result. Callers should add path elements by calls
* to {@link #addDependency(Node, Dependency, Predicate, Path, PathModularizationCache)}. * to {@link #addDependency(Node, Dependency, Predicate, Path)}.
* *
* @param cache cache of module information about each dependency * @param cache cache of module information about each dependency
* @param exceptions the exceptions that occurred while building the dependency graph * @param exceptions the exceptions that occurred while building the dependency graph
@ -201,6 +201,15 @@ void addOutputDirectory(Path main, Path test) throws IOException {
} }
} }
/**
* Adds a dependency node to the result.
*
* @param node the dependency node
*/
void addNode(Node node) {
nodes.add(node);
}
/** /**
* Adds a dependency to the result. This method populates the {@link #nodes}, {@link #paths}, * Adds a dependency to the result. This method populates the {@link #nodes}, {@link #paths},
* {@link #dispatchedPaths} and {@link #dependencies} collections with the given arguments. * {@link #dispatchedPaths} and {@link #dependencies} collections with the given arguments.

View File

@ -85,7 +85,7 @@ public static Session createSession() {
injector.bindImplicit(DefaultArtifactInstaller.class); injector.bindImplicit(DefaultArtifactInstaller.class);
injector.bindImplicit(DefaultArtifactResolver.class); injector.bindImplicit(DefaultArtifactResolver.class);
injector.bindImplicit(DefaultChecksumAlgorithmService.class); injector.bindImplicit(DefaultChecksumAlgorithmService.class);
injector.bindImplicit(DefaultDependencyCollector.class); injector.bindImplicit(DefaultDependencyResolver.class);
injector.bindImplicit(DefaultDependencyCoordinateFactory.class); injector.bindImplicit(DefaultDependencyCoordinateFactory.class);
injector.bindImplicit(DefaultLocalRepositoryManager.class); injector.bindImplicit(DefaultLocalRepositoryManager.class);
injector.bindImplicit(DefaultMessageBuilderFactory.class); injector.bindImplicit(DefaultMessageBuilderFactory.class);

View File

@ -1,110 +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.internal.impl;
import javax.inject.Named;
import javax.inject.Singleton;
import java.io.IOException;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.apache.maven.api.*;
import org.apache.maven.api.Artifact;
import org.apache.maven.api.ArtifactCoordinate;
import org.apache.maven.api.Dependency;
import org.apache.maven.api.Node;
import org.apache.maven.api.PathType;
import org.apache.maven.api.Session;
import org.apache.maven.api.services.*;
import org.eclipse.aether.graph.DependencyFilter;
import org.eclipse.aether.graph.DependencyNode;
import static org.apache.maven.internal.impl.Utils.cast;
import static org.apache.maven.internal.impl.Utils.map;
import static org.apache.maven.internal.impl.Utils.nonNull;
@Named
@Singleton
public class DefaultDependencyResolver implements DependencyResolver {
@Override
public List<Node> flatten(Session s, Node node, PathScope scope) throws DependencyResolverException {
InternalSession session = InternalSession.from(s);
DependencyNode root = cast(AbstractNode.class, node, "node").getDependencyNode();
List<DependencyNode> dependencies = session.getRepositorySystem()
.flattenDependencyNodes(session.getSession(), root, getScopeDependencyFilter(scope));
dependencies.remove(root);
return map(dependencies, session::getNode);
}
private static DependencyFilter getScopeDependencyFilter(PathScope scope) {
Set<String> scopes =
scope.dependencyScopes().stream().map(DependencyScope::id).collect(Collectors.toSet());
return (n, p) -> {
org.eclipse.aether.graph.Dependency d = n.getDependency();
return d == null || scopes.contains(d.getScope());
};
}
/**
* Collects, flattens and resolves the dependencies.
*
* @param request the request to resolve
* @return the result of the resolution
*/
@Override
public DependencyResolverResult resolve(DependencyResolverRequest request)
throws DependencyCollectorException, DependencyResolverException, ArtifactResolverException {
InternalSession session =
InternalSession.from(nonNull(request, "request").getSession());
Predicate<PathType> filter = request.getPathTypeFilter();
PathModularizationCache cache = new PathModularizationCache(); // TODO: should be project-wide cache.
DependencyCollectorResult collectorResult =
session.getService(DependencyCollector.class).collect(request);
List<Node> nodes = flatten(session, collectorResult.getRoot(), request.getPathScope());
List<ArtifactCoordinate> coordinates = nodes.stream()
.map(Node::getDependency)
.filter(Objects::nonNull)
.map(Artifact::toCoordinate)
.collect(Collectors.toList());
Map<Artifact, Path> artifacts = session.resolveArtifacts(coordinates);
DefaultDependencyResolverResult result = new DefaultDependencyResolverResult(
cache, collectorResult.getExceptions(), collectorResult.getRoot(), nodes.size());
for (Node node : nodes) {
Dependency d = node.getDependency();
Path path = (d != null) ? artifacts.get(d) : null;
try {
result.addDependency(node, d, filter, path);
} catch (IOException e) {
throw cannotReadModuleInfo(path, e);
}
}
return result;
}
private static DependencyResolverException cannotReadModuleInfo(final Path path, final IOException cause) {
return new DependencyResolverException("Cannot read module information of " + path, cause);
}
}

View File

@ -31,11 +31,10 @@
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
import org.apache.maven.api.Node;
import org.apache.maven.api.Project; import org.apache.maven.api.Project;
import org.apache.maven.api.annotations.Nonnull; import org.apache.maven.api.annotations.Nonnull;
import org.apache.maven.api.services.BuilderProblem; import org.apache.maven.api.services.BuilderProblem;
import org.apache.maven.api.services.DependencyCollectorResult; import org.apache.maven.api.services.DependencyResolverResult;
import org.apache.maven.api.services.ProjectBuilder; import org.apache.maven.api.services.ProjectBuilder;
import org.apache.maven.api.services.ProjectBuilderException; import org.apache.maven.api.services.ProjectBuilderException;
import org.apache.maven.api.services.ProjectBuilderRequest; import org.apache.maven.api.services.ProjectBuilderRequest;
@ -170,19 +169,10 @@ public Severity getSeverity() {
@Nonnull @Nonnull
@Override @Override
public Optional<DependencyCollectorResult> getDependencyResolverResult() { public Optional<DependencyResolverResult> getDependencyResolverResult() {
return Optional.ofNullable(res.getDependencyResolutionResult()) return Optional.ofNullable(res.getDependencyResolutionResult())
.map(r -> new DependencyCollectorResult() { .map(r -> new DefaultDependencyResolverResult(
@Override null, r.getCollectionErrors(), session.getNode(r.getDependencyGraph()), 0));
public List<Exception> getExceptions() {
return r.getCollectionErrors();
}
@Override
public Node getRoot() {
return session.getNode(r.getDependencyGraph());
}
});
} }
}; };
} catch (ProjectBuildingException e) { } catch (ProjectBuildingException e) {

View File

@ -11,8 +11,8 @@ org.apache.maven.internal.impl.DefaultArtifactFactory
org.apache.maven.internal.impl.DefaultArtifactInstaller org.apache.maven.internal.impl.DefaultArtifactInstaller
org.apache.maven.internal.impl.DefaultArtifactResolver org.apache.maven.internal.impl.DefaultArtifactResolver
org.apache.maven.internal.impl.DefaultChecksumAlgorithmService org.apache.maven.internal.impl.DefaultChecksumAlgorithmService
org.apache.maven.internal.impl.DefaultDependencyCollector
org.apache.maven.internal.impl.DefaultDependencyCoordinateFactory org.apache.maven.internal.impl.DefaultDependencyCoordinateFactory
org.apache.maven.internal.impl.DefaultDependencyResolver
org.apache.maven.internal.impl.DefaultLocalRepositoryManager org.apache.maven.internal.impl.DefaultLocalRepositoryManager
org.apache.maven.internal.impl.DefaultMessageBuilderFactory org.apache.maven.internal.impl.DefaultMessageBuilderFactory
org.apache.maven.internal.impl.DefaultModelUrlNormalizer org.apache.maven.internal.impl.DefaultModelUrlNormalizer