View Javadoc

1   /*
2    * $Id: Scriptable.java 19191 2010-08-25 21:05:23Z tcarlson $
3    * --------------------------------------------------------------------------------------
4    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
5    *
6    * The software in this package is published under the terms of the CPAL v1.0
7    * license, a copy of which has been included with this distribution in the
8    * LICENSE.txt file.
9    */
10  
11  package org.mule.module.scripting.component;
12  
13  import org.mule.DefaultMuleEventContext;
14  import org.mule.DefaultMuleMessage;
15  import org.mule.api.MuleContext;
16  import org.mule.api.MuleEvent;
17  import org.mule.api.MuleMessage;
18  import org.mule.api.context.MuleContextAware;
19  import org.mule.api.lifecycle.Initialisable;
20  import org.mule.api.lifecycle.InitialisationException;
21  import org.mule.api.service.Service;
22  import org.mule.config.i18n.CoreMessages;
23  import org.mule.config.i18n.MessageFactory;
24  import org.mule.transport.NullPayload;
25  import org.mule.util.CollectionUtils;
26  import org.mule.util.IOUtils;
27  import org.mule.util.StringUtils;
28  
29  import java.io.IOException;
30  import java.io.InputStream;
31  import java.io.InputStreamReader;
32  import java.io.Reader;
33  import java.io.StringReader;
34  import java.util.Map;
35  import java.util.Properties;
36  
37  import javax.script.Bindings;
38  import javax.script.Compilable;
39  import javax.script.CompiledScript;
40  import javax.script.ScriptEngine;
41  import javax.script.ScriptEngineManager;
42  import javax.script.ScriptException;
43  
44  import org.apache.commons.logging.Log;
45  import org.apache.commons.logging.LogFactory;
46  
47  /**
48   * A JSR 223 Script service. Allows any JSR 223 compliant script engines such as
49   * JavaScript, Groovy or Rhino to be embedded as Mule components.
50   */
51  public class Scriptable implements Initialisable, MuleContextAware
52  {
53      /** The actual body of the script */
54      private String scriptText;
55      
56      /** A file from which the script will be loaded */
57      private String scriptFile;
58      
59      /** Parameters to be made available to the script as variables */
60      private Properties properties;
61  
62      /** The name of the JSR 223 scripting engine (e.g., "groovy") */
63      private String scriptEngineName;
64  
65      /////////////////////////////////////////////////////////////////////////////
66      // Internal variables, not exposed as properties
67      /////////////////////////////////////////////////////////////////////////////
68  
69      /** A compiled version of the script, if the scripting engine supports it */
70      private CompiledScript compiledScript;
71      
72      private ScriptEngine scriptEngine;
73      private ScriptEngineManager scriptEngineManager;
74  
75      private MuleContext muleContext;
76      
77      protected transient Log logger = LogFactory.getLog(getClass());
78  
79      public Scriptable()
80      {
81          //For Stpring
82      }
83  
84      public Scriptable(MuleContext muleContext)
85      {
86          this.muleContext = muleContext;
87      }
88  
89      //For Spring
90      public void setMuleContext(MuleContext context)
91      {
92          this.muleContext = context;
93      }
94  
95      public void initialise() throws InitialisationException
96      {
97          scriptEngineManager = new ScriptEngineManager();
98  
99          // Create scripting engine
100         if (scriptEngineName != null)
101         {
102             scriptEngine = createScriptEngineByName(scriptEngineName);
103             if (scriptEngine == null)
104             {
105                 throw new InitialisationException(MessageFactory.createStaticMessage("Scripting engine '" + scriptEngineName + "' not found.  Available engines are: " + listAvailableEngines()), this);
106             }
107         }
108         // Determine scripting engine to use by file extension
109         else if (scriptFile != null)
110         {
111             int i = scriptFile.lastIndexOf(".");
112             if (i > -1)
113             {
114                 logger.info("Script Engine name not set. Guessing by file extension.");
115                 String ext = scriptFile.substring(i + 1);
116                 scriptEngine = createScriptEngineByExtension(ext);
117                 if (scriptEngine == null)
118                 {
119                     throw new InitialisationException(MessageFactory.createStaticMessage("File extension '" + ext + "' does not map to a scripting engine.  Available engines are: " + listAvailableEngines()), this);
120                 }
121                 else
122                 {
123                     setScriptEngineName(scriptEngine.getFactory().getEngineName());
124                 }
125             }
126         }
127 
128         Reader script;
129         // Load script from variable
130         if (StringUtils.isNotBlank(scriptText))
131         {
132             script = new StringReader(scriptText);
133         }
134         // Load script from file
135         else if (scriptFile != null)
136         {
137             InputStream is;
138             try
139             {
140                 is = IOUtils.getResourceAsStream(scriptFile, getClass());
141             }
142             catch (IOException e)
143             {
144                 throw new InitialisationException(CoreMessages.cannotLoadFromClasspath(scriptFile), e, this);
145             }
146             if (is == null)
147             {
148                 throw new InitialisationException(CoreMessages.cannotLoadFromClasspath(scriptFile), this);
149             }
150             script = new InputStreamReader(is);
151         }
152         else
153         {
154             throw new InitialisationException(CoreMessages.propertiesNotSet("scriptText, scriptFile"), this);
155         }
156 
157         // Pre-compile script if scripting engine supports compilation.
158         if (scriptEngine instanceof Compilable)
159         {
160             try
161             {
162                 compiledScript = ((Compilable) scriptEngine).compile(script);
163             }
164             catch (ScriptException e)
165             {
166                 throw new InitialisationException(e, this);
167             }
168         }
169     }
170 
171     public void populateDefaultBindings(Bindings bindings)
172     {
173         if (properties != null)
174         {
175             bindings.putAll((Map) properties);
176         }
177         bindings.put("log", logger);
178         //A place holder for a retuen result if the script doesn't return a result.
179         //The script can overwrite this binding
180         bindings.put("result", NullPayload.getInstance());
181         bindings.put("muleContext", muleContext);
182         bindings.put("registry", muleContext.getRegistry());
183     }
184 
185     public void populateBindings(Bindings bindings, Object payload)
186     {
187         populateDefaultBindings(bindings);
188         bindings.put("payload", payload);
189         //For backward compatability. Usually used by the script transformer since
190         //src maps with the argument passed into the transformer
191         bindings.put("src", payload);
192     }
193     
194     public void populateBindings(Bindings bindings, MuleMessage message)
195     {
196         populateDefaultBindings(bindings);
197         if (message == null)
198         {
199             message = new DefaultMuleMessage(NullPayload.getInstance(), muleContext);
200         }
201         bindings.put("message", message);
202         //This will get overwritten if populateBindings(Bindings bindings, MuleEvent event) is called
203         //and not this method directly.
204         bindings.put("payload", message.getPayload());
205         //For backward compatability
206         bindings.put("src", message.getPayload());
207     }
208 
209     public void populateBindings(Bindings bindings, MuleEvent event)
210     {
211         populateBindings(bindings, event.getMessage());
212         bindings.put("originalPayload", event.getMessage().getPayload());
213         bindings.put("payload", event.getMessage().getPayload());
214         bindings.put("eventContext", new DefaultMuleEventContext(event));
215         bindings.put("id", event.getId());
216         bindings.put("flowConstruct", event.getFlowConstruct());
217         if (event.getFlowConstruct() instanceof Service)
218         {
219             bindings.put("service", event.getFlowConstruct());
220         }
221     }
222     
223     public Object runScript(Bindings bindings) throws ScriptException
224     {
225         Object result;
226         try
227         {
228             if (compiledScript != null)
229             {
230                 result = compiledScript.eval(bindings);
231             }
232             else
233             {
234                 result = scriptEngine.eval(scriptText, bindings);
235             }
236 
237             // The result of the script can be returned directly or it can
238             // be set as the variable "result".
239             if (result == null)
240             {
241                 result = bindings.get("result");
242             }
243         }
244         catch (ScriptException e)
245         {
246             // re-throw
247             throw e;
248         }
249         catch (Exception ex)
250         {
251             throw new ScriptException(ex);
252         }
253         return result;
254     }
255 
256     protected ScriptEngine createScriptEngineByName(String name)
257     {
258         return scriptEngineManager.getEngineByName(name);
259     }
260 
261     protected ScriptEngine createScriptEngineByExtension(String ext)
262     {
263         return scriptEngineManager.getEngineByExtension(ext);
264     }
265 
266     protected String listAvailableEngines()
267     {
268         return CollectionUtils.toString(scriptEngineManager.getEngineFactories(), false);
269     }
270 
271     ////////////////////////////////////////////////////////////////////////////////
272     // Getters and setters
273     ////////////////////////////////////////////////////////////////////////////////
274 
275     public String getScriptText()
276     {
277         return scriptText;
278     }
279 
280     public void setScriptText(String scriptText)
281     {
282         this.scriptText = scriptText;
283     }
284 
285     public String getScriptFile()
286     {
287         return scriptFile;
288     }
289 
290     public void setScriptFile(String scriptFile)
291     {
292         this.scriptFile = scriptFile;
293     }
294 
295     public void setScriptEngineName(String scriptEngineName)
296     {
297         this.scriptEngineName = scriptEngineName;
298     }
299 
300     public String getScriptEngineName()
301     {
302         return scriptEngineName;
303     }
304     
305     public Properties getProperties()
306     {
307         return properties;
308     }
309 
310     public void setProperties(Properties properties)
311     {
312         this.properties = properties;
313     }
314     
315     public ScriptEngine getScriptEngine()
316     {
317         return scriptEngine;
318     }
319 
320     protected void setScriptEngine(ScriptEngine scriptEngine)
321     {
322         this.scriptEngine = scriptEngine;
323     }
324 
325     protected CompiledScript getCompiledScript()
326     {
327         return compiledScript;
328     }
329 
330     protected void setCompiledScript(CompiledScript compiledScript)
331     {
332         this.compiledScript = compiledScript;
333     }
334 }