org.apache.axis.attachments.MultiPartRelatedInputStream.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.axis.attachments.MultiPartRelatedInputStream.java

Source

/*
 * Copyright 2001-2004 The Apache Software Foundation.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.axis.attachments;

import org.apache.axis.Part;
import org.apache.axis.components.logger.LogFactory;
import org.apache.axis.transport.http.HTTPConstants;
import org.apache.axis.utils.Messages;
import org.apache.axis.utils.IOUtils;
import org.apache.commons.logging.Log;

import javax.activation.DataHandler;
import javax.mail.internet.MimeUtility;
import java.io.IOException;
import java.io.BufferedInputStream;

/**
 * This simulates the multipart stream.
 *
 * @author Rick Rineholt
 */
public class MultiPartRelatedInputStream extends MultiPartInputStream {

    /** Field log           */
    protected static Log log = LogFactory.getLog(MultiPartRelatedInputStream.class.getName());

    /** Field MIME_MULTIPART_RELATED           */
    public static final String MIME_MULTIPART_RELATED = "multipart/related";

    /** Field parts           */
    protected java.util.HashMap parts = new java.util.HashMap();

    /** Field orderedParts           */
    protected java.util.LinkedList orderedParts = new java.util.LinkedList();

    /** Field rootPartLength           */
    protected int rootPartLength = 0;

    /** Field closed           */
    protected boolean closed = false; // If true the stream has been closed.

    /** Field eos           */
    protected boolean eos = false; // This is set once the SOAP packet has reached the end of stream.

    // protected java.io.InputStream is = null; //The orginal multipart/related stream.
    // This stream controls and manages the  boundary.

    /** Field boundaryDelimitedStream           */
    protected org.apache.axis.attachments.BoundaryDelimitedStream boundaryDelimitedStream = null;

    /** Field soapStream           */
    protected java.io.InputStream soapStream = null; // Set the soap stream once found.

    /** Field soapStreamBDS           */
    protected java.io.InputStream soapStreamBDS = null; // Set to the boundary delimited stream assoc. with soap stream once found.

    /** Field boundary           */
    protected byte[] boundary = null;

    /** Field cachedSOAPEnvelope           */
    protected java.io.ByteArrayInputStream cachedSOAPEnvelope = null; // Caches the soap stream if it is

    // Still open and a reference to read data in a later attachment occurs.

    /** Field contentLocation           */
    protected String contentLocation = null;

    /** Field contentId           */
    protected String contentId = null;

    /** Field MAX_CACHED */
    private static final int MAX_CACHED = 16 * 1024;

