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.LifecyclePhase; 037import org.apache.maven.plugins.annotations.Mojo; 038import org.apache.maven.plugins.annotations.Parameter; 039import org.apache.maven.plugins.annotations.ResolutionScope; 040import org.apache.maven.project.MavenProject; 041import org.codehaus.plexus.PlexusConstants; 042import org.codehaus.plexus.PlexusContainer; 043import org.codehaus.plexus.context.Context; 044import org.codehaus.plexus.context.ContextException; 045import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable; 046 047/** 048 * This goal executes the defined enforcer-rules once per module. 049 * 050 * @author <a href="mailto:brianf@apache.org">Brian Fox</a> 051 * @version $Id$ 052 */ 053// CHECKSTYLE_OFF: LineLength 054@Mojo( name = "enforce", defaultPhase = LifecyclePhase.VALIDATE, requiresDependencyCollection = ResolutionScope.TEST, threadSafe = true ) 055//CHECKSTYLE_ON: LineLength 056public class EnforceMojo 057 extends AbstractMojo 058 implements Contextualizable 059{ 060 /** 061 * This is a static variable used to persist the cached results across plugin invocations. 062 */ 063 protected static Hashtable<String, EnforcerRule> cache = new Hashtable<String, EnforcerRule>(); 064 065 /** 066 * MojoExecution needed by the ExpressionEvaluator 067 */ 068 @Parameter( defaultValue = "${mojoExecution}", readonly = true, required = true ) 069 protected MojoExecution mojoExecution; 070 071 /** 072 * The MavenSession 073 */ 074 @Parameter( defaultValue = "${session}", readonly = true, required = true ) 075 protected MavenSession session; 076 077 /** 078 * POM 079 */ 080 @Parameter( defaultValue = "${project}", readonly = true, required = true ) 081 protected MavenProject project; 082 083 /** 084 * Flag to easily skip all checks 085 */ 086 @Parameter( property = "enforcer.skip", defaultValue = "false" ) 087 protected boolean skip = false; 088 089 /** 090 * Flag to fail the build if a version check fails. 091 */ 092 @Parameter( property = "enforcer.fail", defaultValue = "true" ) 093 private boolean fail = true; 094 095 /** 096 * Fail on the first rule that doesn't pass 097 */ 098 @Parameter( property = "enforcer.failFast", defaultValue = "false" ) 099 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}