mirror of
https://github.com/internetarchive/heritrix3.git
synced 2026-09-22 21:55:54 +00:00
Merge branch 'topic/scripting-xml-fix'
This commit is contained in:
@@ -20,8 +20,6 @@
|
||||
package org.archive.crawler.restlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -31,14 +29,11 @@ import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.script.Bindings;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineFactory;
|
||||
import javax.script.ScriptEngineManager;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.archive.crawler.framework.BeanLookupBindings;
|
||||
import org.archive.crawler.restlet.models.ScriptModel;
|
||||
import org.archive.crawler.restlet.models.ViewModel;
|
||||
import org.restlet.Context;
|
||||
@@ -52,7 +47,6 @@ import org.restlet.resource.Representation;
|
||||
import org.restlet.resource.ResourceException;
|
||||
import org.restlet.resource.Variant;
|
||||
import org.restlet.resource.WriterRepresentation;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import freemarker.template.Configuration;
|
||||
import freemarker.template.ObjectWrapper;
|
||||
@@ -84,7 +78,6 @@ public class ScriptResource extends JobRelatedResource {
|
||||
}
|
||||
});
|
||||
}
|
||||
ScriptExecution scriptExec = null;
|
||||
|
||||
protected String chosenEngine = FACTORIES.isEmpty() ? "" : FACTORIES.getFirst().getNames().get(0);
|
||||
private Configuration _templateConfiguration;
|
||||
@@ -99,6 +92,8 @@ public class ScriptResource extends JobRelatedResource {
|
||||
tmpltCfg.setClassForTemplateLoading(this.getClass(),"");
|
||||
tmpltCfg.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER);
|
||||
setTemplateConfiguration(tmpltCfg);
|
||||
|
||||
scriptingConsole = new ScriptingConsole(cj);
|
||||
}
|
||||
public void setTemplateConfiguration(Configuration tmpltCfg) {
|
||||
_templateConfiguration=tmpltCfg;
|
||||
@@ -106,92 +101,8 @@ public class ScriptResource extends JobRelatedResource {
|
||||
public Configuration getTemplateConfiguration(){
|
||||
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();
|
||||
}
|
||||
public String getScript() {
|
||||
return script;
|
||||
}
|
||||
}
|
||||
|
||||
private ScriptingConsole scriptingConsole;
|
||||
|
||||
@Override
|
||||
public void acceptRepresentation(Representation entity) throws ResourceException {
|
||||
@@ -204,12 +115,9 @@ public class ScriptResource extends JobRelatedResource {
|
||||
|
||||
ScriptEngine eng = MANAGER.getEngineByName(chosenEngine);
|
||||
|
||||
scriptExec = new ScriptExecution(eng, cj.getJobContext(), script);
|
||||
scriptExec.bind("job", cj);
|
||||
scriptExec.bind("scriptResource", this);
|
||||
scriptExec.execute();
|
||||
scriptExec.unbind("job");
|
||||
scriptExec.unbind("scriptResource");
|
||||
scriptingConsole.bind("scriptResource", this);
|
||||
scriptingConsole.execute(eng, script);
|
||||
scriptingConsole.unbind("scriptResource");
|
||||
|
||||
//TODO: log script, results somewhere; job log INFO?
|
||||
|
||||
@@ -262,10 +170,9 @@ public class ScriptResource extends JobRelatedResource {
|
||||
}
|
||||
Reference baseRefRef = new Reference(baseRef);
|
||||
|
||||
ScriptModel model = new ScriptModel(cj.getShortName(),
|
||||
ScriptModel model = new ScriptModel(scriptingConsole,
|
||||
new Reference(baseRefRef, "..").getTargetRef().toString(),
|
||||
getAvailableScriptEngines(),
|
||||
scriptExec);
|
||||
getAvailableScriptEngines());
|
||||
|
||||
return model;
|
||||
}
|
||||
@@ -284,7 +191,7 @@ public class ScriptResource extends JobRelatedResource {
|
||||
viewModel.put("cssRef", getStylesheetRef());
|
||||
viewModel.put("staticRef", getStaticRef(""));
|
||||
viewModel.put("baseResourceRef",getRequest().getRootRef().toString()+"/engine/static/");
|
||||
viewModel.put("model",makeDataModel());
|
||||
viewModel.put("model", makeDataModel());
|
||||
viewModel.put("selectedEngine", chosenEngine);
|
||||
|
||||
try {
|
||||
|
||||
@@ -0,0 +1,129 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.archive.crawler.restlet;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.script.Bindings;
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptException;
|
||||
|
||||
import org.archive.crawler.framework.BeanLookupBindings;
|
||||
import org.archive.crawler.framework.CrawlJob;
|
||||
|
||||
/**
|
||||
* ScriptingConsole implements view-independent logic of scripting console.
|
||||
*
|
||||
* Currently it is short-lived; it is created by ScriptResource for each request and
|
||||
* destroyed after rendering the view.
|
||||
*
|
||||
* @contributor kenji
|
||||
*
|
||||
*/
|
||||
public class ScriptingConsole {
|
||||
private final CrawlJob cj;
|
||||
|
||||
private ScriptEngine eng;
|
||||
private String script;
|
||||
private Bindings bindings;
|
||||
|
||||
private StringWriter rawString;
|
||||
private StringWriter htmlString;
|
||||
private Throwable exception;
|
||||
private int linesExecuted;
|
||||
|
||||
private List<Map<String, String>> availableGlobalVariables;
|
||||
|
||||
public ScriptingConsole(CrawlJob job) {
|
||||
this.cj = job;
|
||||
this.bindings = new BeanLookupBindings(this.cj.getJobContext());
|
||||
this.script = "";
|
||||
|
||||
setupAvailableGlobalVariables();
|
||||
}
|
||||
protected void addGlobalVariable(String name, String desc) {
|
||||
Map<String, String> var = new LinkedHashMap<String, String>();
|
||||
var.put("variable", name);
|
||||
var.put("description", desc);
|
||||
availableGlobalVariables.add(var);
|
||||
}
|
||||
private void setupAvailableGlobalVariables() {
|
||||
availableGlobalVariables = new LinkedList<Map<String,String>>();
|
||||
addGlobalVariable("rawOut", "a PrintWriter for arbitrary text output to this page");
|
||||
addGlobalVariable("htmlOut", "a PrintWriter for HTML output to this page");
|
||||
addGlobalVariable("job", "the current CrawlJob instance");
|
||||
addGlobalVariable("appCtx", "current job ApplicationContext, if any");
|
||||
// TODO: a bit awkward to have this here, because ScriptingConsole has no ref to
|
||||
// ScriptResource. better to have ScriptResource call #addGlobalVariable(String, String)?
|
||||
addGlobalVariable("scriptResource",
|
||||
"the ScriptResource implementing this page, which offers utility methods");
|
||||
}
|
||||
|
||||
public void bind(String name, Object obj) {
|
||||
bindings.put(name, obj);
|
||||
}
|
||||
public Object unbind(String name) {
|
||||
return bindings.remove(name);
|
||||
}
|
||||
public void execute(ScriptEngine eng, String script) {
|
||||
// TODO: update through setter rather than passing as method arguments?
|
||||
this.eng = eng;
|
||||
this.script = script;
|
||||
|
||||
bind("job", cj);
|
||||
rawString = new StringWriter();
|
||||
htmlString = new StringWriter();
|
||||
PrintWriter rawOut = new PrintWriter(rawString);
|
||||
PrintWriter htmlOut = new PrintWriter(htmlString);
|
||||
bind("rawOut", rawOut);
|
||||
bind("htmlOut", htmlOut);
|
||||
bind("appCtx", cj.getJobContext());
|
||||
exception = null;
|
||||
try {
|
||||
this.eng.eval(this.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");
|
||||
unbind("job");
|
||||
}
|
||||
}
|
||||
public CrawlJob getCrawlJob( ) {
|
||||
return cj;
|
||||
}
|
||||
public Throwable getException() {
|
||||
return exception;
|
||||
}
|
||||
public int getLinesExecuted() {
|
||||
return linesExecuted;
|
||||
}
|
||||
public String getRawOutput() {
|
||||
return rawString != null ? rawString.toString() : "";
|
||||
}
|
||||
public String getHtmlOutput() {
|
||||
return htmlString != null ? htmlString.toString() : "";
|
||||
}
|
||||
public String getScript() {
|
||||
return script;
|
||||
}
|
||||
|
||||
public List<Map<String, String>> getAvailableGlobalVariables() {
|
||||
return availableGlobalVariables;
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,23 @@
|
||||
|
||||
package org.archive.crawler.restlet;
|
||||
|
||||
import java.beans.BeanInfo;
|
||||
import java.beans.IntrospectionException;
|
||||
import java.beans.Introspector;
|
||||
import java.beans.PropertyDescriptor;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlTransient;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.restlet.util.XmlWriter;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
@@ -90,6 +103,88 @@ public class XmlMarshaller {
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* sort PropertyDescriptors according to propOrder.
|
||||
* properties listed in propOrder come first, in the order they are listed, and
|
||||
* then come remaining unlisted properties, in arbitrary order (likely alphabetical).
|
||||
* this semantics is rather relaxed compared to original JAXB semantics, where props
|
||||
* and propOrder must be the same set (except for those marked XmlTransient).
|
||||
* @param props PropertyDescriptor array to be sorted in-place.
|
||||
* @param propOrder list of property names in order of desired appearance.
|
||||
*/
|
||||
protected static void orderProperties(PropertyDescriptor[] props, final String[] propOrder) {
|
||||
if (propOrder == null || propOrder.length == 0) return;
|
||||
final Map<String, Integer> order = new HashMap<String, Integer>();
|
||||
for (int i = 0; i < propOrder.length; i++) {
|
||||
order.put(propOrder[i], i);
|
||||
}
|
||||
final Integer LAST = Integer.valueOf(propOrder.length);
|
||||
Arrays.sort(props, new Comparator<PropertyDescriptor>() {
|
||||
@Override
|
||||
public int compare(PropertyDescriptor o1, PropertyDescriptor o2) {
|
||||
Integer c1 = order.get(o1.getName());
|
||||
Integer c2 = order.get(o2.getName());
|
||||
return (c1 != null ? c1 : LAST).compareTo(c2 != null ? c2 : LAST);
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* test if {@code obj} has {@link XmlRootElement} annotation.
|
||||
* to avoid unexpected side effects, objects are mapped to nested XML structure
|
||||
* only when its class has {@link XmlRootElement} annotation.
|
||||
* note semantics is slightly different from JAXB - just borrowing XmlRootElement
|
||||
* as substitute of XmlElement, because Map entry cannot be annotated.
|
||||
* @param obj object to test
|
||||
* @return true if obj's class has XmlRootElement annotation.
|
||||
*/
|
||||
protected static boolean marshalAsElement(Object obj) {
|
||||
XmlRootElement ann = obj.getClass().getAnnotation(XmlRootElement.class);
|
||||
return ann != null;
|
||||
}
|
||||
/**
|
||||
* generate nested XML structure for a bean {@code obj}. enclosing element
|
||||
* will not be generated if {@code key} is empty. each readable JavaBeans property
|
||||
* is mapped to an nested element named after its name. Those properties
|
||||
* annotated with {@link XmlTransient} are ignored.
|
||||
* @param xmlWriter XmlWriter
|
||||
* @param key name of enclosing element
|
||||
* @param obj bean
|
||||
* @throws SAXException
|
||||
*/
|
||||
protected static void marshalBean(XmlWriter xmlWriter, String key, Object obj) throws SAXException {
|
||||
if (!StringUtils.isEmpty(key))
|
||||
xmlWriter.startElement(key);
|
||||
try {
|
||||
BeanInfo beanInfo = Introspector.getBeanInfo(obj.getClass(), Object.class);
|
||||
PropertyDescriptor[] props = beanInfo.getPropertyDescriptors();
|
||||
XmlType xmlType = obj.getClass().getAnnotation(XmlType.class);
|
||||
if (xmlType != null) {
|
||||
String[] propOrder = xmlType.propOrder();
|
||||
if (propOrder != null) {
|
||||
// TODO: should cache this sorted version?
|
||||
orderProperties(props, propOrder);
|
||||
}
|
||||
}
|
||||
for (PropertyDescriptor prop : props) {
|
||||
Method m = prop.getReadMethod();
|
||||
if (m == null || m.getAnnotation(XmlTransient.class) != null)
|
||||
continue;
|
||||
try {
|
||||
Object propValue = m.invoke(obj);
|
||||
if (propValue != null && !"".equals(propValue)) {
|
||||
marshal(xmlWriter, prop.getName(), propValue);
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
// generate empty element, for now. generate comment?
|
||||
xmlWriter.emptyElement(prop.getName());
|
||||
}
|
||||
}
|
||||
} catch (IntrospectionException ex) {
|
||||
// ignored, for now.
|
||||
}
|
||||
if (!StringUtils.isEmpty(key))
|
||||
xmlWriter.endElement(key);
|
||||
}
|
||||
|
||||
protected static void marshal(XmlWriter xmlWriter, String key, Object value) throws SAXException {
|
||||
if (value == null) {
|
||||
@@ -98,6 +193,8 @@ public class XmlMarshaller {
|
||||
marshal(xmlWriter, key, (Map<?,?>) value);
|
||||
} else if (value instanceof Iterable<?>) {
|
||||
marshal(xmlWriter, key, (Iterable<?>) value);
|
||||
} else if (marshalAsElement(value)) {
|
||||
marshalBean(xmlWriter, key, value);
|
||||
} else {
|
||||
xmlWriter.dataElement(key, value.toString());
|
||||
}
|
||||
|
||||
@@ -1,56 +1,71 @@
|
||||
package org.archive.crawler.restlet.models;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.archive.crawler.restlet.ScriptResource.ScriptExecution;
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public class ScriptModel extends LinkedHashMap<String, Object> {
|
||||
import org.archive.crawler.restlet.ScriptingConsole;
|
||||
|
||||
public ScriptModel(String crawlJobShortName, String crawlJobUrl,
|
||||
Collection<Map<String, String>> 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 (scriptExec != null)
|
||||
this.put("scriptExec", scriptExec);
|
||||
|
||||
List<Map<String,String>> vars = new LinkedList<Map<String,String>>();
|
||||
Map<String,String> var;
|
||||
|
||||
var = new LinkedHashMap<String,String>();
|
||||
var.put("variable", "rawOut");
|
||||
var.put("description", "a PrintWriter for arbitrary text output to this page");
|
||||
vars.add(var);
|
||||
|
||||
var = new LinkedHashMap<String,String>();
|
||||
var.put("variable", "htmlOut");
|
||||
var.put("description", "a PrintWriter for HTML output to this page");
|
||||
vars.add(var);
|
||||
|
||||
var = new LinkedHashMap<String,String>();
|
||||
var.put("variable", "job");
|
||||
var.put("description", "the current CrawlJob instance");
|
||||
vars.add(var);
|
||||
|
||||
var = new LinkedHashMap<String,String>();
|
||||
var.put("variable", "appCtx");
|
||||
var.put("description", "current job ApplicationContext, if any");
|
||||
vars.add(var);
|
||||
|
||||
var = new LinkedHashMap<String,String>();
|
||||
var.put("variable", "scriptResource");
|
||||
var.put("description", "the ScriptResource implementing this page, which offers utility methods");
|
||||
vars.add(var);
|
||||
this.put("availableGlobalVariables", vars);
|
||||
@XmlRootElement(name="script")
|
||||
@XmlType(propOrder={
|
||||
"crawlJobUrl", "crawlJobShortName", "availableScriptEngines",
|
||||
"script", "linesExecuted", "exception", "rawOutput", "htmlOutput",
|
||||
"availableGlobalVariables"
|
||||
})
|
||||
public class ScriptModel {
|
||||
private String crawlJobUrl;
|
||||
private Collection<Map<String, String>> availableScriptEngines;
|
||||
private ScriptingConsole scriptingConsole;
|
||||
|
||||
public ScriptModel(ScriptingConsole cc,
|
||||
String crawlJobUrl,
|
||||
Collection<Map<String, String>> scriptEngines) {
|
||||
scriptingConsole = cc;
|
||||
this.crawlJobUrl = crawlJobUrl;
|
||||
this.availableScriptEngines = scriptEngines;
|
||||
}
|
||||
|
||||
public boolean isFailure() {
|
||||
return scriptingConsole.getException() != null;
|
||||
}
|
||||
public String getStackTrace() {
|
||||
Throwable exception = scriptingConsole.getException();
|
||||
if (exception == null) return "";
|
||||
StringWriter s = new StringWriter();
|
||||
exception.printStackTrace(new PrintWriter(s));
|
||||
return s.toString();
|
||||
}
|
||||
public Throwable getException() {
|
||||
return scriptingConsole.getException();
|
||||
}
|
||||
public int getLinesExecuted() {
|
||||
return scriptingConsole.getLinesExecuted();
|
||||
}
|
||||
public String getRawOutput() {
|
||||
return scriptingConsole.getRawOutput();
|
||||
}
|
||||
public String getHtmlOutput() {
|
||||
return scriptingConsole.getHtmlOutput();
|
||||
}
|
||||
public String getScript() {
|
||||
return scriptingConsole.getScript();
|
||||
}
|
||||
|
||||
public String getCrawlJobShortName() {
|
||||
return scriptingConsole.getCrawlJob().getShortName();
|
||||
}
|
||||
public Collection<Map<String, String>> getAvailableScriptEngines() {
|
||||
return availableScriptEngines;
|
||||
}
|
||||
public List<Map<String, String>> getAvailableGlobalVariables() {
|
||||
return scriptingConsole.getAvailableGlobalVariables();
|
||||
}
|
||||
public String getCrawlJobUrl() {
|
||||
return crawlJobUrl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,29 +15,26 @@
|
||||
</head>
|
||||
<body>
|
||||
<h1>Execute script for job <i><a href='/engine/job/${model.crawlJobShortName}'>${model.crawlJobShortName}</a></i></h1>
|
||||
|
||||
<#if model.scriptExec??>
|
||||
<#if (model.scriptExec.linesExecuted > 0)>
|
||||
<span class='success'>${model.scriptExec.linesExecuted} ${(model.scriptExec.linesExecuted>1)?string("lines","line")} executed<span>
|
||||
<#if (model.linesExecuted > 0)>
|
||||
<span class='success'>${model.linesExecuted} ${(model.linesExecuted>1)?string("lines","line")} executed<span>
|
||||
</#if>
|
||||
<#if model.scriptExec.failure>
|
||||
<pre style='color:red; height:150px; overflow:auto'>${model.scriptExec.stackTrace}
|
||||
<#if model.failure>
|
||||
<pre style='color:red; height:150px; overflow:auto'>${model.stackTrace}
|
||||
</pre>
|
||||
</#if>
|
||||
<#assign htmlOutput=model.scriptExec.htmlOutput>
|
||||
<#assign htmlOutput=model.htmlOutput>
|
||||
<#if (htmlOutput?length > 0)>
|
||||
<fieldset><legend>htmlOut</legend>
|
||||
${htmlOutput}
|
||||
</fieldset>
|
||||
</#if>
|
||||
<#assign rawOutput=model.scriptExec.rawOutput>
|
||||
<#assign rawOutput=model.rawOutput>
|
||||
<#if (rawOutput?length > 0)>
|
||||
<fieldset><legend>rawOutput</legend>
|
||||
<pre style="margin:0;">${rawOutput}
|
||||
</pre>
|
||||
</fieldset>
|
||||
</#if>
|
||||
</#if>
|
||||
|
||||
<form method="POST">
|
||||
<input type="submit" value="execute">
|
||||
@@ -46,7 +43,7 @@
|
||||
<option<#if selectedEngine=scriptEngine.engine> selected="selected"</#if> value="${scriptEngine.engine}">${scriptEngine.language}</option>
|
||||
</#list>
|
||||
</select>
|
||||
<textarea rows='20' style='width:100%' name='script' id='editor'>${(model.scriptExec.script)!""}</textarea>
|
||||
<textarea rows='20' style='width:100%' name='script' id='editor'>${(model.script)!""}</textarea>
|
||||
<input type='submit' value='execute'></input>
|
||||
</form>
|
||||
The script will be executed in an engine preloaded
|
||||
@@ -69,7 +66,3 @@
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
package org.archive.crawler.restlet;
|
||||
|
||||
import javax.script.ScriptEngine;
|
||||
import javax.script.ScriptEngineManager;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.archive.crawler.framework.CrawlJob;
|
||||
import org.archive.spring.PathSharingContext;
|
||||
|
||||
public class ScriptingConsoleTest extends TestCase {
|
||||
// barebone CrawlJob object.
|
||||
public static class TestCrawlJob extends CrawlJob {
|
||||
public TestCrawlJob() {
|
||||
super(null);
|
||||
this.ac = new PathSharingContext(new String[0]);
|
||||
}
|
||||
@Override
|
||||
protected void scanJobLog() {
|
||||
}
|
||||
}
|
||||
CrawlJob cj;
|
||||
ScriptingConsole sc;
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
cj = new TestCrawlJob();
|
||||
sc = new ScriptingConsole(cj);
|
||||
}
|
||||
|
||||
public void testInitialState() {
|
||||
assertEquals("script is empty", "", sc.getScript());
|
||||
assertNull("exception is null", sc.getException());
|
||||
}
|
||||
|
||||
public void testExecute() {
|
||||
final String script = "rawOut.println 'elk'";
|
||||
final ScriptEngineManager manager = new ScriptEngineManager();
|
||||
final ScriptEngine eng = manager.getEngineByName("groovy");
|
||||
sc.execute(eng, script);
|
||||
|
||||
assertNull("exception is null", sc.getException());
|
||||
assertEquals("has the same script", sc.getScript(), script);
|
||||
assertEquals("linesExecuted", 1, sc.getLinesExecuted());
|
||||
assertEquals("rawOut", "elk\n", sc.getRawOutput());
|
||||
|
||||
}
|
||||
|
||||
public void testExecuteError() {
|
||||
final String script = "rawOut.println undef";
|
||||
final ScriptEngineManager manager = new ScriptEngineManager();
|
||||
final ScriptEngine eng = manager.getEngineByName("groovy");
|
||||
sc.execute(eng, script);
|
||||
|
||||
assertNotNull("exception is non-null", sc.getException());
|
||||
assertEquals("rawOut", "", sc.getRawOutput());
|
||||
assertEquals("linesExecuted", 0, sc.getLinesExecuted());
|
||||
|
||||
// extra test - it is okay to fail this test is okay because
|
||||
// ScriptingConsole is single-use now.
|
||||
sc.execute(eng, "rawOut.println 1");
|
||||
assertNull("exception is cleared", sc.getException());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
package org.archive.crawler.restlet;
|
||||
|
||||
import java.io.StringWriter;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.xml.bind.annotation.XmlRootElement;
|
||||
import javax.xml.bind.annotation.XmlType;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* @author kenji
|
||||
*
|
||||
*/
|
||||
public class XmlMarshallerTest extends TestCase {
|
||||
|
||||
/* (non-Javadoc)
|
||||
* @see junit.framework.TestCase#setUp()
|
||||
*/
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
/**
|
||||
* Map with nested Map
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testMarshalMap() throws Exception {
|
||||
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
map.put("c", 1);
|
||||
map.put("a", "a-value");
|
||||
Map<String, Object> nestedMap = new LinkedHashMap<String, Object>();
|
||||
nestedMap.put("x", 10);
|
||||
nestedMap.put("y", "y-value");
|
||||
map.put("m", nestedMap);
|
||||
|
||||
StringWriter w = new StringWriter();
|
||||
XmlMarshaller.marshalDocument(w, "doc", map);
|
||||
|
||||
String expected = "(?s:)" +
|
||||
"^<\\?xml version=\"1\\.0\" standalone='yes'\\?>\\s*" +
|
||||
"<doc>\\s*<c>1</c>\\s*<a>a-value</a>\\s*" +
|
||||
"<m>\\s*<x>10</x>\\s*<y>y-value</y>\\s*</m>\\s*" +
|
||||
"</doc>\\s*$";
|
||||
String xml = w.toString();
|
||||
System.out.println(xml);
|
||||
assertTrue("xml matches expected RE", xml.matches(expected));
|
||||
}
|
||||
|
||||
@XmlRootElement
|
||||
@XmlType(propOrder={"c", "a", "m"})
|
||||
public static class Model {
|
||||
public int getC() { return 1; }
|
||||
public String getA() { return "a-value"; }
|
||||
public Object getM() {
|
||||
return new NestedModel();
|
||||
}
|
||||
}
|
||||
@XmlRootElement
|
||||
@XmlType(propOrder={"x", "y"})
|
||||
public static class NestedModel {
|
||||
public int getX() { return 10; }
|
||||
public String getY() { return "y-value"; }
|
||||
}
|
||||
|
||||
/**
|
||||
* Bean with nested Bean
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testMashalBean() throws Exception {
|
||||
Object bean = new Model();
|
||||
StringWriter w = new StringWriter();
|
||||
XmlMarshaller.marshalDocument(w, "doc", bean);
|
||||
|
||||
String expected = "(?s:)" +
|
||||
"^<\\?xml version=\"1\\.0\" standalone='yes'\\?>\\s*" +
|
||||
"<doc>\\s*<c>1</c>\\s*<a>a-value</a>\\s*" +
|
||||
"<m>\\s*<x>10</x>\\s*<y>y-value</y>\\s*</m>\\s*" +
|
||||
"</doc>\\s*$";
|
||||
String xml = w.toString();
|
||||
System.out.println(xml);
|
||||
assertTrue("xml matches expected RE", xml.matches(expected));
|
||||
}
|
||||
|
||||
/**
|
||||
* Map with nested Bean
|
||||
* @throws Exception
|
||||
*/
|
||||
public void testMarshalBeanInMap() throws Exception {
|
||||
Map<String, Object> map = new LinkedHashMap<String, Object>();
|
||||
map.put("c", 1);
|
||||
map.put("a", "a-value");
|
||||
Object m = new NestedModel();
|
||||
map.put("m", m);
|
||||
|
||||
StringWriter w = new StringWriter();
|
||||
XmlMarshaller.marshalDocument(w, "doc", map);
|
||||
|
||||
String expected = "(?s:)" +
|
||||
"^<\\?xml version=\"1\\.0\" standalone='yes'\\?>\\s*" +
|
||||
"<doc>\\s*<c>1</c>\\s*<a>a-value</a>\\s*" +
|
||||
"<m>\\s*<x>10</x>\\s*<y>y-value</y>\\s*</m>\\s*" +
|
||||
"</doc>\\s*$";
|
||||
String xml = w.toString();
|
||||
System.out.println(xml);
|
||||
assertTrue("xml matches expected RE", xml.matches(expected));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user