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.shared.dependency.graph.internal;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.List;
27  import java.util.Map;
28  
29  import org.apache.maven.RepositoryUtils;
30  import org.apache.maven.artifact.Artifact;
31  import org.apache.maven.artifact.repository.ArtifactRepository;
32  import org.apache.maven.artifact.resolver.filter.ArtifactFilter;
33  import org.apache.maven.model.Dependency;
34  import org.apache.maven.project.MavenProject;
35  import org.apache.maven.project.ProjectBuildingRequest;
36  import org.apache.maven.shared.dependency.graph.DependencyCollectorBuilder;
37  import org.apache.maven.shared.dependency.graph.DependencyCollectorBuilderException;
38  import org.apache.maven.shared.dependency.graph.DependencyCollectorRequest;
39  import org.apache.maven.shared.dependency.graph.DependencyNode;
40  import org.eclipse.aether.DefaultRepositorySystemSession;
41  import org.eclipse.aether.RepositorySystem;
42  import org.eclipse.aether.RepositorySystemSession;
43  import org.eclipse.aether.artifact.ArtifactTypeRegistry;
44  import org.eclipse.aether.collection.CollectRequest;
45  import org.eclipse.aether.collection.CollectResult;
46  import org.eclipse.aether.collection.DependencyCollectionException;
47  import org.eclipse.aether.graph.DependencyVisitor;
48  import org.eclipse.aether.graph.Exclusion;
49  import org.eclipse.aether.util.graph.manager.DependencyManagerUtils;
50  import org.eclipse.aether.util.graph.transformer.ConflictResolver;
51  import org.eclipse.aether.util.graph.visitor.TreeDependencyVisitor;
52  import org.eclipse.aether.version.VersionConstraint;
53  import org.slf4j.Logger;
54  import org.slf4j.LoggerFactory;
55  
56  /**
57   * Project dependency raw dependency collector API, abstracting Maven 3.1+'s Aether implementation.
58   *
59   * @author Gabriel Belingueres
60   * @since 3.1.0
61   */
62  @Named
63  public class DefaultDependencyCollectorBuilder implements DependencyCollectorBuilder {
64      private static final Logger LOGGER = LoggerFactory.getLogger(DefaultDependencyCollectorBuilder.class);
65  
66      private final RepositorySystem repositorySystem;
67  
68      @Inject
69      public DefaultDependencyCollectorBuilder(RepositorySystem repositorySystem) {
70          this.repositorySystem = repositorySystem;
71      }
72  
73      @Override
74      public DependencyNode collectDependencyGraph(DependencyCollectorRequest dependencyCollectorRequest)
75              throws DependencyCollectorBuilderException {
76          DefaultRepositorySystemSession session = null;
77          try {
78              ProjectBuildingRequest buildingRequest = dependencyCollectorRequest.getBuildingRequest();
79              MavenProject project = buildingRequest.getProject();
80  
81              Artifact projectArtifact = project.getArtifact();
82              List<ArtifactRepository> remoteArtifactRepositories = project.getRemoteArtifactRepositories();
83  
84              RepositorySystemSession repositorySession = buildingRequest.getRepositorySession();
85  
86              session = new DefaultRepositorySystemSession(repositorySession);
87  
88              session.setDependencyGraphTransformer(dependencyCollectorRequest.getDependencyGraphTransformer());
89  
90              session.setDependencySelector(dependencyCollectorRequest.getDependencySelector());
91  
92              for (Map.Entry<String, Object> entry :
93                      dependencyCollectorRequest.getConfigProperties().entrySet()) {
94                  session.setConfigProperty(entry.getKey(), entry.getValue());
95              }
96  
97              org.eclipse.aether.artifact.Artifact aetherArtifact = RepositoryUtils.toArtifact(projectArtifact);
98  
99              List<org.eclipse.aether.repository.RemoteRepository> aetherRepos =
100                     RepositoryUtils.toRepos(remoteArtifactRepositories);
101 
102             CollectRequest collectRequest = new CollectRequest();
103             collectRequest.setRootArtifact(aetherArtifact);
104             collectRequest.setRepositories(aetherRepos);
105 
106             org.eclipse.aether.artifact.ArtifactTypeRegistry stereotypes = session.getArtifactTypeRegistry();
107             collectDependencyList(collectRequest, project, stereotypes);
108             collectManagedDependencyList(collectRequest, project, stereotypes);
109 
110             CollectResult collectResult = repositorySystem.collectDependencies(session, collectRequest);
111 
112             org.eclipse.aether.graph.DependencyNode rootNode = collectResult.getRoot();
113 
114             if (LOGGER.isDebugEnabled()) {
115                 logTree(rootNode);
116             }
117 
118             return buildDependencyNode(null, rootNode, projectArtifact, dependencyCollectorRequest.getFilter());
119         } catch (DependencyCollectionException e) {
120             throw new DependencyCollectorBuilderException("Could not collect dependencies: " + e.getResult(), e);
121         } finally {
122             if (session != null) {
123                 session.setReadOnly();
124             }
125         }
126     }
127 
128     private void logTree(org.eclipse.aether.graph.DependencyNode rootNode) {
129         // print the node tree with its associated data Map
130         rootNode.accept(new TreeDependencyVisitor(new DependencyVisitor() {
131             String indent = "";
132 
133             @Override
134             public boolean visitEnter(org.eclipse.aether.graph.DependencyNode dependencyNode) {
135                 LOGGER.debug("{}Aether node: {} data map: {}", indent, dependencyNode, dependencyNode.getData());
136                 indent += "    ";
137                 return true;
138             }
139 
140             @Override
141             public boolean visitLeave(org.eclipse.aether.graph.DependencyNode dependencyNode) {
142                 indent = indent.substring(0, indent.length() - 4);
143                 return true;
144             }
145         }));
146     }
147 
148     private void collectManagedDependencyList(
149             CollectRequest collectRequest, MavenProject project, ArtifactTypeRegistry stereotypes) {
150         if (project.getDependencyManagement() != null) {
151             for (Dependency dependency : project.getDependencyManagement().getDependencies()) {
152                 org.eclipse.aether.graph.Dependency aetherDep = RepositoryUtils.toDependency(dependency, stereotypes);
153                 collectRequest.addManagedDependency(aetherDep);
154             }
155         }
156     }
157 
158     private void collectDependencyList(
159             CollectRequest collectRequest,
160             MavenProject project,
161             org.eclipse.aether.artifact.ArtifactTypeRegistry stereotypes) {
162         for (Dependency dependency : project.getDependencies()) {
163             org.eclipse.aether.graph.Dependency aetherDep = RepositoryUtils.toDependency(dependency, stereotypes);
164             collectRequest.addDependency(aetherDep);
165         }
166     }
167 
168     private Artifact getDependencyArtifact(org.eclipse.aether.graph.Dependency dep) {
169         org.eclipse.aether.artifact.Artifact artifact = dep.getArtifact();
170 
171         Artifact mavenArtifact = RepositoryUtils.toArtifact(artifact);
172         mavenArtifact.setScope(dep.getScope());
173         mavenArtifact.setOptional(dep.isOptional());
174 
175         return mavenArtifact;
176     }
177 
178     private DependencyNode buildDependencyNode(
179             DependencyNode parent,
180             org.eclipse.aether.graph.DependencyNode node,
181             Artifact artifact,
182             ArtifactFilter filter) {
183         String premanagedVersion = DependencyManagerUtils.getPremanagedVersion(node);
184         String premanagedScope = DependencyManagerUtils.getPremanagedScope(node);
185 
186         Boolean optional = null;
187         if (node.getDependency() != null) {
188             optional = node.getDependency().isOptional();
189         }
190 
191         List<org.apache.maven.model.Exclusion> exclusions = null;
192         if (node.getDependency() != null) {
193             exclusions = new ArrayList<>(node.getDependency().getExclusions().size());
194             for (Exclusion exclusion : node.getDependency().getExclusions()) {
195                 org.apache.maven.model.Exclusion modelExclusion = new org.apache.maven.model.Exclusion();
196                 modelExclusion.setGroupId(exclusion.getGroupId());
197                 modelExclusion.setArtifactId(exclusion.getArtifactId());
198                 exclusions.add(modelExclusion);
199             }
200         }
201 
202         org.eclipse.aether.graph.DependencyNode winner =
203                 (org.eclipse.aether.graph.DependencyNode) node.getData().get(ConflictResolver.NODE_DATA_WINNER);
204         String winnerVersion = null;
205         String ignoredScope = null;
206         if (winner != null) {
207             winnerVersion = winner.getArtifact().getBaseVersion();
208         } else {
209             ignoredScope = (String) node.getData().get(VerboseJavaScopeSelector.REDUCED_SCOPE);
210         }
211 
212         ConflictData data = new ConflictData(winnerVersion, ignoredScope);
213 
214         VerboseDependencyNode current = new VerboseDependencyNode(
215                 parent,
216                 artifact,
217                 premanagedVersion,
218                 premanagedScope,
219                 getVersionSelectedFromRange(node.getVersionConstraint()),
220                 optional,
221                 exclusions,
222                 data);
223 
224         List<DependencyNode> nodes = new ArrayList<>(node.getChildren().size());
225         for (org.eclipse.aether.graph.DependencyNode child : node.getChildren()) {
226             Artifact childArtifact = getDependencyArtifact(child.getDependency());
227 
228             if ((filter == null) || filter.include(childArtifact)) {
229                 nodes.add(buildDependencyNode(current, child, childArtifact, filter));
230             }
231         }
232 
233         current.setChildren(Collections.unmodifiableList(nodes));
234 
235         return current;
236     }
237 
238     private String getVersionSelectedFromRange(VersionConstraint constraint) {
239         if ((constraint == null) || (constraint.getVersion() != null)) {
240             return null;
241         }
242 
243         return constraint.getRange().toString();
244     }
245 }