    /**
     * Create a new Multipart stream.
     * @param contentType  the string that holds the contentType
     * @param stream       the true input stream from where the source
     *
     * @throws org.apache.axis.AxisFault if the stream could not be created
     */
    public MultiPartRelatedInputStream(String contentType, java.io.InputStream stream)
            throws org.apache.axis.AxisFault {

        super(null); // don't cache this stream.

        if (!(stream instanceof BufferedInputStream)) {
            stream = new BufferedInputStream(stream);
        }

        try {

            // First find the start and boundary parameters. There are real weird rules regard what
            // can be in real headers what needs to be escaped etc  let mail parse it.
            javax.mail.internet.ContentType ct = new javax.mail.internet.ContentType(contentType);
            String rootPartContentId = ct.getParameter("start"); // Get the root part content.

            if (rootPartContentId != null) {
                rootPartContentId = rootPartContentId.trim();

                if (rootPartContentId.startsWith("<")) {
                    rootPartContentId = rootPartContentId.substring(1);
                }

                if (rootPartContentId.endsWith(">")) {
                    rootPartContentId = rootPartContentId.substring(0, rootPartContentId.length() - 1);
                }

            }

            if (ct.getParameter("boundary") != null) {
                String boundaryStr = "--" + ct.getParameter("boundary"); // The boundary with -- add as always the case.

                // if start is null then the first attachment is the rootpart
                // First read the start boundary -- this is done with brute force since the servlet may swallow the crlf between headers.
                // after this we use the more efficient boundarydelimeted stream.  There should never be any data here anyway.
                byte[][] boundaryMarker = new byte[2][boundaryStr.length() + 2];

                IOUtils.readFully(stream, boundaryMarker[0]);

                boundary = (boundaryStr + "\r\n").getBytes("US-ASCII");

                int current = 0;

                // This just goes brute force one byte at a time to find the first boundary.
                // in most cases this just a crlf.
                for (boolean found = false; !found; ++current) {
                    if (!(found = java.util.Arrays.equals(boundaryMarker[current & 0x1], boundary))) {
                        System.arraycopy(boundaryMarker[current & 0x1], 1, boundaryMarker[(current + 1) & 0x1], 0,
                                boundaryMarker[0].length - 1);

                        if (stream.read(boundaryMarker[(current + 1) & 0x1], boundaryMarker[0].length - 1, 1) < 1) {
                            throw new org.apache.axis.AxisFault(
                                    Messages.getMessage("mimeErrorNoBoundary", new String(boundary)));
                        }
                    }
                }

                // after the first boundary each boundary will have a cr lf at the beginning since after the data in any part there
                // is a cr lf added to put the boundary at the begining of a line.
                boundaryStr = "\r\n" + boundaryStr;
                boundary = boundaryStr.getBytes("US-ASCII");
            } else {
                // Since boundary is not specified, we try to find one.
                for (boolean found = false; !found;) {
                    boundary = readLine(stream);
                    if (boundary == null)
                        throw new org.apache.axis.AxisFault(Messages.getMessage("mimeErrorNoBoundary", "--"));
                    found = boundary.length > 4 && boundary[2] == '-' && boundary[3] == '-';
                }
            }

            // create the boundary delmited stream.
            boundaryDelimitedStream = new org.apache.axis.attachments.BoundaryDelimitedStream(stream, boundary,
                    1024);

            // Now read through all potential streams until we have found the root part.
            String contentTransferEncoding = null;

            do {
                contentId = null;
                contentLocation = null;
                contentTransferEncoding = null;

                // Read this attachments headers from the stream.
                javax.mail.internet.InternetHeaders headers = new javax.mail.internet.InternetHeaders(
                        boundaryDelimitedStream);

                // Use java mail utility to read through the headers.
                contentId = headers.getHeader(HTTPConstants.HEADER_CONTENT_ID, null);

                // Clean up the headers and remove any < >
                if (contentId != null) {
                    contentId = contentId.trim();

                    if (contentId.startsWith("<")) {
                        contentId = contentId.substring(1);
                    }

                    if (contentId.endsWith(">")) {
                        contentId = contentId.substring(0, contentId.length() - 1);
                    }

                    contentId = contentId.trim();

                    //  if (!contentId.startsWith("cid:")) {
                    //      contentId =
                    //              "cid:"
                    //              + contentId;        // make sure its identified as cid
                    //  }
                }

                contentLocation = headers.getHeader(HTTPConstants.HEADER_CONTENT_LOCATION, null);

                if (contentLocation != null) {
                    contentLocation = contentLocation.trim();

                    if (contentLocation.startsWith("<")) {
                        contentLocation = contentLocation.substring(1);
                    }

                    if (contentLocation.endsWith(">")) {
                        contentLocation = contentLocation.substring(0, contentLocation.length() - 1);
                    }

                    contentLocation = contentLocation.trim();
                }

                contentType = headers.getHeader(HTTPConstants.HEADER_CONTENT_TYPE, null);

                if (contentType != null) {
                    contentType = contentType.trim();
                }

                contentTransferEncoding = headers.getHeader(HTTPConstants.HEADER_CONTENT_TRANSFER_ENCODING, null);

                if (contentTransferEncoding != null) {
                    contentTransferEncoding = contentTransferEncoding.trim();
                }

                java.io.InputStream decodedStream = boundaryDelimitedStream;

                if ((contentTransferEncoding != null) && (0 != contentTransferEncoding.length())) {
                    decodedStream = MimeUtility.decode(decodedStream, contentTransferEncoding);
                }

                if ((rootPartContentId != null) && !rootPartContentId.equals(contentId)) { // This is a part that has come in prior to the root part. Need to buffer it up.
                    javax.activation.DataHandler dh = new javax.activation.DataHandler(
                            new org.apache.axis.attachments.ManagedMemoryDataSource(decodedStream, MAX_CACHED,
                                    contentType, true));
                    AttachmentPart ap = new AttachmentPart(dh);

                    if (contentId != null) {
                        ap.setMimeHeader(HTTPConstants.HEADER_CONTENT_ID, contentId);
                    }

                    if (contentLocation != null) {
                        ap.setMimeHeader(HTTPConstants.HEADER_CONTENT_LOCATION, contentLocation);
                    }

                    for (java.util.Enumeration en = headers.getNonMatchingHeaders(
                            new String[] { HTTPConstants.HEADER_CONTENT_ID, HTTPConstants.HEADER_CONTENT_LOCATION,
                                    HTTPConstants.HEADER_CONTENT_TYPE }); en.hasMoreElements();) {
                        javax.mail.Header header = (javax.mail.Header) en.nextElement();
                        String name = header.getName();
                        String value = header.getValue();

                        if ((name != null) && (value != null)) {
                            name = name.trim();

                            if (name.length() != 0) {
                                ap.addMimeHeader(name, value);
                            }
                        }
                    }

                    addPart(contentId, contentLocation, ap);

                    boundaryDelimitedStream = boundaryDelimitedStream.getNextStream(); // Gets the next stream.
                }
            } while ((null != boundaryDelimitedStream) && (rootPartContentId != null)
                    && !rootPartContentId.equals(contentId));

            if (boundaryDelimitedStream == null) {
                throw new org.apache.axis.AxisFault(Messages.getMessage("noRoot", rootPartContentId));
            }

            soapStreamBDS = boundaryDelimitedStream;

            if ((contentTransferEncoding != null) && (0 != contentTransferEncoding.length())) {
                soapStream = MimeUtility.decode(boundaryDelimitedStream, contentTransferEncoding);
            } else {
                soapStream = boundaryDelimitedStream; // This should be the SOAP part
            }

            // Read from the input stream all attachments prior to the root part.
        } catch (javax.mail.internet.ParseException e) {
            throw new org.apache.axis.AxisFault(Messages.getMessage("mimeErrorParsing", e.getMessage()));
        } catch (java.io.IOException e) {
            throw new org.apache.axis.AxisFault(Messages.getMessage("readError", e.getMessage()));
        } catch (javax.mail.MessagingException e) {
            throw new org.apache.axis.AxisFault(Messages.getMessage("readError", e.getMessage()));
        }
    }

