Coverage Report - org.apache.maven.plugin.invoker.ScriptRunner
 
Classes in this File Line Coverage Branch Coverage Complexity
ScriptRunner
0%
0/69
0%
0/44
3.875
 
 1  
 package org.apache.maven.plugin.invoker;
 2  
 
 3  
 /*
 4  
  * Licensed to the Apache Software Foundation (ASF) under one
 5  
  * or more contributor license agreements.  See the NOTICE file
 6  
  * distributed with this work for additional information
 7  
  * regarding copyright ownership.  The ASF licenses this file
 8  
  * to you under the Apache License, Version 2.0 (the
 9  
  * "License"); you may not use this file except in compliance
 10  
  * with the License.  You may obtain a copy of the License at
 11  
  *
 12  
  *  http://www.apache.org/licenses/LICENSE-2.0
 13  
  *
 14  
  * Unless required by applicable law or agreed to in writing,
 15  
  * software distributed under the License is distributed on an
 16  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 17  
  * KIND, either express or implied.  See the License for the
 18  
  * specific language governing permissions and limitations
 19  
  * under the License.
 20  
  */
 21  
 
 22  
 import java.io.File;
 23  
 import java.io.IOException;
 24  
 import java.io.PrintStream;
 25  
 import java.util.ArrayList;
 26  
 import java.util.HashMap;
 27  
 import java.util.Iterator;
 28  
 import java.util.LinkedHashMap;
 29  
 import java.util.List;
 30  
 import java.util.Locale;
 31  
 import java.util.Map;
 32  
 
 33  
 import org.apache.maven.plugin.MojoExecutionException;
 34  
 import org.apache.maven.plugin.logging.Log;
 35  
 import org.codehaus.plexus.util.FileUtils;
 36  
 import org.codehaus.plexus.util.StringUtils;
 37  
 
 38  
 /**
 39  
  * Runs pre-/post-build hook scripts.
 40  
  * 
 41  
  * @author Benjamin Bentmann
 42  
  * @version $Id: ScriptRunner.java 827900 2009-10-21 06:38:13Z stephenc $
 43  
  */
 44  
 class ScriptRunner
 45  
 {
 46  
 
 47  
     /**
 48  
      * The mojo logger to print diagnostic to, never <code>null</code>.
 49  
      */
 50  
     private Log log;
 51  
 
 52  
     /**
 53  
      * The supported script interpreters, indexed by the lower-case file extension of their associated script files,
 54  
      * never <code>null</code>.
 55  
      */
 56  
     private Map scriptInterpreters;
 57  
 
 58  
     /**
 59  
      * The common set of global variables to pass into the script interpreter, never <code>null</code>.
 60  
      */
 61  
     private Map globalVariables;
 62  
 
 63  
     /**
 64  
      * The additional class path for the script interpreter, never <code>null</code>.
 65  
      */
 66  
     private List classPath;
 67  
 
 68  
     /**
 69  
      * The file encoding of the hook scripts or <code>null</code> to use platform encoding.
 70  
      */
 71  
     private String encoding;
 72  
 
 73  
     /**
 74  
      * Creates a new script runner.
 75  
      * 
 76  
      * @param log The mojo logger to print diagnostic to, must not be <code>null</code>.
 77  
      */
 78  
     public ScriptRunner( Log log )
 79  0
     {
 80  0
         if ( log == null )
 81  
         {
 82  0
             throw new IllegalArgumentException( "missing logger" );
 83  
         }
 84  0
         this.log = log;
 85  0
         scriptInterpreters = new LinkedHashMap();
 86  0
         scriptInterpreters.put( "bsh", new BeanShellScriptInterpreter() );
 87  0
         scriptInterpreters.put( "groovy", new GroovyScriptInterpreter() );
 88  0
         globalVariables = new HashMap();
 89  0
         classPath = new ArrayList();
 90  0
     }
 91  
 
 92  
     /**
 93  
      * Gets the mojo logger.
 94  
      * 
 95  
      * @return The mojo logger, never <code>null</code>.
 96  
      */
 97  
     private Log getLog()
 98  
     {
 99  0
         return log;
 100  
     }
 101  
 
 102  
     /**
 103  
      * Sets a global variable for the script interpeter.
 104  
      * 
 105  
      * @param name The name of the variable, must not be <code>null</code>.
 106  
      * @param value The value of the variable, may be <code>null</code>.
 107  
      */
 108  
     public void setGlobalVariable( String name, Object value )
 109  
     {
 110  0
         this.globalVariables.put( name, value );
 111  0
     }
 112  
 
 113  
     /**
 114  
      * Sets the additional class path for the hook scripts. Note that the provided list is copied, so any later changes
 115  
      * will not affect the scripts.
 116  
      * 
 117  
      * @param classPath The additional class path for the script interpreter, may be <code>null</code> or empty if only
 118  
      *            the plugin realm should be used for the script evaluation. If specified, this class path will precede
 119  
      *            the artifacts from the plugin class path.
 120  
      */
 121  
     public void setClassPath( List classPath )
 122  
     {
 123  0
         this.classPath = ( classPath != null ) ? new ArrayList( classPath ) : new ArrayList();
 124  0
     }
 125  
 
 126  
     /**
 127  
      * Sets the file encoding of the hook scripts.
 128  
      * 
 129  
      * @param encoding The file encoding of the hook scripts, may be <code>null</code> or empty to use the platform's
 130  
      *            default encoding.
 131  
      */
 132  
     public void setScriptEncoding( String encoding )
 133  
     {
 134  0
         this.encoding = StringUtils.isNotEmpty( encoding ) ? encoding : null;
 135  0
     }
 136  
 
 137  
     /**
 138  
      * Runs the specified hook script of the specified project (if any).
 139  
      * 
 140  
      * @param scriptDescription The description of the script to use for logging, must not be <code>null</code>.
 141  
      * @param basedir The base directory of the project, must not be <code>null</code>.
 142  
      * @param relativeScriptPath The path to the script relative to the project base directory, may be <code>null</code>
 143  
      *            to skip the script execution.
 144  
      * @param context The key-value storage used to share information between hook scripts, may be <code>null</code>.
 145  
      * @param logger The logger to redirect the script output to, may be <code>null</code> to use stdout/stderr.
 146  
      * @param stage The stage of the build job the script is invoked in, must not be <code>null</code>.
 147  
      * @param failOnException If <code>true</code> and the script throws an exception, then a {@link BuildFailureException}
 148  
      *            will be thrown, otherwise a {@link BuildErrorException} will be thrown on script exception.
 149  
      * @throws MojoExecutionException If an I/O error occurred while reading the script file.
 150  
      * @throws BuildFailureException If the script did not return <code>true</code> of threw an exception.
 151  
      */
 152  
     public void run( final String scriptDescription, final File basedir, final String relativeScriptPath,
 153  
                      final Map context, final FileLogger logger, String stage, boolean failOnException )
 154  
         throws MojoExecutionException, BuildFailureException
 155  
     {
 156  0
         if ( relativeScriptPath == null )
 157  
         {
 158  0
             return;
 159  
         }
 160  
 
 161  0
         final File scriptFile = resolveScript( new File( basedir, relativeScriptPath ) );
 162  
 
 163  0
         if ( !scriptFile.exists() )
 164  
         {
 165  0
             return;
 166  
         }
 167  
 
 168  0
         Map globalVariables = new HashMap( this.globalVariables );
 169  0
         globalVariables.put( "basedir", basedir );
 170  0
         globalVariables.put( "context", context );
 171  
 
 172  0
         PrintStream out = ( logger != null ) ? logger.getPrintStream() : null;
 173  
 
 174  0
         ScriptInterpreter interpreter = getInterpreter( scriptFile );
 175  0
         if ( getLog().isDebugEnabled() )
 176  
         {
 177  0
             String name = interpreter.getClass().getName();
 178  0
             name = name.substring( name.lastIndexOf( '.' ) + 1 );
 179  0
             getLog().debug( "Running script with " + name + ": " + scriptFile );
 180  
         }
 181  
 
 182  
         String script;
 183  
         try
 184  
         {
 185  0
             script = FileUtils.fileRead( scriptFile, encoding );
 186  
         }
 187  0
         catch ( IOException e )
 188  
         {
 189  0
             String errorMessage =
 190  
                 "error reading " + scriptDescription + " " + scriptFile.getPath() + ", " + e.getMessage();
 191  0
             throw new MojoExecutionException( errorMessage, e );
 192  0
         }
 193  
 
 194  
         Object result;
 195  
         try
 196  
         {
 197  0
             if ( logger != null )
 198  
             {
 199  0
                 logger.consumeLine( "Running " + scriptDescription + ": " + scriptFile );
 200  
             }
 201  0
             result = interpreter.evaluateScript( script, classPath, globalVariables, out );
 202  0
             if ( logger != null )
 203  
             {
 204  0
                 logger.consumeLine( "Finished " + scriptDescription + ": " + scriptFile );
 205  
             }
 206  
         }
 207  0
         catch ( ScriptEvaluationException e )
 208  
         {
 209  0
             Throwable t = ( e.getCause() != null ) ? e.getCause() : e;
 210  0
             String msg = ( t.getMessage() != null ) ? t.getMessage() : t.toString();
 211  0
             if ( getLog().isDebugEnabled() )
 212  
             {
 213  0
                 String errorMessage = "Error evaluating " + scriptDescription + " " + scriptFile.getPath() + ", " + t;
 214  0
                 getLog().debug( errorMessage, t );
 215  
             }
 216  0
             if ( logger != null )
 217  
             {
 218  0
                 t.printStackTrace( logger.getPrintStream() );
 219  
             }
 220  0
             if ( failOnException )
 221  
             {
 222  0
                 throw new BuildFailureException( "The " + scriptDescription + " did not succeed. " + msg, stage );
 223  
             }
 224  
             else
 225  
             {
 226  0
                 throw new BuildErrorException( "The " + scriptDescription + " did not succeed. " + msg, stage, t );
 227  
             }
 228  0
         }
 229  
 
 230  0
         if ( !( result == null || Boolean.TRUE.equals( result ) || "true".equals( result ) ) )
 231  
         {
 232  0
             throw new BuildFailureException( "The " + scriptDescription + " returned " + result + ".", stage );
 233  
         }
 234  0
     }
 235  
 
 236  
     /**
 237  
      * Gets the effective path to the specified script. For convenience, we allow to specify a script path as "verify"
 238  
      * and have the plugin auto-append the file extension to search for "verify.bsh" and "verify.groovy".
 239  
      * 
 240  
      * @param scriptFile The script file to resolve, may be <code>null</code>.
 241  
      * @return The effective path to the script file or <code>null</code> if the input was <code>null</code>.
 242  
      */
 243  
     private File resolveScript( File scriptFile )
 244  
     {
 245  0
         if ( scriptFile != null && !scriptFile.exists() )
 246  
         {
 247  0
             for ( Iterator it = this.scriptInterpreters.keySet().iterator(); it.hasNext(); )
 248  
             {
 249  0
                 String ext = (String) it.next();
 250  0
                 File candidateFile = new File( scriptFile.getPath() + '.' + ext );
 251  0
                 if ( candidateFile.exists() )
 252  
                 {
 253  0
                     scriptFile = candidateFile;
 254  0
                     break;
 255  
                 }
 256  
             }
 257  
         }
 258  0
         return scriptFile;
 259  
     }
 260  
 
 261  
     /**
 262  
      * Determines the script interpreter for the specified script file by looking at its file extension. In this
 263  
      * context, file extensions are considered case-insensitive. For backward compatibility with plugin versions 1.2-,
 264  
      * the BeanShell interpreter will be used for any unrecognized extension.
 265  
      * 
 266  
      * @param scriptFile The script file for which to determine an interpreter, must not be <code>null</code>.
 267  
      * @return The script interpreter for the file, never <code>null</code>.
 268  
      */
 269  
     private ScriptInterpreter getInterpreter( File scriptFile )
 270  
     {
 271  0
         String ext = FileUtils.extension( scriptFile.getName() ).toLowerCase( Locale.ENGLISH );
 272  0
         ScriptInterpreter interpreter = (ScriptInterpreter) scriptInterpreters.get( ext );
 273  0
         if ( interpreter == null )
 274  
         {
 275  0
             interpreter = (ScriptInterpreter) scriptInterpreters.get( "bsh" );
 276  
         }
 277  0
         return interpreter;
 278  
     }
 279  
 
 280  
 }