Skip to content

Commit

Permalink
[MENFORCER-435] Replacing maven-compat and maven-dependency-tree usag…
Browse files Browse the repository at this point in the history
…e with Resolver
  • Loading branch information
jarmoniuk committed Dec 23, 2022
1 parent 374d7d6 commit d453c5f
Show file tree
Hide file tree
Showing 26 changed files with 933 additions and 759 deletions.
3 changes: 2 additions & 1 deletion enforcer-rules/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<!-- TODO: Consider removing this in 4.0+ -->
<dependency>
<groupId>org.apache.maven.shared</groupId>
<artifactId>maven-dependency-tree</artifactId>
Expand All @@ -127,7 +128,7 @@
</exclusion>
</exclusions>
</dependency>
<!-- needed for ArtifactCollector and maven-dependency-tree 2.2 -->
<!-- TODO: Consider removing this in 4.0+ -->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-compat</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,14 @@
*/
package org.apache.maven.plugins.enforcer;

import java.util.HashSet;
import java.util.Set;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.enforcer.utils.ArtifactUtils;
import org.apache.maven.project.DefaultProjectBuildingRequest;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
import org.apache.maven.shared.dependency.graph.DependencyNode;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;

/**
* Abstract Rule for banning dependencies.
Expand All @@ -45,51 +37,36 @@ public abstract class AbstractBanDependencies extends AbstractNonCacheableEnforc
/** Specify if transitive dependencies should be searched (default) or only look at direct dependencies. */
private boolean searchTransitive = true;

private transient DependencyGraphBuilder graphBuilder;

