Package io.apigee.trireme.core.modules

Source Code of io.apigee.trireme.core.modules.NativeModule

/**
* Copyright 2013 Apigee Corporation.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package io.apigee.trireme.core.modules;

import io.apigee.trireme.core.NodeModule;
import io.apigee.trireme.core.NodeRuntime;
import io.apigee.trireme.core.internal.ModuleRegistry;
import io.apigee.trireme.core.internal.ScriptRunner;
import io.apigee.trireme.core.Utils;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Function;
import org.mozilla.javascript.FunctionObject;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.Scriptable;
import org.mozilla.javascript.ScriptableObject;
import org.mozilla.javascript.annotations.JSFunction;
import org.mozilla.javascript.annotations.JSGetter;
import org.mozilla.javascript.annotations.JSSetter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static io.apigee.trireme.core.ArgUtils.*;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;

/**
* This class implements the NativeModule, which is normally part of "node.js" itself. It is the bootstrapper
* for the module system and also loads all native modules. The "module" module calls it first to load
* any internal modules.
*/
public class NativeModule
    implements NodeModule
{
    protected static final Logger log = LoggerFactory.getLogger(NativeModule.class);

    public static final String MODULE_NAME = "native_module";
    public static final String NODE_SCRIPT_BASE = "/io/apigee/trireme/fromnode/";
    public static final String NR_SCRIPT_BASE = "/io/apigee/trireme/scripts/";
    public static final String SCRIPT_SUFFIX = ".js";

    @Override
    public String getModuleName()
    {
        return MODULE_NAME;
    }

    @Override
    public Scriptable registerExports(Context cx, Scriptable scope, NodeRuntime runner)
        throws InvocationTargetException, IllegalAccessException, InstantiationException
    {
        ScriptableObject.defineClass(scope, NativeImpl.class);
        ScriptableObject.defineClass(scope, ModuleImpl.class);
        NativeImpl nat = (NativeImpl)cx.newObject(scope, NativeImpl.CLASS_NAME);
        nat.initialize(cx, runner);
        return nat;
    }

    private static ScriptRunner getRunner(Context cx)
    {
        return (ScriptRunner)cx.getThreadLocal(ScriptRunner.RUNNER);
    }

    public static class NativeImpl
        extends ScriptableObject
    {
        public static final String WRAP_PREFIX  =
            "(function (exports, require, module, __filename, __dirname) {";
        public static final String WRAP_POSTFIX =
            "\n});";

        public static final String CLASS_NAME = "NativeModule";

        private ScriptRunner runner;
        private String     fileName;
        private String     id;
        private Scriptable exports;
        private boolean    loaded;
        private Scriptable cache;

        void initialize(Context cx, NodeRuntime runner)
        {
            // This is an internal-only module and it's OK to use the internal interface here.
            this.runner = (ScriptRunner)runner;
            this.cache = cx.newObject(this);
        }

        @Override
        public String getClassName()
        {
            return CLASS_NAME;
        }

        @JSGetter("filename")
        public String getFileName() {
            return fileName;
        }

        @JSGetter("id")
        public String getId() {
            return id;
        }

        @JSGetter("loaded")
        public boolean isLoaded() {
            return loaded;
        }

        @JSGetter("exports")
        public Scriptable getExports() {
            return exports;
        }

        @JSSetter("exports")
        public void setExports(Scriptable s) {
            this.exports = s;
        }

        @JSGetter("_cache")
        public Scriptable getCache() {
            return cache;
        }

        @JSFunction
        public static Object require(Context cx, Scriptable thisObj, Object[] args, Function func)
            throws InvocationTargetException, InstantiationException, IllegalAccessException
        {
            String name = stringArg(args, 0);
            if ((thisObj != null) && (thisObj instanceof NativeImpl)) {
                NativeImpl self = (NativeImpl)thisObj;
                return self.internalRequire(name, cx);
            } else {
                ScriptRunner r = getRunner(cx);
                return r.getNativeModule().internalRequire(name, cx);
            }
        }

        /**
         * This function is called by "require" on this internal module and also by any internal code
         * that wishes to look up another module via ScriptRunner.require.
         */
        public Scriptable internalRequire(String name, Context cx)
            throws InstantiationException, IllegalAccessException, InvocationTargetException
        {
            ModuleImpl ret = runner.getCachedModule(name);
            if (ret != null) {
                return ret.getExports();
            }

            // First try to find a native Java module
            Object exp = runner.initializeModule(name, ModuleRegistry.ModuleType.PUBLIC, cx, runner.getScriptScope());
            if (exp != null) {
                if (log.isTraceEnabled()) {
                    log.trace("Loaded {} from Java object {}", name, exp);
                }
                ModuleImpl mod = ModuleImpl.newModule(cx, runner.getScriptScope(),
                                                      name, name);
                mod.setExports((Scriptable)exp);
                mod.setLoaded(true);
                runner.cacheModule(name, mod);
                return mod.getExports();
            }

            Script compiled = runner.getRegistry().getCompiledModule(name);
            if (compiled != null) {
                // We found a compiled script -- run it and register.
                // Notice that to prevent cyclical dependencies we cache the "exports" first.
                if (log.isTraceEnabled()) {
                    log.trace("Loading {} from compiled script", name);
                }
                ModuleImpl mod = ModuleImpl.newModule(cx, runner.getScriptScope(),
                                                      name, name + ".js");
                mod.setExports(cx.newObject(runner.getScriptScope()));
                runner.cacheModule(name, mod);
                runCompiledModule(compiled, cx, mod);
                return mod.getExports();
            }
            return null;
        }

        private void runCompiledModule(Script compiled, Context cx, ModuleImpl mod)
        {
            Function requireFunc = new FunctionObject("require",
                                                      Utils.findMethod(NativeImpl.class, "require"),
                                                      this);

            // The script code found in src/main/javascript is wrapped with a function by the Rhino compiler
            // (see the pom.xml for the wrapper code). What we actually
            // need to do here is to invoke the wrapper function after running the script.

            Object ret = compiled.exec(cx, runner.getScriptScope());
            Function fn = (Function)ret;
            fn.call(cx, runner.getScriptScope(), null, new Object[] { mod.getExports(), requireFunc,
                                                                      mod, mod.getFileName() });
            mod.setLoaded(true);
        }

        @JSFunction
        public static Object getCached(Context cx, Scriptable thisObj, Object[] args, Function func)
        {
            String name = stringArg(args, 0);
            NativeImpl self = (NativeImpl)thisObj;
            return self.runner.getCachedModule(name);
        }

        @JSFunction
        public static boolean exists(Context cx, Scriptable thisObj, Object[] args, Function func)
        {
            String name = stringArg(args, 0);
            NativeImpl self = (NativeImpl)thisObj;
            return self.runner.isNativeModule(name);
        }

        @JSFunction
        public static String wrap(String source)
        {
            return WRAP_PREFIX + source + WRAP_POSTFIX;
        }

        @JSFunction
        public static Object getSource(Context cx, Scriptable thisObj, Object[] args, Function func)
        {
            String name = stringArg(args, 0);

            InputStream in = NativeImpl.class.getResourceAsStream(NODE_SCRIPT_BASE + name + SCRIPT_SUFFIX);
            if (in == null) {
                in = NativeImpl.class.getResourceAsStream(NR_SCRIPT_BASE + name + SCRIPT_SUFFIX);
            }
            if (in == null) {
                return Context.getUndefinedValue();
            }

            try {
                try {
                    return Utils.readStream(in);
                } catch (IOException ioe) {
                    log.debug("Error reading native code stream: {}", ioe);
                    return Context.getUndefinedValue();
                }
            } finally {
                try {
                    in.close();
                } catch (IOException ioe) {
                    log.debug("Error closing stream {}", ioe);
                }
            }
        }

        @JSGetter("wrapper")
        public Object getWrapper()
        {
            Object[] wrap = new Object[2];
            wrap[0] = WRAP_PREFIX;
            wrap[1] = WRAP_POSTFIX;
            return Context.getCurrentContext().newArray(this, wrap);
        }
    }

    public static class ModuleImpl
        extends ScriptableObject
    {
        public static final String CLASS_NAME = "_moduleImplClass";

        private String fileName;
        private String id;
        private Scriptable exports;
        private boolean loaded;

        public static ModuleImpl newModule(Context cx, Scriptable scope, String id, String fileName)
        {
            ModuleImpl mod = (ModuleImpl)cx.newObject(scope, CLASS_NAME);
            mod.setFileName(fileName);
            mod.setId(id);
            return mod;
        }

        @Override
        public String getClassName()
        {
            return CLASS_NAME;
        }

        @JSGetter("fileName")
        public String getFileName()
        {
            return fileName;
        }

        @JSSetter("fileName")
        public void setFileName(String fileName)
        {
            this.fileName = fileName;
        }

        @JSGetter("id")
        public String getId()
        {
            return id;
        }

        @JSSetter("id")
        public void setId(String id)
        {
            this.id = id;
        }

        @JSGetter("exports")
        public Scriptable getExports()
        {
            return exports;
        }

        @JSSetter("exports")
        public void setExports(Scriptable exports)
        {
            this.exports = exports;
        }

        @JSGetter("loaded")
        public boolean isLoaded()
        {
            return loaded;
        }

        @JSSetter("loaded")
        public void setLoaded(boolean loaded)
        {
            this.loaded = loaded;
        }
    }
}
TOP

Related Classes of io.apigee.trireme.core.modules.NativeModule

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.