com.mirth.connect.connectors.http.HttpMessageConverter.java Source code

Java tutorial

Introduction

Here is the source code for com.mirth.connect.connectors.http.HttpMessageConverter.java

Source

/*
 * Copyright (c) Mirth Corporation. All rights reserved.
 * 
 * http://www.mirthcorp.com
 * 
 * The software in this package is published under the terms of the MPL license a copy of which has
 * been included with this distribution in the LICENSE.txt file.
 */

package com.mirth.connect.connectors.http;

import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import javax.mail.BodyPart;
import javax.mail.MessagingException;
import javax.mail.internet.MimeMultipart;
import javax.servlet.http.HttpServletRequest;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.stream.StreamSource;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HeaderElement;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.entity.ContentType;
import org.apache.http.message.BasicHeaderValueFormatter;
import org.apache.http.message.BasicHeaderValueParser;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.message.ParserCursor;
import org.apache.http.util.CharArrayBuffer;
import org.apache.log4j.Logger;
import org.w3c.dom.Document;

import com.mirth.connect.donkey.util.Base64Util;
import com.mirth.connect.donkey.util.DonkeyElement;
import com.mirth.connect.donkey.util.DonkeyElement.DonkeyElementException;

public class HttpMessageConverter {
    private static Logger logger = Logger.getLogger(HttpMessageConverter.class);

    private static BinaryContentTypeResolver defaultResolver = new BinaryContentTypeResolver() {
        @Override
        public boolean isBinaryContentType(ContentType contentType) {
            return StringUtils.startsWithAny(contentType.getMimeType(),
                    new String[] { "application/", "image/", "video/", "audio/" });
        }
    };

    public static String httpRequestToXml(HttpRequestMessage request, boolean parseMultipart,
            boolean includeMetadata, BinaryContentTypeResolver resolver) {
        try {
            Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
            DonkeyElement requestElement = new DonkeyElement(document.createElement("HttpRequest"));

            if (includeMetadata) {
                requestElement.addChildElement("RemoteAddress", request.getRemoteAddress());
                requestElement.addChildElement("RequestUrl", request.getRequestUrl());
                requestElement.addChildElement("Method", request.getMethod());
                requestElement.addChildElement("RequestPath", request.getQueryString());
                requestElement.addChildElement("RequestContextPath", request.getContextPath());

                if (!request.getParameters().isEmpty()) {
                    DonkeyElement parametersElement = requestElement.addChildElement("Parameters");

                    for (Entry<String, List<String>> entry : request.getParameters().entrySet()) {
                        for (String value : entry.getValue()) {
                            parametersElement.addChildElement(entry.getKey(), value);
                        }
                    }
                }

                DonkeyElement headerElement = requestElement.addChildElement("Header");

                for (Entry<String, List<String>> entry : request.getHeaders().entrySet()) {
                    for (String value : entry.getValue()) {
                        headerElement.addChildElement(entry.getKey(), value);
                    }
                }
            }

            processContent(requestElement.addChildElement("Content"), request.getContent(),
                    request.getContentType(), parseMultipart, resolver);

            return requestElement.toXml();
        } catch (Exception e) {
            logger.error("Error converting HTTP request.", e);
        }

        return null;
    }

    public static String contentToXml(Object content, ContentType contentType, boolean parseMultipart,
            BinaryContentTypeResolver resolver)
            throws DonkeyElementException, MessagingException, IOException, ParserConfigurationException {
        DonkeyElement contentElement = new DonkeyElement("<Content/>");
        processContent(contentElement, content, contentType, parseMultipart, resolver);
        return contentElement.toXml();
    }

    private static void processContent(DonkeyElement contentElement, Object content, ContentType contentType,
            boolean parseMultipart, BinaryContentTypeResolver resolver)
            throws DonkeyElementException, MessagingException, IOException {
        if (resolver == null) {
            resolver = defaultResolver;
        }

        if (parseMultipart && content instanceof MimeMultipart) {
            contentElement.setAttribute("multipart", "yes");
            MimeMultipart multipart = (MimeMultipart) content;

            String boundary = contentType.getParameter("boundary");
            if (StringUtils.isNotBlank(boundary)) {
                contentElement.setAttribute("boundary", boundary);
            }

            if (StringUtils.isNotEmpty(multipart.getPreamble())) {
                contentElement.addChildElement("Preamble", multipart.getPreamble());
            }

            for (int partIndex = 0; partIndex < multipart.getCount(); partIndex++) {
                BodyPart part = multipart.getBodyPart(partIndex);
                DonkeyElement partElement = contentElement.addChildElement("Part");
                DonkeyElement headersElement = partElement.addChildElement("Headers");
                ContentType partContentType = contentType;

                for (Enumeration<javax.mail.Header> en = part.getAllHeaders(); en.hasMoreElements();) {
                    javax.mail.Header header = en.nextElement();
                    headersElement.addChildElement(header.getName(), header.getValue());

                    if (header.getName().equalsIgnoreCase("Content-Type")) {
                        try {
                            partContentType = ContentType.parse(header.getValue());
                        } catch (RuntimeException e) {
                        }
                    }
                }

                processContent(partElement.addChildElement("Content"), part.getContent(), partContentType, true,
                        resolver);
            }
        } else {
            contentElement.setAttribute("multipart", "no");
            String charset = getDefaultHttpCharset(
                    contentType.getCharset() != null ? contentType.getCharset().name() : null);

            // Call the resolver to determine if the content should be Base64 encoded
            if (resolver.isBinaryContentType(contentType)) {
                contentElement.setAttribute("encoding", "Base64");
                byte[] contentByteArray = null;

                if (content instanceof StreamSource) {
                    StreamSource streamSource = (StreamSource) content;
                    if (streamSource.getInputStream() != null) {
                        contentByteArray = IOUtils.toByteArray(streamSource.getInputStream());
                    } else if (streamSource.getReader() != null) {
                        contentByteArray = IOUtils.toString(streamSource.getReader()).getBytes(charset);
                    }
                } else if (content instanceof InputStream) {
                    contentByteArray = IOUtils.toByteArray((InputStream) content);
                } else if (content instanceof byte[]) {
                    contentByteArray = (byte[]) content;
                }

                if (contentByteArray == null) {
                    contentByteArray = (content != null ? content.toString() : "").getBytes(charset);
                }

                contentElement.setTextContent(new String(Base64Util.encodeBase64(contentByteArray), "US-ASCII"));
            } else {
                String contentText = null;

                if (content instanceof StreamSource) {
                    StreamSource streamSource = (StreamSource) content;
                    if (streamSource.getInputStream() != null) {
                        contentText = IOUtils.toString(streamSource.getInputStream(), charset);
                    } else if (streamSource.getReader() != null) {
                        contentText = IOUtils.toString(streamSource.getReader());
                    }
                } else if (content instanceof InputStream) {
                    contentText = IOUtils.toString((InputStream) content, charset);
                } else if (content instanceof byte[]) {
                    contentText = new String((byte[]) content, charset);
                }

                if (contentText == null) {
                    contentText = content != null ? content.toString() : "";
                }

                contentElement.setTextContent(contentText);
            }
        }
    }

