Coverage Report - org.apache.maven.plugin.invoker.ScriptRunner
 
Classes in this File Line Coverage Branch Coverage Complexity
ScriptRunner
0%
0/67
0%
0/42
3,625
 
 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 751403 2009-03-08 11:52:40Z bentmann $
 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  
      * @throws MojoExecutionException If an I/O error occurred while reading the script file.
 148  
      * @throws BuildFailureException If the script did not return <code>true</code> of threw an exception.
 149  
      */
 150  
     public void run( final String scriptDescription, final File basedir, final String relativeScriptPath,
 151  
                      final Map context, final FileLogger logger, String stage )
 152  
         throws MojoExecutionException, BuildFailureException
 153  
     {
 154  0
         if ( relativeScriptPath == null )
 155  
         {
 156  0
             return;
 157  
         }
 158  
 
 159  0
         final File scriptFile = resolveScript( new File( basedir, relativeScriptPath ) );
 160  
 
 161  0
         if ( !scriptFile.exists() )
 162  
         {
 163  0
             return;
 164  
         }
 165  
 
 166  0
         Map globalVariables = new HashMap( this.globalVariables );
 167  0
         globalVariables.put( "basedir", basedir );
 168  0
         globalVariables.put( "context", context );
 169  
 
 170  0
         PrintStream out = ( logger != null ) ? logger.getPrintStream() : null;
 171  
 
 172  0
         ScriptInterpreter interpreter = getInterpreter( scriptFile );
 173  0
         if ( getLog().isDebugEnabled() )
 174  
         {
 175  0
             String name = interpreter.getClass().getName();
 176  0
             name = name.substring( name.lastIndexOf( '.' ) + 1 );
 177  0
             getLog().debug( "Running script with " + name + ": " + scriptFile );
 178  
         }
 179  
 
 180  
         String script;
 181  
         try
 182  
         {
 183  0
             script = FileUtils.fileRead( scriptFile, encoding );
 184  
         }
 185  0
         catch ( IOException e )
 186  
         {
 187  0
             String errorMessage =
 188  
                 "error reading " + scriptDescription + " " + scriptFile.getPath() + ", " + e.getMessage();
 189  0
             throw new MojoExecutionException( errorMessage, e );
 190  0
         }
 191  
 
 192  
         Object result;
 193  
         try
 194  
         {
 195  0
             if ( logger != null )
 196  
             {
 197  0
                 logger.consumeLine( "Running " + scriptDescription + ": " + scriptFile );
 198  
             }
 199  0
             result = interpreter.evaluateScript( script, classPath, globalVariables, out );
 200  0
             if ( logger != null )
 201  
             {
 202  0
                 logger.consumeLine( "Finished " + scriptDescription + ": " + scriptFile );
 203  
             }
 204  
         }
 205  0
         catch ( ScriptEvaluationException e )
 206  
         {
 207  0
             Throwable t = ( e.getCause() != null ) ? e.getCause() : e;
 208  0
             String msg = ( t.getMessage() != null ) ? t.getMessage() : t.toString();
 209  0
             if ( getLog().isDebugEnabled() )
 210  
             {
 211  0
                 String errorMessage = "Error evaluating " + scriptDescription + " " + scriptFile.getPath() + ", " + t;
 212  0
                 getLog().debug( errorMessage, t );
 213  
             }
 214  0
             if ( logger != null )
 215  
             {
 216  0
                 t.printStackTrace( logger.getPrintStream() );
 217  
             }
 218  0
             throw new BuildFailureException( "The " + scriptDescription + " did not succeed. " + msg, stage );
 219  0
         }
 220  
 
 221  0
         if ( !( result == null || Boolean.TRUE.equals( result ) || "true".equals( result ) ) )
 222  
         {
 223  0
             throw new BuildFailureException( "The " + scriptDescription + " returned " + result + ".", stage );
 224  
         }
 225  0
     }
 226  
 
 227  
     /**
 228  
      * Gets the effective path to the specified script. For convenience, we allow to specify a script path as "verify"
 229  
      * and have the plugin auto-append the file extension to search for "verify.bsh" and "verify.groovy".
 230  
      * 
 231  
      * @param scriptFile The script file to resolve, may be <code>null</code>.
 232  
      * @return The effective path to the script file or <code>null</code> if the input was <code>null</code>.
 233  
      */
 234  
     private File resolveScript( File scriptFile )
 235  
     {
 236  0
         if ( scriptFile != null && !scriptFile.exists() )
 237  
         {
 238  0
             for ( Iterator it = this.scriptInterpreters.keySet().iterator(); it.hasNext(); )
 239  
             {
 240  0
                 String ext = (String) it.next();
 241  0
                 File candidateFile = new File( scriptFile.getPath() + '.' + ext );
 242  0
                 if ( candidateFile.exists() )
 243  
                 {
 244  0
                     scriptFile = candidateFile;
 245  0
                     break;
 246  
                 }
 247  
             }
 248  
         }
 249  0
         return scriptFile;
 250  
     }
 251  
 
 252  
     /**
 253  
      * Determines the script interpreter for the specified script file by looking at its file extension. In this
 254  
      * context, file extensions are considered case-insensitive. For backward compatibility with plugin versions 1.2-,
 255  
      * the BeanShell interpreter will be used for any unrecognized extension.
 256  
      * 
 257  
      * @param scriptFile The script file for which to determine an interpreter, must not be <code>null</code>.
 258  
      * @return The script interpreter for the file, never <code>null</code>.
 259  
      */
 260  
     private ScriptInterpreter getInterpreter( File scriptFile )
 261  
     {
 262  0
         String ext = FileUtils.extension( scriptFile.getName() ).toLowerCase( Locale.ENGLISH );
 263  0
         ScriptInterpreter interpreter = (ScriptInterpreter) scriptInterpreters.get( ext );
 264  0
         if ( interpreter == null )
 265  
         {
 266  0
             interpreter = (ScriptInterpreter) scriptInterpreters.get( "bsh" );
 267  
         }
 268  0
         return interpreter;
 269  
     }
 270  
 
 271  
 }