Java tutorial
/* * $Id$ * -------------------------------------------------------------------------------------- * Copyright (c) MuleSoft, Inc. All rights reserved. http://www.mulesoft.com * * The software in this package is published under the terms of the CPAL v1.0 * license, a copy of which has been included with this distribution in the * LICENSE.txt file. */ package org.mule.module.scripting.component; import org.mule.DefaultMuleEventContext; import org.mule.DefaultMuleMessage; import org.mule.api.MuleContext; import org.mule.api.MuleEvent; import org.mule.api.MuleMessage; import org.mule.api.context.MuleContextAware; import org.mule.api.lifecycle.Initialisable; import org.mule.api.lifecycle.InitialisationException; import org.mule.api.service.Service; import org.mule.api.transport.PropertyScope; import org.mule.config.i18n.CoreMessages; import org.mule.config.i18n.MessageFactory; import org.mule.transport.NullPayload; import org.mule.util.CollectionUtils; import org.mule.util.IOUtils; import org.mule.util.StringUtils; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; import java.util.Collection; import java.util.Map; import java.util.Properties; import java.util.Set; import javax.script.Bindings; import javax.script.Compilable; import javax.script.CompiledScript; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * A JSR 223 Script service. Allows any JSR 223 compliant script engines such as JavaScript, Groovy or Rhino * to be embedded as Mule components. */ public class Scriptable implements Initialisable, MuleContextAware { /** The actual body of the script */ private String scriptText; /** A file from which the script will be loaded */ private String scriptFile; /** Parameters to be made available to the script as variables */ private Properties properties; /** The name of the JSR 223 scripting engine (e.g., "groovy") */ private String scriptEngineName; // /////////////////////////////////////////////////////////////////////////// // Internal variables, not exposed as properties // /////////////////////////////////////////////////////////////////////////// /** A compiled version of the script, if the scripting engine supports it */ private CompiledScript compiledScript; private ScriptEngine scriptEngine; private ScriptEngineManager scriptEngineManager; private MuleContext muleContext; protected transient Log logger = LogFactory.getLog(getClass()); public Scriptable() { // For Spring } public Scriptable(MuleContext muleContext) { this.muleContext = muleContext; } public void setMuleContext(MuleContext context) { this.muleContext = context; } public void initialise() throws InitialisationException { scriptEngineManager = new ScriptEngineManager(); // Create scripting engine if (scriptEngineName != null) { scriptEngine = createScriptEngineByName(scriptEngineName); if (scriptEngine == null) { throw new InitialisationException(MessageFactory.createStaticMessage("Scripting engine '" + scriptEngineName + "' not found. Available engines are: " + listAvailableEngines()), this); } } // Determine scripting engine to use by file extension else if (scriptFile != null) { int i = scriptFile.lastIndexOf("."); if (i > -1) { logger.info("Script Engine name not set. Guessing by file extension."); String ext = scriptFile.substring(i + 1); scriptEngine = createScriptEngineByExtension(ext); if (scriptEngine == null) { throw new InitialisationException(MessageFactory.createStaticMessage("File extension '" + ext + "' does not map to a scripting engine. Available engines are: " + listAvailableEngines()), this); } else { setScriptEngineName(scriptEngine.getFactory().getEngineName()); } } } Reader script; // Load script from variable if (StringUtils.isNotBlank(scriptText)) { script = new StringReader(scriptText); } // Load script from file else if (scriptFile != null) { InputStream is; try { is = IOUtils.getResourceAsStream(scriptFile, getClass()); } catch (IOException e) { throw new InitialisationException(CoreMessages.cannotLoadFromClasspath(scriptFile), e, this); } if (is == null) { throw new InitialisationException(CoreMessages.cannotLoadFromClasspath(scriptFile), this); } script = new InputStreamReader(is); } else { throw new InitialisationException(CoreMessages.propertiesNotSet("scriptText, scriptFile"), this); } // Pre-compile script if scripting engine supports compilation. if (scriptEngine instanceof Compilable) { try { compiledScript = ((Compilable) scriptEngine).compile(script); } catch (ScriptException e) { throw new InitialisationException(e, this); } } } public void populateDefaultBindings(Bindings bindings) { if (properties != null) { bindings.putAll((Map) properties); } bindings.put("log", logger); // A place holder for a returned result if the script doesn't return a result. // The script can overwrite this binding bindings.put("result", NullPayload.getInstance()); bindings.put("muleContext", muleContext); bindings.put("registry", muleContext.getRegistry()); } public void populateBindings(Bindings bindings, Object payload) { populateDefaultBindings(bindings); bindings.put("payload", payload); // For backward compatability. Usually used by the script transformer since // src maps with the argument passed into the transformer bindings.put("src", payload); } public void populateBindings(Bindings bindings, MuleMessage message) { populateDefaultBindings(bindings); if (message == null) { message = new DefaultMuleMessage(NullPayload.getInstance(), muleContext); } populateVariablesInOrder(bindings, message); bindings.put("message", message); // This will get overwritten if populateBindings(Bindings bindings, MuleEvent event) is called // and not this method directly. bindings.put("payload", message.getPayload()); // For backward compatability bindings.put("src", message.getPayload()); populateHeadersVariablesAndException(bindings, message); } public void populateBindings(Bindings bindings, MuleEvent event) { populateBindings(bindings, event.getMessage()); bindings.put("originalPayload", event.getMessage().getPayload()); bindings.put("payload", event.getMessage().getPayload()); bindings.put("eventContext", new DefaultMuleEventContext(event)); bindings.put("id", event.getId()); bindings.put("flowConstruct", event.getFlowConstruct()); if (event.getFlowConstruct() instanceof Service) { bindings.put("service", event.getFlowConstruct()); } } private void populateHeadersVariablesAndException(Bindings bindings, MuleMessage message) { bindings.put("flowVars", new MesssagePropertyMap(message, PropertyScope.INVOCATION)); bindings.put("sessionVars", new MesssagePropertyMap(message, PropertyScope.SESSION)); // Only add exception is present if (message.getExceptionPayload() != null) { bindings.put("exception", message.getExceptionPayload().getException()); } else { bindings.put("exception", null); } } private void populateVariablesInOrder(Bindings bindings, MuleMessage message) { for (String key : message.getSessionPropertyNames()) { bindings.put(key, message.getSessionProperty(key)); } for (String key : message.getInvocationPropertyNames()) { bindings.put(key, message.getInvocationProperty(key)); } } public Object runScript(Bindings bindings) throws ScriptException { Object result; try { RegistryLookupBindings registryLookupBindings = new RegistryLookupBindings(muleContext.getRegistry(), bindings); if (compiledScript != null) { result = compiledScript.eval(registryLookupBindings); } else { result = scriptEngine.eval(scriptText, registryLookupBindings); } // The result of the script can be returned directly or it can // be set as the variable "result". if (result == null) { result = registryLookupBindings.get("result"); } } catch (ScriptException e) { // re-throw throw e; } catch (Exception ex) { throw new ScriptException(ex); } return result; } protected ScriptEngine createScriptEngineByName(String name) { return scriptEngineManager.getEngineByName(name); } protected ScriptEngine createScriptEngineByExtension(String ext) { return scriptEngineManager.getEngineByExtension(ext); } protected String listAvailableEngines() { return CollectionUtils.toString(scriptEngineManager.getEngineFactories(), false); } // ////////////////////////////////////////////////////////////////////////////// // Getters and setters // ////////////////////////////////////////////////////////////////////////////// public String getScriptText() { return scriptText; } public void setScriptText(String scriptText) { this.scriptText = scriptText; } public String getScriptFile() { return scriptFile; } public void setScriptFile(String scriptFile) { this.scriptFile = scriptFile; } public void setScriptEngineName(String scriptEngineName) { this.scriptEngineName = scriptEngineName; } public String getScriptEngineName() { return scriptEngineName; } public Properties getProperties() { return properties; } public void setProperties(Properties properties) { this.properties = properties; } public ScriptEngine getScriptEngine() { return scriptEngine; } protected void setScriptEngine(ScriptEngine scriptEngine) { this.scriptEngine = scriptEngine; } protected CompiledScript getCompiledScript() { return compiledScript; } protected void setCompiledScript(CompiledScript compiledScript) { this.compiledScript = compiledScript; } private static class MesssagePropertyMap implements Map<String, Object> { MuleMessage message; PropertyScope propertyScope; public MesssagePropertyMap(MuleMessage message, PropertyScope propertyScope) { this.message = message; this.propertyScope = propertyScope; } @Override public void clear() { message.clearProperties(propertyScope); } @Override public boolean containsKey(Object key) { return message.getPropertyNames(propertyScope).contains(key); } @Override public boolean containsValue(Object value) { throw new UnsupportedOperationException(); } @Override public Set<java.util.Map.Entry<String, Object>> entrySet() { throw new UnsupportedOperationException(); } @Override public Object get(Object key) { return message.getProperty((String) key, propertyScope); } @Override public boolean isEmpty() { return message.getPropertyNames(propertyScope).isEmpty(); } @Override public Set<String> keySet() { return message.getPropertyNames(propertyScope); } @Override public Object put(String key, Object value) { if (PropertyScope.INBOUND.equals(propertyScope)) { throw new UnsupportedOperationException("Inbound message properties are read-only"); } else { message.setProperty(key, value, propertyScope); return value; } } @Override public void putAll(Map<? extends String, ? extends Object> m) { for (Map.Entry<? extends String, ? extends Object> entry : m.entrySet()) { put(entry.getKey(), entry.getValue()); } } @Override public Object remove(Object key) { if (PropertyScope.INBOUND.equals(propertyScope)) { throw new UnsupportedOperationException("Inbound message properties are read-only"); } else { return message.removeProperty((String) key, propertyScope); } } @Override public int size() { return message.getPropertyNames(propertyScope).size(); } @Override public Collection<Object> values() { throw new UnsupportedOperationException(); } } }