PageRenderTime 42ms CodeModel.GetById 12ms RepoModel.GetById 1ms app.codeStats 0ms

/src/main/java/com/onresolve/jira/groovy/GroovyRunner.java

https://bitbucket.org/sorin/jira-plugin-intellij
Java | 362 lines | 271 code | 71 blank | 20 comment | 38 complexity | 46e7c906a38756e4494a3f921498c0c3 MD5 | raw file
  1. package com.onresolve.jira.groovy;
  2. import com.atlassian.jira.ComponentManager;
  3. import com.atlassian.jira.component.ComponentAccessor;
  4. import com.atlassian.jira.issue.AttachmentManager;
  5. import com.atlassian.jira.issue.IssueManager;
  6. import com.onresolve.jira.groovy.canned.util.JoinClassLoader;
  7. import com.opensymphony.util.TextUtils;
  8. import groovy.lang.GroovyClassLoader;
  9. import org.apache.log4j.Category;
  10. import webwork.action.Action;
  11. import webwork.action.ActionSupport;
  12. import javax.script.*;
  13. import java.io.*;
  14. import java.util.*;
  15. /**
  16. * Allow running Groovy script from within JIRA
  17. * @author Jamie Echlin
  18. */
  19. public class GroovyRunner extends ActionSupport {
  20. private static final long serialVersionUID = 1L;
  21. private String script = null;
  22. private String filename = "";
  23. private boolean allowedToRun = true;
  24. private String result;
  25. private String cannedScript;
  26. private ScriptResult scriptResult;
  27. private Exception scriptException;
  28. private String scriptExceptionStackTrace;
  29. private String scriptLanguage;
  30. private Map<String,Object> cannedScriptArgs;
  31. private static Map<String,ScriptEngine> scriptEngineCache = new HashMap<String,ScriptEngine>();
  32. protected ScriptManager scriptManager;
  33. protected Category log = Category.getInstance(GroovyRunner.class);
  34. public GroovyRunner(ScriptManager scriptManager) {
  35. this.scriptManager = scriptManager;
  36. }
  37. public Map run (String scriptFileOrClass, Map<String, Object> bindVars) throws Exception {
  38. if (TextUtils.stringSet(scriptFileOrClass)) {
  39. if (scriptFileOrClass.startsWith("com.onresolve.jira.groovy.canned")) { // todo - put into static
  40. Map returnVars = (new CannedScriptRunner(scriptManager)).runCannedScript(scriptFileOrClass, bindVars);
  41. setResult (returnVars.containsKey("output") ? returnVars.get("output").toString() : "no return value");
  42. return returnVars;
  43. }
  44. else {
  45. return runFile(new File(scriptFileOrClass), bindVars);
  46. }
  47. }
  48. else {
  49. return runFile(null, bindVars);
  50. }
  51. }
  52. public Map runFile (File scriptFile, Map<String, Object> bindVars) throws Exception {
  53. Map<String, Object> returns = new HashMap<String, Object>();
  54. ScriptEngine scriptEngine = getScriptEngine(scriptFile);
  55. long t1 = 0L;
  56. try {
  57. log.debug("Run called with args: " + scriptFile + ", " + bindVars);
  58. // clear any current bind vars
  59. Bindings bindings;
  60. if (bindVars.isEmpty()) {
  61. bindings = new SimpleBindings();
  62. }
  63. else {
  64. bindings = new SimpleBindings(bindVars);
  65. }
  66. bindings.put("log", log);
  67. bindings.put("componentManager", ComponentManager.getInstance());
  68. t1 = System.currentTimeMillis();
  69. Object value;
  70. if (scriptFile != null) {
  71. // todo check file is readable etc
  72. FileInputStream fstream;
  73. BufferedReader br = null;
  74. try {
  75. fstream = new FileInputStream(scriptFile);
  76. br = new BufferedReader(new InputStreamReader(fstream)); // , "UTF-8" - System.getProperty("file.encoding")
  77. value = scriptEngine.eval(br, bindings);
  78. // Using the code below works for setting breakpoints, but don't want to change this
  79. // Could handle this differently if atlassian.dev.mode is set to true or something
  80. // GroovyShell shell = new GroovyShell();
  81. // value = shell.evaluate(scriptFile);
  82. } finally {
  83. if (br != null)
  84. br.close();
  85. }
  86. }
  87. else {
  88. value = scriptEngine.eval(getScript(), bindings);
  89. }
  90. Iterator i = bindVars.keySet().iterator();
  91. while (i.hasNext()) {
  92. String fieldId = (String) i.next();
  93. returns.put(fieldId, bindings.get(fieldId));
  94. }
  95. setResult (value == null ? "no return value" : value.toString() );
  96. return returns;
  97. } catch (Exception e) {
  98. result = e.toString();
  99. log.error("The script failed : " + getResult());
  100. throw e;
  101. }
  102. finally {
  103. long t2 = System.currentTimeMillis();
  104. log.debug("Time taken: " + (t2 - t1));
  105. }
  106. }
  107. protected AttachmentManager getAttachmentManager() {
  108. return ComponentManager.getInstance().getAttachmentManager();
  109. }
  110. protected IssueManager getIssueManager() {
  111. return ComponentManager.getInstance().getIssueManager();
  112. }
  113. protected BufferedReader getReader(File scriptFile) throws FileNotFoundException {
  114. FileInputStream fstream = new FileInputStream(scriptFile);
  115. return new BufferedReader(new InputStreamReader(fstream));
  116. }
  117. public static ScriptEngine getGse() {
  118. if (! scriptEngineCache.containsKey("groovy")) {
  119. ScriptEngineManager mgr = getScriptEngineManager();
  120. ScriptEngine scriptEngine = mgr.getEngineByName("groovy");
  121. if (scriptEngine != null) {
  122. scriptEngineCache.put("groovy", scriptEngine);
  123. }
  124. }
  125. return scriptEngineCache.get("groovy");
  126. }
  127. private static ScriptEngineManager getScriptEngineManager() {
  128. ClassLoader joinClassLoader = new JoinClassLoader(ComponentAccessor.class.getClassLoader(), GroovyRunner.class.getClassLoader(), ComponentAccessor.class.getClassLoader());
  129. return new ScriptEngineManager(joinClassLoader);
  130. // return new ScriptEngineManager(scriptManager.getClass().getClassLoader());
  131. }
  132. public ScriptEngine getScriptEngine(File scriptFile) throws Exception {
  133. ScriptEngineManager mgr = getScriptEngineManager();
  134. ScriptEngine scriptEngine;
  135. // https://studio.plugins.atlassian.com/wiki/display/GRV/Script+Runner?focusedCommentId=66355586#comment-66355586
  136. /* the following property is necessary for ruby, otherwise we can't retrieve the variables set in ruby
  137. http://yokolet.blogspot.com/2009/08/redbridge-and-jruby-embed-api-update.html
  138. */
  139. System.setProperty("org.jruby.embed.localvariable.behavior", "persistent");
  140. if (scriptFile == null) {
  141. if (! TextUtils.stringSet(getScriptLanguage())) {
  142. throw new Exception("You must choose a script engine if not using a file.");
  143. }
  144. if (scriptEngineCache.get(getScriptLanguage()) != null) {
  145. return scriptEngineCache.get(getScriptLanguage());
  146. }
  147. scriptEngine = mgr.getEngineByName(getScriptLanguage());
  148. if (scriptEngine == null) {
  149. throw new Exception("Could not find a script engine for language: " + getScriptLanguage());
  150. }
  151. else {
  152. scriptEngineCache.put(getScriptLanguage(), scriptEngine);
  153. }
  154. }
  155. else {
  156. // guess the engine from the extension
  157. String fileName = scriptFile.getName();
  158. String ext = fileName.substring(fileName.lastIndexOf('.')+1, fileName.length());
  159. if (scriptEngineCache.get(ext) != null) {
  160. return scriptEngineCache.get(ext);
  161. }
  162. scriptEngine = mgr.getEngineByExtension(ext);
  163. if (scriptEngine == null) {
  164. throw new Exception("Cannot find a script engine for extension: \"" + ext + "\", (make sure it's on the classpath).");
  165. }
  166. else {
  167. scriptEngineCache.put(ext, scriptEngine);
  168. }
  169. }
  170. return scriptEngine;
  171. }
  172. public List<ScriptEngineFactory> getScriptEngines () {
  173. List<ScriptEngineFactory> scriptEngines = new ArrayList<ScriptEngineFactory>();
  174. ScriptEngineManager mgr = getScriptEngineManager();
  175. List<ScriptEngineFactory> factories = mgr.getEngineFactories();
  176. for (ScriptEngineFactory factory: factories) {
  177. scriptEngines.add(factory);
  178. }
  179. Collections.sort(scriptEngines, new ScriptEngineComparator());
  180. return scriptEngines;
  181. }
  182. protected String doExecute() throws Exception {
  183. ComponentManager.getInstance().getWebResourceManager().requireResource("com.onresolve.jira.groovy.groovyrunner:codemirror");
  184. try {
  185. Map<String, Object> bindVars = new HashMap<String, Object>();
  186. if (TextUtils.stringSet(getFilename())) {
  187. run(getFilename(), bindVars);
  188. }
  189. else if (TextUtils.stringSet(getScript())) {
  190. run(null, bindVars);
  191. }
  192. else {
  193. return Action.SUCCESS;
  194. }
  195. result = (getResult() == null ? "no return value" : getResult());
  196. } catch (Exception e) {
  197. result = e.toString();
  198. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  199. PrintStream ps = new PrintStream(baos);
  200. e.printStackTrace(ps);
  201. setScriptExceptionStackTrace(baos.toString("UTF8"));
  202. log.error("The script failed : " + result, e);
  203. setScriptException(e);
  204. }
  205. return Action.SUCCESS;
  206. }
  207. public GroovyClassLoader getGcl() {
  208. // ScriptManager scriptManager = (ScriptManager) ComponentManager.getComponentInstanceOfType(ScriptManager.class);
  209. return scriptManager.getGcl();
  210. }
  211. public String getScript() {
  212. return script;
  213. }
  214. public void setScript(String script) {
  215. this.script = script;
  216. }
  217. public boolean isAllowedToRun() {
  218. //TODO protect running the script only if -Djira.groovy.on=true ala jellyRunner
  219. return allowedToRun;
  220. }
  221. public void setAllowedToRun(boolean allowedToRun) {
  222. this.allowedToRun = allowedToRun;
  223. }
  224. public String getFilename() {
  225. return filename;
  226. }
  227. public void setFilename(String filename) {
  228. this.filename = filename;
  229. }
  230. public String getResult() {
  231. return result;
  232. }
  233. public void setResult(String result) {
  234. this.result = result;
  235. }
  236. public ScriptResult getScriptResult() {
  237. return scriptResult;
  238. }
  239. public void setScriptResult(ScriptResult scriptResult) {
  240. this.scriptResult = scriptResult;
  241. }
  242. public Exception getScriptException() {
  243. return scriptException;
  244. }
  245. public void setScriptException(Exception scriptException) {
  246. this.scriptException = scriptException;
  247. }
  248. public String getScriptExceptionStackTrace() {
  249. return scriptExceptionStackTrace;
  250. }
  251. public void setScriptExceptionStackTrace(String scriptExceptionStackTrace) {
  252. this.scriptExceptionStackTrace = scriptExceptionStackTrace;
  253. }
  254. public String getScriptLanguage() {
  255. return scriptLanguage == null ? "" : scriptLanguage;
  256. }
  257. public void setScriptLanguage(String scriptLanguage) {
  258. this.scriptLanguage = scriptLanguage;
  259. }
  260. public String getCannedScript() {
  261. return cannedScript;
  262. }
  263. public void setCannedScript(String cannedScript) {
  264. this.cannedScript = cannedScript;
  265. }
  266. public Map<String,Object> getCannedScriptArgs() {
  267. return cannedScriptArgs;
  268. }
  269. public void setCannedScriptArgs(Map<String,Object> cannedScriptArgs) {
  270. this.cannedScriptArgs = cannedScriptArgs;
  271. }
  272. @Override
  273. public String execute() throws Exception {
  274. return doExecute();
  275. }
  276. class ScriptEngineComparator implements Comparator<ScriptEngineFactory> {
  277. public int compare(ScriptEngineFactory o1, ScriptEngineFactory o2) {
  278. return (o1.getLanguageName().compareTo(o2.getLanguageName()));
  279. }
  280. }
  281. // copied from JiraWebActionSupport
  282. // need to not extend that due to groovy compilation issues
  283. public final String getActionName() {
  284. final String classname = getClass().getName();
  285. return classname.substring(classname.lastIndexOf('.') + 1);
  286. }
  287. public boolean hasAnyErrors()
  288. {
  289. return !getErrors().isEmpty() || !getErrorMessages().isEmpty();
  290. }
  291. }