diff --git a/engine/src/main/java/org/archive/crawler/restlet/ScriptResource.java b/engine/src/main/java/org/archive/crawler/restlet/ScriptResource.java index add7debe..aaf406d1 100644 --- a/engine/src/main/java/org/archive/crawler/restlet/ScriptResource.java +++ b/engine/src/main/java/org/archive/crawler/restlet/ScriptResource.java @@ -19,7 +19,6 @@ package org.archive.crawler.restlet; -import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; @@ -38,14 +37,10 @@ import javax.script.ScriptEngineFactory; import javax.script.ScriptEngineManager; import javax.script.ScriptException; -import org.apache.commons.lang.StringEscapeUtils; import org.apache.commons.lang.StringUtils; import org.archive.crawler.framework.BeanLookupBindings; -import org.archive.crawler.restlet.models.CrawlJobModel; -import org.archive.crawler.restlet.models.EngineModel; import org.archive.crawler.restlet.models.ScriptModel; import org.archive.crawler.restlet.models.ViewModel; -import org.archive.util.TextUtils; import org.restlet.Context; import org.restlet.data.CharacterSet; import org.restlet.data.Form; @@ -89,11 +84,13 @@ public class ScriptResource extends JobRelatedResource { } }); } - protected String script = ""; - protected Exception ex = null; - protected int linesExecuted = 0; - protected String rawOutput = ""; - protected String htmlOutput = ""; + //protected String script = ""; + //protected Exception ex = null; + //protected int linesExecuted = 0; + //protected String rawOutput = ""; + //protected String htmlOutput = ""; + ScriptExecution scriptExec = null; + protected String chosenEngine = FACTORIES.isEmpty() ? "" : FACTORIES.getFirst().getNames().get(0); private Configuration _templateConfiguration; @@ -115,48 +112,107 @@ public class ScriptResource extends JobRelatedResource { return _templateConfiguration; } + /** + * JavaBean that packages script, its execution and the result. + * + * TODO: the script could create an object that persists and retains a + * reference to the Bindings. by encapsulating this way, I've made it + * a bit more difficult? we could {@code eng} and {@code script} arguments + * to execute() method. + */ + public static class ScriptExecution { + private ScriptEngine eng; + private ApplicationContext appCtx; + private Bindings bindings; + private String script; + + private StringWriter rawString; + private StringWriter htmlString; + private Throwable exception; + private int linesExecuted; + + public ScriptExecution(ScriptEngine eng, ApplicationContext appCtx, String script) { + this.eng = eng; + this.appCtx = appCtx; + this.bindings = new BeanLookupBindings(appCtx); + this.script = script; + if (StringUtils.isBlank(this.script)) { + this.script = ""; + } + this.rawString = new StringWriter(); + this.htmlString = new StringWriter(); + } + public void bind(String name, Object obj) { + bindings.put(name, obj); + } + public Object unbind(String name) { + return bindings.remove(name); + } + public void execute() { + PrintWriter rawOut = new PrintWriter(rawString); + PrintWriter htmlOut = new PrintWriter(htmlString); + bind("rawOut", rawOut); + bind("htmlOut", htmlOut); + bind("appCtx", appCtx); + try { + eng.eval(script, bindings); + // TODO: should count with RE rather than creating String[]? + linesExecuted = script.split("\r?\n").length; + } catch (ScriptException ex) { + Throwable cause = ex.getCause(); + exception = cause != null ? cause : ex; + } catch (RuntimeException ex) { + exception = ex; + } finally { + rawOut.flush(); + htmlOut.flush(); + // TODO: are these really necessary? + unbind("rawOut"); + unbind("htmlOut"); + unbind("appCtx"); + } + } + public boolean isFailure() { + return exception != null; + } + public String getStackTrace() { + if (exception == null) return ""; + StringWriter s = new StringWriter(); + exception.printStackTrace(new PrintWriter(s)); + return s.toString(); + } + public Throwable getException() { + return exception; + } + public int getLinesExecuted() { + return linesExecuted; + } + public String getRawOutput() { + return rawString.toString(); + } + public String getHtmlOutput() { + return htmlString.toString(); + } + } + @Override public void acceptRepresentation(Representation entity) throws ResourceException { Form form = getRequest().getEntityAsForm(); chosenEngine = form.getFirstValue("engine"); - script = form.getFirstValue("script"); + String script = form.getFirstValue("script"); if(StringUtils.isBlank(script)) { script=""; } ScriptEngine eng = MANAGER.getEngineByName(chosenEngine); - StringWriter rawString = new StringWriter(); - PrintWriter rawOut = new PrintWriter(rawString); - ApplicationContext appCtx = cj.getJobContext(); - Bindings bindings = new BeanLookupBindings(appCtx); - bindings.put("rawOut", rawOut); - StringWriter htmlString = new StringWriter(); - PrintWriter htmlOut = new PrintWriter(htmlString); - bindings.put("htmlOut", htmlOut); - bindings.put("job", cj); - bindings.put("appCtx", appCtx); - bindings.put("scriptResource", this); - try { - eng.eval(script, bindings); - linesExecuted = script.split("\r?\n").length; - } catch (ScriptException e) { - ex = e; - } catch (RuntimeException e) { - ex = e; - } finally { - rawOut.flush(); - rawOutput = rawString.toString(); - htmlOut.flush(); - htmlOutput = htmlString.toString(); - - // the script could create an object that persists and retains a reference to the Bindings - bindings.put("rawOut", null); - bindings.put("htmlOut", null); - bindings.put("job", null); - bindings.put("appCtx", null); - bindings.put("scriptResource", null); - } + scriptExec = new ScriptExecution(eng, cj.getJobContext(), script); + scriptExec.bind("job", cj); + scriptExec.bind("scriptResource", this); + scriptExec.execute(); + scriptExec.unbind("job"); + scriptExec.unbind("scriptResource"); + //TODO: log script, results somewhere; job log INFO? getResponse().setEntity(represent()); @@ -211,10 +267,7 @@ public class ScriptResource extends JobRelatedResource { ScriptModel model = new ScriptModel(cj.getShortName(), new Reference(baseRefRef, "..").getTargetRef().toString(), getAvailableScriptEngines(), - linesExecuted, - ex, - rawOutput, - htmlOutput); + scriptExec); return model; } @@ -235,7 +288,6 @@ public class ScriptResource extends JobRelatedResource { viewModel.put("baseResourceRef",getRequest().getRootRef().toString()+"/engine/static/"); viewModel.put("model",makeDataModel()); viewModel.put("selectedEngine", chosenEngine); - viewModel.put("script", script); try { Template template = tmpltCfg.getTemplate("Script.ftl"); diff --git a/engine/src/main/java/org/archive/crawler/restlet/models/ScriptModel.java b/engine/src/main/java/org/archive/crawler/restlet/models/ScriptModel.java index d6c093fa..f7ef1c9c 100644 --- a/engine/src/main/java/org/archive/crawler/restlet/models/ScriptModel.java +++ b/engine/src/main/java/org/archive/crawler/restlet/models/ScriptModel.java @@ -6,29 +6,23 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; -import org.apache.commons.lang.StringUtils; +import org.archive.crawler.restlet.ScriptResource.ScriptExecution; @SuppressWarnings("serial") public class ScriptModel extends LinkedHashMap { - public ScriptModel(String crawlJobShortName, String crawlJobUrl, Collection> scriptEngines, int linesExecuted, Exception exception, String rawOutput, String htmlOutput){ + public ScriptModel(String crawlJobShortName, String crawlJobUrl, + Collection> scriptEngines, + ScriptExecution scriptExec) {// int linesExecuted, Exception + // exception, String rawOutput, + // String htmlOutput){ super(); this.put("crawlJobUrl",crawlJobUrl); this.put("crawlJobShortName", crawlJobShortName); this.put("availableScriptEngines", scriptEngines); - if (linesExecuted > 0) { - this.put("linesExecuted", linesExecuted); - } - if (exception != null) { - this.put("exception", exception); - } - if (StringUtils.isNotBlank(rawOutput)) { - this.put("rawOutput", rawOutput); - } - if (StringUtils.isNotBlank(htmlOutput)) { - this.put("htmlOutput", htmlOutput); - } + if (scriptExec != null) + this.put("scriptExec", scriptExec); List> vars = new LinkedList>(); Map var; diff --git a/engine/src/main/resources/org/archive/crawler/restlet/Script.ftl b/engine/src/main/resources/org/archive/crawler/restlet/Script.ftl index 3d95a886..2b5b0bc3 100644 --- a/engine/src/main/resources/org/archive/crawler/restlet/Script.ftl +++ b/engine/src/main/resources/org/archive/crawler/restlet/Script.ftl @@ -16,35 +16,37 @@

Execute script for job ${model.crawlJobShortName}

- <#if model.linesExecuted??> - ${model.linesExecuted} lines executed + <#if model.scriptExec??> + <#if (model.scriptExec.linesExecuted > 0)> + ${model.scriptExec.linesExecuted} lines executed - <#if model.ex??> -
-	${model.ex.printStackTrace()}
+	<#if model.scriptExec.failure>
+	
${model.scriptExec.stackTrace}
 	
- <#if model.htmlOutput??> + <#assign htmlOutput=model.scriptExec.htmlOutput> + <#if (htmlOutput?length > 0)>
htmlOut - ${model.htmlOutput} + ${htmlOutput}
- <#if model.rawOutput??> + <#assign rawOutput=model.scriptExec.rawOutput> + <#if (rawOutput?length > 0)>
rawOutput -
-	${model.rawOutput?html}
+		
${rawOutput}
 		
+
- +
The script will be executed in an engine preloaded