All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.xmlcalabash.piperack.BaseResource Maven / Gradle / Ivy

The newest version!
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;
import net.sf.saxon.s9api.QName;
import net.sf.saxon.s9api.SaxonApiException;
import net.sf.saxon.s9api.XdmAtomicValue;
import net.sf.saxon.s9api.XdmDestination;
import net.sf.saxon.s9api.XdmNode;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.InputSource;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * Ths file is part of XMLCalabash.
 * Created by ndw on 10/25/13.
 */
public class BaseResource extends ServerResource {
    protected static final String NS_PR = "http://xmlcalabash.com/ns/piperack";
    protected static final QName pr_pipeline = new QName("", NS_PR, "pipeline");
    protected static final QName pr_input = new QName("", NS_PR, "input");
    protected static final QName pr_output = new QName("", NS_PR, "output");
    protected static final QName pr_has_run = new QName("", NS_PR, "has-run");
    protected static final QName pr_uri = new QName("", NS_PR, "uri");
    protected static final QName pr_status = new QName("", NS_PR, "status");
    protected static final QName pr_version = new QName("", NS_PR, "version");
    protected static final QName pr_saxon_version = new QName("", NS_PR, "saxon-version");
    protected static final QName pr_saxon_edition = new QName("", NS_PR, "saxon-edition");
    protected static final QName pr_copyright = new QName("", NS_PR, "copyright");
    protected static final QName pr_message = new QName("", NS_PR, "message");
    protected static final QName pr_expires = new QName("", NS_PR, "expires");
    protected static final QName pr_pipelines = new QName("", NS_PR, "pipelines");
    protected static final QName pr_help = new QName("", NS_PR, "help");
    protected static final QName pr_description = new QName("", NS_PR, "description");
    protected static final QName pr_endpoint = new QName("", NS_PR, "endpoint");
    protected static final QName pr_error = new QName("", NS_PR, "error");
    protected static final QName pr_response = new QName("", NS_PR, "response");
    protected static final QName pr_code = new QName("", NS_PR, "code");
    protected static final QName pr_option = new QName("", NS_PR, "option");
    protected static final QName pr_parameter = new QName("", NS_PR, "parameter");
    protected static final QName pr_name = new QName("", NS_PR, "name");
    protected static final QName pr_value = new QName("", NS_PR, "value");
    protected static final QName _primary = new QName("primary");
    protected static final QName _default = new QName("default");
    protected static final QName _documents = new QName("documents");
    protected static final QName _initialized = new QName("initialized");
    protected static final QName _format = new QName("format");
    protected static final QName _method = new QName("method");

    protected static final Pattern xmlnsRE = Pattern.compile("^xmlns:(.+)$");
    protected static final Pattern qnameRE = Pattern.compile("^(.+):(.+)$");
    protected static final Pattern portRE = Pattern.compile("(\\w+)@(.+)");
    protected static final HashSet emptyExcludeNS = new HashSet ();

    protected Logger logger = null;

    public BaseResource() {
        super();
        logger = LoggerFactory.getLogger(this.getClass());
        getVariants().add(new Variant(MediaType.TEXT_PLAIN));
        getVariants().add(new Variant(MediaType.APPLICATION_XML));
        getVariants().add(new Variant(MediaType.TEXT_HTML));
        getVariants().add(new Variant(MediaType.APPLICATION_JSON));
    }

    protected ConcurrentMap getPipelines() {
        return ((PiperackApplication) getApplication()).getPipelines();
    }

    protected XProcConfiguration getConfiguration() {
        return ((PiperackApplication) getApplication()).getConfiguration();
    }

    protected XProcRuntime getGlobalRuntime() {
        return ((PiperackApplication) getApplication()).getGlobalRuntime();
    }

    protected XdmNode xsl() {
        return ((PiperackApplication) getApplication()).xsl();
    }

    protected String pipelineUri(String id) {
        ServerInfo serverInfo = getServerInfo();
        String hostname = "localhost";
        if (serverInfo.getAddress() != null) {
            // I bet this is a number not a name :-(
            hostname = serverInfo.getAddress();
        }

        return "http://" + hostname + ":" + serverInfo.getPort() + "/pipelines/" + id;
    }

    protected boolean isXml(MediaType type) {
        String isxml = type.getSubType();
        return MediaType.APPLICATION_XML.equals(type) || isxml.endsWith("+xml");
    }

    protected void formatExpires(TreeWriter tree, Calendar expires) {
        if (expires.getTimeInMillis() != Long.MAX_VALUE) {
            tree.addStartElement(pr_expires);
            tree.startContent();
            SimpleDateFormat gmtFrmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
            gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT"));
            tree.addText(gmtFrmt.format(expires.getTime()));
            tree.addEndElement();
        }
    }

    protected QName qnameFromForm(String name, Form params) {
        HashMap bindings = bindingsFromForm(params);
        return qnameFromForm(name, bindings);
    }

    protected QName qnameFromForm(String name, HashMap bindings) {
        Matcher matcher = qnameRE.matcher(name);
        if (matcher.matches()) {
            String ns = bindings.get(matcher.group(1));
            return new QName(matcher.group(1), ns, matcher.group(2));
        } else {
            return new QName(name);
        }
    }

