001 package org.apache.maven.plugin; 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 022 import java.io.File; 023 import java.util.Properties; 024 025 import org.apache.maven.execution.MavenSession; 026 import org.apache.maven.plugin.descriptor.MojoDescriptor; 027 import org.apache.maven.plugin.descriptor.PluginDescriptor; 028 import org.apache.maven.project.MavenProject; 029 import org.apache.maven.project.path.PathTranslator; 030 import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException; 031 import org.codehaus.plexus.component.configurator.expression.TypeAwareExpressionEvaluator; 032 import org.codehaus.plexus.logging.Logger; 033 import org.codehaus.plexus.util.introspection.ReflectionValueExtractor; 034 035 /** 036 * @author Jason van Zyl 037 */ 038 public class PluginParameterExpressionEvaluator 039 implements TypeAwareExpressionEvaluator 040 { 041 private MavenSession session; 042 043 private MojoExecution mojoExecution; 044 045 private MavenProject project; 046 047 private String basedir; 048 049 private Properties properties; 050 051 @Deprecated //TODO: used by the Enforcer plugin 052 public PluginParameterExpressionEvaluator( MavenSession session, MojoExecution mojoExecution, 053 PathTranslator pathTranslator, Logger logger, MavenProject project, 054 Properties properties ) 055 { 056 this( session, mojoExecution ); 057 } 058 059 public PluginParameterExpressionEvaluator( MavenSession session ) 060 { 061 this( session, null ); 062 } 063 064 public PluginParameterExpressionEvaluator( MavenSession session, MojoExecution mojoExecution ) 065 { 066 this.session = session; 067 this.mojoExecution = mojoExecution; 068 this.properties = session.getExecutionProperties(); 069 this.project = session.getCurrentProject(); 070 071 String basedir = null; 072 073 if ( project != null ) 074 { 075 File projectFile = project.getBasedir(); 076 077 // this should always be the case for non-super POM instances... 078 if ( projectFile != null ) 079 { 080 basedir = projectFile.getAbsolutePath(); 081 } 082 } 083 084 if ( ( basedir == null ) && ( session != null ) ) 085 { 086 basedir = session.getExecutionRootDirectory(); 087 } 088 089 if ( basedir == null ) 090 { 091 basedir = System.getProperty( "user.dir" ); 092 } 093 094 this.basedir = basedir; 095 } 096 097 public Object evaluate( String expr ) 098 throws ExpressionEvaluationException 099 { 100 return evaluate( expr, null ); 101 } 102 103 public Object evaluate( String expr, Class<?> type ) 104 throws ExpressionEvaluationException 105 { 106 Object value = null; 107 108 if ( expr == null ) 109 { 110 return null; 111 } 112 113 String expression = stripTokens( expr ); 114 if ( expression.equals( expr ) ) 115 { 116 int index = expr.indexOf( "${" ); 117 if ( index >= 0 ) 118 { 119 int lastIndex = expr.indexOf( "}", index ); 120 if ( lastIndex >= 0 ) 121 { 122 String retVal = expr.substring( 0, index ); 123 124 if ( ( index > 0 ) && ( expr.charAt( index - 1 ) == '$' ) ) 125 { 126 retVal += expr.substring( index + 1, lastIndex + 1 ); 127 } 128 else 129 { 130 Object subResult = evaluate( expr.substring( index, lastIndex + 1 ) ); 131 132 if ( subResult != null ) 133 { 134 retVal += subResult; 135 } 136 else 137 { 138 retVal += "$" + expr.substring( index + 1, lastIndex + 1 ); 139 } 140 } 141 142 retVal += evaluate( expr.substring( lastIndex + 1 ) ); 143 return retVal; 144 } 145 } 146 147 // Was not an expression 148 if ( expression.indexOf( "$$" ) > -1 ) 149 { 150 return expression.replaceAll( "\\$\\$", "\\$" ); 151 } 152 else 153 { 154 return expression; 155 } 156 } 157 158 MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor(); 159 160 if ( "localRepository".equals( expression ) ) 161 { 162 value = session.getLocalRepository(); 163 } 164 else if ( "session".equals( expression ) ) 165 { 166 value = session; 167 } 168 else if ( expression.startsWith( "session" ) ) 169 { 170 try 171 { 172 int pathSeparator = expression.indexOf( "/" ); 173 174 if ( pathSeparator > 0 ) 175 { 176 String pathExpression = expression.substring( 1, pathSeparator ); 177 value = ReflectionValueExtractor.evaluate( pathExpression, session ); 178 value = value + expression.substring( pathSeparator ); 179 } 180 else 181 { 182 value = ReflectionValueExtractor.evaluate( expression.substring( 1 ), session ); 183 } 184 } 185 catch ( Exception e ) 186 { 187 // TODO: don't catch exception 188 throw new ExpressionEvaluationException( "Error evaluating plugin parameter expression: " + expression, 189 e ); 190 } 191 } 192 else if ( "reactorProjects".equals( expression ) ) 193 { 194 value = session.getProjects(); 195 } 196 else if ( "mojoExecution".equals( expression ) ) 197 { 198 value = mojoExecution; 199 } 200 else if ( "project".equals( expression ) ) 201 { 202 value = project; 203 } 204 else if ( "executedProject".equals( expression ) ) 205 { 206 value = project.getExecutionProject(); 207 } 208 else if ( expression.startsWith( "project" ) || expression.startsWith( "pom" ) ) 209 { 210 try 211 { 212 int pathSeparator = expression.indexOf( "/" ); 213 214 if ( pathSeparator > 0 ) 215 { 216 String pathExpression = expression.substring( 0, pathSeparator ); 217 value = ReflectionValueExtractor.evaluate( pathExpression, project ); 218 value = value + expression.substring( pathSeparator ); 219 } 220 else 221 { 222 value = ReflectionValueExtractor.evaluate( expression.substring( 1 ), project ); 223 } 224 } 225 catch ( Exception e ) 226 { 227 // TODO: don't catch exception 228 throw new ExpressionEvaluationException( "Error evaluating plugin parameter expression: " + expression, 229 e ); 230 } 231 } 232 else if ( expression.equals( "repositorySystemSession" ) ) 233 { 234 value = session.getRepositorySession(); 235 } 236 else if ( expression.equals( "mojo" ) ) 237 { 238 value = mojoExecution; 239 } 240 else if ( expression.startsWith( "mojo" ) ) 241 { 242 try 243 { 244 int pathSeparator = expression.indexOf( "/" ); 245 246 if ( pathSeparator > 0 ) 247 { 248 String pathExpression = expression.substring( 1, pathSeparator ); 249 value = ReflectionValueExtractor.evaluate( pathExpression, mojoExecution ); 250 value = value + expression.substring( pathSeparator ); 251 } 252 else 253 { 254 value = ReflectionValueExtractor.evaluate( expression.substring( 1 ), mojoExecution ); 255 } 256 } 257 catch ( Exception e ) 258 { 259 // TODO: don't catch exception 260 throw new ExpressionEvaluationException( "Error evaluating plugin parameter expression: " + expression, 261 e ); 262 } 263 } 264 else if ( expression.equals( "plugin" ) ) 265 { 266 value = mojoDescriptor.getPluginDescriptor(); 267 } 268 else if ( expression.startsWith( "plugin" ) ) 269 { 270 try 271 { 272 int pathSeparator = expression.indexOf( "/" ); 273 274 PluginDescriptor pluginDescriptor = mojoDescriptor.getPluginDescriptor(); 275 276 if ( pathSeparator > 0 ) 277 { 278 String pathExpression = expression.substring( 1, pathSeparator ); 279 value = ReflectionValueExtractor.evaluate( pathExpression, pluginDescriptor ); 280 value = value + expression.substring( pathSeparator ); 281 } 282 else 283 { 284 value = ReflectionValueExtractor.evaluate( expression.substring( 1 ), pluginDescriptor ); 285 } 286 } 287 catch ( Exception e ) 288 { 289 throw new ExpressionEvaluationException( "Error evaluating plugin parameter expression: " + expression, 290 e ); 291 } 292 } 293 else if ( "settings".equals( expression ) ) 294 { 295 value = session.getSettings(); 296 } 297 else if ( expression.startsWith( "settings" ) ) 298 { 299 try 300 { 301 int pathSeparator = expression.indexOf( "/" ); 302 303 if ( pathSeparator > 0 ) 304 { 305 String pathExpression = expression.substring( 1, pathSeparator ); 306 value = ReflectionValueExtractor.evaluate( pathExpression, session.getSettings() ); 307 value = value + expression.substring( pathSeparator ); 308 } 309 else 310 { 311 value = ReflectionValueExtractor.evaluate( expression.substring( 1 ), session.getSettings() ); 312 } 313 } 314 catch ( Exception e ) 315 { 316 // TODO: don't catch exception 317 throw new ExpressionEvaluationException( "Error evaluating plugin parameter expression: " + expression, 318 e ); 319 } 320 } 321 else if ( "basedir".equals( expression ) ) 322 { 323 value = basedir; 324 } 325 else if ( expression.startsWith( "basedir" ) ) 326 { 327 int pathSeparator = expression.indexOf( "/" ); 328 329 if ( pathSeparator > 0 ) 330 { 331 value = basedir + expression.substring( pathSeparator ); 332 } 333 } 334 335 /* 336 * MNG-4312: We neither have reserved all of the above magic expressions nor is their set fixed/well-known (it 337 * gets occasionally extended by newer Maven versions). This imposes the risk for existing plugins to 338 * unintentionally use such a magic expression for an ordinary system property. So here we check whether we 339 * ended up with a magic value that is not compatible with the type of the configured mojo parameter (a string 340 * could still be converted by the configurator so we leave those alone). If so, back off to evaluating the 341 * expression from properties only. 342 */ 343 if ( value != null && type != null && !( value instanceof String ) && !isTypeCompatible( type, value ) ) 344 { 345 value = null; 346 } 347 348 if ( value == null ) 349 { 350 // The CLI should win for defining properties 351 352 if ( ( value == null ) && ( properties != null ) ) 353 { 354 // We will attempt to get nab a system property as a way to specify a 355 // parameter to a plugins. My particular case here is allowing the surefire 356 // plugin to run a single test so I want to specify that class on the cli 357 // as a parameter. 358 359 value = properties.getProperty( expression ); 360 } 361 362 if ( ( value == null ) && ( ( project != null ) && ( project.getProperties() != null ) ) ) 363 { 364 value = project.getProperties().getProperty( expression ); 365 } 366 367 } 368 369 if ( value instanceof String ) 370 { 371 // TODO: without #, this could just be an evaluate call... 372 373 String val = (String) value; 374 375 int exprStartDelimiter = val.indexOf( "${" ); 376 377 if ( exprStartDelimiter >= 0 ) 378 { 379 if ( exprStartDelimiter > 0 ) 380 { 381 value = val.substring( 0, exprStartDelimiter ) + evaluate( val.substring( exprStartDelimiter ) ); 382 } 383 else 384 { 385 value = evaluate( val.substring( exprStartDelimiter ) ); 386 } 387 } 388 } 389 390 return value; 391 } 392 393 private static boolean isTypeCompatible( Class<?> type, Object value ) 394 { 395 if ( type.isInstance( value ) ) 396 { 397 return true; 398 } 399 // likely Boolean -> boolean, Short -> int etc. conversions, it's not the problem case we try to avoid 400 return ( ( type.isPrimitive() || type.getName().startsWith( "java.lang." ) ) 401 && value.getClass().getName().startsWith( "java.lang." ) ); 402 } 403 404 private String stripTokens( String expr ) 405 { 406 if ( expr.startsWith( "${" ) && ( expr.indexOf( "}" ) == expr.length() - 1 ) ) 407 { 408 expr = expr.substring( 2, expr.length() - 1 ); 409 } 410 return expr; 411 } 412 413 public File alignToBaseDirectory( File file ) 414 { 415 // TODO: Copied from the DefaultInterpolator. We likely want to resurrect the PathTranslator or at least a 416 // similar component for re-usage 417 if ( file != null ) 418 { 419 if ( file.isAbsolute() ) 420 { 421 // path was already absolute, just normalize file separator and we're done 422 } 423 else if ( file.getPath().startsWith( File.separator ) ) 424 { 425 // drive-relative Windows path, don't align with project directory but with drive root 426 file = file.getAbsoluteFile(); 427 } 428 else 429 { 430 // an ordinary relative path, align with project directory 431 file = new File( new File( basedir, file.getPath() ).toURI().normalize() ).getAbsoluteFile(); 432 } 433 } 434 return file; 435 } 436 437 }