/src/main/java/com/onresolve/jira/groovy/GroovyRunner.java
Java | 362 lines | 271 code | 71 blank | 20 comment | 38 complexity | 46e7c906a38756e4494a3f921498c0c3 MD5 | raw file
- package com.onresolve.jira.groovy;
-
- import com.atlassian.jira.ComponentManager;
- import com.atlassian.jira.component.ComponentAccessor;
- import com.atlassian.jira.issue.AttachmentManager;
- import com.atlassian.jira.issue.IssueManager;
- import com.onresolve.jira.groovy.canned.util.JoinClassLoader;
- import com.opensymphony.util.TextUtils;
- import groovy.lang.GroovyClassLoader;
- import org.apache.log4j.Category;
- import webwork.action.Action;
- import webwork.action.ActionSupport;
-
- import javax.script.*;
- import java.io.*;
- import java.util.*;
-
-
- /**
- * Allow running Groovy script from within JIRA
- * @author Jamie Echlin
- */
- public class GroovyRunner extends ActionSupport {
-
- private static final long serialVersionUID = 1L;
- private String script = null;
- private String filename = "";
- private boolean allowedToRun = true;
- private String result;
- private String cannedScript;
- private ScriptResult scriptResult;
- private Exception scriptException;
- private String scriptExceptionStackTrace;
- private String scriptLanguage;
- private Map<String,Object> cannedScriptArgs;
- private static Map<String,ScriptEngine> scriptEngineCache = new HashMap<String,ScriptEngine>();
- protected ScriptManager scriptManager;
- protected Category log = Category.getInstance(GroovyRunner.class);
-
- public GroovyRunner(ScriptManager scriptManager) {
- this.scriptManager = scriptManager;
- }
-
- public Map run (String scriptFileOrClass, Map<String, Object> bindVars) throws Exception {
- if (TextUtils.stringSet(scriptFileOrClass)) {
- if (scriptFileOrClass.startsWith("com.onresolve.jira.groovy.canned")) { // todo - put into static
- Map returnVars = (new CannedScriptRunner(scriptManager)).runCannedScript(scriptFileOrClass, bindVars);
- setResult (returnVars.containsKey("output") ? returnVars.get("output").toString() : "no return value");
- return returnVars;
- }
- else {
- return runFile(new File(scriptFileOrClass), bindVars);
- }
- }
- else {
- return runFile(null, bindVars);
- }
- }
-
- public Map runFile (File scriptFile, Map<String, Object> bindVars) throws Exception {
-
- Map<String, Object> returns = new HashMap<String, Object>();
-
- ScriptEngine scriptEngine = getScriptEngine(scriptFile);
-
- long t1 = 0L;
- try {
- log.debug("Run called with args: " + scriptFile + ", " + bindVars);
-
- // clear any current bind vars
- Bindings bindings;
- if (bindVars.isEmpty()) {
- bindings = new SimpleBindings();
- }
- else {
- bindings = new SimpleBindings(bindVars);
- }
-
- bindings.put("log", log);
- bindings.put("componentManager", ComponentManager.getInstance());
-
- t1 = System.currentTimeMillis();
-
- Object value;
- if (scriptFile != null) {
- // todo check file is readable etc
- FileInputStream fstream;
- BufferedReader br = null;
- try {
- fstream = new FileInputStream(scriptFile);
- br = new BufferedReader(new InputStreamReader(fstream)); // , "UTF-8" - System.getProperty("file.encoding")
- value = scriptEngine.eval(br, bindings);
-
- // Using the code below works for setting breakpoints, but don't want to change this
- // Could handle this differently if atlassian.dev.mode is set to true or something
- // GroovyShell shell = new GroovyShell();
- // value = shell.evaluate(scriptFile);
-
- } finally {
- if (br != null)
- br.close();
- }
- }
- else {
- value = scriptEngine.eval(getScript(), bindings);
- }
-
-
- Iterator i = bindVars.keySet().iterator();
- while (i.hasNext()) {
- String fieldId = (String) i.next();
- returns.put(fieldId, bindings.get(fieldId));
- }
-
- setResult (value == null ? "no return value" : value.toString() );
- return returns;
- } catch (Exception e) {
- result = e.toString();
- log.error("The script failed : " + getResult());
- throw e;
- }
- finally {
- long t2 = System.currentTimeMillis();
- log.debug("Time taken: " + (t2 - t1));
- }
- }
-
- protected AttachmentManager getAttachmentManager() {
- return ComponentManager.getInstance().getAttachmentManager();
- }
-
- protected IssueManager getIssueManager() {
- return ComponentManager.getInstance().getIssueManager();
- }
-
- protected BufferedReader getReader(File scriptFile) throws FileNotFoundException {
- FileInputStream fstream = new FileInputStream(scriptFile);
- return new BufferedReader(new InputStreamReader(fstream));
- }
-
- public static ScriptEngine getGse() {
- if (! scriptEngineCache.containsKey("groovy")) {
- ScriptEngineManager mgr = getScriptEngineManager();
- ScriptEngine scriptEngine = mgr.getEngineByName("groovy");
- if (scriptEngine != null) {
- scriptEngineCache.put("groovy", scriptEngine);
- }
- }
- return scriptEngineCache.get("groovy");
- }
-
- private static ScriptEngineManager getScriptEngineManager() {
-
- ClassLoader joinClassLoader = new JoinClassLoader(ComponentAccessor.class.getClassLoader(), GroovyRunner.class.getClassLoader(), ComponentAccessor.class.getClassLoader());
- return new ScriptEngineManager(joinClassLoader);
-
- // return new ScriptEngineManager(scriptManager.getClass().getClassLoader());
- }
-
- public ScriptEngine getScriptEngine(File scriptFile) throws Exception {
- ScriptEngineManager mgr = getScriptEngineManager();
- ScriptEngine scriptEngine;
-
- // https://studio.plugins.atlassian.com/wiki/display/GRV/Script+Runner?focusedCommentId=66355586#comment-66355586
- /* the following property is necessary for ruby, otherwise we can't retrieve the variables set in ruby
- http://yokolet.blogspot.com/2009/08/redbridge-and-jruby-embed-api-update.html
- */
- System.setProperty("org.jruby.embed.localvariable.behavior", "persistent");
-
- if (scriptFile == null) {
- if (! TextUtils.stringSet(getScriptLanguage())) {
- throw new Exception("You must choose a script engine if not using a file.");
- }
-
- if (scriptEngineCache.get(getScriptLanguage()) != null) {
- return scriptEngineCache.get(getScriptLanguage());
- }
-
- scriptEngine = mgr.getEngineByName(getScriptLanguage());
-
- if (scriptEngine == null) {
- throw new Exception("Could not find a script engine for language: " + getScriptLanguage());
- }
- else {
- scriptEngineCache.put(getScriptLanguage(), scriptEngine);
- }
- }
- else {
- // guess the engine from the extension
- String fileName = scriptFile.getName();
- String ext = fileName.substring(fileName.lastIndexOf('.')+1, fileName.length());
-
- if (scriptEngineCache.get(ext) != null) {
- return scriptEngineCache.get(ext);
- }
-
- scriptEngine = mgr.getEngineByExtension(ext);
- if (scriptEngine == null) {
- throw new Exception("Cannot find a script engine for extension: \"" + ext + "\", (make sure it's on the classpath).");
- }
- else {
- scriptEngineCache.put(ext, scriptEngine);
- }
- }
- return scriptEngine;
- }
-
- public List<ScriptEngineFactory> getScriptEngines () {
- List<ScriptEngineFactory> scriptEngines = new ArrayList<ScriptEngineFactory>();
-
- ScriptEngineManager mgr = getScriptEngineManager();
- List<ScriptEngineFactory> factories = mgr.getEngineFactories();
- for (ScriptEngineFactory factory: factories) {
- scriptEngines.add(factory);
- }
-
- Collections.sort(scriptEngines, new ScriptEngineComparator());
- return scriptEngines;
- }
-
- protected String doExecute() throws Exception {
- ComponentManager.getInstance().getWebResourceManager().requireResource("com.onresolve.jira.groovy.groovyrunner:codemirror");
-
- try {
- Map<String, Object> bindVars = new HashMap<String, Object>();
-
- if (TextUtils.stringSet(getFilename())) {
- run(getFilename(), bindVars);
- }
- else if (TextUtils.stringSet(getScript())) {
- run(null, bindVars);
- }
- else {
- return Action.SUCCESS;
- }
-
- result = (getResult() == null ? "no return value" : getResult());
- } catch (Exception e) {
- result = e.toString();
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- PrintStream ps = new PrintStream(baos);
- e.printStackTrace(ps);
- setScriptExceptionStackTrace(baos.toString("UTF8"));
-
- log.error("The script failed : " + result, e);
- setScriptException(e);
-
- }
- return Action.SUCCESS;
- }
-
- public GroovyClassLoader getGcl() {
- // ScriptManager scriptManager = (ScriptManager) ComponentManager.getComponentInstanceOfType(ScriptManager.class);
- return scriptManager.getGcl();
- }
-
- public String getScript() {
- return script;
- }
-
- public void setScript(String script) {
- this.script = script;
- }
-
- public boolean isAllowedToRun() {
- //TODO protect running the script only if -Djira.groovy.on=true ala jellyRunner
- return allowedToRun;
- }
-
- public void setAllowedToRun(boolean allowedToRun) {
- this.allowedToRun = allowedToRun;
- }
-
- public String getFilename() {
- return filename;
- }
-
- public void setFilename(String filename) {
- this.filename = filename;
- }
-
- public String getResult() {
- return result;
- }
-
- public void setResult(String result) {
- this.result = result;
- }
-
- public ScriptResult getScriptResult() {
- return scriptResult;
- }
-
- public void setScriptResult(ScriptResult scriptResult) {
- this.scriptResult = scriptResult;
- }
-
- public Exception getScriptException() {
- return scriptException;
- }
-
- public void setScriptException(Exception scriptException) {
- this.scriptException = scriptException;
- }
-
- public String getScriptExceptionStackTrace() {
- return scriptExceptionStackTrace;
- }
-
- public void setScriptExceptionStackTrace(String scriptExceptionStackTrace) {
- this.scriptExceptionStackTrace = scriptExceptionStackTrace;
- }
-
- public String getScriptLanguage() {
- return scriptLanguage == null ? "" : scriptLanguage;
- }
-
- public void setScriptLanguage(String scriptLanguage) {
- this.scriptLanguage = scriptLanguage;
- }
-
- public String getCannedScript() {
- return cannedScript;
- }
-
- public void setCannedScript(String cannedScript) {
- this.cannedScript = cannedScript;
- }
-
- public Map<String,Object> getCannedScriptArgs() {
- return cannedScriptArgs;
- }
-
- public void setCannedScriptArgs(Map<String,Object> cannedScriptArgs) {
- this.cannedScriptArgs = cannedScriptArgs;
- }
-
- @Override
- public String execute() throws Exception {
- return doExecute();
- }
-
- class ScriptEngineComparator implements Comparator<ScriptEngineFactory> {
- public int compare(ScriptEngineFactory o1, ScriptEngineFactory o2) {
- return (o1.getLanguageName().compareTo(o2.getLanguageName()));
- }
- }
-
- // copied from JiraWebActionSupport
- // need to not extend that due to groovy compilation issues
- public final String getActionName() {
- final String classname = getClass().getName();
- return classname.substring(classname.lastIndexOf('.') + 1);
- }
-
- public boolean hasAnyErrors()
- {
- return !getErrors().isEmpty() || !getErrorMessages().isEmpty();
- }
-
- }