    protected HashMap bindingsFromForm(Form params) {
        HashMap bindings = new HashMap ();
        for (String key : params.getNames()) {
            Matcher matcher = xmlnsRE.matcher(key);
            if (matcher.matches()) {
                bindings.put(matcher.group(1), params.getFirstValue(key));
            }
        }
        return bindings;
    }

    protected HashMap convertForm(Form params) {
        HashMap converted = new HashMap();
        HashMap bindings = bindingsFromForm(params);

        for (String key : params.getNames()) {
            Matcher matcher = xmlnsRE.matcher(key);
            if (matcher.matches()) {
                // nop
            } else {
                QName name = qnameFromForm(key, bindings);
                converted.put(name, params.getFirstValue(key));
            }
        }

        return converted;
    }

    protected HashMap convertFormStrings(Form params) {
        HashMap converted = new HashMap();

        for (String key : params.getNames()) {
            Matcher matcher = xmlnsRE.matcher(key);
            if (matcher.matches()) {
                // nop
            } else {
                converted.put(key, params.getFirstValue(key));
            }
        }

        return converted;
    }

    protected String serialize(XdmNode doc, MediaType type) {
        XProcRuntime runtime = getGlobalRuntime();
        String format = "text";

        if (MediaType.TEXT_HTML.equals(type)) {
            format = "html";
        } else if (MediaType.APPLICATION_XML.equals(type)) {
            format = "xml";
        } else if (MediaType.APPLICATION_JSON.equals(type)) {
            format = "json";
        }

        if (xsl() != null) {
            XdmDestination result = null;
            try {
                XsltCompiler compiler = runtime.getProcessor().newXsltCompiler();
                XsltExecutable exec = compiler.compile(xsl().asSource());
                XsltTransformer transformer = exec.load();
                transformer.setParameter(_format, new XdmAtomicValue(format));
                transformer.setInitialContextNode(doc);
                result = new XdmDestination();
                transformer.setDestination(result);
                transformer.transform();
            } catch (SaxonApiException e) {
                throw new XProcException(e);
            }

            doc = result.getXdmNode();
        }

        return doc.toString();
    }

    protected Representation badRequest(Status status, String msg, MediaType type) {
        TreeWriter tree = new TreeWriter(getGlobalRuntime());
        tree.startDocument(URI.create("http://example.com/"));
        tree.addStartElement(pr_error);
        tree.startContent();
        tree.addStartElement(pr_code);
        tree.startContent();
        tree.addText("" + status.getCode());
        tree.addEndElement();
        tree.addStartElement(pr_message);
        tree.startContent();
        tree.addText("Bad request: " + msg);
        tree.addEndElement();
        tree.addEndElement();
        tree.endDocument();
        setStatus(status);
        return new StringRepresentation(serialize(tree.getResult(), type), type);
    }

    protected Representation okResponse(String msg, MediaType type) {
        return okResponse(msg, type, Status.SUCCESS_OK);
    }

    protected Representation okResponse(String msg, MediaType type, Status status) {
        TreeWriter tree = new TreeWriter(getGlobalRuntime());
        tree.startDocument(URI.create("http://example.com/"));
        tree.addStartElement(pr_response);
        tree.startContent();
        tree.addStartElement(pr_code);
        tree.startContent();
        tree.addText("" + status.getCode());
        tree.addEndElement();
        tree.addStartElement(pr_message);
        tree.startContent();
        tree.addText(msg);
        tree.addEndElement();
        tree.addEndElement();
        tree.endDocument();
        return new StringRepresentation(serialize(tree.getResult(), type), type);
    }

    protected Representation runPipeline(String id) {
        PipelineConfiguration pipeconfig = getPipelines().get(id);
        XPipeline pipeline = pipeconfig.pipeline;

        try {
            pipeline.run();
            pipeconfig.ran = true;

            for (String port : pipeline.getOutputs()) {
                Vector nodes = new Vector ();
                ReadablePipe rpipe = pipeline.readFrom(port);
                while (rpipe.moreDocuments()) {
                    nodes.add(rpipe.read());
                }
                pipeconfig.outputs.put(port, nodes);
            }
        } catch (Exception e) {
            throw new XProcException(e);
        }

        if (pipeconfig.defoutput != null) {
            return getOutput(pipeconfig, pipeconfig.defoutput);
        } else {
            setStatus(Status.SUCCESS_OK);
            return new EmptyRepresentation();
        }
    }

