123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533 |
- /*
- * Copyright (C) 2004-2015 L2J Server
- *
- * This file is part of L2J Server.
- *
- * L2J Server 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.
- *
- * L2J Server 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/>.
- */
- package com.l2jserver.gameserver.scripting;
- import java.io.BufferedReader;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.io.LineNumberReader;
- import java.util.HashMap;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.Map;
- import java.util.logging.Level;
- import java.util.logging.Logger;
- import javax.script.Compilable;
- import javax.script.CompiledScript;
- import javax.script.ScriptContext;
- import javax.script.ScriptEngine;
- import javax.script.ScriptEngineFactory;
- import javax.script.ScriptEngineManager;
- import javax.script.ScriptException;
- import javax.script.SimpleScriptContext;
- import com.l2jserver.Config;
- import com.l2jserver.script.jython.JythonScriptEngine;
- /**
- * Caches script engines and provides functionality for executing and managing scripts.
- * @author KenM
- */
- public final class L2ScriptEngineManager
- {
- private static final Logger _log = Logger.getLogger(L2ScriptEngineManager.class.getName());
-
- public static final File SCRIPT_FOLDER = new File(Config.DATAPACK_ROOT.getAbsolutePath(), "data/scripts");
-
- public static L2ScriptEngineManager getInstance()
- {
- return SingletonHolder._instance;
- }
-
- private final Map<String, ScriptEngine> _nameEngines = new HashMap<>();
- private final Map<String, ScriptEngine> _extEngines = new HashMap<>();
- private final List<ScriptManager<?>> _scriptManagers = new LinkedList<>();
-
- private File _currentLoadingScript;
-
- // Configs
- // TODO move to config file
- /**
- * Informs(logs) the scripts being loaded.<BR>
- * Apply only when executing script from files.<BR>
- */
- private static final boolean VERBOSE_LOADING = false;
-
- /**
- * If the script engine supports compilation the script is compiled before execution.<BR>
- */
- private static final boolean ATTEMPT_COMPILATION = true;
-
- /**
- * Clean an previous error log(if such exists) for the script being loaded before trying to load.<BR>
- * Apply only when executing script from files.<BR>
- */
- private static final boolean PURGE_ERROR_LOG = true;
-
- protected L2ScriptEngineManager()
- {
- ScriptEngineManager scriptEngineManager = new ScriptEngineManager();
- List<ScriptEngineFactory> factories = scriptEngineManager.getEngineFactories();
-
- for (ScriptEngineFactory factory : factories)
- {
- try
- {
- ScriptEngine engine = factory.getScriptEngine();
- boolean reg = false;
- for (String name : factory.getNames())
- {
- ScriptEngine existentEngine = _nameEngines.get(name);
-
- if (existentEngine != null)
- {
- double engineVer = Double.parseDouble(factory.getEngineVersion());
- double existentEngVer = Double.parseDouble(existentEngine.getFactory().getEngineVersion());
-
- if (engineVer <= existentEngVer)
- {
- continue;
- }
- }
-
- reg = true;
- _nameEngines.put(name, engine);
- }
-
- if (reg)
- {
- _log.info("Script Engine: " + factory.getEngineName() + " " + factory.getEngineVersion() + " - Language: " + factory.getLanguageName() + " - Language Version: " + factory.getLanguageVersion());
- }
-
- for (String ext : factory.getExtensions())
- {
- if (!ext.equals("java") || factory.getLanguageName().equals("java"))
- {
- _extEngines.put(ext, engine);
- }
- }
- }
- catch (Exception e)
- {
- _log.log(Level.WARNING, "Failed initializing factory: " + e.getMessage(), e);
- }
- }
-
- preConfigure();
- }
-
- private void preConfigure()
- {
- // Jython sys.path
- String dataPackDirForwardSlashes = SCRIPT_FOLDER.getPath().replaceAll("\\\\", "/");
- String configScript = "import sys;sys.path.insert(0,'" + dataPackDirForwardSlashes + "');";
- try
- {
- eval("jython", configScript);
- }
- catch (ScriptException e)
- {
- _log.severe("Failed preconfiguring jython: " + e.getMessage());
- }
- }
-
- private ScriptEngine getEngineByName(String name)
- {
- return _nameEngines.get(name);
- }
-
- private ScriptEngine getEngineByExtension(String ext)
- {
- return _extEngines.get(ext);
- }
-
- public void executeScriptList(File list) throws IOException
- {
- if (Config.ALT_DEV_NO_QUESTS)
- {
- if (!Config.ALT_DEV_NO_HANDLERS)
- {
- try
- {
- executeScript(new File(SCRIPT_FOLDER, "handlers/MasterHandler.java"));
- _log.info("Handlers loaded, all other scripts skipped");
- }
- catch (ScriptException se)
- {
- _log.log(Level.WARNING, "", se);
- }
- }
- return;
- }
-
- if (list.isFile())
- {
- try (FileInputStream fis = new FileInputStream(list);
- InputStreamReader isr = new InputStreamReader(fis);
- LineNumberReader lnr = new LineNumberReader(isr))
- {
- String line;
- while ((line = lnr.readLine()) != null)
- {
- if (Config.ALT_DEV_NO_HANDLERS && line.contains("MasterHandler.java"))
- {
- continue;
- }
-
- String[] parts = line.trim().split("#");
-
- if ((parts.length > 0) && !parts[0].isEmpty() && (parts[0].charAt(0) != '#'))
- {
- line = parts[0];
-
- if (line.endsWith("/**"))
- {
- line = line.substring(0, line.length() - 3);
- }
- else if (line.endsWith("/*"))
- {
- line = line.substring(0, line.length() - 2);
- }
-
- File file = new File(SCRIPT_FOLDER, line);
-
- if (file.isDirectory() && parts[0].endsWith("/**"))
- {
- executeAllScriptsInDirectory(file, true, 32);
- }
- else if (file.isDirectory() && parts[0].endsWith("/*"))
- {
- executeAllScriptsInDirectory(file);
- }
- else if (file.isFile())
- {
- try
- {
- executeScript(file);
- }
- catch (ScriptException e)
- {
- reportScriptFileError(file, e);
- }
- }
- else
- {
- _log.warning("Failed loading: (" + file.getCanonicalPath() + ") @ " + list.getName() + ":" + lnr.getLineNumber() + " - Reason: doesnt exists or is not a file.");
- }
- }
- }
- }
- }
- else
- {
- throw new IllegalArgumentException("Argument must be an file containing a list of scripts to be loaded");
- }
- }
-
- public void executeAllScriptsInDirectory(File dir)
- {
- executeAllScriptsInDirectory(dir, false, 0);
- }
-
- public void executeAllScriptsInDirectory(File dir, boolean recurseDown, int maxDepth)
- {
- executeAllScriptsInDirectory(dir, recurseDown, maxDepth, 0);
- }
-
- private void executeAllScriptsInDirectory(File dir, boolean recurseDown, int maxDepth, int currentDepth)
- {
- if (dir.isDirectory())
- {
- final File[] files = dir.listFiles();
- if (files == null)
- {
- return;
- }
-
- for (File file : files)
- {
- if (file.isDirectory() && recurseDown && (maxDepth > currentDepth))
- {
- if (VERBOSE_LOADING)
- {
- _log.info("Entering folder: " + file.getName());
- }
- executeAllScriptsInDirectory(file, recurseDown, maxDepth, currentDepth + 1);
- }
- else if (file.isFile())
- {
- try
- {
- String name = file.getName();
- int lastIndex = name.lastIndexOf('.');
- String extension;
- if (lastIndex != -1)
- {
- extension = name.substring(lastIndex + 1);
- ScriptEngine engine = getEngineByExtension(extension);
- if (engine != null)
- {
- executeScript(engine, file);
- }
- }
- }
- catch (ScriptException e)
- {
- reportScriptFileError(file, e);
- }
- }
- }
- }
- else
- {
- throw new IllegalArgumentException("The argument directory either doesnt exists or is not an directory.");
- }
- }
-
- public void executeScript(File file) throws ScriptException
- {
- String name = file.getName();
- int lastIndex = name.lastIndexOf('.');
- String extension;
- if (lastIndex != -1)
- {
- extension = name.substring(lastIndex + 1);
- }
- else
- {
- throw new ScriptException("Script file (" + name + ") doesnt has an extension that identifies the ScriptEngine to be used.");
- }
-
- ScriptEngine engine = getEngineByExtension(extension);
- if (engine == null)
- {
- throw new ScriptException("No engine registered for extension (" + extension + ")");
- }
- executeScript(engine, file);
- }
-
- public void executeScript(String engineName, File file) throws ScriptException
- {
- ScriptEngine engine = getEngineByName(engineName);
- if (engine == null)
- {
- throw new ScriptException("No engine registered with name (" + engineName + ")");
- }
- executeScript(engine, file);
- }
-
- public void executeScript(ScriptEngine engine, File file) throws ScriptException
- {
- if (VERBOSE_LOADING)
- {
- _log.info("Loading Script: " + file.getAbsolutePath());
- }
-
- if (PURGE_ERROR_LOG)
- {
- String name = file.getAbsolutePath() + ".error.log";
- File errorLog = new File(name);
- if (errorLog.isFile())
- {
- errorLog.delete();
- }
- }
-
- final String relativeName = file.getAbsolutePath().substring(SCRIPT_FOLDER.getAbsolutePath().length() + 1).replace('\\', '/');
- try (FileInputStream fis = new FileInputStream(file);
- InputStreamReader isr = new InputStreamReader(fis);
- BufferedReader reader = new BufferedReader(isr))
- {
- if ((engine instanceof Compilable) && ATTEMPT_COMPILATION)
- {
- ScriptContext context = new SimpleScriptContext();
- context.setAttribute("mainClass", getClassForFile(file).replace('/', '.').replace('\\', '.'), ScriptContext.ENGINE_SCOPE);
- context.setAttribute(ScriptEngine.FILENAME, relativeName, ScriptContext.ENGINE_SCOPE);
- context.setAttribute("classpath", SCRIPT_FOLDER.getAbsolutePath(), ScriptContext.ENGINE_SCOPE);
- context.setAttribute("sourcepath", SCRIPT_FOLDER.getAbsolutePath(), ScriptContext.ENGINE_SCOPE);
- context.setAttribute(JythonScriptEngine.JYTHON_ENGINE_INSTANCE, engine, ScriptContext.ENGINE_SCOPE);
-
- setCurrentLoadingScript(file);
- ScriptContext ctx = engine.getContext();
- try
- {
- engine.setContext(context);
- Compilable eng = (Compilable) engine;
- CompiledScript cs = eng.compile(reader);
- cs.eval(context);
- }
- finally
- {
- engine.setContext(ctx);
- setCurrentLoadingScript(null);
- context.removeAttribute(ScriptEngine.FILENAME, ScriptContext.ENGINE_SCOPE);
- context.removeAttribute("mainClass", ScriptContext.ENGINE_SCOPE);
- }
- }
- else
- {
- ScriptContext context = new SimpleScriptContext();
- context.setAttribute("mainClass", getClassForFile(file).replace('/', '.').replace('\\', '.'), ScriptContext.ENGINE_SCOPE);
- context.setAttribute(ScriptEngine.FILENAME, relativeName, ScriptContext.ENGINE_SCOPE);
- context.setAttribute("classpath", SCRIPT_FOLDER.getAbsolutePath(), ScriptContext.ENGINE_SCOPE);
- context.setAttribute("sourcepath", SCRIPT_FOLDER.getAbsolutePath(), ScriptContext.ENGINE_SCOPE);
- setCurrentLoadingScript(file);
- try
- {
- engine.eval(reader, context);
- }
- finally
- {
- setCurrentLoadingScript(null);
- engine.getContext().removeAttribute(ScriptEngine.FILENAME, ScriptContext.ENGINE_SCOPE);
- engine.getContext().removeAttribute("mainClass", ScriptContext.ENGINE_SCOPE);
- }
-
- }
- }
- catch (IOException e)
- {
- _log.log(Level.WARNING, "Error executing script!", e);
- }
- }
-
- public static String getClassForFile(File script)
- {
- String path = script.getAbsolutePath();
- String scpPath = SCRIPT_FOLDER.getAbsolutePath();
- if (path.startsWith(scpPath))
- {
- int idx = path.lastIndexOf('.');
- return path.substring(scpPath.length() + 1, idx);
- }
- return null;
- }
-
- public ScriptContext getScriptContext(ScriptEngine engine)
- {
- return engine.getContext();
- }
-
- public ScriptContext getScriptContext(String engineName)
- {
- ScriptEngine engine = getEngineByName(engineName);
- if (engine == null)
- {
- throw new IllegalStateException("No engine registered with name (" + engineName + ")");
- }
- return getScriptContext(engine);
- }
-
- public Object eval(ScriptEngine engine, String script, ScriptContext context) throws ScriptException
- {
- if ((engine instanceof Compilable) && ATTEMPT_COMPILATION)
- {
- Compilable eng = (Compilable) engine;
- CompiledScript cs = eng.compile(script);
- return context != null ? cs.eval(context) : cs.eval();
- }
- return context != null ? engine.eval(script, context) : engine.eval(script);
- }
-
- public Object eval(String engineName, String script) throws ScriptException
- {
- return eval(engineName, script, null);
- }
-
- public Object eval(String engineName, String script, ScriptContext context) throws ScriptException
- {
- ScriptEngine engine = getEngineByName(engineName);
- if (engine == null)
- {
- throw new ScriptException("No engine registered with name (" + engineName + ")");
- }
- return eval(engine, script, context);
- }
-
- public Object eval(ScriptEngine engine, String script) throws ScriptException
- {
- return eval(engine, script, null);
- }
-
- public void reportScriptFileError(File script, ScriptException e)
- {
- String dir = script.getParent();
- String name = script.getName() + ".error.log";
- if (dir != null)
- {
- final File file = new File(dir + "/" + name);
- try (FileOutputStream fos = new FileOutputStream(file))
- {
- String errorHeader = "Error on: " + file.getCanonicalPath() + Config.EOL + "Line: " + e.getLineNumber() + " - Column: " + e.getColumnNumber() + Config.EOL + Config.EOL;
- fos.write(errorHeader.getBytes());
- fos.write(e.getMessage().getBytes());
- _log.warning("Failed executing script: " + script.getAbsolutePath() + ". See " + file.getName() + " for details.");
- }
- catch (IOException ioe)
- {
- _log.log(Level.WARNING, "Failed executing script: " + script.getAbsolutePath() + Config.EOL + e.getMessage() + "Additionally failed when trying to write an error report on script directory. Reason: " + ioe.getMessage(), ioe);
- }
- }
- else
- {
- _log.log(Level.WARNING, "Failed executing script: " + script.getAbsolutePath() + Config.EOL + e.getMessage() + "Additionally failed when trying to write an error report on script directory.", e);
- }
- }
-
- public void registerScriptManager(ScriptManager<?> manager)
- {
- _scriptManagers.add(manager);
- }
-
- public void removeScriptManager(ScriptManager<?> manager)
- {
- _scriptManagers.remove(manager);
- }
-
- public List<ScriptManager<?>> getScriptManagers()
- {
- return _scriptManagers;
-
- }
-
- /**
- * @param currentLoadingScript The currentLoadingScript to set.
- */
- protected void setCurrentLoadingScript(File currentLoadingScript)
- {
- _currentLoadingScript = currentLoadingScript;
- }
-
- /**
- * @return Returns the currentLoadingScript.
- */
- public File getCurrentLoadingScript()
- {
- return _currentLoadingScript;
- }
-
- private static class SingletonHolder
- {
- protected static final L2ScriptEngineManager _instance = new L2ScriptEngineManager();
- }
- }
|