1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.enforcer.rules.dependency;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.Collections;
27 import java.util.List;
28 import java.util.Objects;
29
30 import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
31 import org.apache.maven.enforcer.rules.AbstractStandardEnforcerRule;
32 import org.apache.maven.enforcer.rules.utils.ArtifactUtils;
33 import org.eclipse.aether.graph.DependencyNode;
34
35 import static org.apache.maven.artifact.Artifact.SCOPE_PROVIDED;
36 import static org.apache.maven.artifact.Artifact.SCOPE_TEST;
37
38
39
40
41 @Named("dependencyConvergence")
42 public final class DependencyConvergence extends AbstractStandardEnforcerRule {
43
44
45
46 private boolean uniqueVersions;
47
48 private List<String> includes;
49
50 private List<String> excludes;
51
52 private List<String> excludedScopes = Arrays.asList(SCOPE_TEST, SCOPE_PROVIDED);
53
54
55
56 private DependencyVersionMap dependencyVersionMap;
57
58 private final ResolverUtil resolverUtil;
59
60 @Inject
61 public DependencyConvergence(ResolverUtil resolverUtil) {
62 this.resolverUtil = Objects.requireNonNull(resolverUtil);
63 }
64
65 @Override
66 public void execute() throws EnforcerRuleException {
67
68 DependencyNode node = resolverUtil.resolveTransitiveDependenciesVerbose(excludedScopes);
69 dependencyVersionMap = new DependencyVersionMap().setUniqueVersions(uniqueVersions);
70 node.accept(dependencyVersionMap);
71
72 List<String> errorMsgs =
73 getConvergenceErrorMsgs(dependencyVersionMap.getConflictedVersionNumbers(includes, excludes));
74
75 if (!errorMsgs.isEmpty()) {
76 throw new EnforcerRuleException("Failed while enforcing releasability." + System.lineSeparator()
77 + String.join(System.lineSeparator(), errorMsgs));
78 }
79 }
80
81 private StringBuilder buildTreeString(DependencyNode node) {
82 List<String> loc = new ArrayList<>();
83 DependencyNode currentNode = node;
84 while (currentNode != null) {
85
86 loc.add(ArtifactUtils.toArtifact(currentNode).toString());
87 currentNode = dependencyVersionMap.getParent(currentNode);
88 }
89 Collections.reverse(loc);
90 StringBuilder builder = new StringBuilder();
91 for (int i = 0; i < loc.size(); i++) {
92 for (int j = 0; j < i; j++) {
93 builder.append(" ");
94 }
95 builder.append("+-").append(loc.get(i)).append(System.lineSeparator());
96 }
97 return builder;
98 }
99
100 private List<String> getConvergenceErrorMsgs(List<List<DependencyNode>> errors) {
101 List<String> errorMsgs = new ArrayList<>();
102 for (List<DependencyNode> nodeList : errors) {
103 errorMsgs.add(buildConvergenceErrorMsg(nodeList));
104 }
105 return errorMsgs;
106 }
107
108 private String buildConvergenceErrorMsg(List<DependencyNode> nodeList) {
109 StringBuilder builder = new StringBuilder();
110 builder.append(System.lineSeparator())
111 .append("Dependency convergence error for ")
112 .append(nodeList.get(0).getArtifact().toString())
113 .append(" paths to dependency are:")
114 .append(System.lineSeparator());
115 if (nodeList.size() > 0) {
116 builder.append(buildTreeString(nodeList.get(0)));
117 }
118 for (DependencyNode node : nodeList.subList(1, nodeList.size())) {
119 builder.append("and").append(System.lineSeparator()).append(buildTreeString(node));
120 }
121 return builder.toString();
122 }
123
124 @Override
125 public String toString() {
126 return String.format(
127 "DependencyConvergence[includes=%s, excludes=%s, uniqueVersions=%b]",
128 includes, excludes, uniqueVersions);
129 }
130 }