    //when searching for a MIME boundary it MUST be terminated with CR LF. LF alone is NOT sufficient.
    private final byte[] readLine(java.io.InputStream is) throws IOException {

        java.io.ByteArrayOutputStream input = new java.io.ByteArrayOutputStream(1024);
        int c = 0;
        input.write('\r');
        input.write('\n');

        int next = -1;
        for (; c != -1;) {
            c = -1 != next ? next : is.read();
            next = -1;
            switch (c) {
            case -1:
                break;
            case '\r':
                next = is.read();
                if (next == '\n') //found a line.
                    return input.toByteArray();
                if (next == -1)
                    return null;
                //fall through
            default:
                input.write((byte) c);
                break;
            }
        }
        //even if there is stuff in buffer if EOF then this can't be a boundary.
        return null;
    }

    public Part getAttachmentByReference(final String[] id) throws org.apache.axis.AxisFault {

        // First see if we have read it in yet.
        Part ret = null;

        for (int i = id.length - 1; (ret == null) && (i > -1); --i) {
            ret = (AttachmentPart) parts.get(id[i]);
        }

        if (null == ret) {
            ret = readTillFound(id);
        }

        log.debug(Messages.getMessage("return02", "getAttachmentByReference(\"" + id + "\"",
                ((ret == null) ? "null" : ret.toString())));

        return ret;
    }

    /**
     * Add an <code>AttachmentPart</code> together with its content and location
     * IDs.
     *
     * @param contentId     the content ID
     * @param locationId    the location ID
     * @param ap            the <code>AttachmentPart</code>
     */
    protected void addPart(String contentId, String locationId, AttachmentPart ap) {

        if ((contentId != null) && (contentId.trim().length() != 0)) {
            parts.put(contentId, ap);
        }

        if ((locationId != null) && (locationId.trim().length() != 0)) {
            parts.put(locationId, ap);
        }

        orderedParts.add(ap);
    }

    /** Field READ_ALL           */
    protected static final String[] READ_ALL = { " * \0 ".intern() }; // Shouldn't never match

    /**
     * Read all data.
     *
     * @throws org.apache.axis.AxisFault if there was a problem reading all the
     *              data
     */
    protected void readAll() throws org.apache.axis.AxisFault {
        readTillFound(READ_ALL);
    }

    public java.util.Collection getAttachments() throws org.apache.axis.AxisFault {

        readAll();

        return orderedParts;
    }

