View Javadoc

1   /**
2    * Copyright 2005-2006 the original author or authors.
3    *
4    * Licensed under the Gnu General Pubic License, Version 2.0 (the
5    * "License"); you may not use this file except in compliance with
6    * the License. You may obtain a copy of the License at
7    *
8    *      http://www.opensource.org/licenses/gpl-license.php
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13   * See the Gnu General Public License for more details.
14   */
15  package org.figure8.join.services.scripting.jsr223;
16  
17  import org.figure8.join.core.ParameterDefinition;
18  import org.figure8.join.core.InvalidParameterException;
19  import org.figure8.join.services.scripting.ScriptLauncher;
20  import org.figure8.join.services.scripting.ScriptException;
21  import org.figure8.join.util.LogUtil;
22  
23  import org.apache.commons.logging.Log;
24  
25  import javax.script.Namespace;
26  import javax.script.Compilable;
27  import javax.script.CompiledScript;
28  import javax.script.ScriptEngine;
29  import javax.script.ScriptEngineManager;
30  
31  import java.util.List;
32  import java.util.ArrayList;
33  import java.util.Properties;
34  import java.util.Enumeration;
35  import java.io.IOException;
36  import java.io.PrintWriter;
37  import java.io.FileInputStream;
38  import java.io.InputStreamReader;
39  /**
40   * Extension of {@link ScriptLauncher} dedicated to the Java Scripting API
41   * (aka JSR-223, see http://www.jcp.org/en/jsr/detail?id=223). This implementation
42   * lets you wrap the execution of JSR-223 compatible scripts within a
43   * ScriptLauncher.<br/>
44   * As a {@link org.figure8.join.core.Configurable} implementation, this
45   * launcher defines 1 more configurable parameters that is the name of the
46   * script engine to use for running the specified script.
47   *
48   * @see org.figure8.join.core.Configurable
49   *
50   * @author <a href="mailto:laurent.broudoux@free.fr">Laurent Broudoux</a>
51   * @version $Revision: 1.2 $
52   */
53  public class JSR223ScriptLauncher extends ScriptLauncher{
54  
55     // Static -------------------------------------------------------------------
56  
57     /** Get a commons logger. */
58     private static final Log log = LogUtil.getLog(JSR223ScriptLauncher.class);
59  
60     /** Name of configurable parameter denoting the engine for executing script. */
61     public static final String ENGINE_PARAM = "engine";
62  
63     /** List of {@link ParameterDefinition}s supported by this launcher */
64     protected static List parameters = new ArrayList();
65  
66     /** ParameterDefinition of the parameter representing the engine for executing script. */
67     protected static final ParameterDefinition engineParam = new ParameterDefinition(ENGINE_PARAM,
68             "The script engine for evaluating script", "js", true);
69  
70  
71     // Attributes ---------------------------------------------------------------
72  
73     /** The name of script engine for evaluating script. */
74     private String engine;
75  
76     /** A handler on current JSR ScriptEngine. */
77     private ScriptEngine scriptEngine = null;
78     /** A handler on current compiled script (for optimization if engine is compilable). */
79     private CompiledScript compiledScript = null;
80     /** Copy of base class value (for optimization on compilation) */
81     private String scriptPath = super.getScriptPath();
82  
83  
84     // Constructors -------------------------------------------------------------
85  
86     /** Creates a new instance of JSR223ScriptLauncher. */
87     public JSR223ScriptLauncher(){
88     }
89  
90     /**
91      * Creates a new instance of JSR223ScriptLauncher with script path
92      * @param scriptPath the path of script to later execute
93      * @throws InvalidParameterException if path cannot be accessed
94      */
95     public JSR223ScriptLauncher(String scriptPath) throws InvalidParameterException{
96        super(scriptPath);
97     }
98  
99  
100    // Public -------------------------------------------------------------------
101 
102    /** @return The name of the script engine used for evaluationg script */
103    public String getEngine(){
104       return engine;
105    }
106    /** @param engine The name of the script engine used for evaluating script */
107    public void setEngine(String engine){
108       this.engine = engine;
109       this.scriptEngine = null;
110       this.compiledScript = null;
111    }
112 
113 
114    // Implementation of ScriptLauncher -----------------------------------------
115 
116    /**
117     * Run the Groovy script file identified by <code>scriptPath</code> inner
118     * field according to the specified <code>engine</code>. Properties passed as
119     * param are injected into the script context as namespace properties. Script
120     * can directly use them using the ${property_name} form. <b>Warning: </b> "."
121     * in properties keys passing in are modified into "_" before injected. This
122     * is necessary because "." may have a special meaning in target script language
123     * (nested field) that we don't want to have here.
124     * @param properties The runtime properties to inject into execution environment
125     * @throws ScriptException if something wrong occurs during script parsing, evaluation, ...
126     */
127    public void runScript(Properties properties) throws ScriptException{
128       // Prepare a writer for logging.
129       PrintWriter writer = null;
130       try{
131          // Create engine if necessary.
132          if (scriptEngine == null){
133             ScriptEngineManager manager = new ScriptEngineManager();
134             scriptEngine = manager.getEngineByName(engine);
135          }
136 
137          // Compile script if possible.
138          if (compiledScript==null || scriptHasChanged()){
139             if (scriptEngine instanceof Compilable){
140                Compilable compilable = (Compilable)scriptEngine;
141                log.info("Compiling JSR-223 script '" + getScript().getPath() + "'");
142                compiledScript = compilable.compile(new InputStreamReader(
143                        new FileInputStream(getScript())));
144             }
145          }
146 
147          // Create and fill Namespace.
148          Namespace namespace = scriptEngine.createNamespace();
149          Enumeration keys = properties.keys();
150          while (keys != null && keys.hasMoreElements()){
151             String key = (String)keys.nextElement();
152             String value = properties.getProperty(key);
153             // Dot may represents a nested field in script language,
154             // replace dot by underscore into property names.
155             if (log.isDebugEnabled())
156                log.debug("Adding property '" + key.replace('.', '_') + "' (" + value + ") as namespace property");
157             namespace.put(key.replace('.', '_'), value);
158          }
159 
160          // Use a logger if output stream is available.
161          if (getLogOutputStream() != null){
162             writer = new PrintWriter(getLogOutputStream());
163             scriptEngine.getContext().setWriter(writer);
164             scriptEngine.getContext().setErrorWriter(writer);
165          }
166 
167          // Launch script execution.
168          if (compiledScript != null)
169             compiledScript.eval(namespace);
170          else
171             scriptEngine.eval(new InputStreamReader(new FileInputStream(getScript())),
172                     namespace);
173       }
174       catch (javax.script.ScriptException se){
175          // Log and wrap into a ScriptException.
176          log.error("The compilation or evaluation of '" + scriptPath + "' JSR-223 script failed !");
177          log.error("Here's the detailed message: " + se.getMessage());
178          throw new ScriptException("Compilation or evaluation of JSR-223 script failed", se);
179       }
180       catch (IOException ioe){
181          // Log and wrap into a ScriptException.
182          log.error("IOException while accessing JSR-223 script '" + scriptPath + "'");
183          throw new ScriptException("IOException while accessing JSR-223 script", ioe);
184       }
185       finally{
186          // Close writer on log file.
187          if (writer != null)
188             writer.close();
189       }
190    }
191 
192 
193    // Override of ScriptLauncher -----------------------------------------------
194 
195    /**
196     * Get this object parameters definitions as a list
197     * @return A list of {@code ParameterDefinition} objects
198     */
199    public List getParameterDefinitionsAsList(){
200       // Use super parameters as base.
201       if (parameters.isEmpty()){
202          parameters.addAll(super.getParameterDefinitionsAsList());
203          parameters.add(engineParam);
204       }
205       return parameters;
206    }
207 
208    /**
209     * Get this object parameters definitions as an array
210     * @return An array of {@code ParameterDefinition} objects
211     */
212    public ParameterDefinition[] getParameterDefinitions(){
213       List result = getParameterDefinitionsAsList();
214       return (ParameterDefinition[])result.toArray(new ParameterDefinition[result.size()]);
215    }
216 
217    /**
218     * Set the value of a parameter using its nama
219     * @param parameterName The name of parameter so set value for
220     * @param parameterValue The value of the paramater
221     * @throws InvalidParameterException if this parameter is not supported by this object
222     */
223    public void setParameter(String parameterName, String parameterValue) throws InvalidParameterException{
224       try{
225          // Delegate to base class first.
226          super.setParameter(parameterName, parameterValue);
227       }
228       catch (InvalidParameterException ipe){
229          // Then test the managed parameter.
230          if (ENGINE_PARAM.equals(parameterName))
231             setEngine(parameterValue);
232          else{
233             log.error("The parameter '" + parameterName + "' is not supported by JSR223ScriptLauncher");
234             throw new InvalidParameterException("The parameter '" + parameterName + "' is not supported by AntScriptLauncher");
235          }
236       }
237    }
238 
239 
240    // Private ------------------------------------------------------------------
241 
242    /** @return True if script has changed since last parsing */
243    private boolean scriptHasChanged(){
244       // Has path to script file changed ?
245       if (getScriptPath() != null && !getScriptPath().equals(scriptPath)){
246          log.info("Script has changed, getting " + getScriptPath());
247          scriptPath = getScriptPath();
248          return true;
249       }
250       return false;
251    }
252 }