001package org.apache.maven.plugins.enforcer; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.maven.artifact.Artifact; 023import org.apache.maven.enforcer.rule.api.EnforcerRuleException; 024import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper; 025import org.apache.maven.execution.MavenSession; 026import org.apache.maven.plugin.logging.Log; 027import org.apache.maven.plugins.enforcer.utils.ArtifactUtils; 028import org.apache.maven.project.DefaultProjectBuildingRequest; 029import org.apache.maven.project.MavenProject; 030import org.apache.maven.project.ProjectBuildingRequest; 031import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder; 032import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException; 033import org.apache.maven.shared.dependency.graph.DependencyNode; 034import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; 035import org.codehaus.plexus.component.repository.exception.ComponentLookupException; 036 037import java.util.HashSet; 038import java.util.Set; 039 040/** 041 * Abstract Rule for banning dependencies. 042 * 043 * @author <a href="mailto:brianf@apache.org">Brian Fox</a> 044 */ 045public abstract class AbstractBanDependencies 046 extends AbstractNonCacheableEnforcerRule 047{ 048 049 /** Specify if transitive dependencies should be searched (default) or only look at direct dependencies. */ 050 private boolean searchTransitive = true; 051 052 private transient DependencyGraphBuilder graphBuilder; 053 054 @Override 055 public void execute( EnforcerRuleHelper helper ) 056 throws EnforcerRuleException 057 { 058 MavenProject project; 059 try 060 { 061 project = (MavenProject) helper.evaluate( "${project}" ); 062 } 063 catch ( ExpressionEvaluationException eee ) 064 { 065 throw new EnforcerRuleException( "Unable to retrieve the MavenProject: ", eee ); 066 } 067 068 MavenSession session; 069 try 070 { 071 session = (MavenSession) helper.evaluate( "${session}" ); 072 } 073 catch ( ExpressionEvaluationException eee ) 074 { 075 throw new EnforcerRuleException( "Unable to retrieve the reactor MavenProject: ", eee ); 076 } 077 078 try 079 { 080 graphBuilder = helper.getComponent( DependencyGraphBuilder.class ); 081 } 082 catch ( ComponentLookupException e ) 083 { 084 throw new EnforcerRuleException( "Unable to lookup DependencyGraphBuilder: ", e ); 085 } 086 087 ProjectBuildingRequest buildingRequest = 088 new DefaultProjectBuildingRequest( session.getProjectBuildingRequest() ); 089 buildingRequest.setProject( project ); 090 091 // get the correct list of dependencies 092 Set<Artifact> dependencies = getDependenciesToCheck( helper, buildingRequest ); 093 094 // look for banned dependencies 095 Set<Artifact> foundExcludes = checkDependencies( dependencies, helper.getLog() ); 096 097 // if any are found, fail the check but list all of them 098 if ( foundExcludes != null && !foundExcludes.isEmpty() ) 099 { 100 String message = getMessage(); 101 102 StringBuilder buf = new StringBuilder(); 103 if ( message != null ) 104 { 105 buf.append( message + System.lineSeparator() ); 106 } 107 for ( Artifact artifact : foundExcludes ) 108 { 109 buf.append( getErrorMessage( artifact ) ); 110 } 111 message = buf.toString() + "Use 'mvn dependency:tree' to locate the source of the banned dependencies."; 112 113 throw new EnforcerRuleException( message ); 114 } 115 116 } 117 118 protected CharSequence getErrorMessage( Artifact artifact ) 119 { 120 return "Found Banned Dependency: " + artifact.getId() + System.lineSeparator(); 121 } 122 123 private Set<Artifact> getDependenciesToCheck( EnforcerRuleHelper helper, 124 ProjectBuildingRequest buildingRequest ) 125 { 126 String cacheKey = buildingRequest.getProject().getId() + "_" + searchTransitive; 127 128 // check in the cache 129 Set<Artifact> dependencies = 130 (Set<Artifact>) helper.getCache( cacheKey, () -> getDependenciesToCheck( buildingRequest ) ); 131 132 return dependencies; 133 } 134 135 protected Set<Artifact> getDependenciesToCheck( ProjectBuildingRequest buildingRequest ) 136 { 137 Set<Artifact> dependencies = null; 138 try 139 { 140 DependencyNode node = graphBuilder.buildDependencyGraph( buildingRequest, null ); 141 if ( searchTransitive ) 142 { 143 dependencies = ArtifactUtils.getAllDescendants( node ); 144 } 145 else if ( node.getChildren() != null ) 146 { 147 dependencies = new HashSet<>(); 148 for ( DependencyNode depNode : node.getChildren() ) 149 { 150 dependencies.add( depNode.getArtifact() ); 151 } 152 } 153 } 154 catch ( DependencyGraphBuilderException e ) 155 { 156 // otherwise we need to change the signature of this protected method 157 throw new RuntimeException( e ); 158 } 159 return dependencies; 160 } 161 162 /** 163 * Checks the set of dependencies against the list of excludes. 164 * 165 * @param dependencies the dependencies 166 * @param log the log 167 * @return the sets the 168 * @throws EnforcerRuleException the enforcer rule exception 169 */ 170 protected abstract Set<Artifact> checkDependencies( Set<Artifact> dependencies, Log log ) 171 throws EnforcerRuleException; 172 173 /** 174 * Checks if is search transitive. 175 * 176 * @return the searchTransitive 177 */ 178 public boolean isSearchTransitive() 179 { 180 return this.searchTransitive; 181 } 182 183 /** 184 * Sets the search transitive. 185 * 186 * @param theSearchTransitive the searchTransitive to set 187 */ 188 public void setSearchTransitive( boolean theSearchTransitive ) 189 { 190 this.searchTransitive = theSearchTransitive; 191 } 192 193}