001/*
002 * Stallion Core: A Modern Web Framework
003 *
004 * Copyright (C) 2015 - 2016 Stallion Software LLC.
005 *
006 * This program is free software: you can redistribute it and/or modify it under the terms of the
007 * GNU General Public License as published by the Free Software Foundation, either version 2 of
008 * the License, or (at your option) any later version. This program is distributed in the hope that
009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
011 * License for more details. You should have received a copy of the GNU General Public License
012 * along with this program.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
013 *
014 *
015 *
016 */
017
018package io.stallion.tools;
019
020import io.stallion.boot.AppContextLoader;
021import io.stallion.boot.StallionRunAction;
022import io.stallion.exceptions.CommandException;
023import io.stallion.plugins.StallionJavaPlugin;
024import io.stallion.plugins.PluginRegistry;
025import io.stallion.plugins.javascript.*;
026import io.stallion.utils.json.JSON;
027import org.apache.commons.io.IOUtils;
028import org.apache.commons.lang3.exception.ExceptionUtils;
029
030
031import javax.script.ScriptEngine;
032import javax.script.ScriptEngineManager;
033import java.io.File;
034import java.net.URL;
035import java.util.List;
036import java.util.Scanner;
037
038import static io.stallion.utils.Literals.*;
039import static io.stallion.Context.*;
040
041
042public class ScriptExecBase implements StallionRunAction<ScriptOptions> {
043
044
045
046    @Override
047    public ScriptOptions newCommandOptions() {
048        return new ScriptOptions();
049    }
050
051    @Override
052    public String getActionName() {
053        return "exec-script";
054    }
055
056    @Override
057    public String getHelp() {
058        return "Executes a jython or javascript script.";
059    }
060
061    @Override
062    public void loadApp(ScriptOptions options) {
063
064        AppContextLoader.loadCompletelyForScript(options);
065    }
066
067    @Override
068    public void execute(ScriptOptions options) throws Exception {
069        if (options.getArguments().size() < 2) {
070            throw new CommandException("You must pass in the script that you want to execute as a positional argument");
071        }
072        String scriptArg = options.getArguments().get(1);
073
074        String plugin = "";
075        String path = scriptArg;
076        String[] parts = scriptArg.split(":");
077        String scriptPath = "";
078        if (parts.length > 1) {
079            plugin = parts[0];
080            path = parts[1];
081        }
082
083        URL url = null;
084        String folder = null;
085        if (!empty(plugin)) {
086            if ("stallion".equals(plugin)) {
087                url = getClass().getResource(path);
088            }
089            if (url == null && PluginRegistry.instance() != null) {
090                StallionJavaPlugin booter = PluginRegistry.instance().getJavaPluginByName().getOrDefault(plugin, null);
091                if (booter != null) {
092                    url = booter.getClass().getResource(path);
093                }
094            }
095            if (url == null) {
096                if (plugin.equals("js")) {
097                    String fullPath = settings().getTargetFolder() + "/js" + path;
098                    File file = new File(fullPath);
099                    if (file.isFile()) {
100                        url = new URL("file:" + fullPath);
101                        scriptPath = file.getParent();
102                        folder = settings().getTargetFolder() + "/js";
103                    }
104
105                } else {
106
107                    String fullPath = settings().getTargetFolder() + "/plugins/" + plugin + path;
108                    File file = new File(fullPath);
109                    if (file.isFile()) {
110                        url = new URL("file:" + fullPath);
111                        scriptPath = file.getParent();
112                        folder = settings().getTargetFolder() + "/plugins/" + plugin;
113                    }
114                }
115            }
116        } else {
117            url = new URL("file:" + settings().getTargetFolder() + "/js/" + path);
118            folder = settings().getTargetFolder() + "/js/";
119        }
120        if (url == null) {
121            throw new CommandException("Could not find matching script for '" + scriptArg + "' in any folder or jar resource.");
122        }
123        if (empty(folder)) {
124            folder = new File(url.toString()).getParent();
125        }
126        String source = IOUtils.toString(url, UTF8);
127        List<String> args = list(path);
128        if (options.getArguments().size() > 2) {
129            args.addAll(options.getArguments().subList(2, options.getArguments().size()));
130        }
131        if (path.endsWith(".js")) {
132            executeJavascript(source, url, scriptPath, folder, args, plugin);
133        } else {
134            throw new CommandException("Unknown extension for path. Supported extensions are .py for jython and .js for javascript. Path was: " + path);
135        }
136    }
137
138    public void executeJavascript(String source, URL url, String scriptPath, String folder, List<String> args, String plugin) throws Exception {
139        ScriptEngine scriptEngine = null;
140        if (plugin.equals("js") || plugin.equals("stallion")) {
141            JsPluginEngine pluginEngine = PluginRegistry.instance().getEngine("main.js");
142            if (pluginEngine != null) {
143                scriptEngine = pluginEngine.getScriptEngine();
144            }
145        } else if (!empty(plugin)) {
146            JsPluginEngine pluginEngine = PluginRegistry.instance().getEngine(plugin);
147            if (pluginEngine != null) {
148                scriptEngine = pluginEngine.getScriptEngine();
149            }
150        }
151        if (scriptEngine == null) {
152            ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
153            scriptEngine = scriptEngineManager.getEngineByName("nashorn");
154            scriptEngine.eval(IOUtils.toString(getClass().getResource("/jslib/jvm-npm.js"), UTF8));
155            scriptEngine.eval(IOUtils.toString(getClass().getResource("/jslib/stallion_shared.js"), UTF8));
156            String nodePath = folder + "/node_modules";
157            scriptEngine.eval("require.NODE_PATH = \"" + nodePath + "\"");
158            scriptEngine.put("myContext", new SandboxedContext(plugin, Sandbox.allPermissions(), new JsPluginSettings()));
159        }
160        if (true || newCommandOptions().isDevMode()) {
161            Scanner in = new Scanner(System.in);
162            while (true) {
163                source = IOUtils.toString(url, UTF8);
164                try {
165                    scriptEngine.eval("load(" + JSON.stringify(map(val("script", source), val("name", url.toString()))) + ");");
166                    //scriptEngine.eval(IOUtils.)
167                } catch(Exception e) {
168                    ExceptionUtils.printRootCauseStackTrace(e);
169                } finally {
170
171                }
172                System.out.println("Hit enter to re-run the script. Type quit and hit enter to stop.");
173                String line = in.nextLine().trim();
174                if (empty(line)) {
175                    continue;
176                } else {
177                    break;
178                }
179            }
180        } else {
181            scriptEngine.eval(source);
182        }
183
184    }
185
186
187}