    protected Representation getOutput(PipelineConfiguration pipeconfig, String port) {
        XProcConfiguration config = getConfiguration();
        XPipeline pipeline = pipeconfig.pipeline;
        XProcRuntime runtime = pipeconfig.runtime;
        Serialization serial = pipeline.getSerialization(port);

        if (serial == null) {
            // Use the configuration options
            // FIXME: should each of these be considered separately?
            // FIXME: should there be command-line options to override these settings?
            serial = new Serialization(runtime, pipeline.getNode()); // The node's a hack
            for (String name : config.serializationOptions.keySet()) {
                String value = config.serializationOptions.get(name);

                if ("byte-order-mark".equals(name)) serial.setByteOrderMark("true".equals(value));
                if ("escape-uri-attributes".equals(name)) serial.setEscapeURIAttributes("true".equals(value));
                if ("include-content-type".equals(name)) serial.setIncludeContentType("true".equals(value));
                if ("indent".equals(name)) serial.setIndent("true".equals(value));
                if ("omit-xml-declaration".equals(name)) serial.setOmitXMLDeclaration("true".equals(value));
                if ("undeclare-prefixes".equals(name)) serial.setUndeclarePrefixes("true".equals(value));
                if ("method".equals(name)) serial.setMethod(new QName("", value));

                // FIXME: if ("cdata-section-elements".equals(name)) serial.setCdataSectionElements();
                if ("doctype-public".equals(name)) serial.setDoctypePublic(value);
                if ("doctype-system".equals(name)) serial.setDoctypeSystem(value);
                if ("encoding".equals(name)) serial.setEncoding(value);
                if ("media-type".equals(name)) serial.setMediaType(value);
                if ("normalization-form".equals(name)) serial.setNormalizationForm(value);
                if ("standalone".equals(name)) serial.setStandalone(value);
                if ("version".equals(name)) serial.setVersion(value);
            }
        }

        if (!pipeconfig.outputs.containsKey(port)) {
            return badRequest(Status.CLIENT_ERROR_BAD_REQUEST, "no port named: " + port, MediaType.APPLICATION_XML);
        }

        Vector nodes = pipeconfig.outputs.get(port);
        if (nodes.size() == 0) {
            setStatus(Status.SUCCESS_NO_CONTENT);
            return new EmptyRepresentation();
        }

        XdmNode doc = nodes.firstElement();
        nodes.remove(0);

        // I wonder if there's a better way...
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        WritableDocument wd = new WritableDocument(runtime, doc.getBaseURI().toASCIIString(), serial, bos);
        wd.write(doc);

        try {
            String xml = bos.toString("UTF-8");
            Representation result = new StringRepresentation(xml, MediaType.APPLICATION_XML);
            setStatus(Status.SUCCESS_OK);
            return result;
        } catch (Exception e) {
            throw new XProcException(e);
        }
    }

    protected Representation processMultipartForm(PipelineConfiguration pipeconfig, Representation entity, Variant variant) {
        String id = (String) getRequest().getAttributes().get("id");

        XPipeline xpipeline = pipeconfig.pipeline;
        XProcRuntime runtime = pipeconfig.runtime;

        if (pipeconfig.ran) {
            pipeconfig.reset();
            xpipeline.reset();
        }

        String message = "";

        HashMap nameValuePairs = new HashMap ();
        HashMap nsBindings = new HashMap ();

        DiskFileItemFactory factory = new DiskFileItemFactory();
        factory.setSizeThreshold(100240);

        RestletFileUpload upload = new RestletFileUpload(factory);
        List items;
        try {
            items = upload.parseRequest(getRequest());

            File file = null;
            String filename = null;

            for (final Iterator 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()));
                            logger.debug("Posting XML document to " + port + " for " + id);
                        } else {
                            ReadablePipe pipe = null;
                            pipe = new ReadableData(runtime, XProcConstants.c_data, fi.getInputStream(), fi.getContentType());
                            doc = pipe.read();
                            logger.debug("Posting non-XML document to " + port + " for " + id);
                        }
                        xpipeline.writeTo(port, doc);
                    } catch (Exception e) {
                        throw new XProcException(e);
                    }

                    message += "Posted input to port '" + port + "'\n";
                }
            }


            DeclareStep pipeline = xpipeline.getDeclareStep();
            for (String fieldName : nameValuePairs.keySet()) {
                RuntimeValue value = new RuntimeValue(nameValuePairs.get(fieldName));

                if (fieldName.startsWith("-p")) {
                    fieldName = fieldName.substring(2);

                    String port= null;
                    Matcher matcher = portRE.matcher(fieldName);
                    if (matcher.matches()) {
                        port = matcher.group(1);
                        fieldName = matcher.group(2);
                    }

                    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.");
                    }

                    logger.debug("Parameter " + fieldName + "=" + value.getString() + " for " + id);

                    QName qname = qnameFromForm(fieldName, nsBindings);
                    xpipeline.setParameter(port, qname, value);
                    pipeconfig.setParameter(qname, value.getString());
                    message += "Parameter " + qname.getClarkName() + "=" + value.getString() + "\n";
                } else {
                    logger.debug("Option " + fieldName + "=" + value.getString() + " for " + id);

                    QName qname = qnameFromForm(fieldName, nsBindings);
                    xpipeline.passOption(qname, value);
                    pipeconfig.setGVOption(qname);
                    message += "Option " + qname.getClarkName() + "=" + value.getString() + "\n";
                }
            }

            return okResponse(message, variant.getMediaType(), Status.SUCCESS_OK);
        } catch (XProcException e) {
            pipeconfig.reset();
            xpipeline.reset();
            throw e;
        } catch (Exception e) {
            pipeconfig.reset();
            xpipeline.reset();
            throw new XProcException(e);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy