io.stallion.plugins.javascript.JsPluginEngine.java Source code

Java tutorial

Introduction

Here is the source code for io.stallion.plugins.javascript.JsPluginEngine.java

Source

/*
 * Stallion Core: A Modern Web Framework
 *
 * Copyright (C) 2015 - 2016 Stallion Software LLC.
 *
 * This program is free software: you can redistribute it and/or modify it under the terms of the
 * GNU General Public License as published by the Free Software Foundation, either version 2 of
 * the License, or (at your option) any later version. This program is distributed in the hope that
 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
 * License for more details. You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
 *
 *
 *
 */

package io.stallion.plugins.javascript;

import com.moandjiezana.toml.Toml;
import io.stallion.exceptions.UsageException;
import io.stallion.plugins.PluginRegistry;
import io.stallion.services.Log;
import io.stallion.settings.Settings;
import io.stallion.settings.SettingsLoader;
import io.stallion.settings.childSections.CustomSettings;
import io.stallion.utils.json.JSON;
import jdk.nashorn.api.scripting.NashornScriptEngine;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import jdk.nashorn.internal.objects.Global;
import jdk.nashorn.internal.runtime.Context;
import jdk.nashorn.internal.runtime.ErrorManager;
import jdk.nashorn.internal.runtime.ScriptFunction;
import jdk.nashorn.internal.runtime.options.Options;
import org.apache.commons.io.IOUtils;

import javax.script.ScriptEngine;
import javax.script.ScriptException;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import static io.stallion.utils.Literals.*;

public class JsPluginEngine {

    private ScriptEngine scriptEngine;
    //private ScriptEngineManager scriptEngineManager;
    private HashSet<String> watchedPaths = new HashSet<>();
    private Boolean watchFiles = true;
    private String folder;

    public JsPluginEngine() {
        this(true);
    }

    public JsPluginEngine(Boolean watchFiles) {
        this.watchFiles = watchFiles;

    }

    public void shutdown() {

    }

    public Object evaluate(String source) throws Exception {
        Log.info("eval {0}", source);
        return scriptEngine.eval(source);
    }

    public void loadJavascript(String plugin, String fullPath) throws Exception {

        JsPluginSettings pluginSettings = new SettingsLoader().loadSettings(plugin, JsPluginSettings.class);

        Sandbox box = Sandbox.forPlugin(plugin);
        if (box == null) {
            loadUnrestrictedJavascript(fullPath, pluginSettings);
        } else {
            loadSandboxedJavascript(box, fullPath, pluginSettings);
        }

    }

    public void loadSandboxedJavascript(Sandbox box, String fullPath, JsPluginSettings pluginSettings)
            throws Exception {
        SandboxClassFilter classFilter = new SandboxClassFilter(box);

        this.scriptEngine = new NashornScriptEngineFactory().getScriptEngine(classFilter);

        Log.info("Load sandboxed js file {0}", fullPath);
        folder = new File(fullPath).getParent();
        scriptEngine.put("javaToJsHelpers", new JavaToJsHelpers(box));

        SandboxedContext ctx = new SandboxedContext(folder, box, pluginSettings);
        scriptEngine.put("myContext", ctx);
        String stallionSharedJs = IOUtils.toString(getClass().getResource("/jslib/stallion_shared.js"), UTF8);
        classFilter.setDisabled(true); // Turn off white-listing while loading stallion_shared, since we need access to more classes

        scriptEngine.eval("load("
                + JSON.stringify(map(val("script", stallionSharedJs), val("name", "stallion_shared.js"))) + ");");
        classFilter.setDisabled(false); // Turn whitelisting back on
        scriptEngine.put("stallionClassLoader", new SandboxedClassLoader(box));
        //scriptEngine.eval("Java = {extend: Java.extend, type: function(className) { return stallionClassLoader.loadClass(className).static;  }}");
        //scriptEngine.eval("Packages = undefined;java = undefined;");
        scriptEngine.eval("load(\"" + fullPath + "\");");

        Log.info("Loaded js plugin {0}", fullPath);
    }

    public void runTestsInFileAndPrintResults(String testFile) {
        List<TestResults> allResults = runTestsInFile(testFile);

        boolean hasError = false;

        for (TestResults results : allResults) {
            if (results.hasAnyErrors()) {
                hasError = true;
            }
            results.printResults();
        }

        if (hasError) {
            throw new AssertionError("Javascript tests failed with errors for file: " + testFile);
        }
    }

    public List<TestResults> runTestsInFile(String testFile) {

        if (testFile.contains("..") || testFile.startsWith("/")) {
            throw new UsageException("Invalid file name: " + testFile);
        }
        String path = folder + "/" + testFile;
        if (!new File(path).isFile()) {
            throw new UsageException("Javascript test file not found: " + path);
        }
        Log.info("Running javascript tests for file {0}", path);
        SuitesHolder holder = new SuitesHolder().setFile(testFile);
        scriptEngine.put("jsSuitesHolder", holder);
        try {
            scriptEngine.eval("load(\"" + path + "\");");
        } catch (ScriptException e) {
            throw new RuntimeException(e);
        }
        boolean hasError = false;
        List<TestResults> results = list();

        for (JsTestSuite suite : holder.getSuites()) {
            results.add(suite.getResults());
        }
        return results;
    }

