View Javadoc
1   package org.apache.maven.plugins.enforcer;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.ArrayList;
23  import java.util.Hashtable;
24  import java.util.List;
25  
26  import org.apache.maven.enforcer.rule.api.EnforcerLevel;
27  import org.apache.maven.enforcer.rule.api.EnforcerRule;
28  import org.apache.maven.enforcer.rule.api.EnforcerRule2;
29  import org.apache.maven.enforcer.rule.api.EnforcerRuleException;
30  import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper;
31  import org.apache.maven.execution.MavenSession;
32  import org.apache.maven.plugin.AbstractMojo;
33  import org.apache.maven.plugin.MojoExecution;
34  import org.apache.maven.plugin.MojoExecutionException;
35  import org.apache.maven.plugin.logging.Log;
36  import org.apache.maven.plugins.annotations.Component;
37  import org.apache.maven.plugins.annotations.LifecyclePhase;
38  import org.apache.maven.plugins.annotations.Mojo;
39  import org.apache.maven.plugins.annotations.Parameter;
40  import org.apache.maven.plugins.annotations.ResolutionScope;
41  import org.apache.maven.project.MavenProject;
42  import org.apache.maven.project.path.PathTranslator;
43  import org.codehaus.plexus.PlexusConstants;
44  import org.codehaus.plexus.PlexusContainer;
45  import org.codehaus.plexus.context.Context;
46  import org.codehaus.plexus.context.ContextException;
47  import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable;
48  
49  /**
50   * This goal executes the defined enforcer-rules once per module.
51   *
52   * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
53   * @version $Id: EnforceMojo.java 1714313 2015-11-14 13:00:58Z khmarbaise $
54   */
55  // CHECKSTYLE_OFF: LineLength
56  @Mojo( name = "enforce", defaultPhase = LifecyclePhase.VALIDATE, requiresDependencyCollection = ResolutionScope.TEST, threadSafe = true )
57  //CHECKSTYLE_ON: LineLength
58  public class EnforceMojo
59      extends AbstractMojo
60      implements Contextualizable
61  {
62      /**
63       * This is a static variable used to persist the cached results across plugin invocations.
64       */
65      protected static Hashtable<String, EnforcerRule> cache = new Hashtable<String, EnforcerRule>();
66  
67      /**
68       * Path Translator needed by the ExpressionEvaluator
69       */
70      @Component( role = PathTranslator.class )
71      protected PathTranslator translator;
72  
73      /**
74       * MojoExecution needed by the ExpressionEvaluator
75       */
76      @Parameter( defaultValue = "${mojoExecution}", readonly = true, required = true )
77      protected MojoExecution mojoExecution;
78  
79      /**
80       * The MavenSession
81       */
82      @Parameter( defaultValue = "${session}", readonly = true, required = true )
83      protected MavenSession session;
84  
85      /**
86       * POM
87       */
88      @Parameter( defaultValue = "${project}", readonly = true, required = true )
89      protected MavenProject project;
90  
91      /**
92       * Flag to easily skip all checks
93       */
94      @Parameter( property = "enforcer.skip", defaultValue = "false" )
95      protected boolean skip = false;
96  
97      /**
98       * Flag to fail the build if a version check fails.
99       */
100     @Parameter( property = "enforcer.fail", defaultValue = "true" )
101     private boolean fail = true;
102 
103     /**
104      * Fail on the first rule that doesn't pass
105      */
106     @Parameter( property = "enforcer.failFast", defaultValue = "false" )
107     private boolean failFast = false;
108 
109     /**
110      * Array of objects that implement the EnforcerRule interface to execute.
111      */
112     @Parameter( required = true )
113     private EnforcerRule[] rules;
114 
115     /**
116      * Use this flag to disable rule result caching. This will cause all rules to execute on each project even if the
117      * rule indicates it can safely be cached.
118      */
119     @Parameter( property = "enforcer.ignoreCache", defaultValue = "false" )
120     protected boolean ignoreCache = false;
121 
122     // set by the contextualize method. Only way to get the
123     // plugin's container in 2.0.x
124     protected PlexusContainer container;
125 
126     public void contextualize( Context context )
127         throws ContextException
128     {
129         container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
130     }
131 
132     private boolean havingRules()
133     {
134         return rules != null && rules.length > 0;
135     }
136 
137     /**
138      * Entry point to the mojo
139      * 
140      * @throws MojoExecutionException
141      */
142     public void execute()
143         throws MojoExecutionException
144     {
145         Log log = this.getLog();
146 
147         EnforcerExpressionEvaluator evaluator =
148             new EnforcerExpressionEvaluator( session, translator, project, mojoExecution );
149 
150         if ( isSkip() )
151         {
152             log.info( "Skipping Rule Enforcement." );
153             return;
154         }
155 
156         if ( !havingRules() )
157         {
158             // CHECKSTYLE_OFF: LineLength
159             throw new MojoExecutionException( "No rules are configured. Use the skip flag if you want to disable execution." );
160             // CHECKSTYLE_ON: LineLength
161         }
162 
163         // list to store exceptions
164         List<String> list = new ArrayList<String>();
165 
166         String currentRule = "Unknown";
167 
168         // create my helper
169         EnforcerRuleHelper helper = new DefaultEnforcementRuleHelper( session, evaluator, log, container );
170 
171         // if we are only warning, then disable
172         // failFast
173         if ( !fail )
174         {
175             failFast = false;
176         }
177 
178         boolean hasErrors = false;
179 
180         // go through each rule
181         for ( int i = 0; i < rules.length; i++ )
182         {
183 
184             // prevent against empty rules
185             EnforcerRule rule = rules[i];
186             final EnforcerLevel level = getLevel( rule );
187             if ( rule != null )
188             {
189                 // store the current rule for
190                 // logging purposes
191                 currentRule = rule.getClass().getName();
192                 log.debug( "Executing rule: " + currentRule );
193                 try
194                 {
195                     if ( ignoreCache || shouldExecute( rule ) )
196                     {
197                         // execute the rule
198                         // noinspection
199                         // SynchronizationOnLocalVariableOrMethodParameter
200                         synchronized ( rule )
201                         {
202                             rule.execute( helper );
203                         }
204                     }
205                 }
206                 catch ( EnforcerRuleException e )
207                 {
208                     // i can throw an exception
209                     // because failfast will be
210                     // false if fail is false.
211                     if ( failFast && level == EnforcerLevel.ERROR )
212                     {
213                         throw new MojoExecutionException( currentRule + " failed with message:\n" + e.getMessage(), e );
214                     }
215                     else
216                     {
217                         if ( level == EnforcerLevel.ERROR )
218                         {
219                             hasErrors = true;
220                             list.add( "Rule " + i + ": " + currentRule + " failed with message:\n" + e.getMessage() );
221                             log.debug( "Adding failure due to exception", e );
222                         }
223                         else
224                         {
225                             list.add( "Rule " + i + ": " + currentRule + " warned with message:\n" + e.getMessage() );
226                             log.debug( "Adding warning due to exception", e );
227                         }
228                     }
229                 }
230             }
231         }
232 
233         // if we found anything
234         // CHECKSTYLE_OFF: LineLength
235         if ( !list.isEmpty() )
236         {
237             for ( String failure : list )
238             {
239                 log.warn( failure );
240             }
241             if ( fail && hasErrors )
242             {
243                 throw new MojoExecutionException( "Some Enforcer rules have failed. Look above for specific messages explaining why the rule failed." );
244             }
245         }
246         // CHECKSTYLE_ON: LineLength
247     }
248 
249     /**
250      * This method determines if a rule should execute based on the cache
251      *
252      * @param rule the rule to verify
253      * @return {@code true} if rule should be executed, otherwise {@code false}
254      */
255     protected boolean shouldExecute( EnforcerRule rule )
256     {
257         if ( rule.isCacheable() )
258         {
259             Log log = this.getLog();
260             log.debug( "Rule " + rule.getClass().getName() + " is cacheable." );
261             String key = rule.getClass().getName() + " " + rule.getCacheId();
262             if ( EnforceMojo.cache.containsKey( key ) )
263             {
264                 log.debug( "Key " + key + " was found in the cache" );
265                 if ( rule.isResultValid( (EnforcerRule) cache.get( key ) ) )
266                 {
267                     log.debug( "The cached results are still valid. Skipping the rule: " + rule.getClass().getName() );
268                     return false;
269                 }
270             }
271 
272             // add it to the cache of executed rules
273             EnforceMojo.cache.put( key, rule );
274         }
275         return true;
276     }
277 
278     /**
279      * @return the fail
280      */
281     public boolean isFail()
282     {
283         return this.fail;
284     }
285 
286     /**
287      * @param theFail the fail to set
288      */
289     public void setFail( boolean theFail )
290     {
291         this.fail = theFail;
292     }
293 
294     /**
295      * @return the rules
296      */
297     public EnforcerRule[] getRules()
298     {
299         return this.rules;
300     }
301 
302     /**
303      * @param theRules the rules to set
304      */
305     public void setRules( EnforcerRule[] theRules )
306     {
307         this.rules = theRules;
308     }
309 
310     /**
311      * @param theFailFast the failFast to set
312      */
313     public void setFailFast( boolean theFailFast )
314     {
315         this.failFast = theFailFast;
316     }
317 
318     public boolean isFailFast()
319     {
320         return failFast;
321     }
322 
323     protected String createRuleMessage( int i, String currentRule, EnforcerRuleException e )
324     {
325         return "Rule " + i + ": " + currentRule + " failed with message:\n" + e.getMessage();
326     }
327 
328     /**
329      * @param theTranslator the translator to set
330      */
331     public void setTranslator( PathTranslator theTranslator )
332     {
333         this.translator = theTranslator;
334     }
335 
336     /**
337      * Returns the level of the rule, defaults to {@link EnforcerLevel#ERROR} for backwards compatibility.
338      *
339      * @param rule might be of type {@link EnforcerRule2}.
340      * @return level of the rule.
341      */
342     private EnforcerLevel getLevel( EnforcerRule rule )
343     {
344         if ( rule instanceof EnforcerRule2 )
345         {
346             return ( (EnforcerRule2) rule ).getLevel();
347         }
348         else
349         {
350             return EnforcerLevel.ERROR;
351         }
352     }
353 
354     /**
355      * @return the skip
356      */
357     public boolean isSkip()
358     {
359         return this.skip;
360     }
361 
362     /**
363      * @param theSkip the skip to set
364      */
365     public void setSkip( boolean theSkip )
366     {
367         this.skip = theSkip;
368     }
369 
370     /**
371      * @return the project
372      */
373     public MavenProject getProject()
374     {
375         return this.project;
376     }
377 
378     /**
379      * @param theProject the project to set
380      */
381     public void setProject( MavenProject theProject )
382     {
383         this.project = theProject;
384     }
385 
386     /**
387      * @return the session
388      */
389     public MavenSession getSession()
390     {
391         return this.session;
392     }
393 
394     /**
395      * @param theSession the session to set
396      */
397     public void setSession( MavenSession theSession )
398     {
399         this.session = theSession;
400     }
401 
402     /**
403      * @return the translator
404      */
405     public PathTranslator getTranslator()
406     {
407         return this.translator;
408     }
409 }