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.EnforcerLevel; 027import org.apache.maven.enforcer.rule.api.EnforcerRule; 028import org.apache.maven.enforcer.rule.api.EnforcerRule2; 029import org.apache.maven.enforcer.rule.api.EnforcerRuleException; 030import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper; 031import org.apache.maven.execution.MavenSession; 032import org.apache.maven.plugin.AbstractMojo; 033import org.apache.maven.plugin.MojoExecution; 034import org.apache.maven.plugin.MojoExecutionException; 035import org.apache.maven.plugin.logging.Log; 036import org.apache.maven.plugins.annotations.Component; 037import org.apache.maven.plugins.annotations.LifecyclePhase; 038import org.apache.maven.plugins.annotations.Mojo; 039import org.apache.maven.plugins.annotations.Parameter; 040import org.apache.maven.plugins.annotations.ResolutionScope; 041import org.apache.maven.project.MavenProject; 042import org.apache.maven.project.path.PathTranslator; 043import org.codehaus.plexus.PlexusConstants; 044import org.codehaus.plexus.PlexusContainer; 045import org.codehaus.plexus.context.Context; 046import org.codehaus.plexus.context.ContextException; 047import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable; 048 049/** 050 * This goal executes the defined enforcer-rules once per module. 051 * 052 * @author <a href="mailto:brianf@apache.org">Brian Fox</a> 053 * @version $Id: EnforceMojo.java 1696663 2015-08-19 20:20:14Z khmarbaise $ 054 */ 055@Mojo( 056 name = "enforce", 057 defaultPhase = LifecyclePhase.VALIDATE, 058 requiresDependencyCollection = ResolutionScope.TEST, 059 threadSafe = true 060) 061public class EnforceMojo 062 extends AbstractMojo 063 implements Contextualizable 064{ 065 /** 066 * This is a static variable used to persist the cached results across plugin invocations. 067 */ 068 protected static Hashtable<String, EnforcerRule> cache = new Hashtable<String, EnforcerRule>(); 069 070 /** 071 * Path Translator needed by the ExpressionEvaluator 072 */ 073 @Component( role = PathTranslator.class ) 074 protected PathTranslator translator; 075 076 /** 077 * MojoExecution needed by the ExpressionEvaluator 078 */ 079 @Parameter( defaultValue = "${mojoExecution}", readonly = true, required = true ) 080 protected MojoExecution mojoExecution; 081 082 /** 083 * The MavenSession 084 */ 085 @Parameter( defaultValue = "${session}", readonly = true, required = true ) 086 protected MavenSession session; 087 088 /** 089 * POM 090 */ 091 @Parameter( defaultValue = "${project}", readonly = true, required = true ) 092 protected MavenProject project; 093 094 /** 095 * Flag to easily skip all checks 096 */ 097 @Parameter( property = "enforcer.skip", defaultValue = "false" ) 098 protected boolean skip = false; 099 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}