    private void loadUnrestrictedJavascript(String fullPath, JsPluginSettings pluginSettings) throws Exception {
        folder = new File(fullPath).getParent();
        this.scriptEngine = (NashornScriptEngine) new NashornScriptEngineFactory().getScriptEngine();

        scriptEngine.put("javaToJsHelpers", new JavaToJsHelpers(null));

        String jvmNpm = IOUtils.toString(getClass().getResource("/jslib/jvm-npm.js"), UTF8);
        scriptEngine.eval("load(" + JSON.stringify(map(val("script", jvmNpm), val("name", "jvm-npm.js"))) + ");");

        SandboxedContext ctx = new SandboxedContext(folder, Sandbox.allPermissions(), pluginSettings);
        scriptEngine.put("myContext", ctx);

        String stallionSharedJs = IOUtils.toString(getClass().getResource("/jslib/stallion_shared.js"), UTF8);
        scriptEngine.eval("load("
                + JSON.stringify(map(val("script", stallionSharedJs), val("name", "stallion_shared.js"))) + ");");

        scriptEngine.put("stallionClassLoader", new UnrestrictedJsClassLoader());
        //scriptEngine.eval("Java.type = function(className) { var cls = stallionClassLoader.loadClass(className); if (cls) {return cls.static} else { print('Could not find class ' + className); return null; }  };");

        scriptEngine.put("pluginFolder", folder);
        scriptEngine.put("jsEngine", this);

        //Global global = Context.getGlobal();
        //global.put("astring", "foo", true);

        //Log.info("astring: {0}", scriptEngine.get("astring"));

        Log.info("Load js file {0}", fullPath);
        String folder = new File(fullPath).getParent();
        String nodePath = folder + "/node_modules";
        scriptEngine.eval("require.NODE_PATH = \"" + nodePath + "\"");

        scriptEngine.eval("load(\"" + fullPath + "\");");

        Log.info("Finish loading js {0}", fullPath);

    }

    /*
    private void experimentalloadUnrestrictedJavascript(String fullPath, JsPluginSettings pluginSettings) throws Exception {
    ErrorManager errors = new ErrorManager();
        
    Options options = new Options("nashorn");
    Context context = new Context(options, errors, Thread.currentThread().getContextClassLoader());
        
        
    Global global = context.createGlobal();
    Context.setGlobal(global);
        
    global.put("javaToJsHelpers", new JavaToJsHelpers(null), true);
        
    String jvmNpm = IOUtils.toString(getClass().getResource("/jslib/jvm-npm.js"));
    context.eval(global, "load(" + JSON.stringify(map(val("script", jvmNpm), val("name", "jvm-npm.js"))) + ");", global, "<engineloading>");
        
    String stallionSharedJs = IOUtils.toString(getClass().getResource("/jslib/stallion_shared.js"));
    context.eval(global, "load(" + JSON.stringify(map(val("script", stallionSharedJs), val("name", "stallion_shared.js"))) + ");", global, "<engineloading>");
        
        
    global.put("stallionClassLoader", new UnrestrictedJsClassLoader(), true);
    //scriptEngine.eval("Java.type = function(className) { var cls = stallionClassLoader.loadClass(className); if (cls) {return cls.static} else { print('Could not find class ' + className); return null; }  };");
        
    global.put("pluginFolder", folder, true);
    global.put("jsEngine", this, true);
        
    SandboxedContext ctx = new SandboxedContext(folder, Sandbox.allPermissions(), pluginSettings);
    global.put("myContext", ctx, true);
        
        
    global.put("astring", "foo", true);
        
    //Log.info("astring: {0}", scriptEngine.get("astring"));
        
    global.addShellBuiltins();
        
    //ScriptFunction value = ScriptFunction.createBuiltin("calc", EvalLoop.getCalcHandle2(global, context));
    global.addOwnProperty("calc", 2, value);
        
    Log.info("Load js file {0}", fullPath);
    String folder = new File(fullPath).getParent();
    String nodePath = folder + "/node_modules";
    context.eval(global, "require.NODE_PATH = \"" + nodePath + "\"", global, "<loading>");
        
    context.eval(global, "load(\"" + fullPath + "\");", global, "<evalscript>");
        
    Log.info("Finish loading js {0}", fullPath);
    }
    */

    public void fileChangeCallback(String jsPath) throws Exception {
        //loadJavascript(jsPath);
        Log.info("File change callback {0}", jsPath);
    }

    public ScriptEngine getScriptEngine() {
        return scriptEngine;
    }

    public void setScriptEngine(ScriptEngine scriptEngine) {
        this.scriptEngine = scriptEngine;
    }

}