Package org.apache.tuscany.sca.implementation.script.engines

Source Code of org.apache.tuscany.sca.implementation.script.engines.TuscanyJRubyScriptEngine

/*
* Copyright (c) 2006, Sun Microsystems, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*  - Redistributions of source code must retain the above copyright notice, this
*    list of conditions and the following disclaimer.
*
*  - Redistributions in binary form must reproduce the above copyright notice,
*    this list of conditions and the following disclaimer in the documentation
*    and/or other materials provided with the distribution.
*
*  - Neither the name of the Sun Microsystems, Inc. nor the names of
*    contributors may be used to endorse or promote products derived from this
*    software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.apache.tuscany.sca.implementation.script.engines;

import java.io.File;
import java.io.Reader;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import javax.script.AbstractScriptEngine;
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.ScriptEngineFactory;
import javax.script.ScriptException;
import javax.script.SimpleBindings;

import org.jruby.Ruby;
import org.jruby.RubyException;
import org.jruby.ast.Node;
import org.jruby.exceptions.RaiseException;
import org.jruby.internal.runtime.GlobalVariable;
import org.jruby.internal.runtime.GlobalVariables;
import org.jruby.internal.runtime.ReadonlyAccessor;
import org.jruby.javasupport.Java;
import org.jruby.javasupport.JavaObject;
import org.jruby.javasupport.JavaUtil;
import org.jruby.javasupport.bsf.JRubyEngine;
import org.jruby.runtime.Block;
import org.jruby.runtime.IAccessor;
import org.jruby.runtime.builtin.IRubyObject;

import com.sun.script.jruby.JRubyScriptEngineFactory;

/*
* This class is a copy of the class com.sun.script.ruby.JRubyScriptEngine with some minor modifications
* to work around problems with Tuscany setting SCA properties and references as global variable in JRuby
* Should only need it temporarily till a new BSF release fixes it.
*/
@SuppressWarnings("unchecked")
public class TuscanyJRubyScriptEngine extends AbstractScriptEngine
        implements Compilable, Invocable {

    // my factory, may be null
    private ScriptEngineFactory factory;
    private Ruby runtime;
  
    public TuscanyJRubyScriptEngine() {
        init(System.getProperty("com.sun.script.jruby.loadpath"));
    }

    public TuscanyJRubyScriptEngine(String loadPath) {
        init(loadPath);
    }

    // my implementation for CompiledScript
    private class JRubyCompiledScript extends CompiledScript {
        // my compiled code
        private Node node;    

        JRubyCompiledScript (Node node) {
            this.node = node;
        }

        @Override
        public ScriptEngine getEngine() {
            return TuscanyJRubyScriptEngine.this;
        }

        @Override
        public Object eval(ScriptContext ctx) throws ScriptException {
            return evalNode(node, ctx);
        }
    }

    // Compilable methods
    public CompiledScript compile(String script)
                                  throws ScriptException
        Node node = compileScript(script, context);
        return new JRubyCompiledScript(node);
    }

    public CompiledScript compile (Reader reader)
                                  throws ScriptException
        Node node = compileScript(reader, context);
        return new JRubyCompiledScript(node);
    }

    // Invocable methods
    public Object invokeFunction(String name, Object[] args)
                         throws ScriptException {      
        return invokeImpl(null, name, args, Object.class);
    }

    public Object invokeMethod(Object obj, String name, Object[] args)
                         throws ScriptException {      
        if (obj == null) {
            throw new IllegalArgumentException("script object is null");
        }
        return invokeImpl(obj, name, args, Object.class);
    }

    public Object getInterface(Object obj, Class clazz) {
        if (obj == null) {
            throw new IllegalArgumentException("script object is null");
        }
        return makeInterface(obj, clazz);
    }

    public Object getInterface(Class clazz) {
        return makeInterface(null, clazz);
    }

    private <T> T makeInterface(Object obj, Class<T> clazz) {
        if (clazz == null || !clazz.isInterface()) {
            throw new IllegalArgumentException("interface Class expected");
        }
        final Object thiz = obj;
        return (T) Proxy.newProxyInstance(
              clazz.getClassLoader(),
              new Class[] { clazz },
              new InvocationHandler() {
                  public Object invoke(Object proxy, Method m, Object[] args)
                                       throws Throwable {
                      return invokeImpl(thiz, m.getName(),
                                        args, m.getReturnType());
                  }
              });
    }

    // ScriptEngine methods
    public synchronized Object eval(String str, ScriptContext ctx)
                       throws ScriptException {
        Node node = compileScript(str, ctx);
        return evalNode(node, ctx);
    }

    public synchronized Object eval(Reader reader, ScriptContext ctx)
                       throws ScriptException {
        Node node = compileScript(reader, ctx);
        return evalNode(node, ctx);
    }

    public ScriptEngineFactory getFactory() {
        synchronized (this) {
            if (factory == null) {
                factory = new JRubyScriptEngineFactory();
            }
        }
        return factory;
    }

    public Bindings createBindings() {
        return new SimpleBindings();
    }

    // package-private methods
    void setFactory(ScriptEngineFactory factory) {
        this.factory = factory;
    }

    // internals only below this point   

    private Object rubyToJava(IRubyObject value) {
        return rubyToJava(value, Object.class);
    }

    private Object rubyToJava(IRubyObject value, Class type) {
        return JavaUtil.convertArgument(
                 Java.ruby_to_java(value, value, Block.NULL_BLOCK),
                 type);
    }

    private IRubyObject javaToRuby(Object value) {
        if (value instanceof IRubyObject) {
            return (IRubyObject) value;
        }
        IRubyObject result = JavaUtil.convertJavaToRuby(runtime, value);
        if (result instanceof JavaObject) {
            return runtime.getModule("JavaUtilities").callMethod(runtime.getCurrentContext(), "wrap", result);
        }
        return result;
    }  

    private synchronized Node compileScript(String script, ScriptContext ctx)
                                 throws ScriptException {       
        GlobalVariables oldGlobals = runtime.getGlobalVariables()
        try {
            setGlobalVariables(ctx);
            String filename = (String) ctx.getAttribute(ScriptEngine.FILENAME);
            if (filename == null) {
                filename = "<unknown>";
            }
            return runtime.parse(script, filename, null, 0);
        } catch (Exception exp) {
            throw new ScriptException(exp);
        } finally {
            if (oldGlobals != null) {
                //setGlobalVariables(oldGlobals);
            }
        }
    }

    private synchronized Node compileScript(Reader reader, ScriptContext ctx)
                                 throws ScriptException {       
        GlobalVariables oldGlobals = runtime.getGlobalVariables()
        try {
            setGlobalVariables(ctx);
            String filename = (String) ctx.getAttribute(ScriptEngine.FILENAME);
            if (filename == null) {
                filename = "<unknown>";
            }
            return runtime.parse(reader, filename, null, 0);
        } catch (Exception exp) {
            throw new ScriptException(exp);
        } finally {
            if (oldGlobals != null) {
                //setGlobalVariables(oldGlobals);
            }
        }
    }

    private void setGlobalVariables(final ScriptContext ctx) {
        ctx.setAttribute("context", ctx, ScriptContext.ENGINE_SCOPE);
        setGlobalVariables(new GlobalVariables(runtime) {
                GlobalVariables parent = runtime.getGlobalVariables();
               
                @Override
                public void define(String name, IAccessor accessor) {
                    assert name != null;
                    assert accessor != null;
                    assert name.startsWith("$");
                    synchronized (ctx) {
                        Bindings engineScope = ctx.getBindings(ScriptContext.ENGINE_SCOPE);                 
                        engineScope.put(name, new GlobalVariable(accessor));
                    }
                }


                @Override
                public void defineReadonly(String name, IAccessor accessor) {
                    assert name != null;
                    assert accessor != null;
                    assert name.startsWith("$");
                    synchronized (ctx) {
                        Bindings engineScope = ctx.getBindings(ScriptContext.ENGINE_SCOPE);
                        engineScope.put(name, new GlobalVariable(new
                                             ReadonlyAccessor(name, accessor)));
                    }
                }

                @Override
                public boolean isDefined(String name) {
                    assert name != null;
                    assert name.startsWith("$");
                    synchronized (ctx) {
                        String modifiedName = name.substring(1);
                        boolean defined = ctx.getAttributesScope(modifiedName) != -1;
                        return defined ? true : parent.isDefined(name);
                    }
                }

                @Override
                public void alias(String name, String oldName) {
                    assert name != null;
                    assert oldName != null;
                    assert name.startsWith("$");
                    assert oldName.startsWith("$");

                    if (runtime.getSafeLevel() >= 4) {
                        throw runtime.newSecurityError("Insecure: can't alias global variable");
                    }

                    synchronized (ctx) {
                        int scope = ctx.getAttributesScope(name);
                        if (scope == -1) {
                            scope = ScriptContext.ENGINE_SCOPE;
                        }

                        IRubyObject value = get(oldName);
                        ctx.setAttribute(name, rubyToJava(value), scope);
                    }
                }

                @Override
                public IRubyObject get(String name) {
                    assert name != null;
                    assert name.startsWith("$");

                    synchronized (ctx) {
                        // skip '$' and try
                        String modifiedName = name.substring(1);
                        int scope = ctx.getAttributesScope(modifiedName);
                        if (scope == -1) {
                            return parent.get(name);
                        }

                        Object obj = ctx.getAttribute(modifiedName, scope);
                        if (obj instanceof IAccessor) {
                            return ((IAccessor)obj).getValue();
                        } else {
                            return javaToRuby(obj);
                        }
                    }                   
                }

                @Override
                public IRubyObject set(String name, IRubyObject value) {
                    assert name != null;
                    assert name.startsWith("$");

                    if (runtime.getSafeLevel() >= 4) {
                        throw runtime.newSecurityError("Insecure: can't change global variable value");
                    }

                    synchronized (ctx) {
                        // skip '$' and try
                        String modifiedName = name.substring(1);
                        int scope = ctx.getAttributesScope(modifiedName);
                        if (scope == -1) {
                            scope = ScriptContext.ENGINE_SCOPE;
                        }
                        IRubyObject oldValue = get(name);
                        Object obj = ctx.getAttribute(modifiedName, scope);
                        if (obj instanceof IAccessor) {
                            ((IAccessor)obj).setValue(value);
                        } else {                       
                            ctx.setAttribute(modifiedName, rubyToJava(value), scope);
                        }
                        return oldValue;
                    }
                }

                @Override
                public Iterator getNames() {                   
                    List list = new ArrayList();
                    synchronized (ctx) {
                        Iterator<Integer> iterator = ctx.getScopes().iterator();
                        int scope;
                        while (iterator.hasNext()) {
                            scope = iterator.next().intValue();
                            Bindings b = ctx.getBindings(scope);
                            if (b != null) {
                                Iterator<String> bIterator = b.keySet().iterator();
                                while (bIterator.hasNext()) {
                                    list.add(bIterator.next());
                                }
                            }
                        }
                    }
                    for (Iterator names = parent.getNames(); names.hasNext();) {
                        list.add(names.next());
                    }
                    return Collections.unmodifiableList(list).iterator();
                }
            });
    }

    private void setGlobalVariables(GlobalVariables globals) {
        runtime.setGlobalVariables(globals);
    }

    private synchronized Object evalNode(Node node, ScriptContext ctx)
                            throws ScriptException {
        GlobalVariables oldGlobals = runtime.getGlobalVariables();
        try {
            setGlobalVariables(ctx);
            return rubyToJava(runtime.eval(node));
        } catch (RaiseException exp) {
            RubyException rexp = exp.getException();
            throw new ScriptException(rexp.toString());
        } catch (Exception exp) {
            throw new ScriptException(exp);
        } finally {
            if (oldGlobals != null) {
                //setGlobalVariables(oldGlobals);
            }
        }
    }

    private void init(String loadPath) {       
        runtime = Ruby.getDefaultInstance();
        if (loadPath == null) {
            loadPath = System.getProperty("java.class.path");
        }
        List list = new ArrayList(Arrays.asList(loadPath.split(File.pathSeparator)));
        list.add("META-INF/jruby.home/lib/ruby/site_ruby/1.8");
        list.add("META-INF/jruby.home/lib/ruby/site_ruby/1.8/java");
        list.add("META-INF/jruby.home/lib/ruby/site_ruby");
        list.add("META-INF/jruby.home/lib/ruby/1.8");
        list.add("META-INF/jruby.home/lib/ruby/1.8/java");
        list.add("lib/ruby/1.8");
        runtime.getLoadService().init(list);
        runtime.getLoadService().require("java");
       
    }

    private Object invokeImpl(final Object obj, String method,
                        Object[] args, Class returnType)
                        throws ScriptException {
        if (method == null) {
            throw new NullPointerException("method name is null");
        }

        try {
            IRubyObject rubyRecv = obj != null ?
                  JavaUtil.convertJavaToRuby(runtime, obj) : runtime.getTopSelf();

            IRubyObject[] rubyArgs = JavaUtil.convertJavaArrayToRuby(runtime, args);

            // Create Ruby proxies for any input arguments that are not primitives.
            IRubyObject javaUtilities = runtime.getObject().getConstant("JavaUtilities");
            for (int i = 0; i < rubyArgs.length; i++) {
                IRubyObject tmp = rubyArgs[i];
                if (tmp instanceof JavaObject) {
                    rubyArgs[i] = javaUtilities.callMethod(runtime.getCurrentContext(), "wrap", tmp);
                }
            }

            IRubyObject result = rubyRecv.callMethod(runtime.getCurrentContext(), method, rubyArgs);
            return rubyToJava(result, returnType);
        } catch (Exception exp) {
            throw new ScriptException(exp);
        }
    }
}
TOP

Related Classes of org.apache.tuscany.sca.implementation.script.engines.TuscanyJRubyScriptEngine

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.