1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.nuxeo.build.maven.graph;
18
19 import java.io.File;
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.HashMap;
23 import java.util.LinkedList;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.SortedMap;
27 import java.util.TreeMap;
28
29 import org.apache.maven.RepositoryUtils;
30 import org.apache.maven.artifact.Artifact;
31 import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
32 import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
33 import org.apache.maven.model.DependencyManagement;
34 import org.apache.maven.model.Exclusion;
35 import org.apache.maven.project.MavenProject;
36 import org.apache.tools.ant.BuildException;
37 import org.apache.tools.ant.Project;
38 import org.codehaus.plexus.util.StringUtils;
39 import org.eclipse.aether.artifact.ArtifactType;
40 import org.eclipse.aether.artifact.ArtifactTypeRegistry;
41 import org.eclipse.aether.collection.CollectRequest;
42 import org.eclipse.aether.collection.CollectResult;
43 import org.eclipse.aether.collection.DependencyCollectionException;
44 import org.eclipse.aether.graph.Dependency;
45 import org.eclipse.aether.graph.DependencyNode;
46 import org.eclipse.aether.resolution.DependencyResult;
47 import org.eclipse.aether.util.artifact.ArtifactIdUtils;
48 import org.eclipse.aether.util.artifact.JavaScopes;
49 import org.nuxeo.build.ant.AntClient;
50 import org.nuxeo.build.maven.AntBuildMojo;
51 import org.nuxeo.build.maven.ArtifactDescriptor;
52 import org.nuxeo.build.maven.filter.Filter;
53
54
55
56
57 public class Graph {
58
59 public final TreeMap<String, Node> nodes = new TreeMap<>();
60
61 public final List<Node> roots = new LinkedList<>();
62
63 private AntBuildMojo mojo = AntBuildMojo.getInstance();
64
65 public List<Node> getRoots() {
66 return roots;
67 }
68
69 public Collection<Node> getNodes() {
70 return nodes.values();
71 }
72
73
74
75
76
77
78
79
80 @Deprecated
81 public Node findFirst(String pattern) {
82 return findFirst(pattern, false);
83 }
84
85 public Node findFirst(String pattern, boolean stopIfNotUnique) {
86 if (pattern.contains("::")) {
87
88 return null;
89 }
90 SortedMap<String, Node> map = nodes.subMap(pattern + ':', pattern + ((char) (':' + 1)));
91 int size = map.size();
92 if (size == 0) {
93 return null;
94 }
95 if (stopIfNotUnique && size > 1) {
96 AntClient.getInstance().log(
97 String.format("Pattern '%s' cannot be resolved to a unique node. Matching nodes are: %s", pattern,
98 map.values()), Project.MSG_DEBUG);
99 return null;
100 }
101 return map.get(map.firstKey());
102 }
103
104 public Collection<Node> find(String pattern) {
105 SortedMap<String, Node> map = nodes.subMap(pattern + ':', pattern + ((char) (':' + 1)));
106 return map.values();
107 }
108
109
110
111
112 public Node addRootNode(MavenProject pom) {
113 Node node = nodes.get(Node.genNodeId(pom.getArtifact()));
114 if (node == null) {
115 node = collectRootNode(pom);
116 }
117 return addRootNode(node);
118 }
119
120 public Node addRootNode(String key) {
121 ArtifactDescriptor ad = new ArtifactDescriptor(key);
122 return addRootNode(ad.getDependency());
123 }
124
125
126
127
128 public Node addRootNode(Dependency dependency) {
129 Node node = nodes.get(Node.genNodeId(dependency));
130 if (node == null) {
131 node = collectRootNode(dependency);
132 } else {
133 roots.add(node);
134 AntClient.getInstance().log("Added root node: " + node, Project.MSG_DEBUG);
135 }
136 return node;
137 }
138
139
140
141
142 public Node addRootNode(Node node) {
143 if (!nodes.containsKey(node.id)) {
144 node = collectRootNode(node.getDependency());
145 } else if (!roots.contains(node)) {
146 roots.add(node);
147 AntClient.getInstance().log("Added root node: " + node, Project.MSG_DEBUG);
148 }
149 return nodes.get(node.id);
150 }
151
152 public Node collectRootNode(Dependency dependency) {
153 DependencyNode root = collectDependencies(dependency);
154 return addRootNode(root);
155 }
156
157
158
159
160 public Node addRootNode(DependencyNode root) {
161 Node node = new Node(this, root);
162 roots.add(node);
163 AntClient.getInstance().log("Added root node: " + node, Project.MSG_DEBUG);
164 addNode(node);
165 return node;
166 }
167
168
169
170
171 public Node collectRootNode(MavenProject pom) {
172 DependencyNode root = collectDependencies(pom);
173 return addRootNode(root);
174 }
175
176 private Node resolveRootNode(Node root, Filter filter, int depth) {
177 if (!filter.accept(root, null)) {
178 return null;
179 }
180 DependencyResult result = DependencyUtils.resolveDependencies(root, filter, depth);
181 Node node = new Node(this, result.getRoot());
182 roots.add(node);
183 AntClient.getInstance().log("Added resolved root node: " + node, Project.MSG_DEBUG);
184 addNode(node);
185 return node;
186 }
187
188
189
190
191 public void addNode(Node node) {
192 if (nodes.containsKey(node.getId())) {
193 return;
194 }
195 nodes.put(node.getId(), node);
196 AntClient.getInstance().log("Added node: " + node, Project.MSG_DEBUG);
197 if (!roots.contains(node)) {
198
199
200 List<DependencyNode> removes = new ArrayList<>();
201 for (DependencyNode child : node.getChildren()) {
202 String childScope = child.getDependency().getScope();
203 if (JavaScopes.PROVIDED.equals(childScope) || JavaScopes.TEST.equals(childScope)) {
204 AntClient.getInstance().log("Unexpected child node: " + child + " for " + node, Project.MSG_DEBUG);
205 removes.add(child);
206 }
207 }
208 node.getChildren().removeAll(removes);
209 }
210
211 for (DependencyNode child : node.getChildren()) {
212 Node childNode = nodes.get(Node.genNodeId(child));
213 if (childNode == null) {
214 childNode = new Node(this, child);
215 addNode(childNode);
216 }
217 childNode.addParent(node);
218 }
219 }
220
221 public Node findNode(ArtifactDescriptor ad) {
222 if (ad.isEmpty()) {
223 return null;
224 }
225 String key = ad.getNodeKeyPattern();
226 Collection<Node> nodesToParse = null;
227 if (key == null) {
228 nodesToParse = getNodes();
229 } else {
230 nodesToParse = find(key);
231 }
232 Node returnNode = null;
233 for (Node node : nodesToParse) {
234 Artifact artifact = node.getMavenArtifact();
235 if (ad.getArtifactId() != null && !ad.getArtifactId().equals(artifact.getArtifactId())) {
236 continue;
237 }
238 if (ad.getGroupId() != null && !ad.getGroupId().equals(artifact.getGroupId())) {
239 continue;
240 }
241 if (ad.getVersion() != null && !ad.getVersion().equals(artifact.getVersion())) {
242 continue;
243 }
244 if (ad.getType() != null && !ad.getType().equals(artifact.getType())) {
245 continue;
246 }
247 if (ad.getClassifier() != null && !ad.getClassifier().equals(artifact.getClassifier())
248 || ad.getClassifier() == null && artifact.hasClassifier()) {
249 continue;
250 }
251 try {
252 if (returnNode != null
253 && artifact.getSelectedVersion().compareTo(returnNode.getMavenArtifact().getSelectedVersion()) < 0) {
254 continue;
255 }
256 } catch (OverConstrainedVersionException e) {
257 mojo.getLog().error("Versions comparison failed on " + artifact, e);
258 }
259 returnNode = node;
260 }
261 return returnNode;
262 }
263
264 public DependencyNode collectDependencies(Dependency dependency) {
265 AntClient.getInstance().log(String.format("Collecting " + dependency), Project.MSG_DEBUG);
266 CollectRequest collectRequest = new CollectRequest(dependency, mojo.getRemoteRepositories());
267 collectRequest.setRequestContext("AAMP graph");
268 return collect(collectRequest);
269 }
270
271 protected DependencyNode collect(CollectRequest collectRequest) {
272 try {
273 CollectResult result = mojo.getSystem().collectDependencies(mojo.getSession(), collectRequest);
274 DependencyNode node = result.getRoot();
275 AntClient.getInstance().log("Collect result: " + result, Project.MSG_DEBUG);
276 AntClient.getInstance().log("Collect exceptions: " + result.getExceptions(), Project.MSG_DEBUG);
277 AntClient.getInstance()
278 .log("Direct dependencies: " + String.valueOf(node.getChildren()), Project.MSG_DEBUG);
279 return node;
280 } catch (DependencyCollectionException e) {
281 throw new BuildException("Cannot collect dependency tree for " + collectRequest, e);
282 }
283 }
284
285
286
287
288 public DependencyNode collectDependencies(MavenProject project) {
289 AntClient.getInstance().log(String.format("Collecting " + project), Project.MSG_DEBUG);
290 CollectRequest collectRequest = new CollectRequest();
291 Artifact rootArtifact = project.getArtifact();
292 rootArtifact.setFile(project.getFile());
293 collectRequest.setRootArtifact(RepositoryUtils.toArtifact(rootArtifact));
294 collectRequest.setRoot(RepositoryUtils.toDependency(rootArtifact, null));
295 collectRequest.setRequestContext("AAMP graph");
296 collectRequest.setRepositories(project.getRemoteProjectRepositories());
297
298 ArtifactTypeRegistry stereotypes = mojo.getSession().getArtifactTypeRegistry();
299
300
301 if (project.getDependencyArtifacts() == null) {
302 for (org.apache.maven.model.Dependency dependency : project.getDependencies()) {
303 if (StringUtils.isEmpty(dependency.getGroupId()) || StringUtils.isEmpty(dependency.getArtifactId())
304 || StringUtils.isEmpty(dependency.getVersion())) {
305
306 continue;
307 }
308 collectRequest.addDependency(RepositoryUtils.toDependency(dependency, stereotypes));
309 }
310 } else {
311 Map<String, org.apache.maven.model.Dependency> dependencies = new HashMap<>();
312 for (org.apache.maven.model.Dependency dependency : project.getDependencies()) {
313 String classifier = dependency.getClassifier();
314 if (classifier == null) {
315 ArtifactType type = stereotypes.get(dependency.getType());
316 if (type != null) {
317 classifier = type.getClassifier();
318 }
319 }
320 String key = ArtifactIdUtils.toVersionlessId(dependency.getGroupId(), dependency.getArtifactId(),
321 dependency.getType(), classifier);
322 dependencies.put(key, dependency);
323 }
324 for (Artifact artifact : project.getDependencyArtifacts()) {
325 String key = artifact.getDependencyConflictId();
326 org.apache.maven.model.Dependency dependency = dependencies.get(key);
327 Collection<Exclusion> exclusions = dependency != null ? dependency.getExclusions() : null;
328 org.eclipse.aether.graph.Dependency dep = RepositoryUtils.toDependency(artifact, exclusions);
329 if (!JavaScopes.SYSTEM.equals(dep.getScope()) && dep.getArtifact().getFile() != null) {
330
331 org.eclipse.aether.artifact.Artifact art = dep.getArtifact();
332 art = art.setFile(null).setVersion(art.getBaseVersion());
333 dep = dep.setArtifact(art);
334 }
335 collectRequest.addDependency(dep);
336 }
337 }
338
339 DependencyManagement depMngt = project.getDependencyManagement();
340 if (depMngt != null) {
341 for (org.apache.maven.model.Dependency dependency : depMngt.getDependencies()) {
342 collectRequest.addManagedDependency(RepositoryUtils.toDependency(dependency, stereotypes));
343 }
344 }
345
346 return collect(collectRequest);
347 }
348
349
350
351
352
353
354 public void resolveDependencies(Filter filter, int depth) {
355 List<Node> oldRoots = new LinkedList<>(roots);
356 roots.clear();
357 nodes.clear();
358 for (Node root : oldRoots) {
359 resolveRootNode(root, filter, depth);
360 }
361 }
362
363
364
365
366
367
368
369
370
371
372 protected void tryResolutionOnLocalBaseVersion(Artifact artifact, ArtifactNotFoundException e)
373 throws ArtifactNotFoundException {
374 String resolvedVersion = artifact.getVersion();
375 artifact.updateVersion(artifact.getBaseVersion(), mojo.getLocalRepository());
376 File localFile = new File(mojo.getLocalRepository().getBasedir(), mojo.getLocalRepository().pathOf(artifact));
377 if (localFile.exists()) {
378 mojo.getLog().warn(
379 String.format("Couldn't resolve %s, fallback on local install of unique version %s.",
380 resolvedVersion, artifact.getBaseVersion()));
381 artifact.setResolved(true);
382 } else {
383
384 artifact.updateVersion(resolvedVersion, mojo.getLocalRepository());
385 mojo.getLog().warn("Cannot resolve " + artifact, e);
386 throw e;
387 }
388 }
389
390
391
392
393 public Node getNode(Dependency dependency) {
394 return nodes.get(Node.genNodeId(dependency));
395 }
396
397
398
399
400
401
402 public Node findNode(String key) throws BuildException {
403 return findNode(key, ArtifactDescriptor.parseQuietly(key));
404 }
405
406
407
408
409
410
411
412 public Node findNode(String key, ArtifactDescriptor ad) throws BuildException {
413 Node node = null;
414 if (key != null) {
415 node = findFirst(key, true);
416 }
417 if (node == null && ad != null) {
418 node = findNode(ad);
419 }
420 if (node == null) {
421 throw new BuildException("Artifact with pattern " + (key != null ? key : ad) + " was not found in graph");
422 }
423 return node;
424 }
425
426 }