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