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