All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.complate.NashornScriptingBridge Maven / Gradle / Ivy

package com.github.complate;

import jdk.nashorn.api.scripting.NashornException;
import jdk.nashorn.api.scripting.NashornScriptEngine;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;
import org.springframework.core.io.Resource;

import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import java.io.*;
import java.util.Map;
import java.util.Optional;

public final class NashornScriptingBridge implements ScriptingEngine {

    private final NashornScriptEngine engine;

    private static final String POLYFILLS =
            "if(typeof global === \"undefined\") {\n" +
            "    var global = this;\n" +
            "}\n" +
            "if(typeof console === \"undefined\") {\n" +
            "    var console = { log: print, error: print };\n" +
            "}\n\n";

    public NashornScriptingBridge() {
        this.engine = createEngine();
    }

    public NashornScriptingBridge(final Map bindings) {
        this();
        addEngineScopeBindings(bindings);
    }

    public void invoke(final Resource bundle,
                       final String functionName,
                       final Object... args) throws ScriptingException {

        try (Reader reader = readerForScript(bundle)) {
            engine.eval(reader);
        } catch (IOException err) {
            throw new ScriptingException(String.format(
                    "failed to read script from resource '%s'",
                    bundle.getDescription()), err);
        } catch (ScriptException err) {
            throw extractJavaScriptError(err)
                    .map(jsError -> new ScriptingException(
                            "failed to evaluate script",
                            "filepath", functionName, err, jsError))
                    .orElseGet(() -> new ScriptingException(
                            "failed to evaluate script", "filepath",
                            functionName, err));
        }

        try {
            engine.invokeFunction(functionName, args);
        } catch (ScriptException | NoSuchMethodException err) {
            throw extractJavaScriptError(err)
                    .map(jsError -> new ScriptingException(
                            "failed to invoke function",
                            "filepath", functionName, err, jsError))
                    .orElseGet(() -> new ScriptingException(
                            "failed to invoke function", "filepath",
                            functionName, err));
        }
    }

    private static NashornScriptEngine createEngine() {
        final ScriptEngine engine =
                new NashornScriptEngineFactory().getScriptEngine();
        if (engine == null) {
            throw new ScriptingException(
                    "Cannot instantiate Nashorn Script Engine");
        } else {
            return (NashornScriptEngine) engine;
        }
    }

    private static Reader readerForScript(final Resource scriptLocation)
            throws IOException {
        final InputStream is;
        try {
            is = scriptLocation.getInputStream();
        } catch (IOException err) {
            throw new ScriptingException(String.format(
                    "failed to initialize input stream for resource '%s'",
                    scriptLocation.getDescription()), err);
        }
        InputStream stream = prependPolyfills(is);
        final Reader isr = new InputStreamReader(stream);
        return new BufferedReader(isr);
    }

    private static InputStream prependPolyfills(InputStream is) {
        InputStream polyfillsIS = new ByteArrayInputStream(POLYFILLS.getBytes());
        return new SequenceInputStream(polyfillsIS, is);
    }

    private static Optional extractJavaScriptError(final Exception err) {
        final Throwable cause = err.getCause();
        if (cause instanceof NashornException) {
            return Optional.of(cause.getMessage() + "\n" +
                    NashornException.getScriptStackString(cause));
        } else {
            return Optional.empty();
        }
    }

    private void addEngineScopeBindings(final Map bindings) {
        Bindings engineBindings = engine.getBindings(ScriptContext.ENGINE_SCOPE);
        bindings.forEach(engineBindings::put);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy