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
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
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
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
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
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
129 PrintWriter writer = null;
130 try{
131
132 if (scriptEngine == null){
133 ScriptEngineManager manager = new ScriptEngineManager();
134 scriptEngine = manager.getEngineByName(engine);
135 }
136
137
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
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
154
155 if (log.isDebugEnabled())
156 log.debug("Adding property '" + key.replace('.', '_') + "' (" + value + ") as namespace property");
157 namespace.put(key.replace('.', '_'), value);
158 }
159
160
161 if (getLogOutputStream() != null){
162 writer = new PrintWriter(getLogOutputStream());
163 scriptEngine.getContext().setWriter(writer);
164 scriptEngine.getContext().setErrorWriter(writer);
165 }
166
167
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
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
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
187 if (writer != null)
188 writer.close();
189 }
190 }
191
192
193
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
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
226 super.setParameter(parameterName, parameterValue);
227 }
228 catch (InvalidParameterException ipe){
229
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
241
242 /** @return True if script has changed since last parsing */
243 private boolean scriptHasChanged(){
244
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 }