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.List;
25 import java.util.Objects;
26 import java.util.Set;
27 import java.util.stream.Collectors;
28
29 import org.apache.commons.lang3.StringUtils;
30 import org.apache.maven.RepositoryUtils;
31 import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
32 import org.apache.maven.enforcer.rules.AbstractStandardEnforcerRule;
33 import org.apache.maven.enforcer.rules.utils.ArtifactMatcher;
34 import org.apache.maven.enforcer.rules.utils.ArtifactUtils;
35 import org.apache.maven.execution.MavenSession;
36 import org.eclipse.aether.artifact.ArtifactTypeRegistry;
37 import org.eclipse.aether.graph.Dependency;
38 import org.eclipse.aether.graph.DependencyNode;
39
40 import static java.util.Optional.ofNullable;
41
42
43
44
45
46
47
48 @Named("banTransitiveDependencies")
49 public final class BanTransitiveDependencies extends AbstractStandardEnforcerRule {
50
51
52
53
54
55
56
57
58 private List<String> excludes;
59
60
61
62
63
64
65
66
67 private List<String> includes;
68
69 private final MavenSession session;
70
71 private final ResolverUtil resolverUtil;
72
73 @Inject
74 public BanTransitiveDependencies(MavenSession session, ResolverUtil resolverUtil) {
75 this.session = Objects.requireNonNull(session);
76 this.resolverUtil = Objects.requireNonNull(resolverUtil);
77 }
78
79
80
81
82
83 private static boolean searchTree(
84 DependencyNode node,
85 int level,
86 ArtifactMatcher excludes,
87 Set<Dependency> directDependencies,
88 StringBuilder message) {
89
90 List<DependencyNode> children = node.getChildren();
91
92
93
94
95 boolean hasTransitiveDependencies = level > 1;
96
97 boolean excluded = false;
98
99
100
101
102
103 StringBuilder messageFromChildren = message == null ? null : new StringBuilder();
104
105 if (excludes.match(ArtifactUtils.toArtifact(node))) {
106
107 excluded = true;
108 hasTransitiveDependencies = false;
109 } else if (directDependencies.contains(node.getDependency())) {
110 hasTransitiveDependencies = false;
111 } else {
112 for (DependencyNode childNode : children) {
113
114
115
116 hasTransitiveDependencies = hasTransitiveDependencies
117 || searchTree(childNode, level + 1, excludes, directDependencies, messageFromChildren);
118 }
119 }
120
121 if ((excluded || hasTransitiveDependencies) && message != null)
122 {
123 message.append(StringUtils.repeat(" ", level)).append(node.getArtifact());
124
125 if (excluded) {
126 message.append(" [excluded]").append(System.lineSeparator());
127 }
128
129 if (hasTransitiveDependencies) {
130 if (level > 0) {
131 message.append(" has transitive dependencies:");
132 }
133
134 message.append(System.lineSeparator()).append(messageFromChildren);
135 }
136 }
137
138 return hasTransitiveDependencies;
139 }
140
141 @Override
142 public void execute() throws EnforcerRuleException {
143 ArtifactTypeRegistry artifactTypeRegistry =
144 session.getRepositorySession().getArtifactTypeRegistry();
145 ArtifactMatcher exclusions = new ArtifactMatcher(excludes, includes);
146 Set<Dependency> directDependencies = session.getCurrentProject().getDependencies().stream()
147 .map(d -> RepositoryUtils.toDependency(d, artifactTypeRegistry))
148 .collect(Collectors.toSet());
149
150 DependencyNode rootNode = resolverUtil.resolveTransitiveDependencies();
151 StringBuilder generatedMessage = new StringBuilder();
152 if (searchTree(rootNode, 0, exclusions, directDependencies, generatedMessage)) {
153 throw new EnforcerRuleException(ofNullable(getMessage()).orElse(generatedMessage.toString()));
154 }
155 }
156
157 @Override
158 public String toString() {
159 return String.format("BanTransitiveDependencies[message=%s, excludes=%s]", getMessage(), excludes);
160 }
161 }