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.project;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.util.Collection;
26  import java.util.HashMap;
27  import java.util.List;
28  import java.util.Map;
29  
30  import org.apache.maven.RepositoryUtils;
31  import org.apache.maven.artifact.Artifact;
32  import org.apache.maven.model.Dependency;
33  import org.apache.maven.model.DependencyManagement;
34  import org.apache.maven.model.Exclusion;
35  import org.codehaus.plexus.util.StringUtils;
36  import org.eclipse.aether.DefaultRepositorySystemSession;
37  import org.eclipse.aether.RepositorySystem;
38  import org.eclipse.aether.RepositorySystemSession;
39  import org.eclipse.aether.RequestTrace;
40  import org.eclipse.aether.artifact.ArtifactType;
41  import org.eclipse.aether.artifact.ArtifactTypeRegistry;
42  import org.eclipse.aether.collection.CollectRequest;
43  import org.eclipse.aether.collection.DependencyCollectionException;
44  import org.eclipse.aether.graph.DependencyFilter;
45  import org.eclipse.aether.graph.DependencyNode;
46  import org.eclipse.aether.resolution.ArtifactResult;
47  import org.eclipse.aether.resolution.DependencyRequest;
48  import org.eclipse.aether.util.artifact.ArtifactIdUtils;
49  import org.eclipse.aether.util.artifact.JavaScopes;
50  import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
51  import org.eclipse.aether.util.graph.visitor.DependencyGraphDumper;
52  import org.slf4j.Logger;
53  import org.slf4j.LoggerFactory;
54  
55  /**
56   * @author Benjamin Bentmann
57   */
58  @Singleton
59  @Named
60  public class DefaultProjectDependenciesResolver implements ProjectDependenciesResolver {
61  
62      private final Logger logger = LoggerFactory.getLogger(getClass());
63  
64      @Inject
65      private RepositorySystem repoSystem;
66  
67      @Inject
68      private List<RepositorySessionDecorator> decorators;
69  
70      public DependencyResolutionResult resolve(DependencyResolutionRequest request)
71              throws DependencyResolutionException {
72          final RequestTrace trace = RequestTrace.newChild(null, request);
73  
74          final DefaultDependencyResolutionResult result = new DefaultDependencyResolutionResult();
75  
76          final MavenProject project = request.getMavenProject();
77          final DependencyFilter filter = request.getResolutionFilter();
78          RepositorySystemSession session = request.getRepositorySession();
79          ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry();
80  
81          if (logger.isDebugEnabled()
82                  && session.getConfigProperties().get(DependencyManagerUtils.CONFIG_PROP_VERBOSE) == null) {
83              DefaultRepositorySystemSession verbose = new DefaultRepositorySystemSession(session);
84              verbose.setConfigProperty(DependencyManagerUtils.CONFIG_PROP_VERBOSE, Boolean.TRUE);
85              session = verbose;
86          }
87  
88          for (RepositorySessionDecorator decorator : decorators) {
89              RepositorySystemSession decorated = decorator.decorate(project, session);
90              if (decorated != null) {
91                  session = decorated;
92              }
93          }
94  
95          CollectRequest collect = new CollectRequest();
96          collect.setRootArtifact(RepositoryUtils.toArtifact(project.getArtifact()));
97          collect.setRequestContext("project");
98          collect.setRepositories(project.getRemoteProjectRepositories());
99  
100         if (project.getDependencyArtifacts() == null) {
101             for (Dependency dependency : project.getDependencies()) {
102                 if (StringUtils.isEmpty(dependency.getGroupId())
103                         || StringUtils.isEmpty(dependency.getArtifactId())
104                         || StringUtils.isEmpty(dependency.getVersion())) {
105                     // guard against case where best-effort resolution for invalid models is requested
106                     continue;
107                 }
108                 collect.addDependency(RepositoryUtils.toDependency(dependency, stereotypes));
109             }
110         } else {
111             Map<String, Dependency> dependencies = new HashMap<>();
112             for (Dependency dependency : project.getDependencies()) {
113                 String classifier = dependency.getClassifier();
114                 if (classifier == null) {
115                     ArtifactType type = stereotypes.get(dependency.getType());
116                     if (type != null) {
117                         classifier = type.getClassifier();
118                     }
119                 }
120                 String key = ArtifactIdUtils.toVersionlessId(
121                         dependency.getGroupId(), dependency.getArtifactId(), dependency.getType(), classifier);
122                 dependencies.put(key, dependency);
123             }
124             for (Artifact artifact : project.getDependencyArtifacts()) {
125                 String key = artifact.getDependencyConflictId();
126                 Dependency dependency = dependencies.get(key);
127                 Collection<Exclusion> exclusions = dependency != null ? dependency.getExclusions() : null;
128                 org.eclipse.aether.graph.Dependency dep = RepositoryUtils.toDependency(artifact, exclusions);
129                 if (!JavaScopes.SYSTEM.equals(dep.getScope())
130                         && dep.getArtifact().getFile() != null) {
131                     // enable re-resolution
132                     org.eclipse.aether.artifact.Artifact art = dep.getArtifact();
133                     art = art.setFile(null).setVersion(art.getBaseVersion());
134                     dep = dep.setArtifact(art);
135                 }
136                 collect.addDependency(dep);
137             }
138         }
139 
140         DependencyManagement depMgmt = project.getDependencyManagement();
141         if (depMgmt != null) {
142             for (Dependency dependency : depMgmt.getDependencies()) {
143                 collect.addManagedDependency(RepositoryUtils.toDependency(dependency, stereotypes));
144             }
145         }
146 
147         DependencyRequest depRequest = new DependencyRequest(collect, filter);
148         depRequest.setTrace(trace);
149 
150         DependencyNode node;
151         try {
152             collect.setTrace(RequestTrace.newChild(trace, depRequest));
153             node = repoSystem.collectDependencies(session, collect).getRoot();
154             result.setDependencyGraph(node);
155         } catch (DependencyCollectionException e) {
156             result.setDependencyGraph(e.getResult().getRoot());
157             result.setCollectionErrors(e.getResult().getExceptions());
158 
159             throw new DependencyResolutionException(
160                     result,
161                     "Could not collect dependencies for project " + project.getId(),
162                     logger.isDebugEnabled() ? e : null);
163         }
164 
165         depRequest.setRoot(node);
166 
167         if (logger.isWarnEnabled()) {
168             for (DependencyNode child : node.getChildren()) {
169                 if (!child.getRelocations().isEmpty()) {
170                     org.eclipse.aether.artifact.Artifact relocated =
171                             child.getDependency().getArtifact();
172                     String message = relocated instanceof org.apache.maven.repository.internal.RelocatedArtifact
173                             ? ((org.apache.maven.repository.internal.RelocatedArtifact) relocated).getMessage()
174                             : null;
175                     logger.warn("The artifact " + child.getRelocations().get(0) + " has been relocated to " + relocated
176                             + (message != null ? ": " + message : ""));
177                 }
178             }
179         }
180 
181         if (logger.isDebugEnabled()) {
182             node.accept(new DependencyGraphDumper(logger::debug));
183         }
184 
185         try {
186             process(result, repoSystem.resolveDependencies(session, depRequest).getArtifactResults());
187         } catch (org.eclipse.aether.resolution.DependencyResolutionException e) {
188             process(result, e.getResult().getArtifactResults());
189 
190             throw new DependencyResolutionException(
191                     result,
192                     "Could not resolve dependencies for project " + project.getId(),
193                     logger.isDebugEnabled() ? e : null);
194         }
195 
196         return result;
197     }
198 
199     private void process(DefaultDependencyResolutionResult result, Collection<ArtifactResult> results) {
200         for (ArtifactResult ar : results) {
201             DependencyNode node = ar.getRequest().getDependencyNode();
202             if (ar.isResolved()) {
203                 result.addResolvedDependency(node.getDependency());
204             } else {
205                 result.setResolutionErrors(node.getDependency(), ar.getExceptions());
206             }
207         }
208     }
209 }