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.plugin.MojoExecutionException;
32  import org.apache.maven.plugin.logging.Log;
33  import org.apache.maven.plugins.annotations.LifecyclePhase;
34  import org.apache.maven.plugins.annotations.Mojo;
35  import org.apache.maven.plugins.annotations.Parameter;
36  import org.apache.maven.project.path.PathTranslator;
37  import org.codehaus.plexus.PlexusConstants;
38  import org.codehaus.plexus.PlexusContainer;
39  import org.codehaus.plexus.context.Context;
40  import org.codehaus.plexus.context.ContextException;
41  
42  /**
43   * This goal executes the defined enforcer-rules once per module.
44   *
45   * @author <a href="mailto:brianf@apache.org">Brian Fox</a>
46   * @version $Id: EnforceMojo.java 1649120 2015-01-02 21:01:18Z khmarbaise $
47   */
48  @Mojo( name = "enforce", defaultPhase = LifecyclePhase.VALIDATE, threadSafe = true )
49  public class EnforceMojo
50      extends AbstractEnforceMojo
51  {
52      /**
53       * Flag to fail the build if a version check fails.
54       */
55      @Parameter( property = "enforcer.fail", defaultValue = "true" )
56      private boolean fail = true;
57  
58      /**
59       * Fail on the first rule that doesn't pass
60       */
61      @Parameter( property = "enforcer.failFast", defaultValue = "false" )
62      private boolean failFast = false;
63  
64      /**
65       * Array of objects that implement the EnforcerRule interface to execute.
66       */
67      @Parameter( required = true )
68      private EnforcerRule[] rules;
69  
70      /**
71       * Use this flag to disable rule result caching. This will cause all rules to execute on each project even if the
72       * rule indicates it can safely be cached.
73       */
74      @Parameter( property = "enforcer.ignoreCache", defaultValue = "false" )
75      protected boolean ignoreCache = false;
76  
77      /**
78       * This is a static variable used to persist the cached results across plugin invocations.
79       */
80      protected static Hashtable<String, EnforcerRule> cache = new Hashtable<String, EnforcerRule>();
81  
82      // set by the contextualize method. Only way to get the
83      // plugin's container in 2.0.x
84      protected PlexusContainer container;
85  
86      public void contextualize( Context context )
87          throws ContextException
88      {
89          container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY );
90      }
91  
92      /**
93       * Entry point to the mojo
94       * 
95       * @throws MojoExecutionException
96       */
97      public void execute()
98          throws MojoExecutionException
99      {
100         Log log = this.getLog();
101 
102         EnforcerExpressionEvaluator evaluator = new EnforcerExpressionEvaluator( session, translator, project, 
103                                                                                  mojoExecution );
104 
105         // the entire execution can be easily skipped
106         if ( !skip )
107         {
108             // list to store exceptions
109             List<String> list = new ArrayList<String>();
110 
111             // make sure the rules exist
112             if ( rules != null && rules.length > 0 )
113             {
114                 String currentRule = "Unknown";
115 
116                 // create my helper
117                 EnforcerRuleHelper helper = new DefaultEnforcementRuleHelper( session, evaluator, log, container );
118 
119                 // if we are only warning, then disable
120                 // failFast
121                 if ( !fail )
122                 {
123                     failFast = false;
124                 }
125 
126                 boolean hasErrors = false;
127 
128                 // go through each rule
129                 for ( int i = 0; i < rules.length; i++ )
130                 {
131 
132                     // prevent against empty rules
133                     EnforcerRule rule = rules[i];
134                     final EnforcerLevel level = getLevel( rule );
135                     if ( rule != null )
136                     {
137                         // store the current rule for
138                         // logging purposes
139                         currentRule = rule.getClass().getName();
140                         log.debug( "Executing rule: " + currentRule );
141                         try
142                         {
143                             if ( ignoreCache || shouldExecute( rule ) )
144                             {
145                                 // execute the rule
146                                 // noinspection
147                                 // SynchronizationOnLocalVariableOrMethodParameter
148                                 synchronized ( rule )
149                                 {
150                                     rule.execute( helper );
151                                 }
152                             }
153                         }
154                         catch ( EnforcerRuleException e )
155                         {
156                             // i can throw an exception
157                             // because failfast will be
158                             // false if fail is false.
159                             if ( failFast && level == EnforcerLevel.ERROR )
160                             {
161                                 throw new MojoExecutionException( currentRule + " failed with message:\n"
162                                     + e.getMessage(), e );
163                             }
164                             else
165                             {
166                                 if ( level == EnforcerLevel.ERROR )
167                                 {
168                                     hasErrors = true;
169                                     list.add( "Rule " + i + ": " + currentRule + " failed with message:\n"
170                                         + e.getMessage() );
171                                     log.debug( "Adding failure due to exception", e );
172                                 }
173                                 else
174                                 {
175                                     list.add( "Rule " + i + ": " + currentRule + " warned with message:\n"
176                                         + e.getMessage() );
177                                     log.debug( "Adding warning due to exception", e );
178                                 }
179                             }
180                         }
181                     }
182                 }
183 
184                 // if we found anything
185                 // CHECKSTYLE_OFF: LineLength
186                 if ( !list.isEmpty() )
187                 {
188                     for ( String failure : list )
189                     {
190                         log.warn( failure );
191                     }
192                     if ( fail && hasErrors )
193                     {
194                         throw new MojoExecutionException(
195                                                           "Some Enforcer rules have failed. Look above for specific messages explaining why the rule failed." );
196                     }
197                 }
198                 // CHECKSTYLE_ON: LineLength
199             }
200             else
201             {
202                 // CHECKSTYLE_OFF: LineLength
203                 throw new MojoExecutionException(
204                                                   "No rules are configured. Use the skip flag if you want to disable execution." );
205                 // CHECKSTYLE_ON: LineLength
206             }
207         }
208         else
209         {
210             log.info( "Skipping Rule Enforcement." );
211         }
212     }
213 
214     /**
215      * This method determines if a rule should execute based on the cache
216      *
217      * @param rule the rule to verify
218      * @return {@code true} if rule should be executed, otherwise {@code false}
219      */
220     protected boolean shouldExecute( EnforcerRule rule )
221     {
222         if ( rule.isCacheable() )
223         {
224             Log log = this.getLog();
225             log.debug( "Rule " + rule.getClass().getName() + " is cacheable." );
226             String key = rule.getClass().getName() + " " + rule.getCacheId();
227             if ( EnforceMojo.cache.containsKey( key ) )
228             {
229                 log.debug( "Key " + key + " was found in the cache" );
230                 if ( rule.isResultValid( (EnforcerRule) cache.get( key ) ) )
231                 {
232                     log.debug( "The cached results are still valid. Skipping the rule: " + rule.getClass().getName() );
233                     return false;
234                 }
235             }
236 
237             // add it to the cache of executed rules
238             EnforceMojo.cache.put( key, rule );
239         }
240         return true;
241     }
242 
243     /**
244      * @return the fail
245      */
246     public boolean isFail()
247     {
248         return this.fail;
249     }
250 
251     /**
252      * @param theFail the fail to set
253      */
254     public void setFail( boolean theFail )
255     {
256         this.fail = theFail;
257     }
258 
259     /**
260      * @return the rules
261      */
262     @Override
263     public EnforcerRule[] getRules()
264     {
265         return this.rules;
266     }
267 
268     /**
269      * @param theRules the rules to set
270      */
271     @Override
272     public void setRules( EnforcerRule[] theRules )
273     {
274         this.rules = theRules;
275     }
276 
277     /**
278      * @param theFailFast the failFast to set
279      */
280     @Override
281     public void setFailFast( boolean theFailFast )
282     {
283         this.failFast = theFailFast;
284     }
285 
286     @Override
287     public boolean isFailFast()
288     {
289         return failFast;
290     }
291 
292     @Override
293     protected String createRuleMessage( int i, String currentRule, EnforcerRuleException e )
294     {
295         return "Rule " + i + ": " + currentRule + " failed with message:\n" + e.getMessage();
296     }
297 
298     /**
299      * @param theTranslator the translator to set
300      */
301     public void setTranslator( PathTranslator theTranslator )
302     {
303         this.translator = theTranslator;
304     }
305 
306     /**
307      * Returns the level of the rule, defaults to {@link EnforcerLevel#ERROR} for backwards compatibility.
308      *
309      * @param rule might be of type {@link EnforcerRule2}.
310      * @return level of the rule.
311      */
312     private EnforcerLevel getLevel( EnforcerRule rule )
313     {
314         if ( rule instanceof EnforcerRule2 )
315         {
316             return ( (EnforcerRule2) rule ).getLevel();
317         }
318         else
319         {
320             return EnforcerLevel.ERROR;
321         }
322     }
323 }