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 java.util.ArrayList;
023import java.util.Hashtable;
024import java.util.List;
025
026import org.apache.maven.enforcer.rule.api.EnforcerRule;
027import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
028import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
029import org.apache.maven.execution.MavenSession;
030import org.apache.maven.plugin.AbstractMojo;
031import org.apache.maven.plugin.MojoExecutionException;
032import org.apache.maven.plugin.logging.Log;
033import org.apache.maven.plugins.annotations.Component;
034import org.apache.maven.plugins.annotations.LifecyclePhase;
035import org.apache.maven.plugins.annotations.Mojo;
036import org.apache.maven.plugins.annotations.Parameter;
037import org.apache.maven.project.MavenProject;
038import org.apache.maven.project.path.PathTranslator;
039import org.codehaus.plexus.PlexusConstants;
040import org.codehaus.plexus.PlexusContainer;
041import org.codehaus.plexus.context.Context;
042import org.codehaus.plexus.context.ContextException;
043import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
044
045/**
046 * This goal executes the defined enforcer-rules once per
047 * module.
048 * 
049 * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
050 * @version $Id: EnforceMojo.java 1499852 2013-07-04 19:59:42Z rfscholte $
051 */
052@Mojo( name = "enforce", defaultPhase = LifecyclePhase.VALIDATE, threadSafe = true )
053public class EnforceMojo
054    extends AbstractMojo
055    implements Contextualizable
056{
057
058    /**
059     * Path Translator needed by the ExpressionEvaluator
060     */
061    @Component( role = PathTranslator.class )
062    protected PathTranslator translator;
063
064    /**
065     * The MavenSession
066     */
067    @Component
068    protected MavenSession session;
069
070    /**
071     * POM
072     */
073    @Component
074    protected MavenProject project;
075
076    /**
077     * Flag to fail the build if a version check fails.
078     */
079    @Parameter( property = "enforcer.fail", defaultValue = "true" )
080    protected boolean fail = true;
081
082    /**
083     * Flag to easily skip all checks
084     */
085    @Parameter( property = "enforcer.skip", defaultValue = "false" )
086    protected boolean skip = false;
087
088    /**
089     * Fail on the first rule that doesn't pass
090     */
091    @Parameter( property = "enforcer.failFast", defaultValue = "false" )
092    protected boolean failFast = false;
093
094    /**
095     * Array of objects that implement the EnforcerRule
096     * interface to execute.
097     */
098    @Parameter( required = true )
099    private EnforcerRule[] rules;
100    
101    /**
102     * Use this flag to disable rule result caching. This will cause
103     * all rules to execute on each project even if the rule indicates it can
104     * safely be cached.
105     */
106    @Parameter( property = "enforcer.ignoreCache", defaultValue = "false" )
107    protected boolean ignoreCache = false;
108    
109    /**
110     * This is a static variable used to persist the cached results across plugin invocations.
111     */
112    protected static Hashtable<String, EnforcerRule> cache = new Hashtable<String, EnforcerRule>();
113
114    
115    // set by the contextualize method. Only way to get the
116    // plugin's container in 2.0.x
117    protected PlexusContainer container;
118
119    public void contextualize ( Context context )
120        throws ContextException
121    {
122        container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
123    }
124
125    /**
126     * Entry point to the mojo
127     * @throws MojoExecutionException 
128     */
129    public void execute ()
130        throws MojoExecutionException
131    {
132        Log log = this.getLog();
133
134        EnforcerExpressionEvaluator evaluator = new EnforcerExpressionEvaluator( session, translator, project );
135
136        // the entire execution can be easily skipped
137        if ( !skip )
138        {
139            // list to store exceptions
140            List<String> list = new ArrayList<String>();
141
142            // make sure the rules exist
143            if ( rules != null && rules.length > 0 )
144            {
145                String currentRule = "Unknown";
146
147                // create my helper
148                EnforcerRuleHelper helper = new DefaultEnforcementRuleHelper( session, evaluator, log, container );
149
150                // if we are only warning, then disable
151                // failFast
152                if ( !fail )
153                {
154                    failFast = false;
155                }
156
157                // go through each rule
158                for ( int i = 0; i < rules.length; i++ )
159                {
160
161                    // prevent against empty rules
162                    EnforcerRule rule = rules[i];
163                    if ( rule != null )
164                    {
165                        // store the current rule for
166                        // logging purposes
167                        currentRule = rule.getClass().getName();
168                        log.debug( "Executing rule: " + currentRule );
169                        try
170                        {
171                            if ( ignoreCache || shouldExecute( rule ) )
172                            {
173                                // execute the rule
174                                //noinspection SynchronizationOnLocalVariableOrMethodParameter
175                                synchronized ( rule )
176                                {
177                                   rule.execute( helper );
178                                }
179                            }
180                        }
181                        catch ( EnforcerRuleException e )
182                        {
183                            // i can throw an exception
184                            // because failfast will be
185                            // false if fail is false.
186                            if ( failFast )
187                            {
188                                throw new MojoExecutionException( currentRule + " failed with message:\n"
189                                    + e.getMessage(), e );
190                            }
191                            else
192                            {
193                                list.add( "Rule " + i + ": " + currentRule + " failed with message:\n" + e.getMessage() );
194                                log.debug( "Adding failure due to exception", e );
195                            }
196                        }
197                    }
198                }
199
200                // if we found anything
201                if ( !list.isEmpty() )
202                {
203                    for ( String failure  : list )
204                    {
205                        log.warn( failure );
206                    }
207                    if ( fail )
208                    {
209                        throw new MojoExecutionException(
210                                                          "Some Enforcer rules have failed. Look above for specific messages explaining why the rule failed." );
211                    }
212                }
213            }
214            else
215            {
216                throw new MojoExecutionException(
217                                                  "No rules are configured. Use the skip flag if you want to disable execution." );
218            }
219        }
220        else
221        {
222            log.info( "Skipping Rule Enforcement." );
223        }
224    }
225
226    /**
227     * This method determines if a rule should execute based
228     * on the cache
229     * 
230     * @param rule the rule to verify
231     * @return {@code true} if rule should be executed, otherwise {@code false} 
232     */
233    protected boolean shouldExecute ( EnforcerRule rule )
234    {
235        if ( rule.isCacheable() )
236        {
237            Log log = this.getLog();
238            log.debug( "Rule " + rule.getClass().getName() + " is cacheable." );
239            String key = rule.getClass().getName() + " " + rule.getCacheId();
240            if ( EnforceMojo.cache.containsKey( key ) )
241            {
242                log.debug( "Key " + key + " was found in the cache" );
243                if ( rule.isResultValid( (EnforcerRule) cache.get( key ) ) )
244                {
245                    log.debug( "The cached results are still valid. Skipping the rule: " + rule.getClass().getName() );
246                    return false;
247                }
248            }
249            
250            //add it to the cache of executed rules
251            EnforceMojo.cache.put( key, rule );
252        }
253        return true;
254    }
255
256    /**
257     * @return the fail
258     */
259    public boolean isFail ()
260    {
261        return this.fail;
262    }
263
264    /**
265     * @param theFail the fail to set
266     */
267    public void setFail ( boolean theFail )
268    {
269        this.fail = theFail;
270    }
271
272    /**
273     * @return the rules
274     */
275    public EnforcerRule[] getRules ()
276    {
277        return this.rules;
278    }
279
280    /**
281     * @param theRules the rules to set
282     */
283    public void setRules ( EnforcerRule[] theRules )
284    {
285        this.rules = theRules;
286    }
287
288    /**
289     * @return the skip
290     */
291    public boolean isSkip ()
292    {
293        return this.skip;
294    }
295
296    /**
297     * @param theSkip the skip to set
298     */
299    public void setSkip ( boolean theSkip )
300    {
301        this.skip = theSkip;
302    }
303
304    /**
305     * @return the failFast
306     */
307    public boolean isFailFast ()
308    {
309        return this.failFast;
310    }
311
312    /**
313     * @param theFailFast the failFast to set
314     */
315    public void setFailFast ( boolean theFailFast )
316    {
317        this.failFast = theFailFast;
318    }
319
320    /**
321     * @return the project
322     */
323    public MavenProject getProject ()
324    {
325        return this.project;
326    }
327
328    /**
329     * @param theProject the project to set
330     */
331    public void setProject ( MavenProject theProject )
332    {
333        this.project = theProject;
334    }
335
336    /**
337     * @return the session
338     */
339    public MavenSession getSession ()
340    {
341        return this.session;
342    }
343
344    /**
345     * @param theSession the session to set
346     */
347    public void setSession ( MavenSession theSession )
348    {
349        this.session = theSession;
350    }
351
352    /**
353     * @return the translator
354     */
355    public PathTranslator getTranslator ()
356    {
357        return this.translator;
358    }
359
360    /**
361     * @param theTranslator the translator to set
362     */
363    public void setTranslator ( PathTranslator theTranslator )
364    {
365        this.translator = theTranslator;
366    }
367}