Package org.apache.axiom.attachments

Source Code of org.apache.axiom.attachments.Attachments

/*
* Copyright 2004,2005 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.axiom.attachments;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;
import java.util.HashMap;
import java.util.Set;

import javax.activation.DataHandler;
import javax.mail.MessagingException;
import javax.mail.internet.ContentType;
import javax.mail.internet.ParseException;

import org.apache.axiom.om.OMException;
import org.apache.axiom.om.impl.MTOMConstants;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class Attachments {

    /**
     * <code>ContentType</code> of the MIME message
     */
    ContentType contentType;

    /**
     * Mime <code>boundary</code> which separates mime parts
     */
    byte[] boundary;

    /**
     * <code>applicationType</code> used to distinguish between MTOM & SWA If
     * the message is MTOM optimised type is application/xop+xml If the message
     * is SWA, type is ??have to find out
     */
    String applicationType;

    /**
     * <code>pushbackInStream</code> stores the reference to the incoming
     * stream A PushbackStream has the ability to "push back" or "unread" one
     * byte.
     */
    PushbackInputStream pushbackInStream;

    /**
     * <code>mimeBodyPartsMap</code> stores the already parsed Mime Body
     * Parts. This Map will be keyed using the content-ID's
     */
    HashMap bodyPartsMap;
   
    /**
     * <code>partIndex</code>- Number of Mime parts parsed
     */
    int partIndex = 0;
   
    /** Container to hold streams for direct access */
    IncomingAttachmentStreams streams = null;
   
    /** <code>boolean</code> Indicating if any streams have been directly requested */
    boolean streamsRequested = false;
   
    /** <code>boolean</code> Indicating if any data handlers have been directly requested */
    boolean partsRequested = false;

    /**
     * <code>endOfStreamReached</code> flag which is to be set by
     * MIMEBodyPartStream when MIME message terminator is found.
     */
    boolean endOfStreamReached;

    String firstPartId;

    boolean fileCacheEnable;

    String attachmentRepoDir;

    int fileStorageThreshold;

    protected Log log = LogFactory.getLog(getClass());


    /**
     * Moves the pointer to the beginning of the first MIME part. Reads
     * till first MIME boundary is found or end of stream is reached.
     *
     * @param inStream
     * @param contentTypeString
     * @param fileCacheEnable
     * @param attachmentRepoDir
     * @throws OMException
     */
    public Attachments(InputStream inStream, String contentTypeString,
                       boolean fileCacheEnable, String attachmentRepoDir,
                       String fileThreshold) throws OMException {
        this.attachmentRepoDir = attachmentRepoDir;
        this.fileCacheEnable = fileCacheEnable;
        if (fileThreshold != null && (!"".equals(fileThreshold))) {
            this.fileStorageThreshold = Integer.parseInt(fileThreshold);
        } else {
            this.fileStorageThreshold = 1;
        }
        bodyPartsMap = new HashMap();
        try {
            contentType = new ContentType(contentTypeString);
        } catch (ParseException e) {
            throw new OMException(
                    "Invalid Content Type Field in the Mime Message"
                    , e);
        }
        // Boundary always have the prefix "--".
        this.boundary = ("--" + contentType.getParameter("boundary"))
                .getBytes();

        // do we need to wrap InputStream from a BufferedInputStream before
        // wrapping from PushbackStream
        pushbackInStream = new PushbackInputStream(inStream,
                                                   (this.boundary.length + 2));

        // Move the read pointer to the beginning of the first part
        // read till the end of first boundary
        while (true) {
            int value;
            try {
                value = pushbackInStream.read();
                if ((byte) value == boundary[0]) {
                    int boundaryIndex = 0;
                    while ((boundaryIndex < boundary.length)
                           && ((byte) value == boundary[boundaryIndex])) {
                        value = pushbackInStream.read();
                        if (value == -1) {
                            throw new OMException(
                                    "Unexpected End of Stream while searching for first Mime Boundary");
                        }
                        boundaryIndex++;
                    }
                    if (boundaryIndex == boundary.length) { // boundary found
                        pushbackInStream.read();
                        break;
                    }
                } else if ((byte) value == -1) {
                    throw new OMException(
                            "Mime parts not found. Stream ended while searching for the boundary");
                }
            } catch (IOException e1) {
                throw new OMException("Stream Error" + e1.toString(), e1);
            }
        }
       
        // Read the SOAP part and cache it
        getPart(getSOAPPartContentID());
       
        // Now reset partsRequested. SOAP part is a special case which is always
        // read beforehand, regardless of request.
        partsRequested = false;
    }

    /**
     * Sets file cache to false.
     *
     * @param inStream
     * @param contentTypeString
     * @throws OMException
     */
    public Attachments(InputStream inStream, String contentTypeString)
            throws OMException {
        this(inStream, contentTypeString, false, null, null);
    }

    /**
     * @return whether Message Type is SOAP with Attachments or MTOM optimized,
     *         by checking the application type parameter in the Content Type.
     */
    public String getAttachmentSpecType() {
        if (this.applicationType == null) {
            applicationType = contentType.getParameter("type");
            if ((MTOMConstants.MTOM_TYPE).equalsIgnoreCase(applicationType)) {
                this.applicationType = MTOMConstants.MTOM_TYPE;
            } else if ((MTOMConstants.SWA_TYPE).equalsIgnoreCase(applicationType)) {
                this.applicationType = MTOMConstants.SWA_TYPE;
            } else {
                throw new OMException(
                        "Invalid Application type. Support available for MTOM/SOAP 1.2 & SwA/SOAP 1.l only.");
            }
        }
        return this.applicationType;
    }

    /**
     * @return the InputStream which includes the SOAP Envelope. It assumes that
     *         the root mime part is always pointed by "start" parameter in
     *         content-type.
     */
    public InputStream getSOAPPartInputStream() throws OMException {
        DataHandler dh;
        try {
            dh = getDataHandler(getSOAPPartContentID());
            if (dh == null) {
                throw new OMException(
                        "Mandatory Root MIME part containing the SOAP Envelope is missing");
            }
            return dh.getInputStream();
        } catch (IOException e) {
            throw new OMException(
                    "Problem with DataHandler of the Root Mime Part. ", e);
        }
    }

    /**
     * @return the Content-ID of the SOAP part It'll be the value Start
     *         Parameter of Content-Type header if given in the Content type of
     *         the MIME message. Else it'll be the content-id of the first MIME
     *         part of the MIME message
     */
    private String getSOAPPartContentID() {
        String rootContentID = contentType.getParameter("start");

        // to handle the Start parameter not mentioned situation
        if (rootContentID == null) {
            if (partIndex == 0) {
                getNextPart();
            }
            rootContentID = firstPartId;
        } else {
            rootContentID.trim();

            if ((rootContentID.indexOf("<") > -1)
                & (rootContentID.indexOf(">") > -1)) {
                rootContentID = rootContentID.substring(1, (rootContentID
                        .length() - 1));
            }
        }
        // Strips off the "cid" part from content-id
        if ("cid".equalsIgnoreCase(rootContentID.substring(0, 3))) {
            rootContentID = rootContentID.substring(4);
        }
        return rootContentID;
    }

    public String getSOAPPartContentType() {
        Part soapPart = getPart(getSOAPPartContentID());
        try {
            return soapPart.getContentType();
        } catch (MessagingException e) {
            log.error(e.getMessage());
            throw new OMException(e);
        }
    }

    /**
     * @param blobContentID (without the surrounding angle brackets and "cid:" prefix)
     * @return The DataHandler of the mime part referred by the Content-Id
     * @throws OMException
     */
    public DataHandler getDataHandler(String blobContentID) throws OMException, IllegalStateException {
     
        try {
            return getPart(blobContentID).getDataHandler();
        } catch (MessagingException e) {
            throw new OMException("Problem with Mime Body Part No " + partIndex
                                  + ".  ", e);
        }

    }

    /**
     * Stream based access
     *
     * @return The stream container of type <code>IncomingAttachmentStreams</code>
     * @throws IllegalStateException if application has alreadt started using Part's directly
     */
    public IncomingAttachmentStreams getIncomingAttachmentStreams() throws IllegalStateException {
      if (partsRequested) {
        throw new IllegalStateException("The attachments stream can only be accessed once; either by using the IncomingAttachmentStreams class or by getting a collection of AttachmentPart objects. They cannot both be called within the life time of the same service request.");
      }

      streamsRequested = true;

      if (this.streams == null) {
           BoundaryDelimitedStream boundaryDelimitedStream =
                new BoundaryDelimitedStream(pushbackInStream,
                        boundary, 1024);

           this.streams = new MultipartAttachmentStreams(boundaryDelimitedStream);
      }

      return this.streams;
    }
   
    /**
     * Checks whether the MIME part is already parsed by checking the
     * parts HashMap. If it is not parsed yet then call the getNextPart()
     * till the required part is found.
     *
     * @param blobContentID
     * @return The Part referred by the Content-Id
     * @throws OMException
     */
    public Part getPart(String blobContentID) {
        Part bodyPart;
        if (bodyPartsMap.containsKey(blobContentID)) {
            bodyPart = (Part) bodyPartsMap.get(blobContentID);
            return bodyPart;
        } else {
            //This loop will be terminated by the Exceptions thrown if the Mime
            // part searching was not found
            while (true) {
                bodyPart = this.getNextPart();
                if (bodyPart == null) {
                    return null;
                }
                if (bodyPartsMap.containsKey(blobContentID)) {
                    bodyPart = (Part) bodyPartsMap.get(blobContentID);
                    return bodyPart;
                }
            }
        }
    }

    public String[] getAllContentIDs() {
        Part bodyPart;
        while (true) {
            bodyPart = this.getNextPart();
            if (bodyPart == null) {
                break;
            }
        }
        Set keys = bodyPartsMap.keySet();
        return (String[]) keys.toArray(new String[keys.size()]);
    }

    protected void setEndOfStream(boolean value) {
        this.endOfStreamReached = value;
    }

    /**
     * @return the Next valid MIME part + store the Part in the Parts List
     * @throws OMException throw if content id is null or if two MIME parts contain the
     *                     same content-ID & the exceptions throws by getPart()
     */
    private Part getNextPart() throws OMException {
        Part nextPart;
        nextPart = getPart();
        if (nextPart != null) {
            String partContentID;
            try {
                partContentID = nextPart.getContentID();

                if (partContentID == null & partIndex == 1) {
                    bodyPartsMap.put("firstPart", nextPart);
                    firstPartId = "firstPart";
                    return nextPart;
                }
                if (partContentID == null) {
                    throw new OMException(
                            "Part content ID cannot be blank for non root MIME parts");
                }
                if ((partContentID.indexOf("<") > -1)
                    & (partContentID.indexOf(">") > -1)) {
                    partContentID = partContentID.substring(1, (partContentID
                            .length() - 1));

                } else if (partIndex == 1) {
                    firstPartId = partContentID;
                }
                if (bodyPartsMap.containsKey(partContentID)) {
                    throw new OMException(
                            "Two MIME parts with the same Content-ID not allowed.");
                }
                bodyPartsMap.put(partContentID, nextPart);
                return nextPart;
            } catch (MessagingException e) {
                throw new OMException("Error reading Content-ID from the Part."
                                      + e);
            }
        } else {
            return null;
        }
    }

    /**
     * @return This will return the next available MIME part in the stream.
     * @throws OMException if Stream ends while reading the next part...
     */
    private Part getPart() throws OMException {

      if (streamsRequested) {
        throw new IllegalStateException("The attachments stream can only be accessed once; either by using the IncomingAttachmentStreams class or by getting a collection of AttachmentPart objects. They cannot both be called within the life time of the same service request.");
      }
     
      partsRequested = true;
     
        // endOfStreamReached will be set to true if the message ended in MIME
        // Style having "--" suffix with the last mime boundary
        if (endOfStreamReached) {
            throw new OMException(
                    "Referenced MIME part not found.End of Stream reached.");
        }

        Part part = null;

        try {
            if (fileCacheEnable) {
                try {
                    MIMEBodyPartInputStream partStream;
                    byte[] buffer = new byte[fileStorageThreshold];
                    partStream = new MIMEBodyPartInputStream(pushbackInStream,
                                                             boundary, this);
                    int count = 0;
                    int value;
                    // Make sure not to modify this to a Short Circuit "&". If
                    // removed a byte will be lost
                    while (count != fileStorageThreshold
                           && (!partStream.getBoundaryStatus())) {
                        value = partStream.read();
                        buffer[count] = (byte) value;
                        count++;
                    }
                    if (count == fileStorageThreshold) {
                        PushbackFilePartInputStream filePartStream = new PushbackFilePartInputStream(
                                partStream, buffer);
                        part = new PartOnFile(filePartStream, attachmentRepoDir);
                    } else {
                        ByteArrayInputStream byteArrayInStream = new ByteArrayInputStream(
                                buffer, 0, count - 1);
                        part = new PartOnMemory(byteArrayInStream);
                    }
                } catch (Exception e) {
                    throw new OMException("Error creating temporary File.", e);
                }
            } else {
                MIMEBodyPartInputStream partStream;
                partStream = new MIMEBodyPartInputStream(pushbackInStream,
                                                         boundary, this);
                part = new PartOnMemory(partStream);
            }
           
            // This will take care if stream ended without having MIME
            // message terminator
            if (part.getSize() <= 0) {
                throw new OMException(
                        "Referenced MIME part not found.End of Stream reached.");
            }

        } catch (MessagingException e) {
            throw new OMException(e);
        }
        partIndex++;
        return part;
    }
}
TOP

Related Classes of org.apache.axiom.attachments.Attachments

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.