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.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.DependencyCoordinates;
33  import org.apache.maven.api.DependencyScope;
34  import org.apache.maven.api.Node;
35  import org.apache.maven.api.PathScope;
36  import org.apache.maven.api.PathType;
37  import org.apache.maven.api.Project;
38  import org.apache.maven.api.RemoteRepository;
39  import org.apache.maven.api.Session;
40  import org.apache.maven.api.annotations.Nonnull;
41  import org.apache.maven.api.annotations.Nullable;
42  import org.apache.maven.api.di.Named;
43  import org.apache.maven.api.di.Singleton;
44  import org.apache.maven.api.services.ArtifactResolver;
45  import org.apache.maven.api.services.ArtifactResolverException;
46  import org.apache.maven.api.services.ArtifactResolverResult;
47  import org.apache.maven.api.services.DependencyResolver;
48  import org.apache.maven.api.services.DependencyResolverException;
49  import org.apache.maven.api.services.DependencyResolverRequest;
50  import org.apache.maven.api.services.DependencyResolverResult;
51  import org.apache.maven.api.services.ProjectManager;
52  import org.eclipse.aether.DefaultRepositorySystemSession;
53  import org.eclipse.aether.RepositorySystemSession;
54  import org.eclipse.aether.collection.CollectRequest;
55  import org.eclipse.aether.collection.CollectResult;
56  import org.eclipse.aether.collection.DependencyCollectionException;
57  import org.eclipse.aether.graph.DependencyFilter;
58  import org.eclipse.aether.graph.DependencyNode;
59  import org.eclipse.aether.scope.ResolutionScope;
60  import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
61  import org.eclipse.aether.util.graph.transformer.ConflictResolver;
62  
63  import static org.apache.maven.impl.Utils.cast;
64  import static org.apache.maven.impl.Utils.map;
65  import static org.apache.maven.impl.Utils.nonNull;
66  
67  @Named
68  @Singleton
69  public class DefaultDependencyResolver implements DependencyResolver {
70  
71      @Nonnull
72      @Override
73      public DependencyResolverResult collect(@Nonnull DependencyResolverRequest request)
74              throws DependencyResolverException, IllegalArgumentException {
75          nonNull(request, "request");
76          InternalSession session = InternalSession.from(request.getSession());
77          RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(session, request);
78          try {
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                     .setRequestContext(trace.context())
115                     .setTrace(trace.trace());
116             collectRequest.setResolutionScope(resolutionScope);
117 
118             RepositorySystemSession systemSession = session.getSession();
119             if (request.getVerbose()) {
120                 systemSession = new DefaultRepositorySystemSession(systemSession)
121                         .setConfigProperty(ConflictResolver.CONFIG_PROP_VERBOSE, true)
122                         .setConfigProperty(DependencyManagerUtils.CONFIG_PROP_VERBOSE, true);
123             }
124 
125             try {
126                 final CollectResult result =
127                         session.getRepositorySystem().collectDependencies(systemSession, collectRequest);
128                 return new DefaultDependencyResolverResult(
129                         null, null, result.getExceptions(), session.getNode(result.getRoot(), request.getVerbose()), 0);
130             } catch (DependencyCollectionException e) {
131                 throw new DependencyResolverException("Unable to collect dependencies", e);
132             }
133         } finally {
134             RequestTraceHelper.exit(trace);
135         }
136     }
137 
138     @Nonnull
139     @Override
140     public List<Node> flatten(@Nonnull Session s, @Nonnull Node node, @Nullable PathScope scope)
141             throws DependencyResolverException {
142         InternalSession session = InternalSession.from(s);
143         DependencyNode root = cast(AbstractNode.class, node, "node").getDependencyNode();
144         List<DependencyNode> dependencies = session.getRepositorySystem()
145                 .flattenDependencyNodes(session.getSession(), root, getScopeDependencyFilter(scope));
146         dependencies.remove(root);
147         return map(dependencies, session::getNode);
148     }
149 
150     private static DependencyFilter getScopeDependencyFilter(PathScope scope) {
151         if (scope == null) {
152             return null;
153         }
154         Set<String> scopes =
155                 scope.dependencyScopes().stream().map(DependencyScope::id).collect(Collectors.toSet());
156         return (n, p) -> {
157             org.eclipse.aether.graph.Dependency d = n.getDependency();
158             return d == null || scopes.contains(d.getScope());
159         };
160     }
161 
162     /**
163      * Collects, flattens and resolves the dependencies.
164      *
165      * @param request the request to resolve
166      * @return the result of the resolution
167      */
168     @Override
169     public DependencyResolverResult resolve(DependencyResolverRequest request)
170             throws DependencyResolverException, ArtifactResolverException {
171         InternalSession session =
172                 InternalSession.from(nonNull(request, "request").getSession());
173         RequestTraceHelper.ResolverTrace trace = RequestTraceHelper.enter(session, request);
174         try {
175             DependencyResolverResult result;
176             DependencyResolverResult collectorResult = collect(request);
177             List<RemoteRepository> repositories = request.getRepositories() != null
178                     ? request.getRepositories()
179                     : request.getProject().isPresent()
180                             ? session.getService(ProjectManager.class)
181                                     .getRemoteProjectRepositories(
182                                             request.getProject().get())
183                             : session.getRemoteRepositories();
184             if (request.getRequestType() == DependencyResolverRequest.RequestType.COLLECT) {
185                 result = collectorResult;
186             } else {
187                 List<Node> nodes = flatten(session, collectorResult.getRoot(), request.getPathScope());
188                 List<ArtifactCoordinates> coordinates = nodes.stream()
189                         .map(Node::getDependency)
190                         .filter(Objects::nonNull)
191                         .map(Artifact::toCoordinates)
192                         .collect(Collectors.toList());
193                 Predicate<PathType> filter = request.getPathTypeFilter();
194                 if (request.getRequestType() == DependencyResolverRequest.RequestType.FLATTEN) {
195                     DefaultDependencyResolverResult flattenResult = new DefaultDependencyResolverResult(
196                             null, null, collectorResult.getExceptions(), collectorResult.getRoot(), nodes.size());
197                     for (Node node : nodes) {
198                         flattenResult.addNode(node);
199                     }
200                     result = flattenResult;
201                 } else {
202                     PathModularizationCache cache =
203                             new PathModularizationCache(); // TODO: should be project-wide cache.
204                     DefaultDependencyResolverResult resolverResult = new DefaultDependencyResolverResult(
205                             null, cache, collectorResult.getExceptions(), collectorResult.getRoot(), nodes.size());
206                     ArtifactResolverResult artifactResolverResult =
207                             session.getService(ArtifactResolver.class).resolve(session, coordinates, repositories);
208                     for (Node node : nodes) {
209                         Path path = (node.getArtifact() != null)
210                                 ? artifactResolverResult
211                                         .getResult(node.getArtifact().toCoordinates())
212                                         .getPath()
213                                 : null;
214                         try {
215                             resolverResult.addDependency(node, node.getDependency(), filter, path);
216                         } catch (IOException e) {
217                             throw cannotReadModuleInfo(path, e);
218                         }
219                     }
220                     result = resolverResult;
221                 }
222             }
223             return result;
224         } finally {
225             RequestTraceHelper.exit(trace);
226         }
227     }
228 
229     private static DependencyResolverException cannotReadModuleInfo(final Path path, final IOException cause) {
230         return new DependencyResolverException("Cannot read module information of " + path, cause);
231     }
232 }