Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MENFORCER-435] Replacing maven-compat and maven-dependency-tree usage with Resolver #198

Merged
merged 1 commit into from
Dec 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>
jarmoniak marked this conversation as resolved.
Show resolved Hide resolved
<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 @@
*/
slawekjaranowski marked this conversation as resolved.
Show resolved Hide resolved
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