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