    public static String httpResponseToXml(String status, Map<String, List<String>> headers, Object content,
            ContentType contentType, boolean parseMultipart, boolean includeMetadata,
            BinaryContentTypeResolver resolver) {
        try {
            Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
            DonkeyElement requestElement = new DonkeyElement(document.createElement("HttpResponse"));

            if (includeMetadata) {
                requestElement.addChildElement("Status", status);

                DonkeyElement headerElement = requestElement.addChildElement("Header");

                for (Entry<String, List<String>> entry : headers.entrySet()) {
                    for (String value : entry.getValue()) {
                        DonkeyElement fieldElement = headerElement.addChildElement("Field");
                        fieldElement.addChildElement("Name", entry.getKey());
                        fieldElement.addChildElement("Value", value);
                    }
                }
            }

            processContent(requestElement.addChildElement("Body"), content, contentType, parseMultipart, resolver);

            return requestElement.toXml();
        } catch (Exception e) {
            logger.error("Error converting HTTP request.", e);
        }

        return null;
    }

    public static String getDefaultHttpCharset(String charset) {
        if (charset == null) {
            return "ISO-8859-1"; // default charset for HTTP
        } else {
            return charset;
        }
    }

    public static Map<String, List<String>> convertFieldEnumerationToMap(HttpServletRequest request) {
        Map<String, List<String>> headers = new HashMap<String, List<String>>();

        for (Enumeration<String> enumeration = request.getHeaderNames(); enumeration.hasMoreElements();) {
            String name = enumeration.nextElement();
            Enumeration headerNames = request.getHeaders(name);

            while (headerNames.hasMoreElements()) {
                List<String> list = headers.get(name);

                if (list == null) {
                    list = new ArrayList<String>();
                    headers.put(name, list);
                }

                list.add((String) headerNames.nextElement());
            }
        }

        return headers;
    }

    /**
     * This method takes in a ContentType and returns an equivalent ContentType, only overriding the
     * charset. This is needed because ContentType.withCharset(charset) does not correctly handle
     * headers with multiple parameters. Parsing a ContentType from a String works, calling
     * toString() to get the correct header value works, but there's no way from ContentType itself
     * to update a specific parameter in-place.
     */
    public static ContentType setCharset(ContentType contentType, Charset charset)
            throws ParseException, UnsupportedCharsetException {
        // Get the correct header value
        String contentTypeString = contentType.toString();

        // Parse the header manually the same way ContentType does it
        CharArrayBuffer buffer = new CharArrayBuffer(contentTypeString.length());
        buffer.append(contentTypeString);
        ParserCursor cursor = new ParserCursor(0, contentTypeString.length());
        HeaderElement[] elements = BasicHeaderValueParser.INSTANCE.parseElements(buffer, cursor);

        if (ArrayUtils.isNotEmpty(elements)) {
            String mimeType = elements[0].getName();
            NameValuePair[] params = elements[0].getParameters();
            List<NameValuePair> paramsList = new ArrayList<NameValuePair>();
            boolean charsetFound = false;

            // Iterate through each parameter and override the charset if present
            if (ArrayUtils.isNotEmpty(params)) {
                for (NameValuePair nvp : params) {
                    if (nvp.getName().equalsIgnoreCase("charset")) {
                        charsetFound = true;
                        nvp = new BasicNameValuePair(nvp.getName(), charset.name());
                    }
                    paramsList.add(nvp);
                }
            }

            // Add the charset at the end if it wasn't found before
            if (!charsetFound) {
                paramsList.add(new BasicNameValuePair("charset", charset.name()));
            }

            // Format the header the same way ContentType does it
            CharArrayBuffer newBuffer = new CharArrayBuffer(64);
            newBuffer.append(mimeType);
            newBuffer.append("; ");
            BasicHeaderValueFormatter.INSTANCE.formatParameters(newBuffer,
                    paramsList.toArray(new NameValuePair[paramsList.size()]), false);
            // Once we have the correct string, let ContentType do the rest
            return ContentType.parse(newBuffer.toString());
        } else {
            throw new ParseException("Invalid content type: " + contentTypeString);
        }
    }
}