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