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 1714313 2015-11-14 13:00:58Z khmarbaise $ 054 */ 055// CHECKSTYLE_OFF: LineLength 056@Mojo( name = "enforce", defaultPhase = LifecyclePhase.VALIDATE, requiresDependencyCollection = ResolutionScope.TEST, threadSafe = true ) 057//CHECKSTYLE_ON: LineLength 058public class EnforceMojo 059 extends AbstractMojo 060 implements Contextualizable 061{ 062 /** 063 * This is a static variable used to persist the cached results across plugin invocations. 064 */ 065 protected static Hashtable<String, EnforcerRule> cache = new Hashtable<String, EnforcerRule>(); 066 067 /** 068 * Path Translator needed by the ExpressionEvaluator 069 */ 070 @Component( role = PathTranslator.class ) 071 protected PathTranslator translator; 072 073 /** 074 * MojoExecution needed by the ExpressionEvaluator 075 */ 076 @Parameter( defaultValue = "${mojoExecution}", readonly = true, required = true ) 077 protected MojoExecution mojoExecution; 078 079 /** 080 * The MavenSession 081 */ 082 @Parameter( defaultValue = "${session}", readonly = true, required = true ) 083 protected MavenSession session; 084 085 /** 086 * POM 087 */ 088 @Parameter( defaultValue = "${project}", readonly = true, required = true ) 089 protected MavenProject project; 090 091 /** 092 * Flag to easily skip all checks 093 */ 094 @Parameter( property = "enforcer.skip", defaultValue = "false" ) 095 protected boolean skip = false; 096 097 /** 098 * Flag to fail the build if a version check fails. 099 */ 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}