be.solidx.hot.JSR223ScriptExecutor.java Source code

Java tutorial

Introduction

Here is the source code for be.solidx.hot.JSR223ScriptExecutor.java

Source

package be.solidx.hot;

/*
 * #%L
 * Hot
 * %%
 * Copyright (C) 2010 - 2016 Solidx
 * %%
 * 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 3 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-3.0.html>.
 * #L%
 */

import java.io.Writer;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.SimpleScriptContext;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import be.solidx.hot.exceptions.ScriptException;

public abstract class JSR223ScriptExecutor implements ScriptExecutor<CompiledScript> {

    protected static final Log LOGGER = LogFactory.getLog(JSR223ScriptExecutor.class);

    private ScriptEngine scriptEngine;

    private ScriptEngineManager manager = new ScriptEngineManager();

    protected boolean devMode = false;

    protected String engineName;

    protected Map<String, Script<CompiledScript>> compilableMap = new HashMap<String, Script<CompiledScript>>();

    protected List<Script<CompiledScript>> preExecuteScriptMap = new LinkedList<Script<CompiledScript>>();

    public JSR223ScriptExecutor(String engineName) {
        this.engineName = engineName;
    }

    public JSR223ScriptExecutor(String engineName, List<String> globalScriptsPaths) {
        this(engineName);
        compileGlobalScopeScripts(globalScriptsPaths);
    }

    private ScriptEngine getEngine() {
        // In dev mode we create new engine for recompiling sources
        if (devMode || scriptEngine == null) {
            scriptEngine = manager.getEngineByName(engineName);
        }
        return scriptEngine;
    }

    @Override
    public Object execute(Script<CompiledScript> script) throws ScriptException {
        try {
            ScriptEngine scriptEngine = getEngine();
            CompiledScript compiledScript = getCachedScript(script);
            ScriptContext scriptContext = new SimpleScriptContext();
            executePreExecuteScripts(scriptContext);
            Object object = compiledScript.eval(scriptContext);
            if (object == null)
                return scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE);
            return object;
        } catch (javax.script.ScriptException e) {
            throw new ScriptException(e);
        }
    }

    @Override
    public Object execute(Script<CompiledScript> script, Map<String, Object> contextVars) throws ScriptException {
        try {
            ScriptEngine scriptEngine = getEngine();
            Bindings bindings = scriptEngine.createBindings();
            ScriptContext scriptContext = new SimpleScriptContext();
            scriptContext.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
            bindings.putAll(contextVars);
            executePreExecuteScripts(scriptContext);
            CompiledScript compiledScript = getCachedScript(script);
            Object object = compiledScript.eval(scriptContext);
            if (object == null)
                return bindings;
            return object;
        } catch (javax.script.ScriptException e) {
            throw new ScriptException(e);
        }
    }

    @Override
    public Object execute(Script<CompiledScript> script, Writer writer) throws ScriptException {
        try {
            ScriptEngine scriptEngine = getEngine();
            CompiledScript compiledScript = getCachedScript(script);
            SimpleScriptContext simpleScriptContext = new SimpleScriptContext();
            executePreExecuteScripts(simpleScriptContext);
            simpleScriptContext.setWriter(writer);
            Object object = compiledScript.eval(simpleScriptContext);
            writer.flush();
            if (object == null)
                return scriptEngine.getBindings(ScriptContext.ENGINE_SCOPE);
            return object;
        } catch (Exception e) {
            throw new ScriptException(e);
        }
    }

    @Override
    public Object execute(Script<CompiledScript> script, Map<String, Object> contextVars, Writer writer)
            throws ScriptException {
        try {
            ScriptEngine scriptEngine = getEngine();
            SimpleScriptContext simpleScriptContext = new SimpleScriptContext();
            Bindings bindings = scriptEngine.createBindings();
            bindings.putAll(contextVars);
            simpleScriptContext.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
            executePreExecuteScripts(simpleScriptContext);
            simpleScriptContext.setWriter(writer);
            CompiledScript compiledScript = getCachedScript(script);
            Object object = compiledScript.eval(simpleScriptContext);
            writer.flush();
            if (object == null)
                return bindings;
            return object;
        } catch (Exception e) {
            throw new ScriptException(e);
        }
    }

    @Override
    public void compile(Script<CompiledScript> script) throws ScriptException {
        try {
            System.setProperty("groovy.source.encoding", "UTF-8");
            CompiledScript compiledScript = ((Compilable) getEngine()).compile(script.getCodeUTF8());
            script.setCompiledScript(compiledScript);
        } catch (javax.script.ScriptException e) {
            throw new ScriptException(e);
        }
    }

    private void compileGlobalScopeScripts(List<String> globalScriptsPaths) {
        System.setProperty("groovy.source.encoding", "UTF-8");
        for (String path : globalScriptsPaths) {
            try {
                String correctedPath = path.startsWith("/") ? path : path + "/";
                Script<CompiledScript> script = new Script<CompiledScript>(
                        IOUtils.toByteArray(getClass().getResourceAsStream(correctedPath)), correctedPath);
                compile(script);
                preExecuteScriptMap.add(script);
            } catch (Exception e) {
                LOGGER.error("Failed to compile script " + path, e);
            }
        }
    }

    private void executePreExecuteScripts(ScriptContext scriptContext) {
        for (Script<CompiledScript> compiledScript : preExecuteScriptMap) {
            try {
                compiledScript.getCompiledScript().eval(scriptContext);
            } catch (javax.script.ScriptException e) {
                LOGGER.error("Failed to execute script " + compiledScript.getName(), e);
            }
        }
    }

    private CompiledScript getCachedScript(Script<CompiledScript> script) throws ScriptException {
        Script<CompiledScript> compiledScript = compilableMap.get(script.getMd5());
        if ((compiledScript != null && !devMode)
                || (compiledScript != null && devMode && !script.outdated(compiledScript.getCodeUTF8()))) {
            return compiledScript.getCompiledScript();
        } else {
            synchronized (scriptEngine) {
                compile(script);
                compilableMap.put(script.getMd5(), script);
                if (compiledScript != null && script.outdated(compiledScript.getCodeUTF8())) {
                    compilableMap.remove(compiledScript.getMd5());
                }
            }
            return script.getCompiledScript();
        }
    }

    public void setDevMode(boolean devMode) {
        this.devMode = devMode;
    }
}