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.EnforcerRule; 027import org.apache.maven.enforcer.rule.api.EnforcerRuleException; 028import org.apache.maven.enforcer.rule.api.EnforcerRuleHelper; 029import org.apache.maven.execution.MavenSession; 030import org.apache.maven.plugin.AbstractMojo; 031import org.apache.maven.plugin.MojoExecutionException; 032import org.apache.maven.plugin.logging.Log; 033import org.apache.maven.plugins.annotations.Component; 034import org.apache.maven.plugins.annotations.LifecyclePhase; 035import org.apache.maven.plugins.annotations.Mojo; 036import org.apache.maven.plugins.annotations.Parameter; 037import org.apache.maven.project.MavenProject; 038import org.apache.maven.project.path.PathTranslator; 039import org.codehaus.plexus.PlexusConstants; 040import org.codehaus.plexus.PlexusContainer; 041import org.codehaus.plexus.context.Context; 042import org.codehaus.plexus.context.ContextException; 043import org.codehaus.plexus.personality.plexus.lifecycle.phase.Contextualizable; 044 045/** 046 * This goal executes the defined enforcer-rules once per 047 * module. 048 * 049 * @author <a href="mailto:brianf@apache.org">Brian Fox</a> 050 * @version $Id: EnforceMojo.java 1499852 2013-07-04 19:59:42Z rfscholte $ 051 */ 052@Mojo( name = "enforce", defaultPhase = LifecyclePhase.VALIDATE, threadSafe = true ) 053public class EnforceMojo 054 extends AbstractMojo 055 implements Contextualizable 056{ 057 058 /** 059 * Path Translator needed by the ExpressionEvaluator 060 */ 061 @Component( role = PathTranslator.class ) 062 protected PathTranslator translator; 063 064 /** 065 * The MavenSession 066 */ 067 @Component 068 protected MavenSession session; 069 070 /** 071 * POM 072 */ 073 @Component 074 protected MavenProject project; 075 076 /** 077 * Flag to fail the build if a version check fails. 078 */ 079 @Parameter( property = "enforcer.fail", defaultValue = "true" ) 080 protected boolean fail = true; 081 082 /** 083 * Flag to easily skip all checks 084 */ 085 @Parameter( property = "enforcer.skip", defaultValue = "false" ) 086 protected boolean skip = false; 087 088 /** 089 * Fail on the first rule that doesn't pass 090 */ 091 @Parameter( property = "enforcer.failFast", defaultValue = "false" ) 092 protected boolean failFast = false; 093 094 /** 095 * Array of objects that implement the EnforcerRule 096 * interface to execute. 097 */ 098 @Parameter( required = true ) 099 private EnforcerRule[] rules; 100 101 /** 102 * Use this flag to disable rule result caching. This will cause 103 * all rules to execute on each project even if the rule indicates it can 104 * safely be cached. 105 */ 106 @Parameter( property = "enforcer.ignoreCache", defaultValue = "false" ) 107 protected boolean ignoreCache = false; 108 109 /** 110 * This is a static variable used to persist the cached results across plugin invocations. 111 */ 112 protected static Hashtable<String, EnforcerRule> cache = new Hashtable<String, EnforcerRule>(); 113 114 115 // set by the contextualize method. Only way to get the 116 // plugin's container in 2.0.x 117 protected PlexusContainer container; 118 119 public void contextualize ( Context context ) 120 throws ContextException 121 { 122 container = (PlexusContainer) context.get( PlexusConstants.PLEXUS_KEY ); 123 } 124 125 /** 126 * Entry point to the mojo 127 * @throws MojoExecutionException 128 */ 129 public void execute () 130 throws MojoExecutionException 131 { 132 Log log = this.getLog(); 133 134 EnforcerExpressionEvaluator evaluator = new EnforcerExpressionEvaluator( session, translator, project ); 135 136 // the entire execution can be easily skipped 137 if ( !skip ) 138 { 139 // list to store exceptions 140 List<String> list = new ArrayList<String>(); 141 142 // make sure the rules exist 143 if ( rules != null && rules.length > 0 ) 144 { 145 String currentRule = "Unknown"; 146 147 // create my helper 148 EnforcerRuleHelper helper = new DefaultEnforcementRuleHelper( session, evaluator, log, container ); 149 150 // if we are only warning, then disable 151 // failFast 152 if ( !fail ) 153 { 154 failFast = false; 155 } 156 157 // go through each rule 158 for ( int i = 0; i < rules.length; i++ ) 159 { 160 161 // prevent against empty rules 162 EnforcerRule rule = rules[i]; 163 if ( rule != null ) 164 { 165 // store the current rule for 166 // logging purposes 167 currentRule = rule.getClass().getName(); 168 log.debug( "Executing rule: " + currentRule ); 169 try 170 { 171 if ( ignoreCache || shouldExecute( rule ) ) 172 { 173 // execute the rule 174 //noinspection SynchronizationOnLocalVariableOrMethodParameter 175 synchronized ( rule ) 176 { 177 rule.execute( helper ); 178 } 179 } 180 } 181 catch ( EnforcerRuleException e ) 182 { 183 // i can throw an exception 184 // because failfast will be 185 // false if fail is false. 186 if ( failFast ) 187 { 188 throw new MojoExecutionException( currentRule + " failed with message:\n" 189 + e.getMessage(), e ); 190 } 191 else 192 { 193 list.add( "Rule " + i + ": " + currentRule + " failed with message:\n" + e.getMessage() ); 194 log.debug( "Adding failure due to exception", e ); 195 } 196 } 197 } 198 } 199 200 // if we found anything 201 if ( !list.isEmpty() ) 202 { 203 for ( String failure : list ) 204 { 205 log.warn( failure ); 206 } 207 if ( fail ) 208 { 209 throw new MojoExecutionException( 210 "Some Enforcer rules have failed. Look above for specific messages explaining why the rule failed." ); 211 } 212 } 213 } 214 else 215 { 216 throw new MojoExecutionException( 217 "No rules are configured. Use the skip flag if you want to disable execution." ); 218 } 219 } 220 else 221 { 222 log.info( "Skipping Rule Enforcement." ); 223 } 224 } 225 226 /** 227 * This method determines if a rule should execute based 228 * on the cache 229 * 230 * @param rule the rule to verify 231 * @return {@code true} if rule should be executed, otherwise {@code false} 232 */ 233 protected boolean shouldExecute ( EnforcerRule rule ) 234 { 235 if ( rule.isCacheable() ) 236 { 237 Log log = this.getLog(); 238 log.debug( "Rule " + rule.getClass().getName() + " is cacheable." ); 239 String key = rule.getClass().getName() + " " + rule.getCacheId(); 240 if ( EnforceMojo.cache.containsKey( key ) ) 241 { 242 log.debug( "Key " + key + " was found in the cache" ); 243 if ( rule.isResultValid( (EnforcerRule) cache.get( key ) ) ) 244 { 245 log.debug( "The cached results are still valid. Skipping the rule: " + rule.getClass().getName() ); 246 return false; 247 } 248 } 249 250 //add it to the cache of executed rules 251 EnforceMojo.cache.put( key, rule ); 252 } 253 return true; 254 } 255 256 /** 257 * @return the fail 258 */ 259 public boolean isFail () 260 { 261 return this.fail; 262 } 263 264 /** 265 * @param theFail the fail to set 266 */ 267 public void setFail ( boolean theFail ) 268 { 269 this.fail = theFail; 270 } 271 272 /** 273 * @return the rules 274 */ 275 public EnforcerRule[] getRules () 276 { 277 return this.rules; 278 } 279 280 /** 281 * @param theRules the rules to set 282 */ 283 public void setRules ( EnforcerRule[] theRules ) 284 { 285 this.rules = theRules; 286 } 287 288 /** 289 * @return the skip 290 */ 291 public boolean isSkip () 292 { 293 return this.skip; 294 } 295 296 /** 297 * @param theSkip the skip to set 298 */ 299 public void setSkip ( boolean theSkip ) 300 { 301 this.skip = theSkip; 302 } 303 304 /** 305 * @return the failFast 306 */ 307 public boolean isFailFast () 308 { 309 return this.failFast; 310 } 311 312 /** 313 * @param theFailFast the failFast to set 314 */ 315 public void setFailFast ( boolean theFailFast ) 316 { 317 this.failFast = theFailFast; 318 } 319 320 /** 321 * @return the project 322 */ 323 public MavenProject getProject () 324 { 325 return this.project; 326 } 327 328 /** 329 * @param theProject the project to set 330 */ 331 public void setProject ( MavenProject theProject ) 332 { 333 this.project = theProject; 334 } 335 336 /** 337 * @return the session 338 */ 339 public MavenSession getSession () 340 { 341 return this.session; 342 } 343 344 /** 345 * @param theSession the session to set 346 */ 347 public void setSession ( MavenSession theSession ) 348 { 349 this.session = theSession; 350 } 351 352 /** 353 * @return the translator 354 */ 355 public PathTranslator getTranslator () 356 { 357 return this.translator; 358 } 359 360 /** 361 * @param theTranslator the translator to set 362 */ 363 public void setTranslator ( PathTranslator theTranslator ) 364 { 365 this.translator = theTranslator; 366 } 367}