@Override
public void execute(EnforcerRuleHelper helper) throws EnforcerRuleException {
MavenProject project;
try {
project = (MavenProject) helper.evaluate("${project}");
} catch (ExpressionEvaluationException eee) {
throw new EnforcerRuleException("Unable to retrieve the MavenProject: ", eee);
}

MavenSession session;
try {
session = (MavenSession) helper.evaluate("${session}");
} catch (ExpressionEvaluationException eee) {
throw new EnforcerRuleException("Unable to retrieve the reactor MavenProject: ", eee);
} catch (ExpressionEvaluationException e) {
throw new EnforcerRuleException("Cannot resolve MavenSession", e);
}

try {
graphBuilder = helper.getComponent(DependencyGraphBuilder.class);
} catch (ComponentLookupException e) {
throw new EnforcerRuleException("Unable to lookup DependencyGraphBuilder: ", e);
}

ProjectBuildingRequest buildingRequest = new DefaultProjectBuildingRequest(session.getProjectBuildingRequest());
buildingRequest.setProject(project);

// get the correct list of dependencies
Set<Artifact> dependencies = getDependenciesToCheck(helper, buildingRequest);
Set<Artifact> dependencyArtifacts = searchTransitive
? ArtifactUtils.getDependencyArtifacts(ArtifactUtils.resolveTransitiveDependencies(helper))
: session.getCurrentProject().getDependencyArtifacts();

// look for banned dependencies
Set<Artifact> foundExcludes = checkDependencies(dependencies, helper.getLog());
Set<Artifact> bannedDependencies = checkDependencies(dependencyArtifacts, helper.getLog());

// if any are found, fail the check but list all of them
if (foundExcludes != null && !foundExcludes.isEmpty()) {
if (bannedDependencies != null && !bannedDependencies.isEmpty()) {
String message = getMessage();

StringBuilder buf = new StringBuilder();
if (message != null) {
buf.append(message + System.lineSeparator());
buf.append(message).append(System.lineSeparator());
}
for (Artifact artifact : foundExcludes) {
for (Artifact artifact : bannedDependencies) {
buf.append(getErrorMessage(artifact));
}
message = buf.toString() + "Use 'mvn dependency:tree' to locate the source of the banned dependencies.";
message = buf.append("Use 'mvn dependency:tree' to locate the source of the banned dependencies.")
.toString();

throw new EnforcerRuleException(message);
}
Expand All @@ -99,40 +76,11 @@ protected CharSequence getErrorMessage(Artifact artifact) {
return "Found Banned Dependency: " + artifact.getId() + System.lineSeparator();
}

private Set<Artifact> getDependenciesToCheck(EnforcerRuleHelper helper, ProjectBuildingRequest buildingRequest) {
String cacheKey = buildingRequest.getProject().getId() + "_" + searchTransitive;

// check in the cache
Set<Artifact> dependencies =
(Set<Artifact>) helper.getCache(cacheKey, () -> getDependenciesToCheck(buildingRequest));

return dependencies;
}

protected Set<Artifact> getDependenciesToCheck(ProjectBuildingRequest buildingRequest) {
Set<Artifact> dependencies = null;
try {
DependencyNode node = graphBuilder.buildDependencyGraph(buildingRequest, null);
if (searchTransitive) {
dependencies = ArtifactUtils.getAllDescendants(node);
} else if (node.getChildren() != null) {
dependencies = new HashSet<>();
for (DependencyNode depNode : node.getChildren()) {
dependencies.add(depNode.getArtifact());
}
}
} catch (DependencyGraphBuilderException e) {
// otherwise we need to change the signature of this protected method
throw new RuntimeException(e);
}
return dependencies;
}

/**
* Checks the set of dependencies against the list of excludes.
*
* @param dependencies the dependencies
* @param log the log
* @param dependencies dependencies to be checked against the list of excludes
* @param log the log
* @return the sets the
* @throws EnforcerRuleException the enforcer rule exception
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
import org.apache.maven.plugin.logging.Log;
import org.apache.maven.plugins.enforcer.utils.ArtifactMatcher;
import org.apache.maven.plugins.enforcer.utils.ArtifactUtils;
import org.apache.maven.project.MavenProject;
import org.apache.maven.shared.utils.logging.MessageBuilder;
import org.apache.maven.shared.utils.logging.MessageUtils;
Expand Down Expand Up @@ -291,7 +292,7 @@ private static final class ExcludeArtifactPatternsPredicate implements Predicate
@Override
public boolean test(DependencyNode depNode) {
try {
return artifactMatcher.match(RepositoryUtils.toArtifact(depNode.getArtifact()));
return artifactMatcher.match(ArtifactUtils.toArtifact(depNode));
} catch (InvalidVersionSpecificationException e) {
throw new IllegalArgumentException("Invalid version found for dependency node " + depNode, e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,22 +18,24 @@
*/
package org.apache.maven.plugins.enforcer;

import java.util.Collections;
import static java.util.Optional.ofNullable;

import com.google.common.base.Strings;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import org.apache.maven.RepositoryUtils;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.enforcer.rule.api.EnforcerRule;
import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
import org.apache.maven.execution.MavenSession;
import org.apache.maven.plugins.enforcer.utils.ArtifactMatcher;
import org.apache.maven.project.DefaultProjectBuildingRequest;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.ProjectBuildingRequest;
import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
import org.apache.maven.shared.dependency.graph.DependencyNode;
import org.apache.maven.shared.dependency.graph.internal.DefaultDependencyGraphBuilder;
import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
import org.codehaus.plexus.logging.console.ConsoleLogger;
import org.apache.maven.plugins.enforcer.utils.ArtifactUtils;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
import org.eclipse.aether.artifact.ArtifactTypeRegistry;
import org.eclipse.aether.graph.Dependency;
import org.eclipse.aether.graph.DependencyNode;

/**
* This rule bans all transitive dependencies. There is a configuration option to exclude certain artifacts from being
Expand All @@ -43,8 +45,6 @@
*/
public class BanTransitiveDependencies extends AbstractNonCacheableEnforcerRule implements EnforcerRule {

private EnforcerRuleHelper helper;

/**
* Specify the dependencies that will be ignored. This can be a list of artifacts in the format
* <code>groupId[:artifactId][:version][:type][:scope]</code>. Wildcard '*' can be used to in place of specific
Expand All @@ -69,7 +69,12 @@ public class BanTransitiveDependencies extends AbstractNonCacheableEnforcerRule
*
* @throws InvalidVersionSpecificationException
*/
private static boolean searchTree(DependencyNode node, int level, ArtifactMatcher excludes, StringBuilder message)
private static boolean searchTree(
DependencyNode node,
int level,
ArtifactMatcher excludes,
Set<Dependency> directDependencies,
StringBuilder message)
throws InvalidVersionSpecificationException {

List<DependencyNode> children = node.getChildren();
Expand All @@ -87,34 +92,32 @@ private static boolean searchTree(DependencyNode node, int level, ArtifactMatche
*/
StringBuilder messageFromChildren = message == null ? null : new StringBuilder();

if (excludes.match(node.getArtifact())) {
if (excludes.match(ArtifactUtils.toArtifact(node))) {
// is excluded, we don't care about descendants
excluded = true;
hasTransitiveDependencies = false;
} else if (directDependencies.contains(node.getDependency())) {
hasTransitiveDependencies = false;
} else {
for (DependencyNode childNode : children) {
/*
* if any of the children has transitive d. so does the parent
*/
hasTransitiveDependencies =
(searchTree(childNode, level + 1, excludes, messageFromChildren) || hasTransitiveDependencies);
hasTransitiveDependencies = hasTransitiveDependencies
|| searchTree(childNode, level + 1, excludes, directDependencies, messageFromChildren);
}
}

if ((excluded || hasTransitiveDependencies) && message != null) // then generate message
{
for (int i = 0; i < level; i++) {
message.append(" ");
}

message.append(node.getArtifact());
message.append(Strings.repeat(" ", level)).append(node.getArtifact());

if (excluded) {
message.append(" [excluded]" + System.lineSeparator());
message.append(" [excluded]").append(System.lineSeparator());
}

if (hasTransitiveDependencies) {
if (level == 1) {
if (level > 0) {
message.append(" has transitive dependencies:");
}

Expand All @@ -127,55 +130,27 @@ private static boolean searchTree(DependencyNode node, int level, ArtifactMatche

@Override
public void execute(EnforcerRuleHelper helper) throws EnforcerRuleException {
this.helper = helper;

if (excludes == null) {
excludes = Collections.emptyList();
}
if (includes == null) {
includes = Collections.emptyList();
}

final ArtifactMatcher exclusions = new ArtifactMatcher(excludes, includes);

DependencyNode rootNode = null;

MavenSession session;
try {
MavenProject project = (MavenProject) helper.evaluate("${project}");
MavenSession session = (MavenSession) helper.evaluate("${session}");

ProjectBuildingRequest buildingRequest =
new DefaultProjectBuildingRequest(session.getProjectBuildingRequest());
buildingRequest.setProject(project);

rootNode = createDependencyGraphBuilder().buildDependencyGraph(buildingRequest, null);
} catch (Exception e) {
throw new EnforcerRuleException("Error: Could not construct dependency tree.", e);
}

String message = getMessage();
StringBuilder generatedMessage = null;
if (message == null) {
generatedMessage = new StringBuilder();
session = (MavenSession) helper.evaluate("${session}");
} catch (ExpressionEvaluationException e) {
throw new RuntimeException(e);
}

ArtifactTypeRegistry artifactTypeRegistry =
session.getRepositorySession().getArtifactTypeRegistry();
ArtifactMatcher exclusions = new ArtifactMatcher(excludes, includes);
Set<Dependency> directDependencies = session.getCurrentProject().getDependencies().stream()
.map(d -> RepositoryUtils.toDependency(d, artifactTypeRegistry))
.collect(Collectors.toSet());

DependencyNode rootNode = ArtifactUtils.resolveTransitiveDependencies(helper);
StringBuilder generatedMessage = new StringBuilder();
try {
if (searchTree(rootNode, 0, exclusions, generatedMessage)) {
throw new EnforcerRuleException(message == null ? generatedMessage.toString() : message);
if (searchTree(rootNode, 0, exclusions, directDependencies, generatedMessage)) {
throw new EnforcerRuleException(ofNullable(getMessage()).orElse(generatedMessage.toString()));
}
} catch (InvalidVersionSpecificationException e) {
throw new EnforcerRuleException("Error: Invalid version range.", e);
}
}

private DependencyGraphBuilder createDependencyGraphBuilder() throws ComponentLookupException {
// CHECKSTYLE_OFF: LineLength
DefaultDependencyGraphBuilder builder = (DefaultDependencyGraphBuilder)
helper.getContainer().lookup(DependencyGraphBuilder.class.getCanonicalName(), "default");
// CHECKSTYLE_ON: LineLength

builder.enableLogging(new ConsoleLogger(ConsoleLogger.LEVEL_DISABLED, "DefaultDependencyGraphBuilder"));

return builder;
}
}
Loading

0 comments on commit d453c5f

Please sign in to comment.