View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.internal.impl;
20  
21  import java.io.IOException;
22  import java.nio.file.Path;
23  import java.util.Collection;
24  import java.util.List;
25  import java.util.Objects;
26  import java.util.Set;
27  import java.util.function.Predicate;
28  import java.util.stream.Collectors;
29  
30  import org.apache.maven.api.Artifact;
31  import org.apache.maven.api.ArtifactCoordinates;
32  import org.apache.maven.api.Dependency;
33  import org.apache.maven.api.DependencyCoordinates;
34  import org.apache.maven.api.DependencyScope;
35  import org.apache.maven.api.Node;
36  import org.apache.maven.api.PathScope;
37  import org.apache.maven.api.PathType;
38  import org.apache.maven.api.Project;
39  import org.apache.maven.api.RemoteRepository;
40  import org.apache.maven.api.Session;
41  import org.apache.maven.api.annotations.Nonnull;
42  import org.apache.maven.api.annotations.Nullable;
43  import org.apache.maven.api.di.Named;
44  import org.apache.maven.api.di.Singleton;
45  import org.apache.maven.api.services.ArtifactResolver;
46  import org.apache.maven.api.services.ArtifactResolverException;
47  import org.apache.maven.api.services.ArtifactResolverResult;
48  import org.apache.maven.api.services.DependencyResolver;
49  import org.apache.maven.api.services.DependencyResolverException;
50  import org.apache.maven.api.services.DependencyResolverRequest;
51  import org.apache.maven.api.services.DependencyResolverResult;
52  import org.apache.maven.api.services.ProjectManager;
53  import org.eclipse.aether.DefaultRepositorySystemSession;
54  import org.eclipse.aether.RepositorySystemSession;
55  import org.eclipse.aether.collection.CollectRequest;
56  import org.eclipse.aether.collection.CollectResult;
57  import org.eclipse.aether.collection.DependencyCollectionException;
58  import org.eclipse.aether.graph.DependencyFilter;
59  import org.eclipse.aether.graph.DependencyNode;
60  import org.eclipse.aether.scope.ResolutionScope;
61  import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
62  import org.eclipse.aether.util.graph.transformer.ConflictResolver;
63  
64  import static org.apache.maven.internal.impl.Utils.cast;
65  import static org.apache.maven.internal.impl.Utils.map;
66  import static org.apache.maven.internal.impl.Utils.nonNull;
67  
68  @Named
69  @Singleton
70  public class DefaultDependencyResolver implements DependencyResolver {
71  
72      @Nonnull
73      @Override
74      public DependencyResolverResult collect(@Nonnull DependencyResolverRequest request)
75              throws DependencyResolverException, IllegalArgumentException {
76          nonNull(request, "request");
77          InternalSession session = InternalSession.from(request.getSession());
78  
79          Artifact rootArtifact;
80          DependencyCoordinates root;
81          Collection<DependencyCoordinates> dependencies;
82          Collection<DependencyCoordinates> managedDependencies;
83          List<RemoteRepository> remoteRepositories;
84          if (request.getProject().isPresent()) {
85              Project project = request.getProject().get();
86              rootArtifact = project.getPomArtifact();
87              root = null;
88              dependencies = project.getDependencies();
89              managedDependencies = project.getManagedDependencies();
90              remoteRepositories = request.getRepositories() != null
91                      ? request.getRepositories()
92                      : session.getService(ProjectManager.class).getRemoteProjectRepositories(project);
93          } else {
94              rootArtifact = request.getRootArtifact().orElse(null);
95              root = request.getRoot().orElse(null);
96              dependencies = request.getDependencies();
97              managedDependencies = request.getManagedDependencies();
98              remoteRepositories =
99                      request.getRepositories() != null ? request.getRepositories() : session.getRemoteRepositories();
100         }
101         ResolutionScope resolutionScope = null;
102         if (request.getPathScope() != null) {
103             resolutionScope = session.getSession()
104                     .getScopeManager()
105                     .getResolutionScope(request.getPathScope().id())
106                     .orElseThrow();
107         }
108         CollectRequest collectRequest = new CollectRequest()
109                 .setRootArtifact(rootArtifact != null ? session.toArtifact(rootArtifact) : null)
110                 .setRoot(root != null ? session.toDependency(root, false) : null)
111                 .setDependencies(session.toDependencies(dependencies, false))
112                 .setManagedDependencies(session.toDependencies(managedDependencies, true))
113                 .setRepositories(session.toRepositories(remoteRepositories));
114         collectRequest.setResolutionScope(resolutionScope);
115 
116         RepositorySystemSession systemSession = session.getSession();
117         if (request.getVerbose()) {
118             systemSession = new DefaultRepositorySystemSession(systemSession)
119                     .setConfigProperty(ConflictResolver.CONFIG_PROP_VERBOSE, true)
120                     .setConfigProperty(DependencyManagerUtils.CONFIG_PROP_VERBOSE, true);
121         }
122 
123         try {
124             final CollectResult result =
125                     session.getRepositorySystem().collectDependencies(systemSession, collectRequest);
126             return new DefaultDependencyResolverResult(
127                     null, result.getExceptions(), session.getNode(result.getRoot(), request.getVerbose()), 0);
128         } catch (DependencyCollectionException e) {
129             throw new DependencyResolverException("Unable to collect dependencies", e);
130         }
131     }
132 
133     @Nonnull
134     @Override
135     public List<Node> flatten(@Nonnull Session s, @Nonnull Node node, @Nullable PathScope scope)
136             throws DependencyResolverException {
137         InternalSession session = InternalSession.from(s);
138         DependencyNode root = cast(AbstractNode.class, node, "node").getDependencyNode();
139         List<DependencyNode> dependencies = session.getRepositorySystem()
140                 .flattenDependencyNodes(session.getSession(), root, getScopeDependencyFilter(scope));
141         dependencies.remove(root);
142         return map(dependencies, session::getNode);
143     }
144 
145     private static DependencyFilter getScopeDependencyFilter(PathScope scope) {
146         if (scope == null) {
147             return null;
148         }
149         Set<String> scopes =
150                 scope.dependencyScopes().stream().map(DependencyScope::id).collect(Collectors.toSet());
151         return (n, p) -> {
152             org.eclipse.aether.graph.Dependency d = n.getDependency();
153             return d == null || scopes.contains(d.getScope());
154         };
155     }
156 
157     /**
158      * Collects, flattens and resolves the dependencies.
159      *
160      * @param request the request to resolve
161      * @return the result of the resolution
162      */
163     @Override
164     public DependencyResolverResult resolve(DependencyResolverRequest request)
165             throws DependencyResolverException, DependencyResolverException, ArtifactResolverException {
166         InternalSession session =
167                 InternalSession.from(nonNull(request, "request").getSession());
168         DependencyResolverResult result;
169         DependencyResolverResult collectorResult = collect(request);
170         List<RemoteRepository> repositories =
171                 request.getRepositories() != null ? request.getRepositories() : session.getRemoteRepositories();
172         if (request.getRequestType() == DependencyResolverRequest.RequestType.COLLECT) {
173             result = collectorResult;
174         } else {
175             List<Node> nodes = flatten(session, collectorResult.getRoot(), request.getPathScope());
176             List<ArtifactCoordinates> coordinates = nodes.stream()
177                     .map(Node::getDependency)
178                     .filter(Objects::nonNull)
179                     .map(Artifact::toCoordinates)
180                     .collect(Collectors.toList());
181             Predicate<PathType> filter = request.getPathTypeFilter();
182             if (request.getRequestType() == DependencyResolverRequest.RequestType.FLATTEN) {
183                 DefaultDependencyResolverResult flattenResult = new DefaultDependencyResolverResult(
184                         null, collectorResult.getExceptions(), collectorResult.getRoot(), nodes.size());
185                 for (Node node : nodes) {
186                     flattenResult.addNode(node);
187                 }
188                 result = flattenResult;
189             } else {
190                 PathModularizationCache cache = new PathModularizationCache(); // TODO: should be project-wide cache.
191                 DefaultDependencyResolverResult resolverResult = new DefaultDependencyResolverResult(
192                         cache, collectorResult.getExceptions(), collectorResult.getRoot(), nodes.size());
193                 ArtifactResolverResult artifactResolverResult =
194                         session.getService(ArtifactResolver.class).resolve(session, coordinates, repositories);
195                 for (Node node : nodes) {
196                     Dependency d = node.getDependency();
197                     Path path = (d != null) ? artifactResolverResult.getPath(d) : null;
198                     try {
199                         resolverResult.addDependency(node, d, filter, path);
200                     } catch (IOException e) {
201                         throw cannotReadModuleInfo(path, e);
202                     }
203                 }
204                 result = resolverResult;
205             }
206         }
207         return result;
208     }
209 
210     private static DependencyResolverException cannotReadModuleInfo(final Path path, final IOException cause) {
211         return new DependencyResolverException("Cannot read module information of " + path, cause);
212     }
213 }