package gov.nasa.jpl.mbee.mdk.util; import com.nomagic.magicdraw.automaton.AutomatonPlugin; import com.nomagic.magicdraw.core.Application; import com.nomagic.magicdraw.core.ApplicationEnvironment; import com.nomagic.magicdraw.core.GUILog; import com.nomagic.magicdraw.openapi.uml.SessionManager; import com.nomagic.magicdraw.pathvariables.PathVariablesResolver; import com.nomagic.uml2.ext.jmi.helpers.StereotypesHelper; import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Element; import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.EnumerationLiteral; import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.NamedElement; import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Property; import com.nomagic.uml2.ext.magicdraw.mdprofiles.Stereotype; import gov.nasa.jpl.mbee.mdk.options.MDKOptionsGroup; import javax.script.*; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.HashMap; import java.util.List; import java.util.Map; /** * runs a userscript in [md install dir]/DocGenUserScripts/... * * @author dlam */ public class ScriptRunner { private static String userScriptDirectoryName; public static String getUserScriptDirectoryName() { if (userScriptDirectoryName == null) { userScriptDirectoryName = ApplicationEnvironment.getInstallRoot() + File.separator + "DocGenUserScripts"; } return userScriptDirectoryName; } private static File userScriptDirectory; public static File getUserScriptDirectory() { if (userScriptDirectory == null) { userScriptDirectory = new File(getUserScriptDirectoryName()); } return userScriptDirectory; } /** * runs the script with stereotype tag values plus additional inputs to scriptInput * * @param e * @param s * @param addInputs additional inputs to add to scriptInput * @return * @throws ScriptException */ public static Object runScriptFromStereotype(Element e, Stereotype s, Map<String, Object> addInputs) throws ScriptException { GUILog log = Application.getInstance().getGUILog(); Map<String, Object> inputs = new HashMap<>(); List<Element> queries = Utils.collectDirectedRelatedElementsByRelationshipStereotypeString(e, "Expose", 1, false, 1); if (queries == null || queries.isEmpty()) { // For backward compatibility, also try Queries, the former name for // Expose. queries = Utils.collectDirectedRelatedElementsByRelationshipStereotypeString(e, "Queries", 1, false, 1); } for (NamedElement p : s.getInheritedMember()) { if (p instanceof Property) { inputs.put(p.getName(), StereotypesHelper.getStereotypePropertyValue(e, s, (Property) p)); } } for (Property p : s.getOwnedAttribute()) { inputs.put(p.getName(), StereotypesHelper.getStereotypePropertyValue(e, s, p)); } inputs.put("inputElement", e); File[] paths = MDKOptionsGroup.getMDKOptions().getCustomUserScriptDirectories(); int numDirs = MDKOptionsGroup.getMDKOptions().getNumberOfCustomUserScriptDirectories(); File[] binDirs = new File[2 + numDirs]; for (int i = 0; i < numDirs; i++) { binDirs[i] = paths[i]; } File binDir = getUserScriptDirectory(); binDirs[numDirs] = binDir; boolean notFoundScript = true; int j = 0; File script = null; String lang = "jython"; while (notFoundScript) { File scriptFile = binDirs[j]; String filePath = scriptFile.getPath(); String sname = s.getName(); String[] spaces = sname.split("\\."); for (String namespace : spaces) { filePath += File.separator + namespace; } String extension = ".py"; Object language = StereotypesHelper.getStereotypePropertyFirst(s, "DocGenScript", "language"); if (language != null && language instanceof EnumerationLiteral) { lang = ((EnumerationLiteral) language).getName(); if (lang.equals("groovy")) { extension = ".groovy"; } else if (lang.equals("qvt")) { extension = ".qvto"; } } filePath += extension; script = new File(filePath); if ((script.exists() & !script.isDirectory()) || j == numDirs) { notFoundScript = false; } j++; } binDirs[numDirs + 1] = new File(script.getParent()); if (j > numDirs) { if (numDirs > 0) { log.log("Script not found on paths: "); } for (int i = 0; i < numDirs; i++) { log.log(paths[i].getPath()); } } inputs.putAll(addInputs); if (!inputs.containsKey("DocGenTargets")) { inputs.put("DocGenTargets", queries); } inputs.put("__name__", "__main__"); return runScript(lang, inputs, script, binDirs); } /** * session will be created if it isn't already, session will surround the script run so user don't have to manage sessions * * @param language * @param inputs A map of key/value pair of script input (it can be anything really, as long as the script knows what to do with it. the inputs object will be passed to the script * as 'scriptInput' * @param script File of the script file * @param binDirs File of the script directory * @return a var called scriptInput will be accessible in the script, this is a map of key value pairs, keys will be based on what the script does and what the corresponding * stereotype tags in md are, to return something from the script, assign a map to scriptOutput var in your script * @throws ScriptException */ public static Object runScript(String language, Map<String, Object> inputs, File script, File[] binDirs) throws ScriptException { GUILog log = Application.getInstance().getGUILog(); Object output = null; ClassLoader localClassLoader = Thread.currentThread().getContextClassLoader(); boolean sessionCreated = false; if (!SessionManager.getInstance().isSessionCreated()) { SessionManager.getInstance().createSession(language + " script run"); sessionCreated = true; } String scriptResolvedPath = null; try { String scriptPath = script.getAbsolutePath(); scriptResolvedPath = PathVariablesResolver.getResolvedPath(scriptPath); URL[] urls = new URL[binDirs.length + 1]; int count = 0; for (File binDir : binDirs) { urls[count] = binDir.toURI().toURL(); count++; } urls[count] = (new File(ApplicationEnvironment.getInstallRoot() + File.separator + "plugins" + File.separator + "com.nomagic.magicdraw.jpython" + File.separator + "jython" + File.separator + "Lib")).toURI().toURL(); URLClassLoader automatonClassLoaderWithBinDir = new URLClassLoader(urls, AutomatonPlugin.class.getClassLoader()); Thread.currentThread().setContextClassLoader(automatonClassLoaderWithBinDir); ScriptEngineManager sem = new ScriptEngineManager(); ScriptEngine se = sem.getEngineByName(language); if (null == se) { throw new RuntimeException("Scripting language '" + language + "' not found for executing: " + script); } ScriptContext sc = se.getContext(); Bindings bindings = se.getBindings(ScriptContext.ENGINE_SCOPE); bindings.put(ScriptEngine.FILENAME, scriptResolvedPath); // bindings.put("__name__", "__main__"); // bindings.put(ACTION_EVENT, event); sc.setBindings(bindings, ScriptContext.ENGINE_SCOPE); // sc.setAttribute(ACTION_EVENT, event, // ScriptContext.ENGINE_SCOPE); sc.setAttribute(ScriptEngine.FILENAME, scriptResolvedPath, ScriptContext.ENGINE_SCOPE); sc.setAttribute(ScriptEngine.FILENAME, scriptResolvedPath, ScriptContext.GLOBAL_SCOPE); // sc.setAttribute("__name__", "__main__", ScriptContext.ENGINE_SCOPE); se.put(ScriptEngine.FILENAME, scriptResolvedPath); se.put("__name__", "__main__"); se.put("scriptInput", inputs); FileReader fr = new FileReader(scriptResolvedPath); // se.put("scriptEngine", se); se.eval(fr, sc); output = se.get("scriptOutput"); if (sessionCreated && SessionManager.getInstance().isSessionCreated()) { SessionManager.getInstance().closeSession(); sessionCreated = false; } return output; } catch (MalformedURLException | FileNotFoundException | ScriptException | RuntimeException e) { Application.getInstance().getGUILog().log("An error occurred while attempting to run script" + (scriptResolvedPath != null ? ": " + scriptResolvedPath : "") + ". Reason: " + e.getMessage()); e.printStackTrace(); } finally { Thread.currentThread().setContextClassLoader(localClassLoader); if (sessionCreated && SessionManager.getInstance().isSessionCreated()) { // if we made the session, need to cancel due to script failure SessionManager.getInstance().cancelSession(); } } return output; } }