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 javax.inject.Named;
22  import javax.inject.Singleton;
23  
24  import java.nio.file.Path;
25  import java.util.ArrayList;
26  import java.util.Collection;
27  import java.util.Collections;
28  import java.util.LinkedHashMap;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Objects;
32  import java.util.Set;
33  import java.util.stream.Collectors;
34  import java.util.stream.Stream;
35  
36  import org.apache.maven.api.Artifact;
37  import org.apache.maven.api.ArtifactCoordinate;
38  import org.apache.maven.api.Dependency;
39  import org.apache.maven.api.Node;
40  import org.apache.maven.api.Project;
41  import org.apache.maven.api.ResolutionScope;
42  import org.apache.maven.api.Scope;
43  import org.apache.maven.api.Session;
44  import org.apache.maven.api.services.*;
45  import org.apache.maven.lifecycle.LifecycleExecutionException;
46  import org.apache.maven.lifecycle.internal.LifecycleDependencyResolver;
47  import org.apache.maven.project.DependencyResolutionResult;
48  import org.apache.maven.project.MavenProject;
49  import org.eclipse.aether.graph.DependencyFilter;
50  import org.eclipse.aether.graph.DependencyNode;
51  
52  import static org.apache.maven.internal.impl.Utils.cast;
53  import static org.apache.maven.internal.impl.Utils.map;
54  import static org.apache.maven.internal.impl.Utils.nonNull;
55  
56  @Named
57  @Singleton
58  public class DefaultDependencyResolver implements DependencyResolver {
59  
60      @Override
61      public List<Node> flatten(Session s, Node node, ResolutionScope scope) throws DependencyResolverException {
62          InternalSession session = InternalSession.from(s);
63          DependencyNode root = cast(AbstractNode.class, node, "node").getDependencyNode();
64          List<DependencyNode> dependencies = session.getRepositorySystem()
65                  .flattenDependencyNodes(session.getSession(), root, getScopeDependencyFilter(scope));
66          dependencies.remove(root);
67          return map(dependencies, session::getNode);
68      }
69  
70      private static DependencyFilter getScopeDependencyFilter(ResolutionScope scope) {
71          Set<String> scopes = scope.scopes().stream().map(Scope::id).collect(Collectors.toSet());
72          return (n, p) -> {
73              org.eclipse.aether.graph.Dependency d = n.getDependency();
74              return d == null || scopes.contains(d.getScope());
75          };
76      }
77  
78      @Override
79      public DependencyResolverResult resolve(DependencyResolverRequest request)
80              throws DependencyCollectorException, DependencyResolverException, ArtifactResolverException {
81          nonNull(request, "request can not be null");
82          InternalSession session = InternalSession.from(request.getSession());
83  
84          if (request.getProject().isPresent()) {
85              DependencyResolutionResult result = resolveDependencies(
86                      request.getSession(), request.getProject().get(), request.getResolutionScope());
87  
88              Map<org.eclipse.aether.graph.Dependency, org.eclipse.aether.graph.DependencyNode> nodes = stream(
89                              result.getDependencyGraph())
90                      .filter(n -> n.getDependency() != null)
91                      .collect(Collectors.toMap(DependencyNode::getDependency, n -> n));
92  
93              Node root = session.getNode(result.getDependencyGraph());
94              List<Node> dependencies = new ArrayList<>();
95              Map<Dependency, Path> artifacts = new LinkedHashMap<>();
96              List<Path> paths = new ArrayList<>();
97              for (org.eclipse.aether.graph.Dependency dep : result.getResolvedDependencies()) {
98                  dependencies.add(session.getNode(nodes.get(dep)));
99                  Path path = dep.getArtifact().getFile().toPath();
100                 artifacts.put(session.getDependency(dep), path);
101                 paths.add(path);
102             }
103             return new DefaultDependencyResolverResult(
104                     result.getCollectionErrors(), root, dependencies, paths, artifacts);
105         }
106 
107         DependencyCollectorResult collectorResult =
108                 session.getService(DependencyCollector.class).collect(request);
109         List<Node> nodes = flatten(session, collectorResult.getRoot(), request.getResolutionScope());
110         List<Dependency> deps =
111                 nodes.stream().map(Node::getDependency).filter(Objects::nonNull).collect(Collectors.toList());
112         List<ArtifactCoordinate> coordinates =
113                 deps.stream().map(Artifact::toCoordinate).collect(Collectors.toList());
114         Map<Artifact, Path> artifacts = session.resolveArtifacts(coordinates);
115         Map<Dependency, Path> dependencies = new LinkedHashMap<>();
116         List<Path> paths = new ArrayList<>();
117         for (Dependency d : deps) {
118             Path path = artifacts.get(d);
119             if (dependencies.put(d, path) != null) {
120                 throw new IllegalStateException("Duplicate key");
121             }
122             paths.add(path);
123         }
124 
125         return new DefaultDependencyResolverResult(
126                 collectorResult.getExceptions(), collectorResult.getRoot(), nodes, paths, dependencies);
127     }
128 
129     private Stream<DependencyNode> stream(DependencyNode node) {
130         return Stream.concat(Stream.of(node), node.getChildren().stream().flatMap(this::stream));
131     }
132 
133     private DependencyResolutionResult resolveDependencies(Session session, Project project, ResolutionScope scope) {
134         Collection<String> toResolve = toScopes(scope);
135         try {
136             LifecycleDependencyResolver lifecycleDependencyResolver =
137                     session.getService(Lookup.class).lookup(LifecycleDependencyResolver.class);
138             return lifecycleDependencyResolver.getProjectDependencyResolutionResult(
139                     getMavenProject(project),
140                     toResolve,
141                     toResolve,
142                     InternalSession.from(session).getMavenSession(),
143                     false,
144                     Collections.emptySet());
145         } catch (LifecycleExecutionException e) {
146             throw new DependencyResolverException("Unable to resolve project dependencies", e);
147         }
148     }
149 
150     private MavenProject getMavenProject(Project project) {
151         return ((DefaultProject) project).getProject();
152     }
153 
154     private Collection<String> toScopes(ResolutionScope scope) {
155         return map(scope.scopes(), Scope::id);
156     }
157 
158     static class DefaultDependencyResolverResult implements DependencyResolverResult {
159         private final List<Exception> exceptions;
160         private final Node root;
161         private final List<Node> nodes;
162         private final List<Path> paths;
163         private final Map<Dependency, Path> dependencies;
164 
165         DefaultDependencyResolverResult(
166                 List<Exception> exceptions,
167                 Node root,
168                 List<Node> nodes,
169                 List<Path> paths,
170                 Map<Dependency, Path> dependencies) {
171             this.exceptions = exceptions;
172             this.root = root;
173             this.nodes = nodes;
174             this.paths = paths;
175             this.dependencies = dependencies;
176         }
177 
178         @Override
179         public List<Exception> getExceptions() {
180             return exceptions;
181         }
182 
183         @Override
184         public Node getRoot() {
185             return root;
186         }
187 
188         @Override
189         public List<Node> getNodes() {
190             return nodes;
191         }
192 
193         @Override
194         public List<Path> getPaths() {
195             return paths;
196         }
197 
198         @Override
199         public Map<Dependency, Path> getDependencies() {
200             return dependencies;
201         }
202     }
203 }