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.plugin.logging.Log;
026import org.apache.maven.project.MavenProject;
027import org.apache.maven.shared.dependency.graph.DependencyGraphBuilder;
028import org.apache.maven.shared.dependency.graph.DependencyGraphBuilderException;
029import org.apache.maven.shared.dependency.graph.DependencyNode;
030import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
031import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
032import java.util.HashSet;
033import java.util.Set;
034
035/**
036 * Abstract Rule for banning dependencies.
037 *
038 * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
039 * @version $Id: AbstractBanDependencies.java 1630934 2014-10-10 16:35:26Z khmarbaise $
040 */
041public abstract class AbstractBanDependencies
042    extends AbstractNonCacheableEnforcerRule
043{
044
045    /** Specify if transitive dependencies should be searched (default) or only look at direct dependencies. */
046    private boolean searchTransitive = true;
047
048    private transient DependencyGraphBuilder graphBuilder;
049
050    /**
051     * Execute the rule.
052     *
053     * @param helper the helper
054     * @throws EnforcerRuleException the enforcer rule exception
055     */
056    public void execute( EnforcerRuleHelper helper )
057        throws EnforcerRuleException
058    {
059
060        // get the project
061        MavenProject project = null;
062        try
063        {
064            project = (MavenProject) helper.evaluate( "${project}" );
065        }
066        catch ( ExpressionEvaluationException eee )
067        {
068            throw new EnforcerRuleException( "Unable to retrieve the MavenProject: ", eee );
069        }
070
071        try
072        {
073            graphBuilder = (DependencyGraphBuilder) helper.getComponent( DependencyGraphBuilder.class );
074        }
075        catch ( ComponentLookupException e )
076        {
077            // real cause is probably that one of the Maven3 graph builder could not be initiated and fails with a
078            // ClassNotFoundException
079            try
080            {
081                graphBuilder =
082                    (DependencyGraphBuilder) helper.getComponent( DependencyGraphBuilder.class.getName(), "maven2" );
083            }
084            catch ( ComponentLookupException e1 )
085            {
086                throw new EnforcerRuleException( "Unable to lookup DependencyGraphBuilder: ", e );
087            }
088        }
089
090        // get the correct list of dependencies
091        Set<Artifact> dependencies = getDependenciesToCheck( project );
092
093        // look for banned dependencies
094        Set<Artifact> foundExcludes = checkDependencies( dependencies, helper.getLog() );
095
096        // if any are found, fail the check but list all of them
097        if ( foundExcludes != null && !foundExcludes.isEmpty() )
098        {
099            String message = getMessage();
100
101            StringBuilder buf = new StringBuilder();
102            if ( message != null )
103            {
104                buf.append( message + "\n" );
105            }
106            for ( Artifact artifact : foundExcludes )
107            {
108                buf.append( getErrorMessage( artifact ) );
109            }
110            message = buf.toString() + "Use 'mvn dependency:tree' to locate the source of the banned dependencies.";
111
112            throw new EnforcerRuleException( message );
113        }
114
115    }
116
117    protected CharSequence getErrorMessage( Artifact artifact )
118    {
119        return "Found Banned Dependency: " + artifact.getId() + "\n";
120    }
121
122    protected Set<Artifact> getDependenciesToCheck( MavenProject project )
123    {
124        Set<Artifact> dependencies = null;
125        try
126        {
127            DependencyNode node = graphBuilder.buildDependencyGraph( project, null );
128            if ( searchTransitive )
129            {
130                dependencies = getAllDescendants( node );
131            }
132            else if ( node.getChildren() != null )
133            {
134                dependencies = new HashSet<Artifact>();
135                for ( DependencyNode depNode : node.getChildren() )
136                {
137                    dependencies.add( depNode.getArtifact() );
138                }
139            }
140        }
141        catch ( DependencyGraphBuilderException e )
142        {
143            // otherwise we need to change the signature of this protected method
144            throw new RuntimeException( e );
145        }
146        return dependencies;
147    }
148
149    private Set<Artifact> getAllDescendants( DependencyNode node )
150    {
151        Set<Artifact> children = null;
152        if ( node.getChildren() != null )
153        {
154            children = new HashSet<Artifact>();
155            for ( DependencyNode depNode : node.getChildren() )
156            {
157                children.add( depNode.getArtifact() );
158                Set<Artifact> subNodes = getAllDescendants( depNode );
159                if ( subNodes != null )
160                {
161                    children.addAll( subNodes );
162                }
163            }
164        }
165        return children;
166    }
167
168    /**
169     * Checks the set of dependencies against the list of excludes.
170     *
171     * @param dependencies the dependencies
172     * @param log the log
173     * @return the sets the
174     * @throws EnforcerRuleException the enforcer rule exception
175     */
176    protected abstract Set<Artifact> checkDependencies( Set<Artifact> dependencies, Log log )
177        throws EnforcerRuleException;
178
179    /**
180     * Checks if is search transitive.
181     *
182     * @return the searchTransitive
183     */
184    public boolean isSearchTransitive()
185    {
186        return this.searchTransitive;
187    }
188
189    /**
190     * Sets the search transitive.
191     *
192     * @param theSearchTransitive the searchTransitive to set
193     */
194    public void setSearchTransitive( boolean theSearchTransitive )
195    {
196        this.searchTransitive = theSearchTransitive;
197    }
198
199}