Home » synapse-1.2-src » org.apache.synapse.mediators.bsf » [javadoc | source]

    1   /*
    2    *  Licensed to the Apache Software Foundation (ASF) under one
    3    *  or more contributor license agreements.  See the NOTICE file
    4    *  distributed with this work for additional information
    5    *  regarding copyright ownership.  The ASF licenses this file
    6    *  to you under the Apache License, Version 2.0 (the
    7    *  "License"); you may not use this file except in compliance
    8    *  with the License.  You may obtain a copy of the License at
    9    *
   10    *   http://www.apache.org/licenses/LICENSE-2.0
   11    *
   12    *  Unless required by applicable law or agreed to in writing,
   13    *  software distributed under the License is distributed on an
   14    *   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15    *  KIND, either express or implied.  See the License for the
   16    *  specific language governing permissions and limitations
   17    *  under the License.
   18    */
   19   
   20   package org.apache.synapse.mediators.bsf;
   21   
   22   import org.apache.axiom.om.OMElement;
   23   import org.apache.axiom.om.OMText;
   24   import org.apache.bsf.xml.XMLHelper;
   25   import org.apache.synapse.MessageContext;
   26   import org.apache.synapse.SynapseException;
   27   import org.apache.synapse.config.Entry;
   28   import org.apache.synapse.mediators.AbstractMediator;
   29   
   30   import javax.script;
   31   import javax.activation.DataHandler;
   32   import java.util.TreeMap;
   33   import java.util.Map;
   34   import java.util.Iterator;
   35   import java.io.BufferedReader;
   36   import java.io.InputStreamReader;
   37   import java.io.IOException;
   38   
   39   /**
   40    * A Synapse mediator that calls a function in any scripting language supported by the BSF.
   41    * The ScriptMediator supports scripts specified in-line or those loaded through a registry
   42    * <p/>
   43    * <pre>
   44    *    &lt;script [key=&quot;entry-key&quot;]
   45    *      [function=&quot;script-function-name&quot;] language="javascript|groovy|ruby"&gt
   46    *      (text | xml)?
   47    *    &lt;/script&gt;
   48    * </pre>
   49    * <p/>
   50    * <p/>
   51    * The function is an optional attribute defining the name of the script function to call,
   52    * if not specified it defaults to a function named 'mediate'. The function takes a single
   53    * parameter which is the Synapse MessageContext. The function may return a boolean, if it
   54    * does not then true is assumed.
   55    */
   56   public class ScriptMediator extends AbstractMediator {
   57   
   58       /**
   59        * The name of the variable made available to the scripting language to access the message
   60        */
   61       private static final String MC_VAR_NAME = "mc";
   62   
   63       /**
   64        * The registry entry key for a script loaded from the registry
   65        */
   66       private String key;
   67       /**
   68        * The language of the script code
   69        */
   70       private String language;
   71       /**
   72        * The map of included scripts; key = registry entry key, value = script source 
   73        */
   74       private Map includes = new TreeMap();
   75       /**
   76        * The optional name of the function to be invoked, defaults to mediate
   77        */
   78       private String function = "mediate";
   79       /**
   80        * The source code of the script
   81        */
   82       private String scriptSourceCode;
   83       /**
   84        * The BSF engine created to process each message through the script
   85        */
   86       protected ScriptEngine scriptEngine;
   87       /**
   88        * Does the ScriptEngine support multi-threading
   89        */
   90       private boolean multiThreadedEngine;
   91       /**
   92        * The compiled script. Only used for inline scripts
   93        */
   94       private CompiledScript compiledScript;
   95       /**
   96        * The Invocable script. Only used for external scripts
   97        */
   98       private Invocable invocableScript;
   99       /**
  100        * The BSF helper to convert between the XML representations used by Java and the scripting language
  101        */
  102       private XMLHelper xmlHelper;
  103   
  104       /** Lock used to ensure thread-safe lookup of the object from the registry */
  105       private final Object resourceLock = new Object();
  106   
  107       /**
  108        * Create a script mediator for the given language and given script source
  109        *
  110        * @param language         the BSF language
  111        * @param scriptSourceCode the source code of the script
  112        */
  113       public ScriptMediator(String language, String scriptSourceCode) {
  114           this.language = language;
  115           this.scriptSourceCode = scriptSourceCode;
  116           initInlineScript();
  117       }
  118   
  119       /**
  120        * Create a script mediator for the given language and given script entry key and function
  121        *
  122        * @param language the BSF language
  123        * @param key      the registry entry key to load the script
  124        * @param function the function to be invoked
  125        */
  126       public ScriptMediator(String language, Map includeKeysMap, String key, String function) {
  127           this.language = language;
  128           this.key = key;
  129           this.includes = includeKeysMap;
  130           if (function != null) {
  131               this.function = function;
  132           }
  133   
  134           initScriptEngine();
  135           if (!(scriptEngine instanceof Invocable)) {
  136               throw new SynapseException("Script engine is not an Invocable engine for language: " + language);
  137           }
  138           invocableScript = (Invocable) scriptEngine;
  139       }
  140   
  141       /**
  142        * Perform Script mediation
  143        *
  144        * @param synCtx the Synapse message context
  145        * @return the boolean result from the script invocation
  146        */
  147       public boolean mediate(MessageContext synCtx) {
  148   
  149           boolean traceOn = isTraceOn(synCtx);
  150           boolean traceOrDebugOn = isTraceOrDebugOn(traceOn);
  151   
  152           if (traceOrDebugOn) {
  153               traceOrDebug(traceOn, "Start : Script mediator");
  154   
  155               if (traceOn && trace.isTraceEnabled()) {
  156                   trace.trace("Message : " + synCtx.getEnvelope());
  157               }
  158           }
  159   
  160           if (traceOrDebugOn) {
  161               traceOrDebug(traceOn, "Scripting language : " + language + " source " +
  162                   (key == null ? ": specified inline " : " loaded with key : " + key) +
  163                   (function != null ? " function : " + function : ""));
  164           }
  165   
  166           boolean returnValue;
  167           if (multiThreadedEngine) {
  168               returnValue = invokeScript(synCtx);
  169           } else {
  170               // TODO: change to use a pool of script engines (requires an update to BSF)
  171               synchronized (scriptEngine.getClass()) {
  172                   returnValue = invokeScript(synCtx);
  173               }
  174           }
  175   
  176           if (traceOn && trace.isTraceEnabled()) {
  177               trace.trace("Result message after execution of script : " + synCtx.getEnvelope());
  178           }
  179   
  180           if (traceOrDebugOn) {
  181               traceOrDebug(traceOn, "End : Script mediator return value : " + returnValue);
  182           }
  183   
  184           return returnValue;
  185       }
  186   
  187       private boolean invokeScript(MessageContext synCtx) {
  188           boolean returnValue;
  189           try {
  190   
  191               Object returnObject;
  192               if (key != null) {
  193                   returnObject = mediateWithExternalScript(synCtx);
  194               } else {
  195                   returnObject = mediateForInlineScript(synCtx);
  196               }
  197               if (returnObject != null && returnObject instanceof Boolean) {
  198                   returnValue = ((Boolean) returnObject).booleanValue();
  199               } else {
  200                   returnValue = true;
  201               }
  202   
  203           } catch (ScriptException e) {
  204               handleException("The script engine returned an error executing the " +
  205                   (key == null ? "inlined " : "external ") + language + " script" +
  206                   (key != null? " : " + key : "") +
  207                   (function != null ? " function " + function : ""), e, synCtx);
  208               returnValue = false;
  209           } catch (NoSuchMethodException e) {
  210               handleException("The script engine returned a NoSuchMethodException executing the " +
  211                   (key == null ? "inlined " : "external ") + language + " script" +
  212                   (key != null? " : " + key : "") +
  213                   (function != null ? " function " + function : ""), e, synCtx);
  214               returnValue = false;
  215           }
  216           return returnValue;
  217       }
  218   
  219       /**
  220        * Mediation implementation when the script to be executed should be loaded from the registry
  221        *
  222        * @param synCtx the message context
  223        * @return script result
  224        * @throws ScriptException
  225        */
  226       protected Object mediateWithExternalScript(MessageContext synCtx) throws ScriptException, NoSuchMethodException {
  227           prepareExternalScript(synCtx);
  228           ScriptMessageContext scriptMC = new ScriptMessageContext(synCtx, xmlHelper);
  229           return invocableScript.invokeFunction(function, new Object[]{scriptMC});
  230       }
  231   
  232       /**
  233        * Perform mediation with static inline script of the given scripting language
  234        *
  235        * @param synCtx message context
  236        * @return true, or the script return value
  237        * @throws ScriptException
  238        */
  239       protected Object mediateForInlineScript(MessageContext synCtx) throws ScriptException {
  240   
  241           ScriptMessageContext scriptMC = new ScriptMessageContext(synCtx, xmlHelper);
  242   
  243           Bindings bindings = scriptEngine.createBindings();
  244           bindings.put(MC_VAR_NAME, scriptMC);
  245   
  246           Object response;
  247           if (compiledScript != null) {
  248               response = compiledScript.eval(bindings);
  249           } else {
  250               response = scriptEngine.eval(scriptSourceCode, bindings);
  251           }
  252   
  253           return response;
  254   
  255       }
  256   
  257       /**
  258        * Initialise the Mediator for the inline script
  259        */
  260       protected void initInlineScript() {
  261           try {
  262               initScriptEngine();
  263   
  264               if (scriptEngine instanceof Compilable) {
  265                   if (log.isDebugEnabled()) {
  266                       log.debug("Script engine supports Compilable interface, compiling script code..");
  267                   }
  268                   compiledScript = ((Compilable)scriptEngine).compile(scriptSourceCode);
  269               } else {
  270                   // do nothing. If the script enging doesn't support Compilable then
  271                   // the inline script will be evaluated on each invocation
  272                   if (log.isDebugEnabled()) {
  273                       log.debug("Script engine does not support the Compilable interface, " +
  274                           "inlined script would be evaluated on each invocation..");
  275                   }
  276               }
  277   
  278           } catch (ScriptException e) {
  279               throw new SynapseException("Exception initializing inline script", e);
  280           }
  281       }
  282   
  283       /**
  284        * Prepares the mediator for the invocation of an external script
  285        *
  286        * @throws ScriptException
  287        */
  288       protected synchronized void prepareExternalScript(MessageContext synCtx) throws ScriptException {
  289   
  290           // TODO: only need this synchronized method for dynamic registry entries. If there was a way
  291           // to access the registry entry during mediator initialization then for non-dynamic entries
  292           // this could be done just the once during mediator initialization.
  293   
  294           Entry entry = synCtx.getConfiguration().getEntryDefinition(key);
  295           boolean needsReload = (entry != null) && entry.isDynamic() && (!entry.isCached() || entry.isExpired());
  296           synchronized (resourceLock) {
  297               if (scriptSourceCode == null || needsReload) {
  298                   Object o = synCtx.getEntry(key);
  299                   if (o instanceof OMElement) {
  300                       scriptSourceCode = ((OMElement) (o)).getText();
  301                       scriptEngine.eval(scriptSourceCode);
  302                   } else if (o instanceof String) {
  303                       scriptSourceCode = (String) o;
  304                       scriptEngine.eval(scriptSourceCode);
  305                   } else if (o instanceof OMText) {
  306   
  307                       DataHandler dataHandler = (DataHandler) ((OMText) o).getDataHandler();
  308                       if (dataHandler != null) {
  309                           BufferedReader reader = null;
  310                           try {
  311                               reader = new BufferedReader(
  312                                       new InputStreamReader(dataHandler.getInputStream()));
  313                               scriptEngine.eval(reader);
  314   
  315                           } catch (IOException e) {
  316                               handleException("Error in reading script as a stream ", e, synCtx);
  317                           } finally {
  318   
  319                               if (reader != null) {
  320                                   try {
  321                                       reader.close();
  322                                   } catch (IOException e) {
  323                                       handleException("Error in closing input stream ", e, synCtx);
  324                                   }
  325                               }
  326   
  327                           }
  328                       }
  329                   }
  330   
  331               }
  332           }
  333   
  334           // load <include /> scripts; reload each script if needed
  335           for (Iterator iter = includes.keySet().iterator(); iter.hasNext();) {
  336               String includeKey = (String) iter.next();
  337               String includeSourceCode = (String) includes.get(includeKey);
  338               Entry includeEntry = synCtx.getConfiguration().getEntryDefinition(includeKey);
  339               boolean includeEntryNeedsReload = (includeEntry != null) && includeEntry.isDynamic()
  340                       && (!includeEntry.isCached() || includeEntry.isExpired());
  341               synchronized (resourceLock) {
  342                   if (includeSourceCode == null || includeEntryNeedsReload) {
  343                       log.debug("Re-/Loading the include script with key " + includeKey);
  344                       Object o = synCtx.getEntry(includeKey);
  345                       if (o instanceof OMElement) {
  346                           includeSourceCode = ((OMElement) (o)).getText();
  347                           scriptEngine.eval(includeSourceCode);
  348                       } else if (o instanceof String) {
  349                           includeSourceCode = (String) o;
  350                           scriptEngine.eval(includeSourceCode);
  351                       } else if (o instanceof OMText) {
  352   
  353                           DataHandler dataHandler = (DataHandler) ((OMText) o).getDataHandler();
  354                           if (dataHandler != null) {
  355                               BufferedReader reader = null;
  356                               try {
  357                                   reader = new BufferedReader(
  358                                           new InputStreamReader(dataHandler.getInputStream()));
  359                                   scriptEngine.eval(reader);
  360   
  361                               } catch (IOException e) {
  362                                   handleException("Error in reading script as a stream ", e, synCtx);
  363                               } finally {
  364   
  365                                   if (reader != null) {
  366                                       try {
  367                                           reader.close();
  368                                       } catch (IOException e) {
  369                                           handleException("Error in closing input" +
  370                                                   " stream ", e, synCtx);
  371                                       }
  372                                   }
  373                               }
  374                           }
  375                       }
  376                   }
  377               }
  378           }
  379       }
  380   
  381       protected void initScriptEngine() {
  382           if (log.isDebugEnabled()) {
  383               log.debug("Initializing script mediator for language : " + language);
  384           }
  385   
  386           ScriptEngineManager manager = new ScriptEngineManager();
  387           this.scriptEngine = manager.getEngineByExtension(language);
  388           if (scriptEngine == null) {
  389               handleException("No script engine found for language: " + language);
  390           }
  391           xmlHelper = XMLHelper.getArgHelper(scriptEngine);
  392   
  393           this.multiThreadedEngine = scriptEngine.getFactory().getParameter("THREADING") != null;
  394           log.debug("Script mediator for language : " + language +
  395               " supports multithreading? : " + multiThreadedEngine);
  396       }
  397   
  398       public String getLanguage() {
  399           return language;
  400       }
  401   
  402       public String getKey() {
  403           return key;
  404       }
  405   
  406       public String getFunction() {
  407           return function;
  408       }
  409   
  410       public String getScriptSrc() {
  411           return scriptSourceCode;
  412       }
  413   
  414       private void handleException(String msg) {
  415           log.error(msg);
  416           throw new SynapseException(msg);
  417       }
  418   
  419       public Map getIncludeMap() {
  420         return includes;
  421       }
  422   
  423       public void setIncludeMap(Map includeMap) {
  424         this.includes = includeMap;
  425       }
  426       
  427   }

Home » synapse-1.2-src » org.apache.synapse.mediators.bsf » [javadoc | source]