Skip to content

Commit d58be76

Browse files
authored
Improve performance of transitive dependency checks (#904)
* Keep track of artifacts already validated during transitive dependency enforcement * Optimise common case where part of Pattern accepts everything
1 parent 1f7ac3c commit d58be76

File tree

2 files changed

+71
-9
lines changed

2 files changed

+71
-9
lines changed

enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/dependency/BannedDependenciesBase.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@
1919
package org.apache.maven.enforcer.rules.dependency;
2020

2121
import java.util.Collections;
22+
import java.util.HashSet;
2223
import java.util.List;
2324
import java.util.Objects;
25+
import java.util.Set;
2426

2527
import org.apache.commons.lang3.StringUtils;
2628
import org.apache.maven.artifact.Artifact;
2729
import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
2830
import org.apache.maven.enforcer.rules.AbstractStandardEnforcerRule;
31+
import org.apache.maven.enforcer.rules.utils.ArtifactMatcher.MatchingArtifact;
2932
import org.apache.maven.enforcer.rules.utils.ArtifactUtils;
3033
import org.apache.maven.execution.MavenSession;
3134
import org.eclipse.aether.graph.DependencyNode;
@@ -104,7 +107,8 @@ public void execute() throws EnforcerRuleException {
104107
} else {
105108
StringBuilder messageBuilder = new StringBuilder();
106109
DependencyNode rootNode = resolverUtil.resolveTransitiveDependenciesVerbose(Collections.emptyList());
107-
if (!validate(rootNode, 0, messageBuilder)) {
110+
Set<MatchingArtifact> visitedArtifacts = new HashSet<>();
111+
if (!validate(rootNode, 0, messageBuilder, visitedArtifacts)) {
108112
String message = "";
109113
if (getMessage() != null) {
110114
message = getMessage() + System.lineSeparator();
@@ -114,12 +118,17 @@ public void execute() throws EnforcerRuleException {
114118
}
115119
}
116120

117-
protected boolean validate(DependencyNode node, int level, StringBuilder messageBuilder) {
118-
boolean rootFailed = level > 0 && !validate(ArtifactUtils.toArtifact(node));
121+
protected boolean validate(
122+
DependencyNode node, int level, StringBuilder messageBuilder, Set<MatchingArtifact> visitedArtifacts) {
123+
Artifact artifact = ArtifactUtils.toArtifact(node);
124+
boolean rootFailed = false;
125+
if (level > 0 && visitedArtifacts.add(new MatchingArtifact(artifact))) {
126+
rootFailed = !validate(artifact);
127+
}
119128
StringBuilder childMessageBuilder = new StringBuilder();
120129
if (rootFailed
121130
|| !node.getChildren().stream()
122-
.map(childNode -> validate(childNode, level + 1, childMessageBuilder))
131+
.map(childNode -> validate(childNode, level + 1, childMessageBuilder, visitedArtifacts))
123132
.reduce(true, Boolean::logicalAnd)) {
124133
messageBuilder
125134
.append(StringUtils.repeat(" ", level))

enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/utils/ArtifactMatcher.java

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.util.HashSet;
2323
import java.util.Objects;
2424
import java.util.function.Function;
25+
import java.util.function.Predicate;
2526

2627
import org.apache.maven.artifact.Artifact;
2728
import org.apache.maven.artifact.versioning.ArtifactVersion;
@@ -47,7 +48,7 @@ public static class Pattern {
4748
private final String pattern;
4849

4950
private final String[] parts;
50-
private final java.util.regex.Pattern[] partsRegex;
51+
private final Predicate<String>[] partsRegex;
5152

5253
public Pattern(String pattern) {
5354
if (pattern == null) {
@@ -67,7 +68,7 @@ public Pattern(String pattern) {
6768
throw new IllegalArgumentException("Pattern or its part is empty.");
6869
}
6970
}
70-
partsRegex = new java.util.regex.Pattern[parts.length];
71+
partsRegex = new Predicate[parts.length];
7172
}
7273

7374
public boolean match(Artifact artifact) {
@@ -149,7 +150,6 @@ private boolean matches(int index, String input) {
149150
if (input == null) {
150151
input = "";
151152
}
152-
// return matches(parts[index], input);
153153
if (partsRegex[index] == null) {
154154
String regex = parts[index]
155155
.replace(".", "\\.")
@@ -161,9 +161,14 @@ private boolean matches(int index, String input) {
161161
.replace("(", "\\(")
162162
.replace(")", "\\)");
163163

164-
partsRegex[index] = java.util.regex.Pattern.compile(regex);
164+
if (".*".equals(regex)) {
165+
partsRegex[index] = test -> true;
166+
} else {
167+
partsRegex[index] = test ->
168+
java.util.regex.Pattern.compile(regex).matcher(test).matches();
169+
}
165170
}
166-
return partsRegex[index].matcher(input).matches();
171+
return partsRegex[index].test(input);
167172
}
168173

169174
@Override
@@ -240,4 +245,52 @@ public static boolean containsVersion(VersionRange allowedRange, ArtifactVersion
240245
return compareTo <= 0;
241246
}
242247
}
248+
249+
/**
250+
* To be used for artifacts which are equivalent for the purposes of the {@link ArtifactMatcher}.
251+
*/
252+
public static class MatchingArtifact {
253+
String artifactString;
254+
255+
public MatchingArtifact(Artifact artifact) {
256+
artifactString = new StringBuilder()
257+
.append(artifact.getGroupId())
258+
.append(":")
259+
.append(artifact.getArtifactId())
260+
.append(":")
261+
.append(artifact.getVersion())
262+
.append(":")
263+
.append(artifact.getType())
264+
.append(":")
265+
.append(artifact.getScope())
266+
.append(":")
267+
.append(artifact.getClassifier())
268+
.toString();
269+
}
270+
271+
@Override
272+
public int hashCode() {
273+
return artifactString.hashCode();
274+
}
275+
276+
@Override
277+
public boolean equals(Object obj) {
278+
if (this == obj) {
279+
return true;
280+
}
281+
if (obj == null) {
282+
return false;
283+
}
284+
if (getClass() != obj.getClass()) {
285+
return false;
286+
}
287+
MatchingArtifact other = (MatchingArtifact) obj;
288+
return Objects.equals(artifactString, other.artifactString);
289+
}
290+
291+
@Override
292+
public String toString() {
293+
return artifactString;
294+
}
295+
}
243296
}

0 commit comments

Comments
 (0)