View Javadoc

1   /*
2    * $Id: Scriptable.java 22704 2011-08-19 14:15:02Z pablo.kraan $
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 returned 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             RegistryLookupBindings registryLookupBindings = new RegistryLookupBindings(muleContext.getRegistry(), bindings);
229             if (compiledScript != null)
230             {
231                 result = compiledScript.eval(registryLookupBindings);
232             }
233             else
234             {
235                 result = scriptEngine.eval(scriptText, registryLookupBindings);
236             }
237 
238             // The result of the script can be returned directly or it can
239             // be set as the variable "result".
240             if (result == null)
241             {
242                 result = registryLookupBindings.get("result");
243             }
244         }
245         catch (ScriptException e)
246         {
247             // re-throw
248             throw e;
249         }
250         catch (Exception ex)
251         {
252             throw new ScriptException(ex);
253         }
254         return result;
255     }
256 
257     protected ScriptEngine createScriptEngineByName(String name)
258     {
259         return scriptEngineManager.getEngineByName(name);
260     }
261 
262     protected ScriptEngine createScriptEngineByExtension(String ext)
263     {
264         return scriptEngineManager.getEngineByExtension(ext);
265     }
266 
267     protected String listAvailableEngines()
268     {
269         return CollectionUtils.toString(scriptEngineManager.getEngineFactories(), false);
270     }
271 
272     ////////////////////////////////////////////////////////////////////////////////
273     // Getters and setters
274     ////////////////////////////////////////////////////////////////////////////////
275 
276     public String getScriptText()
277     {
278         return scriptText;
279     }
280 
281     public void setScriptText(String scriptText)
282     {
283         this.scriptText = scriptText;
284     }
285 
286     public String getScriptFile()
287     {
288         return scriptFile;
289     }
290 
291     public void setScriptFile(String scriptFile)
292     {
293         this.scriptFile = scriptFile;
294     }
295 
296     public void setScriptEngineName(String scriptEngineName)
297     {
298         this.scriptEngineName = scriptEngineName;
299     }
300 
301     public String getScriptEngineName()
302     {
303         return scriptEngineName;
304     }
305     
306     public Properties getProperties()
307     {
308         return properties;
309     }
310 
311     public void setProperties(Properties properties)
312     {
313         this.properties = properties;
314     }
315     
316     public ScriptEngine getScriptEngine()
317     {
318         return scriptEngine;
319     }
320 
321     protected void setScriptEngine(ScriptEngine scriptEngine)
322     {
323         this.scriptEngine = scriptEngine;
324     }
325 
326     protected CompiledScript getCompiledScript()
327     {
328         return compiledScript;
329     }
330 
331     protected void setCompiledScript(CompiledScript compiledScript)
332     {
333         this.compiledScript = compiledScript;
334     }
335 }