Package io.apigee.trireme.core.internal

Source Code of io.apigee.trireme.core.internal.ModuleRegistry

/**
* 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.internal;

import io.apigee.trireme.core.InternalNodeModule;
import io.apigee.trireme.core.NativeNodeModule;
import io.apigee.trireme.core.NodeModule;
import io.apigee.trireme.core.NodeScriptModule;
import io.apigee.trireme.core.Utils;
import io.apigee.trireme.core.spi.NodeImplementation;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Script;

import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.ServiceLoader;
import java.util.Set;

/**
* <p>
*     This tracks all the built-in modules that are available to us. There are three types of modules:
* </p>
* <ol>
*     <li>Native modules are built in Java. They are loaded using the ServiceLoader, which means that
*     these modules must implement NodeModule and must be listed in META-INF/services/c.a.n.r.NodeModule.</li>
*     <li>Compiled native modules are built in JavaScript. A Java stub that uses ServiceLoader and implements
*     NodeScriptModule is used to pull in the JavaScript source, which is then compiled and loaded.</li>
*     <li>Internal native modules are also built in Java and are also loaded using the ServiceLoader.
*     However they are loaded using process.binding, not "require" and are intended for internal use only.
*     This way they aren't in the namespace for ordinary users. They implement InternalNodeModule.</li>
*     <li>Internal script modules are written in JavaScript -- their source lives in src/main/javascript and
*     is pre-compiled using Rhino (we have a plugin for this). They are then loaded directly by this
*     class. These modules may only reside within this Maven module.</li>
* </ol>
* <p>
*     The constructor for this class manually defines all compiled script modules. All the rest
*     are loaded using the ServiceLoader, which means that new modules can add new scripts.
* </p>
* <p>
*     This registry is loaded based on a NodeImplementation, which means that there can be many if there
*     are a lot of versions of Node available.
* </p>
*/
public class ModuleRegistry
{
    public enum ModuleType { PUBLIC, INTERNAL, NATIVE }

    /**
     * Add this prefix to internal module code before compiling -- it makes them behave as they expect and as
     * internal modules from "normal" Node behave. This same code must also be included by the Rhino
     * compiler, so it is repeated in pom.xml and must be changed there if it's changed here. This makes these
     * modules be loaded exactly as they would be in traditional Node.
     */
    private static final String CODE_PREFIX = "(function (exports, require, module, __filename, __dirname) {";
    private static final String CODE_POSTFIX = "});";

    private final HashMap<String, NodeModule>         modules         = new HashMap<String, NodeModule>();
    private final HashMap<String, InternalNodeModule> internalModules = new HashMap<String, InternalNodeModule>();
    private final HashMap<String, Script>             compiledModules = new HashMap<String, Script>();
    private final HashMap<String, NativeNodeModule>   nativeModules = new HashMap<String, NativeNodeModule>();
    private final NodeImplementation                  implementation;

    private Script mainScript;
    private boolean loaded;

    public ModuleRegistry(NodeImplementation impl)
    {
        this.implementation = impl;
    }

    public NodeImplementation getImplementation() {
        return implementation;
    }

    public synchronized void load(Context cx)
    {
        if (loaded) {
            return;
        }
        // Load all native Java modules implemented using the "NodeModule" interface
        ServiceLoader<NodeModule> loader = ServiceLoader.load(NodeModule.class);
        for (NodeModule mod : loader) {
            addNativeModule(mod);
        }

        // Load special modules that depend on the version of Java that we have.
        // Load using reflection to avoid classloading in case we are on an old Java version
        if (JavaVersion.get().hasAsyncFileIO()) {
            loadModuleByName("io.apigee.trireme.core.modules.AsyncFilesystem");
        } else {
            loadModuleByName("io.apigee.trireme.core.modules.Filesystem");
        }

        // Load all JavaScript modules implemented using "NodeScriptModule"
        ServiceLoader<NodeScriptModule> scriptLoader = ServiceLoader.load(NodeScriptModule.class);
        for (NodeScriptModule mod: scriptLoader) {
            for (String[] src : mod.getScriptSources()) {
                if (src.length != 2) {
                    throw new AssertionError("Script module " + mod.getClass().getName() +
                                             " returned script source arrays that do not have two elements");
                }
                compileAndAdd(cx, mod, src[0], src[1]);
            }
        }

        loadMainScript(implementation.getMainScriptClass());
        for (String[] builtin : implementation.getBuiltInModules()) {
            addCompiledModule(builtin[0], builtin[1]);
        }
        for (Class<? extends NodeModule> nat : implementation.getNativeModules()) {
            loadModuleByClass(nat);
        }

        loaded = true;
    }

