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