This is an automated email from the ASF dual-hosted git repository. sjaranowski pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/maven-enforcer.git
The following commit(s) were added to refs/heads/master by this push: new d58be76f Improve performance of transitive dependency checks (#904) d58be76f is described below commit d58be76facad6e44838ca1ec2f805458f01a66ad Author: harrisric <48761651+harris...@users.noreply.github.com> AuthorDate: Wed Jul 9 19:54:24 2025 +0100 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 --- .../rules/dependency/BannedDependenciesBase.java | 17 ++++-- .../enforcer/rules/utils/ArtifactMatcher.java | 63 ++++++++++++++++++++-- 2 files changed, 71 insertions(+), 9 deletions(-) diff --git a/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/dependency/BannedDependenciesBase.java b/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/dependency/BannedDependenciesBase.java index 1bf3fa3b..f06e414c 100644 --- a/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/dependency/BannedDependenciesBase.java +++ b/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/dependency/BannedDependenciesBase.java @@ -19,13 +19,16 @@ package org.apache.maven.enforcer.rules.dependency; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Set; import org.apache.commons.lang3.StringUtils; import org.apache.maven.artifact.Artifact; import org.apache.maven.enforcer.rule.api.EnforcerRuleException; import org.apache.maven.enforcer.rules.AbstractStandardEnforcerRule; +import org.apache.maven.enforcer.rules.utils.ArtifactMatcher.MatchingArtifact; import org.apache.maven.enforcer.rules.utils.ArtifactUtils; import org.apache.maven.execution.MavenSession; import org.eclipse.aether.graph.DependencyNode; @@ -104,7 +107,8 @@ abstract class BannedDependenciesBase extends AbstractStandardEnforcerRule { } else { StringBuilder messageBuilder = new StringBuilder(); DependencyNode rootNode = resolverUtil.resolveTransitiveDependenciesVerbose(Collections.emptyList()); - if (!validate(rootNode, 0, messageBuilder)) { + Set<MatchingArtifact> visitedArtifacts = new HashSet<>(); + if (!validate(rootNode, 0, messageBuilder, visitedArtifacts)) { String message = ""; if (getMessage() != null) { message = getMessage() + System.lineSeparator(); @@ -114,12 +118,17 @@ abstract class BannedDependenciesBase extends AbstractStandardEnforcerRule { } } - protected boolean validate(DependencyNode node, int level, StringBuilder messageBuilder) { - boolean rootFailed = level > 0 && !validate(ArtifactUtils.toArtifact(node)); + protected boolean validate( + DependencyNode node, int level, StringBuilder messageBuilder, Set<MatchingArtifact> visitedArtifacts) { + Artifact artifact = ArtifactUtils.toArtifact(node); + boolean rootFailed = false; + if (level > 0 && visitedArtifacts.add(new MatchingArtifact(artifact))) { + rootFailed = !validate(artifact); + } StringBuilder childMessageBuilder = new StringBuilder(); if (rootFailed || !node.getChildren().stream() - .map(childNode -> validate(childNode, level + 1, childMessageBuilder)) + .map(childNode -> validate(childNode, level + 1, childMessageBuilder, visitedArtifacts)) .reduce(true, Boolean::logicalAnd)) { messageBuilder .append(StringUtils.repeat(" ", level)) diff --git a/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/utils/ArtifactMatcher.java b/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/utils/ArtifactMatcher.java index caf2c599..fd4e204d 100644 --- a/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/utils/ArtifactMatcher.java +++ b/enforcer-rules/src/main/java/org/apache/maven/enforcer/rules/utils/ArtifactMatcher.java @@ -22,6 +22,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.Objects; import java.util.function.Function; +import java.util.function.Predicate; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.versioning.ArtifactVersion; @@ -47,7 +48,7 @@ public final class ArtifactMatcher { private final String pattern; private final String[] parts; - private final java.util.regex.Pattern[] partsRegex; + private final Predicate<String>[] partsRegex; public Pattern(String pattern) { if (pattern == null) { @@ -67,7 +68,7 @@ public final class ArtifactMatcher { throw new IllegalArgumentException("Pattern or its part is empty."); } } - partsRegex = new java.util.regex.Pattern[parts.length]; + partsRegex = new Predicate[parts.length]; } public boolean match(Artifact artifact) { @@ -149,7 +150,6 @@ public final class ArtifactMatcher { if (input == null) { input = ""; } - // return matches(parts[index], input); if (partsRegex[index] == null) { String regex = parts[index] .replace(".", "\\.") @@ -161,9 +161,14 @@ public final class ArtifactMatcher { .replace("(", "\\(") .replace(")", "\\)"); - partsRegex[index] = java.util.regex.Pattern.compile(regex); + if (".*".equals(regex)) { + partsRegex[index] = test -> true; + } else { + partsRegex[index] = test -> + java.util.regex.Pattern.compile(regex).matcher(test).matches(); + } } - return partsRegex[index].matcher(input).matches(); + return partsRegex[index].test(input); } @Override @@ -240,4 +245,52 @@ public final class ArtifactMatcher { return compareTo <= 0; } } + + /** + * To be used for artifacts which are equivalent for the purposes of the {@link ArtifactMatcher}. + */ + public static class MatchingArtifact { + String artifactString; + + public MatchingArtifact(Artifact artifact) { + artifactString = new StringBuilder() + .append(artifact.getGroupId()) + .append(":") + .append(artifact.getArtifactId()) + .append(":") + .append(artifact.getVersion()) + .append(":") + .append(artifact.getType()) + .append(":") + .append(artifact.getScope()) + .append(":") + .append(artifact.getClassifier()) + .toString(); + } + + @Override + public int hashCode() { + return artifactString.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + MatchingArtifact other = (MatchingArtifact) obj; + return Objects.equals(artifactString, other.artifactString); + } + + @Override + public String toString() { + return artifactString; + } + } }