Package org.red5.server.script.rhino

Source Code of org.red5.server.script.rhino.RhinoScriptUtils

/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.red5.server.script.rhino;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.regex.PatternSyntaxException;

import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.Invocable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scripting.ScriptCompilationException;
import org.springframework.util.ClassUtils;

/**
* Utility methods for handling Rhino / Javascript objects.
*
* @author Paul Gregoire
* @since 0.6
*/
public class RhinoScriptUtils {

  private static final Logger log = LoggerFactory.getLogger(RhinoScriptUtils.class);

  // ScriptEngine manager
  private static ScriptEngineManager mgr = new ScriptEngineManager();

  // Javascript wrapper
  private static final String jsWrapper = "function Wrapper(obj){return new JSAdapter(){ __has__ : function(name){return true;}, __get__ : function(name){if (name in obj){return obj[name];}else if (typeof(obj['doesNotUnderstand']) == 'function'){return function(){return obj.doesNotUnderstand(name, arguments);}}else{return undefined;}}};}";

  /**
   * Create a new Rhino-scripted object from the given script source.
   *
   * @param scriptSource
   *            the script source text
   * @param interfaces
   *            the interfaces that the scripted Java object is supposed to
   *            implement
   * @param extendedClass
   * @return the scripted Java object
   * @throws ScriptCompilationException
   *             in case of Rhino parsing failure
   * @throws java.io.IOException
   */
  @SuppressWarnings("rawtypes")
  public static Object createRhinoObject(String scriptSource, Class[] interfaces, Class extendedClass) throws ScriptCompilationException, IOException, Exception {
    if (log.isDebugEnabled()) {
      log.debug("Script Engine Manager: " + mgr.getClass().getName());
    }
    ScriptEngine engine = mgr.getEngineByExtension("js");
    if (null == engine) {
      log.warn("Javascript is not supported in this build");
    }
    // set engine scope namespace
    Bindings nameSpace = engine.getBindings(ScriptContext.ENGINE_SCOPE);
    // add the logger to the script
    nameSpace.put("log", log);
    // compile the wrapper script
    CompiledScript wrapper = ((Compilable) engine).compile(jsWrapper);
    nameSpace.put("Wrapper", wrapper);

    // get the function name ie. class name / ctor
    String funcName = RhinoScriptUtils.getFunctionName(scriptSource);
    if (log.isDebugEnabled()) {
      log.debug("New script: " + funcName);
    }
    // set the 'filename'
    nameSpace.put(ScriptEngine.FILENAME, funcName);

    if (null != interfaces) {
      nameSpace.put("interfaces", interfaces);
    }

    if (null != extendedClass) {
      if (log.isDebugEnabled()) {
        log.debug("Extended: " + extendedClass.getName());
      }
      nameSpace.put("supa", extendedClass.newInstance());
    }
    //
    // compile the script
    CompiledScript script = ((Compilable) engine).compile(scriptSource);
    // eval the script with the associated namespace
    Object o = null;
    try {
      o = script.eval();
    } catch (Exception e) {
      log.error("Problem evaluating script", e);
    }
    if (log.isDebugEnabled()) {
      log.debug("Result of script call: " + o);
    }
    // script didnt return anything we can use so try the wrapper
    if (null == o) {
      wrapper.eval();
    } else {
      wrapper.eval();
      o = ((Invocable) engine).invokeFunction("Wrapper", new Object[] { engine.get(funcName) });
      if (log.isDebugEnabled()) {
        log.debug("Result of invokeFunction: " + o);
      }
    }
    return Proxy.newProxyInstance(ClassUtils.getDefaultClassLoader(), interfaces, new RhinoObjectInvocationHandler(engine, o));
  }

  /**
   * InvocationHandler that invokes a Rhino script method.
   */
  private static class RhinoObjectInvocationHandler implements InvocationHandler {

    private final ScriptEngine engine;

    private final Object instance;

    public RhinoObjectInvocationHandler(ScriptEngine engine, Object instance) {
      this.engine = engine;
      this.instance = instance;
    }

    @SuppressWarnings({ "unchecked", "rawtypes" })
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      Object o = null;
      // ensure a set of args are available
      if (args == null || args.length == 0) {
        args = new Object[] { "" };
      }
      String name = method.getName();
      if (log.isDebugEnabled()) {
        log.debug("Calling: " + name);
      }
      try {
        @SuppressWarnings("unused")
        Method apiMethod = null;
        Invocable invocable = (Invocable) engine;
        if (null == instance) {
          o = invocable.invokeFunction(name, args);
        } else {
          try {
            o = invocable.invokeMethod(instance, name, args);
          } catch (NoSuchMethodException nex) {
            log.debug("Method not found: " + name);
            try {
              // try to invoke it directly, this will work if the
              // function is in the engine context
              // ie. the script has been already evaluated
              o = invocable.invokeFunction(name, args);
            } catch (Exception ex) {
              log.debug("Function not found: " + name);
              Class[] interfaces = (Class[]) engine.get("interfaces");
              for (Class clazz : interfaces) {
                // java6 style
                o = invocable.getInterface(engine.get((String) engine.get("className")), clazz);
                if (null != o) {
                  log.debug("Interface return type: " + o.getClass().getName());
                  break;
                }
              }
            }
          }
        }
        if (log.isDebugEnabled()) {
          log.debug("Invocable result: " + o);
        }
      } catch (NoSuchMethodException nex) {
        log.warn("Method not found");
      } catch (Throwable t) {
        log.warn("{}", t);
      }
      return o;
    }
  }

  /**
   * Uses a regex to get the first "function" name, this name is used to
   * create an instance of the javascript object.
   *
   * @param scriptSource
   * @return function name
   */
  private static String getFunctionName(String scriptSource) {
    String ret = "undefined";
    try {
      ret = scriptSource.replaceAll("[\\S\\w\\s]*?function ([\\w]+)\\(\\)[\\S\\w\\s]+", "$1");
      // if undefined then look for the first var
      if (ret.equals("undefined") || ret.length() > 64) {
        ret = scriptSource.replaceAll("[\\S\\w\\s]*?var ([\\w]+)[\\S\\w\\s]+", "$1");
      }
    } catch (PatternSyntaxException ex) {
      log.error("Syntax error in the regular expression");
    } catch (IllegalArgumentException ex) {
      log.error("Syntax error in the replacement text (unescaped $ signs?)");
    } catch (IndexOutOfBoundsException ex) {
      log.error("Non-existent backreference used the replacement text");
    }
    log.debug("Got a function name: {}", ret);
    return ret;
  }

}
TOP

Related Classes of org.red5.server.script.rhino.RhinoScriptUtils

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.