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  import java.util.Objects;
30  
31  import org.apache.maven.RepositoryUtils;
32  import org.apache.maven.artifact.Artifact;
33  import org.apache.maven.model.Dependency;
34  import org.apache.maven.model.DependencyManagement;
35  import org.apache.maven.model.Exclusion;
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.graph.DependencyVisitor;
47  import org.eclipse.aether.resolution.ArtifactResult;
48  import org.eclipse.aether.resolution.DependencyRequest;
49  import org.eclipse.aether.util.artifact.ArtifactIdUtils;
50  import org.eclipse.aether.util.artifact.JavaScopes;
51  import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
52  import org.slf4j.Logger;
53  import org.slf4j.LoggerFactory;
54  
55  /**
56   */
57  @Named
58  @Singleton
59  public class DefaultProjectDependenciesResolver implements ProjectDependenciesResolver {
60      private final Logger logger = LoggerFactory.getLogger(getClass());
61      private final RepositorySystem repoSystem;
62      private final List<RepositorySessionDecorator> decorators;
63  
64      @Inject
65      public DefaultProjectDependenciesResolver(
66              RepositorySystem repoSystem, List<RepositorySessionDecorator> decorators) {
67          this.repoSystem = repoSystem;
68          this.decorators = decorators;
69      }
70  
71      public DependencyResolutionResult resolve(DependencyResolutionRequest request)
72              throws DependencyResolutionException {
73          final RequestTrace trace = RequestTrace.newChild(null, request);
74  
75          final DefaultDependencyResolutionResult result = new DefaultDependencyResolutionResult();
76  
77          final MavenProject project = request.getMavenProject();
78          final DependencyFilter filter = request.getResolutionFilter();
79          RepositorySystemSession session = request.getRepositorySession();
80          ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry();
81  
82          if (logger.isDebugEnabled()
83                  && session.getConfigProperties().get(DependencyManagerUtils.CONFIG_PROP_VERBOSE) == null) {
84              DefaultRepositorySystemSession verbose = new DefaultRepositorySystemSession(session);
85              verbose.setConfigProperty(DependencyManagerUtils.CONFIG_PROP_VERBOSE, Boolean.TRUE);
86              session = verbose;
87          }
88  
89          for (RepositorySessionDecorator decorator : decorators) {
90              RepositorySystemSession decorated = decorator.decorate(project, session);
91              if (decorated != null) {
92                  session = decorated;
93              }
94          }
95  
96          CollectRequest collect = new CollectRequest();
97          collect.setRootArtifact(RepositoryUtils.toArtifact(project.getArtifact()));
98          collect.setRequestContext("project");
99          collect.setRepositories(project.getRemoteProjectRepositories());
100 
101         if (project.getDependencyArtifacts() == null) {
102             for (Dependency dependency : project.getDependencies()) {
103                 if (dependency.getGroupId() == null
104                         || dependency.getGroupId().isEmpty()
105                         || dependency.getArtifactId() == null
106                         || dependency.getArtifactId().isEmpty()
107                         || dependency.getVersion() == null
108                         || dependency.getVersion().isEmpty()) {
109                     // guard against case where best-effort resolution for invalid models is requested
110                     continue;
111                 }
112                 collect.addDependency(RepositoryUtils.toDependency(dependency, stereotypes));
113             }
114         } else {
115             Map<String, Dependency> dependencies = new HashMap<>();
116             for (Dependency dependency : project.getDependencies()) {
117                 String classifier = dependency.getClassifier();
118                 if (classifier == null) {
119                     ArtifactType type = stereotypes.get(dependency.getType());
120                     if (type != null) {
121                         classifier = type.getClassifier();
122                     }
123                 }
124                 String key = ArtifactIdUtils.toVersionlessId(
125                         dependency.getGroupId(), dependency.getArtifactId(), dependency.getType(), classifier);
126                 dependencies.put(key, dependency);
127             }
128             for (Artifact artifact : project.getDependencyArtifacts()) {
129                 String key = artifact.getDependencyConflictId();
130                 Dependency dependency = dependencies.get(key);
131                 Collection<Exclusion> exclusions = dependency != null ? dependency.getExclusions() : null;
132                 org.eclipse.aether.graph.Dependency dep = RepositoryUtils.toDependency(artifact, exclusions);
133                 if (!JavaScopes.SYSTEM.equals(dep.getScope())
134                         && dep.getArtifact().getFile() != null) {
135                     // enable re-resolution
136                     org.eclipse.aether.artifact.Artifact art = dep.getArtifact();
137                     art = art.setFile(null).setVersion(art.getBaseVersion());
138                     dep = dep.setArtifact(art);
139                 }
140                 collect.addDependency(dep);
141             }
142         }
143 
144         DependencyManagement depMgmt = project.getDependencyManagement();
145         if (depMgmt != null) {
146             for (Dependency dependency : depMgmt.getDependencies()) {
147                 collect.addManagedDependency(RepositoryUtils.toDependency(dependency, stereotypes));
148             }
149         }
150 
151         DependencyRequest depRequest = new DependencyRequest(collect, filter);
152         depRequest.setTrace(trace);
153 
154         DependencyNode node;
155         try {
156             collect.setTrace(RequestTrace.newChild(trace, depRequest));
157             node = repoSystem.collectDependencies(session, collect).getRoot();
158             result.setDependencyGraph(node);
159         } catch (DependencyCollectionException e) {
160             result.setDependencyGraph(e.getResult().getRoot());
161             result.setCollectionErrors(e.getResult().getExceptions());
162 
163             throw new DependencyResolutionException(
164                     result, "Could not resolve dependencies for project " + project.getId() + ": " + e.getMessage(), e);
165         }
166 
167         depRequest.setRoot(node);
168 
169         if (logger.isWarnEnabled()) {
170             for (DependencyNode child : node.getChildren()) {
171                 if (!child.getRelocations().isEmpty()) {
172                     org.eclipse.aether.artifact.Artifact relocated =
173                             child.getDependency().getArtifact();
174                     String message = relocated instanceof org.apache.maven.repository.internal.RelocatedArtifact
175                             ? ((org.apache.maven.repository.internal.RelocatedArtifact) relocated).getMessage()
176                             : null;
177                     logger.warn("The artifact " + child.getRelocations().get(0) + " has been relocated to " + relocated
178                             + (message != null ? ": " + message : ""));
179                 }
180             }
181         }
182 
183         if (logger.isDebugEnabled()) {
184             node.accept(new GraphLogger(project));
185         }
186 
187         try {
188             process(result, repoSystem.resolveDependencies(session, depRequest).getArtifactResults());
189         } catch (org.eclipse.aether.resolution.DependencyResolutionException e) {
190             process(result, e.getResult().getArtifactResults());
191 
192             throw new DependencyResolutionException(
193                     result, "Could not resolve dependencies for project " + project.getId() + ": " + e.getMessage(), e);
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 
210     // Keep this class in sync with org.apache.maven.plugin.internal.DefaultPluginDependenciesResolver.GraphLogger
211     class GraphLogger implements DependencyVisitor {
212 
213         private final MavenProject project;
214 
215         private String indent = "";
216 
217         GraphLogger(MavenProject project) {
218             this.project = project;
219         }
220 
221         public boolean visitEnter(DependencyNode node) {
222             StringBuilder buffer = new StringBuilder(128);
223             buffer.append(indent);
224             org.eclipse.aether.graph.Dependency dep = node.getDependency();
225             if (dep != null) {
226                 org.eclipse.aether.artifact.Artifact art = dep.getArtifact();
227 
228                 buffer.append(art);
229                 if (dep.getScope() != null && !dep.getScope().isEmpty()) {
230                     buffer.append(':').append(dep.getScope());
231                 }
232 
233                 if (dep.isOptional()) {
234                     buffer.append(" (optional)");
235                 }
236 
237                 // TODO We currently cannot tell which <dependencyManagement> section contained the management
238                 //      information. When the resolver provides this information, these log messages should be updated
239                 //      to contain it.
240                 if ((node.getManagedBits() & DependencyNode.MANAGED_SCOPE) == DependencyNode.MANAGED_SCOPE) {
241                     final String premanagedScope = DependencyManagerUtils.getPremanagedScope(node);
242                     buffer.append(" (scope managed from ");
243                     buffer.append(Objects.toString(premanagedScope, "default"));
244                     buffer.append(')');
245                 }
246 
247                 if ((node.getManagedBits() & DependencyNode.MANAGED_VERSION) == DependencyNode.MANAGED_VERSION) {
248                     final String premanagedVersion = DependencyManagerUtils.getPremanagedVersion(node);
249                     buffer.append(" (version managed from ");
250                     buffer.append(Objects.toString(premanagedVersion, "default"));
251                     buffer.append(')');
252                 }
253 
254                 if ((node.getManagedBits() & DependencyNode.MANAGED_OPTIONAL) == DependencyNode.MANAGED_OPTIONAL) {
255                     final Boolean premanagedOptional = DependencyManagerUtils.getPremanagedOptional(node);
256                     buffer.append(" (optionality managed from ");
257                     buffer.append(Objects.toString(premanagedOptional, "default"));
258                     buffer.append(')');
259                 }
260 
261                 if ((node.getManagedBits() & DependencyNode.MANAGED_EXCLUSIONS) == DependencyNode.MANAGED_EXCLUSIONS) {
262                     final Collection<org.eclipse.aether.graph.Exclusion> premanagedExclusions =
263                             DependencyManagerUtils.getPremanagedExclusions(node);
264 
265                     buffer.append(" (exclusions managed from ");
266                     buffer.append(Objects.toString(premanagedExclusions, "default"));
267                     buffer.append(')');
268                 }
269 
270                 if ((node.getManagedBits() & DependencyNode.MANAGED_PROPERTIES) == DependencyNode.MANAGED_PROPERTIES) {
271                     final Map<String, String> premanagedProperties =
272                             DependencyManagerUtils.getPremanagedProperties(node);
273 
274                     buffer.append(" (properties managed from ");
275                     buffer.append(Objects.toString(premanagedProperties, "default"));
276                     buffer.append(')');
277                 }
278             } else {
279                 buffer.append(project.getGroupId());
280                 buffer.append(':').append(project.getArtifactId());
281                 buffer.append(':').append(project.getPackaging());
282                 buffer.append(':').append(project.getVersion());
283             }
284 
285             logger.debug(buffer.toString());
286             indent += "   ";
287             return true;
288         }
289 
290         public boolean visitLeave(DependencyNode node) {
291             indent = indent.substring(0, indent.length() - 3);
292             return true;
293         }
294     }
295 }