org.esxx.Response.java Source code

Java tutorial

Introduction

Here is the source code for org.esxx.Response.java

Source

/*
 ESXX - The friendly ECMAscript/XML Application Server
 Copyright (C) 2007-2008 Martin Blom <martin@blom.org>
    
 This program is free software: you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation, either version 3
 of the License, or (at your option) any later version.
    
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
    
 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.esxx;

import au.com.bytecode.opencsv.CSVWriter;
import java.awt.image.RenderedImage;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriter;
import javax.mail.internet.ContentType;
import org.esxx.util.IO;
import org.esxx.util.JS;
import org.esxx.util.KeyValueWrapper;
import org.esxx.util.StringUtil;
import org.json.*;
import org.mozilla.javascript.*;
import org.w3c.dom.Node;

public class Response {

    public static interface HeaderEnumerator {
        public void header(String name, String value);
    }

    public Response(int status, String content_type, Object result, Map<String, String> headers) {
        setStatus(status);
        setContentType(content_type);
        setResult(result);
        httpHeaders = headers;
        contentLength = -1;
    }

    public int getStatus() {
        return httpStatus;
    }

    public void setStatus(int status) {
        httpStatus = status;
    }

    public String getContentType(boolean guess) {
        if (guess) {
            return guessContentType();
        } else {
            return contentType;
        }
    }

    public void setContentType(String content_type) {
        contentType = content_type;
    }

    public Object getResult() {
        return resultObject;
    }

    public void setResult(Object result) {
        resultObject = result;
    }

    public void setBuffered(boolean bool) {
        buffered = bool;
    }

    public boolean isBuffered() {
        return buffered;
    }

    public long getContentLength() throws IOException {
        if (!buffered) {
            throw new IllegalStateException("getContentLength() only works on buffered responses");
        }

        if (contentLength == -1) {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();

            writeResult(bos);
            setResult(bos.toByteArray());
            contentLength = bos.size();
        }

        return contentLength;
    }

    public void unwrapResult() {
        resultObject = JS.toJavaObject(resultObject);
    }

    public Map<String, String> headers() {
        return httpHeaders;
    }

    public void enumerateHeaders(HeaderEnumerator he) {
        if (httpHeaders != null) {
            for (Map.Entry<String, String> e : httpHeaders.entrySet()) {
                he.header(e.getKey(), e.getValue());
            }
        }
    }

    public void writeResult(OutputStream out) throws IOException {
        try {
            writeObject(resultObject, new ContentType(guessContentType()), out);
        } catch (javax.mail.internet.ParseException ex) {
            throw new IOException("Invalid content-type: " + ex.getMessage(), ex);
        }
    }

    public static void writeObject(Object object, ContentType ct, OutputStream out) throws IOException {

        if (object == null) {
            return;
        }

        // Unwrap wrapped objects
        object = JS.toJavaObject(object);

        // Convert complex types to primitive types
        if (object instanceof Node) {
            ESXX esxx = ESXX.getInstance();

            if (ct.match("message/rfc822")) {
                try {
                    String xml = esxx.serializeNode((Node) object);
                    org.esxx.xmtp.XMTPParser xmtpp = new org.esxx.xmtp.XMTPParser();
                    javax.mail.Message msg = xmtpp.convertMessage(new StringReader(xml));
                    object = new ByteArrayOutputStream();
                    msg.writeTo(new FilterOutputStream((OutputStream) object) {
                        @Override
                        public void write(int b) throws IOException {
                            if (b == '\r') {
                                return;
                            } else if (b == '\n') {
                                out.write('\r');
                                out.write('\n');
                            } else {
                                out.write(b);
                            }
                        }
                    });
                } catch (javax.xml.stream.XMLStreamException ex) {
                    throw new ESXXException("Failed to serialize Node as message/rfc822:" + ex.getMessage(), ex);
                } catch (javax.mail.MessagingException ex) {
                    throw new ESXXException("Failed to serialize Node as message/rfc822:" + ex.getMessage(), ex);
                }
            } else {
                object = esxx.serializeNode((Node) object);
            }
        } else if (object instanceof Scriptable) {
            if (ct.match("application/x-www-form-urlencoded")) {
                String cs = Parsers.getParameter(ct, "charset", "UTF-8");

                object = StringUtil.encodeFormVariables(cs, (Scriptable) object);
            } else if (ct.match("text/csv")) {
                object = jsToCSV(ct, (Scriptable) object);
            } else {
                object = jsToJSON(object).toString();
            }
        } else if (object instanceof byte[]) {
            object = new ByteArrayInputStream((byte[]) object);
        } else if (object instanceof File) {
            object = new FileInputStream((File) object);
        }

        // Serialize primitive types
        if (object instanceof ByteArrayOutputStream) {
            ByteArrayOutputStream bos = (ByteArrayOutputStream) object;

            bos.writeTo(out);
        } else if (object instanceof ByteBuffer) {
            // Write result as-is to output stream
            WritableByteChannel wbc = Channels.newChannel(out);
            ByteBuffer bb = (ByteBuffer) object;

            bb.rewind();

            while (bb.hasRemaining()) {
                wbc.write(bb);
            }

            wbc.close();
        } else if (object instanceof InputStream) {
            IO.copyStream((InputStream) object, out);
        } else if (object instanceof Reader) {
            // Write stream as-is, using the specified charset (if present)
            String cs = Parsers.getParameter(ct, "charset", "UTF-8");
            Writer ow = new OutputStreamWriter(out, cs);

            IO.copyReader((Reader) object, ow);
        } else if (object instanceof String) {
            // Write string as-is, using the specified charset (if present)
            String cs = Parsers.getParameter(ct, "charset", "UTF-8");
            Writer ow = new OutputStreamWriter(out, cs);
            ow.write((String) object);
            ow.flush();
        } else if (object instanceof RenderedImage) {
            Iterator<ImageWriter> i = ImageIO.getImageWritersByMIMEType(ct.getBaseType());

            if (!i.hasNext()) {
                throw new ESXXException("No ImageWriter available for " + ct.getBaseType());
            }

            ImageWriter writer = i.next();

            writer.setOutput(ImageIO.createImageOutputStream(out));
            writer.write((RenderedImage) object);
        } else {
            throw new UnsupportedOperationException("Unsupported object class type: " + object.getClass());
        }
    }

    public String guessContentType() {
        if (contentType == null) {
            // Set default content-type, if missing
            if (resultObject instanceof InputStream || resultObject instanceof ByteArrayOutputStream
                    || resultObject instanceof ByteBuffer || resultObject instanceof byte[]) {
                return "application/octet-stream";
            } else if (resultObject instanceof File) {
                return ESXX.fileTypeMap.getContentType((File) resultObject);
            } else if (resultObject instanceof Reader || resultObject instanceof String) {
                return "text/plain; charset=UTF-8";
            } else if (resultObject instanceof RenderedImage) {
                return "image/png";
            } else if (resultObject instanceof Node
                    || resultObject instanceof org.mozilla.javascript.xml.XMLObject) {
                return "application/xml";
            } else if (resultObject instanceof Scriptable) {
                return "application/json";
            } else {
                return "application/octet-stream";
            }
        }

        return contentType;
    }

    private static Object jsToJSON(Object object) {
        try {
            if (object instanceof NativeArray) {
                Object[] array = Context.getCurrentContext().getElements((Scriptable) object);

                for (int i = 0; i < array.length; ++i) {
                    array[i] = jsToJSON(array[i]);
                }

                object = new JSONArray(array);
            } else if (object instanceof Wrapper) {
                object = jsToJSON(((Wrapper) object).unwrap());
            } else if (object instanceof Scriptable) {
                Scriptable jsobject = (Scriptable) object;

                object = new JSONObject();

                for (Object k : jsobject.getIds()) {
                    if (k instanceof String) {
                        String key = (String) k;
                        ((JSONObject) object).put(key, jsToJSON(jsobject.get(key, jsobject)));
                    }
                }
            } else {
                object = Context.jsToJava(object, Object.class);
            }

            return object;
        } catch (JSONException ex) {
            throw new ESXXException("Failed to convert JavaScript object to JSON: " + ex.getMessage(), ex);
        }
    }

    static private Object jsToCSV(ContentType ct, Scriptable object) throws IOException {
        Context cx = Context.getCurrentContext();

        String separator = Parsers.getParameter(ct, "x-separator", ",");
        String quote = Parsers.getParameter(ct, "x-quote", "\"");
        String escape = Parsers.getParameter(ct, "x-escape", "\"");

        if ("none".equals(separator))
            separator = "";
        if ("none".equals(quote))
            quote = "";
        if ("none".equals(escape))
            escape = "";

        if (separator.length() > 1 || quote.length() > 1 || escape.length() > 1) {
            throw new IOException(
                    "x-separator, x-quote and x-escape values must be " + "empty or a single character");
        }

        KeyValueWrapper rows = new KeyValueWrapper(object);
        TreeMap<String, Integer> columns = new TreeMap<String, Integer>();

        // Find all possible colunms
        for (Object r : rows.getValues(cx)) {
            KeyValueWrapper row = new KeyValueWrapper(r);

            for (Object c : row.getKeys()) {
                columns.put(StringUtil.toSortable(c), null);
            }
        }

        // "Sort" them
        int cnt = 0;
        for (Map.Entry<String, Integer> e : columns.entrySet()) {
            e.setValue(cnt++);
        }

        // Write lines
        StringWriter sw = new StringWriter();
        CSVWriter csv = new CSVWriter(sw, separator.isEmpty() ? '\0' : separator.charAt(0),
                quote.isEmpty() ? '\0' : quote.charAt(0), escape.isEmpty() ? '\0' : escape.charAt(0));

        Object headers = rows.getValue(cx, "headers");

        if (headers != Context.getUndefinedValue()) {
            writeCSVRow(cx, csv, columns, headers);
        }

        for (Object k : rows.getKeys()) {
            if (!"headers".equals(k.toString())) {
                writeCSVRow(cx, csv, columns, rows.getValue(cx, k));
            }
        }

        return sw.toString();
    }

    static private void writeCSVRow(Context cx, CSVWriter csv, TreeMap<String, Integer> columns, Object r) {
        KeyValueWrapper row = new KeyValueWrapper(r);
        String[] fields = new String[columns.size()];

        for (Object c : row.getKeys()) {
            int idx = columns.get(StringUtil.toSortable(c));
            // System.out.println("Column " + Context.toString(c) + " has index " + idx);
            // System.out.println(Context.toString(row.getValue(cx, c)));
            fields[idx] = Context.toString(row.getValue(cx, c));
        }

        csv.writeNext(fields);
    }

    private int httpStatus;
    private String contentType;
    private Object resultObject;
    private long contentLength;
    private boolean buffered;
    private Map<String, String> httpHeaders;
}