    /**
     * This will read streams in till the one that is needed is found.
     *
     * @param id id is the stream being sought.
     *
     * @return the part for the id
     *
     * @throws org.apache.axis.AxisFault
     */
    protected Part readTillFound(final String[] id) throws org.apache.axis.AxisFault {

        if (boundaryDelimitedStream == null) {
            return null; // The whole stream has been consumed already
        }

        Part ret = null;

        try {
            if (soapStreamBDS == boundaryDelimitedStream) { // Still on the SOAP stream.
                if (!eos) { // The SOAP packet has not been fully read yet. Need to store it away.
                    java.io.ByteArrayOutputStream soapdata = new java.io.ByteArrayOutputStream(1024 * 8);
                    byte[] buf = new byte[1024 * 16];
                    int byteread = 0;

                    do {
                        byteread = soapStream.read(buf);

                        if (byteread > 0) {
                            soapdata.write(buf, 0, byteread);
                        }
                    } while (byteread > -1);

                    soapdata.close();

                    soapStream = new java.io.ByteArrayInputStream(soapdata.toByteArray());
                }

                boundaryDelimitedStream = boundaryDelimitedStream.getNextStream();
            }

            // Now start searching for the data.
            if (null != boundaryDelimitedStream) {
                do {
                    String contentType = null;
                    String contentId = null;
                    String contentTransferEncoding = null;
                    String contentLocation = null;

                    // Read this attachments headers from the stream.
                    javax.mail.internet.InternetHeaders headers = new javax.mail.internet.InternetHeaders(
                            boundaryDelimitedStream);

                    contentId = headers.getHeader("Content-Id", null);

                    if (contentId != null) {
                        contentId = contentId.trim();

                        if (contentId.startsWith("<")) {
                            contentId = contentId.substring(1);
                        }

                        if (contentId.endsWith(">")) {
                            contentId = contentId.substring(0, contentId.length() - 1);
                        }

                        //   if (!contentId.startsWith("cid:")) {
                        //       contentId = "cid:" + contentId;
                        //   }

                        contentId = contentId.trim();
                    }

                    contentType = headers.getHeader(HTTPConstants.HEADER_CONTENT_TYPE, null);

                    if (contentType != null) {
                        contentType = contentType.trim();
                    }

                    contentLocation = headers.getHeader(HTTPConstants.HEADER_CONTENT_LOCATION, null);

                    if (contentLocation != null) {
                        contentLocation = contentLocation.trim();
                    }

                    contentTransferEncoding = headers.getHeader(HTTPConstants.HEADER_CONTENT_TRANSFER_ENCODING,
                            null);

                    if (contentTransferEncoding != null) {
                        contentTransferEncoding = contentTransferEncoding.trim();
                    }

                    java.io.InputStream decodedStream = boundaryDelimitedStream;

                    if ((contentTransferEncoding != null) && (0 != contentTransferEncoding.length())) {
                        decodedStream = MimeUtility.decode(decodedStream, contentTransferEncoding);
                    }

                    ManagedMemoryDataSource source = new ManagedMemoryDataSource(decodedStream,
                            ManagedMemoryDataSource.MAX_MEMORY_DISK_CACHED, contentType, true);
                    DataHandler dh = new DataHandler(source);
                    AttachmentPart ap = new AttachmentPart(dh);

                    if (contentId != null) {
                        ap.setMimeHeader(HTTPConstants.HEADER_CONTENT_ID, contentId);
                    }

                    if (contentLocation != null) {
                        ap.setMimeHeader(HTTPConstants.HEADER_CONTENT_LOCATION, contentLocation);
                    }

                    for (java.util.Enumeration en = headers.getNonMatchingHeaders(
                            new String[] { HTTPConstants.HEADER_CONTENT_ID, HTTPConstants.HEADER_CONTENT_LOCATION,
                                    HTTPConstants.HEADER_CONTENT_TYPE }); en.hasMoreElements();) {
                        javax.mail.Header header = (javax.mail.Header) en.nextElement();
                        String name = header.getName();
                        String value = header.getValue();

                        if ((name != null) && (value != null)) {
                            name = name.trim();

                            if (name.length() != 0) {
                                ap.addMimeHeader(name, value);
                            }
                        }
                    }

                    addPart(contentId, contentLocation, ap);

                    for (int i = id.length - 1; (ret == null) && (i > -1); --i) {
                        if ((contentId != null) && id[i].equals(contentId)) { // This is the part being sought
                            ret = ap;
                        } else if ((contentLocation != null) && id[i].equals(contentLocation)) {
                            ret = ap;
                        }
                    }

                    boundaryDelimitedStream = boundaryDelimitedStream.getNextStream();
                } while ((null == ret) && (null != boundaryDelimitedStream));
            }
        } catch (Exception e) {
            throw org.apache.axis.AxisFault.makeFault(e);
        }

        return ret;
    }

    public String getContentLocation() {
        return contentLocation;
    }

    public String getContentId() {
        return contentId;
    }

    public int read(byte[] b, int off, int len) throws java.io.IOException {

        if (closed) {
            throw new java.io.IOException(Messages.getMessage("streamClosed"));
        }

        if (eos) {
            return -1;
        }

        int read = soapStream.read(b, off, len);

        if (read < 0) {
            eos = true;
        }

        return read;
    }

    public int read(byte[] b) throws java.io.IOException {
        return read(b, 0, b.length);
    }

    public int read() throws java.io.IOException {

        if (closed) {
            throw new java.io.IOException(Messages.getMessage("streamClosed"));
        }

        if (eos) {
            return -1;
        }

        int ret = soapStream.read();

        if (ret < 0) {
            eos = true;
        }

        return ret;
    }

    public void close() throws java.io.IOException {

        closed = true;

        soapStream.close();
    }

    public int available() throws java.io.IOException {
        return (closed || eos) ? 0 : soapStream.available();
    }

}