Permalink
Browse files

In service of issue #176, support posting multipart/form-data to pipe…

…lines. Also distribute the restlet jars.
  • Loading branch information...
ndw committed Sep 14, 2014
1 parent a9e1855 commit 93f2f2c790c62a589362743ac28f76f78c5e6a2f
View
@@ -539,6 +539,8 @@ java -Xmx1024m -jar %INSTALL_PATH/calabash.jar "$@"
<copy todir="${install.dir}/lib">
<fileset file="lib/commons*.jar"/>
<fileset file="lib/http*.jar"/>
+ <fileset file="lib/org.restlet*.jar"/>
+ <fileset file="lib/servlet-api.jar"/>
<fileset file="lib/log4j*.jar"/>
<fileset file="lib/slf4j*.jar"/>
<fileset file="lib/jcl-over-slf4j*.jar"/>
Binary file not shown.
Binary file not shown.
Binary file not shown.
View
Binary file not shown.
View
Binary file not shown.
@@ -1,10 +1,14 @@
package com.xmlcalabash.piperack;
import com.xmlcalabash.core.XProcConfiguration;
+import com.xmlcalabash.core.XProcConstants;
import com.xmlcalabash.core.XProcException;
import com.xmlcalabash.core.XProcRuntime;
+import com.xmlcalabash.io.ReadableData;
import com.xmlcalabash.io.ReadablePipe;
import com.xmlcalabash.io.WritableDocument;
+import com.xmlcalabash.model.DeclareStep;
+import com.xmlcalabash.model.RuntimeValue;
import com.xmlcalabash.model.Serialization;
import com.xmlcalabash.runtime.XPipeline;
import com.xmlcalabash.util.TreeWriter;
@@ -16,25 +20,25 @@
import net.sf.saxon.s9api.XsltCompiler;
import net.sf.saxon.s9api.XsltExecutable;
import net.sf.saxon.s9api.XsltTransformer;
+import org.apache.commons.fileupload.FileItem;
+import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.restlet.data.Form;
import org.restlet.data.MediaType;
import org.restlet.data.ServerInfo;
import org.restlet.data.Status;
+import org.restlet.ext.fileupload.RestletFileUpload;
import org.restlet.representation.EmptyRepresentation;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.representation.Variant;
import org.restlet.resource.ServerResource;
+import org.xml.sax.InputSource;
import java.io.ByteArrayOutputStream;
+import java.io.File;
import java.net.URI;
import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Locale;
-import java.util.TimeZone;
-import java.util.Vector;
+import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -335,4 +339,120 @@ protected Representation getOutput(PipelineConfiguration pipeconfig, String port
throw new XProcException(e);
}
}
+
+ protected Representation processMultipartForm(PipelineConfiguration pipeconfig, Representation entity, Variant variant) {
+ XPipeline xpipeline = pipeconfig.pipeline;
+ XProcRuntime runtime = pipeconfig.runtime;
+
+ if (pipeconfig.ran) {
+ pipeconfig.reset();
+ xpipeline.reset();
+ }
+
+ String message = "";
+
+ HashMap<String,String> nameValuePairs = new HashMap<String,String> ();
+ HashMap<String,String> nsBindings = new HashMap<String,String> ();
+
+ DiskFileItemFactory factory = new DiskFileItemFactory();
+ factory.setSizeThreshold(100240);
+
+ RestletFileUpload upload = new RestletFileUpload(factory);
+ List<FileItem> items;
+ try {
+ items = upload.parseRequest(getRequest());
+
+ File file = null;
+ String filename = null;
+
+ for (final Iterator<FileItem> it = items.iterator(); it.hasNext(); ) {
+ FileItem fi = it.next();
+ String fieldName = fi.getFieldName();
+ String name = fi.getName();
+
+ if (name == null) {
+ Matcher matcher = xmlnsRE.matcher(fieldName);
+ if (matcher.matches()) {
+ nsBindings.put(matcher.group(1), new String(fi.get(), "utf-8"));
+ } else {
+ nameValuePairs.put(fieldName, new String(fi.get(), "utf-8"));
+ }
+ } else {
+ String port = fieldName;
+
+ if (pipeconfig.documentCount(port) == 0) {
+ xpipeline.clearInputs(port);
+ }
+ pipeconfig.writeTo(port);
+
+ try {
+ XdmNode doc = null;
+ MediaType m = new MediaType(fi.getContentType());
+
+ if (isXml(m)) {
+ doc = runtime.parse(new InputSource(fi.getInputStream()));
+ } else {
+ ReadablePipe pipe = null;
+ pipe = new ReadableData(runtime, XProcConstants.c_data, fi.getInputStream(), fi.getContentType());
+ doc = pipe.read();
+ }
+ xpipeline.writeTo(port, doc);
+ } catch (Exception e) {
+ throw new XProcException(e);
+ }
+
+ message += "Posted input to port '" + port + "'\n";
+ }
+ }
+
+
+ for (String fieldName : nameValuePairs.keySet()) {
+ QName qname = qnameFromForm(fieldName, nsBindings);
+ RuntimeValue value = new RuntimeValue(nameValuePairs.get(fieldName));
+ DeclareStep pipeline = xpipeline.getDeclareStep();
+
+ boolean option = false;
+ for (QName oname : pipeline.getOptions()) {
+ option = option || oname.equals(qname);
+ }
+
+ // Note: We explicitly set option to true again so that the behavior of
+ // multipart forms is consistent with the behavior of non-form data.
+ // Maybe I should support posting parameters like this in both cases.
+
+ option = true;
+
+ if (option) {
+ xpipeline.passOption(qname, value);
+ pipeconfig.setGVOption(qname);
+ message += "Option " + qname.getClarkName() + "=" + value.getString() + "\n";
+ } else {
+ String port = null;
+ if (port == null) {
+ // Figure out the default parameter port
+ for (String iport : xpipeline.getInputs()) {
+ com.xmlcalabash.model.Input input = pipeline.getInput(iport);
+ if (input.getParameterInput() && input.getPrimary()) {
+ port = iport;
+ }
+ }
+ }
+
+ if (port == null) {
+ throw new XProcException("No primary parameter input port.");
+ }
+
+ xpipeline.setParameter(port, qname, value);
+ pipeconfig.setParameter(qname, value.getString());
+ message += "Parameter " + qname.getClarkName() + "=" + value.getString() + "\n";
+ }
+ }
+
+ return okResponse(message, variant.getMediaType(), Status.SUCCESS_OK);
+ } catch (XProcException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new XProcException(e);
+ }
+ }
}
@@ -4,37 +4,22 @@
import com.xmlcalabash.core.XProcException;
import com.xmlcalabash.core.XProcRuntime;
import com.xmlcalabash.io.ReadableData;
-import com.xmlcalabash.io.ReadableDocument;
-import com.xmlcalabash.io.ReadableInline;
import com.xmlcalabash.io.ReadablePipe;
import com.xmlcalabash.model.DeclareStep;
import com.xmlcalabash.model.RuntimeValue;
import com.xmlcalabash.runtime.XPipeline;
import com.xmlcalabash.util.TreeWriter;
import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.XdmNode;
-import net.sf.saxon.s9api.XdmValue;
-import org.restlet.Request;
-import org.restlet.data.Form;
import org.restlet.data.MediaType;
import org.restlet.data.Status;
-import org.restlet.engine.header.Header;
import org.restlet.representation.EmptyRepresentation;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.representation.Variant;
-import org.restlet.util.Series;
import org.xml.sax.InputSource;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
import java.net.URI;
-import java.util.HashMap;
-import java.util.Random;
-import java.util.Vector;
+import java.util.*;
/**
* Ths file is part of XMLCalabash.
@@ -180,41 +165,44 @@ protected Representation post(Representation entity, Variant variant) {
XPipeline xpipeline = pipeconfig.pipeline;
XProcRuntime runtime = pipeconfig.runtime;
- if (pipeconfig.definput == null) {
- return badRequest(Status.CLIENT_ERROR_BAD_REQUEST, "No primary input port", variant.getMediaType());
- }
-
if (pipeconfig.ran) {
pipeconfig.reset();
xpipeline.reset();
}
- if (pipeconfig.documentCount(pipeconfig.definput) == 0) {
- xpipeline.clearInputs(pipeconfig.definput);
- }
- pipeconfig.writeTo(pipeconfig.definput);
-
try {
- XdmNode doc = null;
-
- if (isXml(entity.getMediaType())) {
- doc = runtime.parse(new InputSource(entity.getStream()));
+ if (MediaType.MULTIPART_FORM_DATA.equals(entity.getMediaType(), true)) {
+ processMultipartForm(pipeconfig, entity, variant);
} else {
- ReadablePipe pipe = null;
- pipe = new ReadableData(runtime, XProcConstants.c_data, entity.getStream(), entity.getMediaType().toString());
- doc = pipe.read();
- }
+ if (pipeconfig.definput == null) {
+ return badRequest(Status.CLIENT_ERROR_BAD_REQUEST, "No primary input port", variant.getMediaType());
+ }
+ if (pipeconfig.documentCount(pipeconfig.definput) == 0) {
+ xpipeline.clearInputs(pipeconfig.definput);
+ }
+ pipeconfig.writeTo(pipeconfig.definput);
- xpipeline.writeTo(pipeconfig.definput, doc);
- } catch (Exception e) {
- throw new XProcException(e);
- }
+ XdmNode doc = null;
+
+ if (isXml(entity.getMediaType())) {
+ doc = runtime.parse(new InputSource(entity.getStream()));
+ } else {
+ ReadablePipe pipe = null;
+ pipe = new ReadableData(runtime, XProcConstants.c_data, entity.getStream(), entity.getMediaType().toString());
+ doc = pipe.read();
+ }
- HashMap<QName,String> options = convertForm(getQuery());
+ xpipeline.writeTo(pipeconfig.definput, doc);
- for (QName name : options.keySet()) {
- RuntimeValue value = new RuntimeValue(options.get(name), null, null);
- xpipeline.passOption(name, value);
+ HashMap<QName, String> options = convertForm(getQuery());
+
+ for (QName name : options.keySet()) {
+ RuntimeValue value = new RuntimeValue(options.get(name), null, null);
+ xpipeline.passOption(name, value);
+ }
+ }
+ } catch (Exception e) {
+ return badRequest(Status.CLIENT_ERROR_BAD_REQUEST, e.getMessage(), variant.getMediaType());
}
return runPipeline(id);
@@ -1,8 +1,10 @@
package com.xmlcalabash.piperack;
+import com.xmlcalabash.core.XProcException;
import com.xmlcalabash.model.RuntimeValue;
import com.xmlcalabash.runtime.XPipeline;
import net.sf.saxon.s9api.QName;
+import org.restlet.data.MediaType;
import org.restlet.data.Status;
import org.restlet.representation.Representation;
import org.restlet.representation.Variant;
@@ -21,15 +23,26 @@ protected Representation post(Representation entity, Variant variant) {
return badRequest(Status.CLIENT_ERROR_NOT_FOUND, "no pipeline: " + pipelineUri(id), variant.getMediaType());
}
- HashMap<QName,String> options = convertForm(getQuery());
-
PipelineConfiguration pipeconfig = getPipelines().get(id);
XPipeline xpipeline = pipeconfig.pipeline;
- for (QName name : options.keySet()) {
- RuntimeValue value = new RuntimeValue(options.get(name), null, null);
- xpipeline.passOption(name, value);
+ // Passing options was never documented and this is redundant with the behavior of posting
+ // to the base pipeline endoing, /pipelines/{id}
+ /*
+ if (MediaType.MULTIPART_FORM_DATA.equals(entity.getMediaType(), true)) {
+ try {
+ processMultipartForm(pipeconfig, entity, variant);
+ } catch (XProcException e) {
+ return badRequest(Status.CLIENT_ERROR_BAD_REQUEST, e.getMessage(), variant.getMediaType());
+ }
+ } else {
+ HashMap<QName,String> options = convertForm(getQuery());
+ for (QName name : options.keySet()) {
+ RuntimeValue value = new RuntimeValue(options.get(name), null, null);
+ xpipeline.passOption(name, value);
+ }
}
+ */
return runPipeline(id);
}

0 comments on commit 93f2f2c

Please sign in to comment.