    private void compileAndAdd(Context cx, Object impl, String name, String path)
    {
        String scriptSource;
        InputStream is = impl.getClass().getResourceAsStream(path);
        if (is == null) {
            throw new AssertionError("Script " + path + " cannot be found for module " + name);
        }
        try {
            scriptSource = Utils.readStream(is);
        } catch (IOException ioe) {
            throw new AssertionError("Error reading script " + path + " for module " + name);
        } finally {
            try {
                is.close();
            } catch (IOException ignore) {
            }
        }

        String finalSource = CODE_PREFIX + scriptSource + CODE_POSTFIX;
        Script compiled = cx.compileString(finalSource, name, 1, null);
        compiledModules.put(name, compiled);
    }

    private void addNativeModule(NodeModule mod)
    {
        if (mod instanceof InternalNodeModule) {
            internalModules.put(mod.getModuleName(), (InternalNodeModule) mod);
        } else if (mod instanceof NativeNodeModule) {
            nativeModules.put(mod.getModuleName(), (NativeNodeModule)mod);
        } else {
            modules.put(mod.getModuleName(), mod);
        }
    }

    private void loadMainScript(String className)
    {
        try {
            Class<Script> klass = (Class<Script>)Class.forName(className);
            mainScript = klass.newInstance();
        } catch (ClassNotFoundException e) {
            throw new AssertionError(e);
        } catch (InstantiationException e) {
            throw new AssertionError(e);
        } catch (IllegalAccessException e) {
            throw new AssertionError(e);
        }
    }

    private void loadModuleByName(String className)
    {
        try {
            Class<NodeModule> klass = (Class<NodeModule>)Class.forName(className);
            loadModuleByClass(klass);
        } catch (ClassNotFoundException e) {
            throw new AssertionError(e);
        }
    }

    private void loadModuleByClass(Class<? extends NodeModule> klass)
    {
        try {
            NodeModule mod = klass.newInstance();
            addNativeModule(mod);
        } catch (InstantiationException e) {
            throw new AssertionError(e);
        } catch (IllegalAccessException e) {
            throw new AssertionError(e);
        }
    }

    private void addCompiledModule(String name, String className)
    {
        try {
            Class<Script> cl = (Class<Script>)Class.forName(className);
            Script script = cl.newInstance();
            compiledModules.put(name, script);
        } catch (ClassNotFoundException e) {
            throw new AssertionError("Missing built-in module " + className);
        } catch (InstantiationException e) {
            throw new AssertionError("Error creating Script instance for " + className);
        } catch (IllegalAccessException e) {
            throw new AssertionError("Error creating Script instance for " + className);
        }
    }

    public NodeModule get(String name)
    {
        return modules.get(name);
    }

    public NodeModule getInternal(String name)
    {
        return internalModules.get(name);
    }

    public NodeModule getNative(String name)
    {
        return nativeModules.get(name);
    }

    public Script getCompiledModule(String name)
    {
        return compiledModules.get(name);
    }

    public Set<String> getCompiledModuleNames()
    {
        return Collections.unmodifiableSet(compiledModules.keySet());
    }

    public Script getMainScript()
    {
        return mainScript;
    }
}
TOP

Related Classes of io.apigee.trireme.core.internal.ModuleRegistry

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.