You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ws.apache.org by ve...@apache.org on 2011/07/31 20:17:33 UTC

svn commit: r1152609 - in /webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments: AttachmentSet.java Attachments.java AttachmentsImpl.java MIMEBodyPartInputStream.java MIMEMessage.java

Author: veithen
Date: Sun Jul 31 18:17:31 2011
New Revision: 1152609

URL: http://svn.apache.org/viewvc?rev=1152609&view=rev
Log:
An Attachments object created from an input stream behaves very differently than an Attachments object created programmatically. Internally split the code into two different classes, keeping Attachments as a facade.

Added:
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/AttachmentSet.java   (with props)
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/AttachmentsImpl.java   (with props)
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/MIMEMessage.java   (with props)
Modified:
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/Attachments.java
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/MIMEBodyPartInputStream.java

Added: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/AttachmentSet.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/AttachmentSet.java?rev=1152609&view=auto
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/AttachmentSet.java (added)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/AttachmentSet.java Sun Jul 31 18:17:31 2011
@@ -0,0 +1,95 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.activation.DataHandler;
+import javax.mail.internet.ContentType;
+
+import org.apache.axiom.attachments.lifecycle.LifecycleManager;
+import org.apache.axiom.om.OMException;
+
+class AttachmentSet extends AttachmentsImpl {
+    private final Map attachmentsMap = new LinkedHashMap();
+
+    ContentType getContentType() {
+        return null;
+    }
+
+    LifecycleManager getLifecycleManager() {
+        return null;
+    }
+
+    void setLifecycleManager(LifecycleManager manager) {
+        // Ignore; only stream based attachments need a lifecycle manager
+    }
+
+    DataHandler getDataHandler(String contentID) {
+        return (DataHandler)attachmentsMap.get(contentID);
+    }
+
+    void addDataHandler(String contentID, DataHandler dataHandler) {
+        attachmentsMap.put(contentID, dataHandler);
+    }
+    
+    void removeDataHandler(String blobContentID) {
+        attachmentsMap.remove(blobContentID);
+    }
+
+    InputStream getSOAPPartInputStream() throws OMException {
+        throw new OMException("Invalid operation. Attachments are created programatically.");
+    }
+
+    String getSOAPPartContentID() {
+        return null;
+    }
+
+    String getSOAPPartContentType() {
+        throw new OMException(
+                "The attachments map was created programatically. Unsupported operation.");
+    }
+
+    IncomingAttachmentStreams getIncomingAttachmentStreams() {
+        throw new IllegalStateException(
+                "The attachments map was created programatically. No streams are available.");
+    }
+
+    Set getContentIDs(boolean fetchAll) {
+        return attachmentsMap.keySet();
+    }
+    
+    Map getMap() {
+        return Collections.unmodifiableMap(attachmentsMap);
+    }
+    
+    long getContentLength() throws IOException {
+        return -1;
+    }
+
+    InputStream getIncomingAttachmentsAsSingleStream() {
+        throw new IllegalStateException(
+                "The attachments map was created programatically. No streams are available.");
+    }
+}

Propchange: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/AttachmentSet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/Attachments.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/Attachments.java?rev=1152609&r1=1152608&r2=1152609&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/Attachments.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/Attachments.java Sun Jul 31 18:17:31 2011
@@ -19,109 +19,34 @@
 
 package org.apache.axiom.attachments;
 
-import org.apache.axiom.attachments.impl.PartFactory;
 import org.apache.axiom.attachments.lifecycle.LifecycleManager;
-import org.apache.axiom.attachments.lifecycle.impl.LifecycleManagerImpl;
 import org.apache.axiom.om.OMAttachmentAccessor;
 import org.apache.axiom.om.OMException;
 import org.apache.axiom.om.impl.MTOMConstants;
-import org.apache.axiom.om.util.DetachableInputStream;
-import org.apache.axiom.util.UIDGenerator;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
 
 import javax.activation.DataHandler;
-import javax.mail.MessagingException;
-import javax.mail.internet.ContentType;
-import javax.mail.internet.ParseException;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.PushbackInputStream;
-import java.io.UnsupportedEncodingException;
 import java.util.ArrayList;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Set;
 import java.util.Map;
-import java.util.Collections;
 
 public class Attachments implements OMAttachmentAccessor {
-
-    /** <code>ContentType</code> of the MIME message */
-    private final ContentType contentType;
-    
-    private final int contentLength; // Content Length
-
-    /** Mime <code>boundary</code> which separates mime parts */
-    private final byte[] boundary;
-
+    private final AttachmentsImpl impl;
+   
     /**
      * <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
      */
     private String applicationType;
 
-    /**
-     * <code>pushbackInStream</code> stores the reference to the incoming stream A PushbackStream
-     * has the ability to "push back" or "unread" one byte.
-     */
-    private final PushbackInputStream pushbackInStream;
-    private static final int PUSHBACK_SIZE = 4 * 1024;
-    private final DetachableInputStream filterIS;
-
-    /**
-     * Stores the Data Handlers of the already parsed Mime Body Parts in the order that the attachments
-     * occur in the message. This map is keyed using the content-ID's.
-     */
-    private final Map attachmentsMap = new LinkedHashMap();
-
-    /** <code>partIndex</code>- Number of Mime parts parsed */
-    private int partIndex = 0;
-
-    /** Container to hold streams for direct access */
-    private IncomingAttachmentStreams streams;
-
-    /** <code>boolean</code> Indicating if any streams have been directly requested */
-    private boolean streamsRequested;
-
-    /** <code>boolean</code> Indicating if any data handlers have been directly requested */
-    private boolean partsRequested;
-
-    /**
-     * <code>endOfStreamReached</code> flag which is to be set by MIMEBodyPartStream when MIME
-     * message terminator is found.
-     */
-    private boolean endOfStreamReached;
-
-
-    /**
-     * <code>noStreams</code> flag which is to be set when this class is instantiated by the SwA API
-     * to handle programatic added attachements. An InputStream with attachments is not present at
-     * that occation.
-     */
-    private final boolean noStreams;
-
-    private String firstPartId;
-
-    private final boolean fileCacheEnable;
-
-    private final String attachmentRepoDir;
-
-    private final int fileStorageThreshold;
-    
-    private LifecycleManager manager;
-    
-    private static final Log log = LogFactory.getLog(Attachments.class);
-   
     public LifecycleManager getLifecycleManager() {
-        if(manager == null) {
-            manager = new LifecycleManagerImpl();   
-        }
-        return manager;
+        return impl.getLifecycleManager();
     }
 
     public void setLifecycleManager(LifecycleManager manager) {
-        this.manager = manager;
+        impl.setLifecycleManager(manager);
     }
 
     /**
@@ -154,90 +79,14 @@ public class Attachments implements OMAt
      */
     public Attachments(LifecycleManager manager, InputStream inStream, String contentTypeString, boolean fileCacheEnable,
             String attachmentRepoDir, String fileThreshold, int contentLength) throws OMException {
-        this.manager = manager;
-        this.contentLength = contentLength;
-        this.attachmentRepoDir = attachmentRepoDir;
-        this.fileCacheEnable = fileCacheEnable;
-        noStreams = false;
-        if (log.isDebugEnabled()) {
-            log.debug("Attachments contentLength=" + contentLength + ", contentTypeString=" + contentTypeString);
-        }
+        int fileStorageThreshold;
         if (fileThreshold != null && (!"".equals(fileThreshold))) {
-            this.fileStorageThreshold = Integer.parseInt(fileThreshold);
+            fileStorageThreshold = Integer.parseInt(fileThreshold);
         } else {
-            this.fileStorageThreshold = 1;
-        }
-        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 "--".
-        try {
-            String boundaryParam = contentType.getParameter("boundary");
-            if (boundaryParam == null) {
-                throw new OMException("Content-type has no 'boundary' parameter");
-            }
-            // A MIME boundary only contains ASCII characters (see RFC2046)
-            this.boundary = ("--" + boundaryParam).getBytes("ascii");
-            if (log.isDebugEnabled()) {
-                log.debug("boundary=" + new String(this.boundary));
-            }
-        } catch (UnsupportedEncodingException e) {
-            throw new OMException(e);
-        }
-
-        // If the length is not known, install a TeeInputStream
-        // so that we can retrieve it later.
-        InputStream is = inStream;
-        if (contentLength <= 0) {
-            filterIS = new DetachableInputStream(inStream);
-            is = filterIS;
-        } else {
-            filterIS = null;
-        }
-        pushbackInStream = new PushbackInputStream(is,
-                                                   PUSHBACK_SIZE);
-
-        // 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 (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);
-            }
+            fileStorageThreshold = 1;
         }
-
-        // Read the SOAP part and cache it
-        getDataHandler(getSOAPPartContentID());
-
-        // Now reset partsRequested. SOAP part is a special case which is always 
-        // read beforehand, regardless of request.
-        partsRequested = false;
+        impl = new MIMEMessage(manager, inStream, contentTypeString, fileCacheEnable,
+                attachmentRepoDir, fileStorageThreshold, contentLength);
     }
 
     /**
@@ -290,15 +139,7 @@ public class Attachments implements OMAt
      * through the SwA API.
      */
     public Attachments() {
-        noStreams = true;
-        contentType = null;
-        contentLength = 0;
-        boundary = null;
-        pushbackInStream = null;
-        filterIS = null;
-        fileCacheEnable = false;
-        attachmentRepoDir = null;
-        fileStorageThreshold = 0;
+        impl = new AttachmentSet();
     }
 
     /**
@@ -312,7 +153,7 @@ public class Attachments implements OMAt
      */
     public String getAttachmentSpecType() {
         if (this.applicationType == null) {
-            applicationType = contentType.getParameter("type");
+            applicationType = impl.getContentType().getParameter("type");
             if ((MTOMConstants.MTOM_TYPE).equalsIgnoreCase(applicationType)) {
                 this.applicationType = MTOMConstants.MTOM_TYPE;
             } else if ((MTOMConstants.SWA_TYPE).equalsIgnoreCase(applicationType)) {
@@ -337,21 +178,7 @@ public class Attachments implements OMAt
      *         <code>null</code> if the MIME part referred by the content ID does not exist
      */
     public DataHandler getDataHandler(String contentID) {
-        // Check whether the MIME part is already parsed by checking the attachments HashMap. If it is
-        // not parsed yet then call the getNextPart() till the required part is found.
-        DataHandler dataHandler;
-        if (attachmentsMap.containsKey(contentID)) {
-            dataHandler = (DataHandler) attachmentsMap.get(contentID);
-            return dataHandler;
-        } else if (!noStreams) {
-            while ((dataHandler = this.getNextPartDataHandler()) != null) {
-                if (attachmentsMap.containsKey(contentID)) {
-                    dataHandler = (DataHandler) attachmentsMap.get(contentID);
-                    return dataHandler;
-                }
-            }
-        }
-        return null;
+        return impl.getDataHandler(contentID);
     }
 
     /**
@@ -362,7 +189,7 @@ public class Attachments implements OMAt
      * @param dataHandler
      */
     public void addDataHandler(String contentID, DataHandler dataHandler) {
-        attachmentsMap.put(contentID, dataHandler);
+        impl.addDataHandler(contentID, dataHandler);
     }
 
     /**
@@ -372,16 +199,7 @@ public class Attachments implements OMAt
      * @param blobContentID
      */
     public void removeDataHandler(String blobContentID) {
-        if (attachmentsMap.containsKey(blobContentID)) {
-            attachmentsMap.remove(blobContentID);
-        } else if (!noStreams) {
-            while (this.getNextPartDataHandler() != null) {
-                if (attachmentsMap.containsKey(blobContentID)) {
-                    attachmentsMap.remove(blobContentID);
-                    break;
-                }
-            }
-        }
+        impl.removeDataHandler(blobContentID);
     }
 
     /**
@@ -389,21 +207,7 @@ public class Attachments implements OMAt
      *         is always pointed by "start" parameter in content-type.
      */
     public InputStream getSOAPPartInputStream() throws OMException {
-        DataHandler dh;
-        if (noStreams) {
-            throw new OMException("Invalid operation. Attachments are created programatically.");
-        }
-        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 impl.getSOAPPartInputStream();
     }
 
     /**
@@ -418,35 +222,7 @@ public class Attachments implements OMAt
      * @return the content ID of the SOAP part (without the surrounding angle brackets)
      */
     public String getSOAPPartContentID() {
-        if(contentType == null) {
-            return null;
-        }
-        String rootContentID = contentType.getParameter("start");
-        if (log.isDebugEnabled()) {
-            log.debug("getSOAPPartContentID rootContentID=" + rootContentID);
-        }
-
-        // to handle the Start parameter not mentioned situation
-        if (rootContentID == null) {
-            if (partIndex == 0) {
-                getNextPartDataHandler();
-            }
-            rootContentID = firstPartId;
-        } else {
-            rootContentID = 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 (rootContentID.length() > 4
-                && "cid:".equalsIgnoreCase(rootContentID.substring(0, 4))) {
-            rootContentID = rootContentID.substring(4);
-        }
-        return rootContentID;
+        return impl.getSOAPPartContentID();
     }
 
     /**
@@ -457,20 +233,7 @@ public class Attachments implements OMAt
      *             if the content type could not be determined
      */
     public String getSOAPPartContentType() {
-        if (!noStreams) {
-            String soapPartContentID = getSOAPPartContentID();
-            if (soapPartContentID == null) {
-                throw new OMException("Unable to determine the content ID of the SOAP part");
-            }
-            DataHandler soapPart = getDataHandler(soapPartContentID);
-            if (soapPart == null) {
-                throw new OMException("Unable to locate the SOAP part; content ID was " + soapPartContentID);
-            }
-            return soapPart.getContentType();
-        } else {
-            throw new OMException(
-                    "The attachments map was created programatically. Unsupported operation.");
-        }
+        return impl.getSOAPPartContentType();
     }
 
     /**
@@ -481,38 +244,7 @@ public class Attachments implements OMAt
      */
     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.");
-        }
-        if (noStreams) {
-            throw new IllegalStateException(
-                    "The attachments map was created programatically. No streams are available.");
-        }
-
-        streamsRequested = true;
-
-        if (this.streams == null) {
-            BoundaryDelimitedStream boundaryDelimitedStream =
-                    new BoundaryDelimitedStream(pushbackInStream,
-                                                boundary, 1024);
-
-            this.streams = new MultipartAttachmentStreams(boundaryDelimitedStream);
-        }
-
-        return this.streams;
-    }
-
-    /**
-     * Force reading of all attachments.
-     */
-    private void fetchAllParts() {
-        if (!noStreams) {
-            while (getNextPartDataHandler() != null) {
-                // Just loop until getNextPartDataHandler returns null
-            }
-        }
+        return impl.getIncomingAttachmentStreams();
     }
 
     /**
@@ -524,8 +256,7 @@ public class Attachments implements OMAt
      * @return an array with the content IDs in order of appearance in the message
      */
     public String[] getAllContentIDs() {
-        fetchAllParts();
-        Set cids = attachmentsMap.keySet();
+        Set cids = impl.getContentIDs(true);
         return (String[]) cids.toArray(new String[cids.size()]);
     }
 
@@ -538,8 +269,7 @@ public class Attachments implements OMAt
      * @return the set of content IDs
      */
     public Set getContentIDSet() {
-        fetchAllParts();
-        return attachmentsMap.keySet();
+        return impl.getContentIDs(true);
     }
     
     /**
@@ -551,8 +281,7 @@ public class Attachments implements OMAt
      *         {@link DataHandler} objects as values.
      */
     public Map getMap() {
-        fetchAllParts();
-        return Collections.unmodifiableMap(attachmentsMap);
+        return impl.getMap();
     }
 
     /**
@@ -566,7 +295,7 @@ public class Attachments implements OMAt
      * @return List of content IDs in order of appearance in message
      */
     public List getContentIDList() {
-        return new ArrayList(attachmentsMap.keySet());
+        return new ArrayList(impl.getContentIDs(false));
     }
     
     /**
@@ -577,26 +306,7 @@ public class Attachments implements OMAt
      * backed by an InputStream
      */
     public long getContentLength() throws IOException {
-        if (contentLength > 0) {
-            return contentLength;
-        } else if (filterIS != null) {
-            // Ensure all parts are read
-            this.getContentIDSet();
-            // Now get the count from the filter
-            return filterIS.length();
-        } else {
-            return -1; // not backed by an input stream
-        }
-    }
-    
-    /**
-     * endOfStreamReached will be set to true if the message ended in MIME Style having "--" suffix
-     * with the last mime boundary
-     *
-     * @param value
-     */
-    void setEndOfStream(boolean value) {
-        this.endOfStreamReached = value;
+        return impl.getContentLength();
     }
 
     /**
@@ -604,116 +314,6 @@ public class Attachments implements OMAt
      * soappart (first attachment) with headers and mime boundary. Raw content! 
      */
     public InputStream getIncomingAttachmentsAsSingleStream() 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.");
-        }
-        if (noStreams) {
-            throw new IllegalStateException(
-                    "The attachments map was created programatically. No streams are available.");
-        }
-
-        streamsRequested = true;
-
-    	return this.pushbackInStream;
-    }
-
-    /**
-     * @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 DataHandler getNextPartDataHandler() throws OMException {
-        if (endOfStreamReached) {
-            return null;
-        } else {
-            Part nextPart = getPart();
-            try {
-                long size = nextPart.getSize();
-                String partContentID;
-                DataHandler dataHandler;
-                try {
-                    partContentID = nextPart.getContentID();
-
-                    if (partContentID == null & partIndex == 1) {
-                        String id = "firstPart_" + UIDGenerator.generateContentId();
-                        firstPartId = id;
-                        if (size > 0) {
-                            dataHandler = nextPart.getDataHandler();
-                        } else {
-                            // Either the mime part is empty or the stream ended without having 
-                            // a MIME message terminator
-                            dataHandler = new DataHandler(new ByteArrayDataSource(new byte[]{}));
-                        }
-                        addDataHandler(id, dataHandler);
-                        return dataHandler;
-                    }
-                    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));
-
-                    }
-                    if (partIndex == 1) {
-                        firstPartId = partContentID;
-                    }
-                    if (attachmentsMap.containsKey(partContentID)) {
-                        throw new OMException(
-                                "Two MIME parts with the same Content-ID not allowed.");
-                    }
-                    if (size > 0) {
-                        dataHandler = nextPart.getDataHandler();
-                    } else {
-                        // Either the mime part is empty or the stream ended without having 
-                        // a MIME message terminator
-                        dataHandler = new DataHandler(new ByteArrayDataSource(new byte[]{}));
-                    }
-                    addDataHandler(partContentID, dataHandler);
-                    return dataHandler;
-                } catch (MessagingException e) {
-                    throw new OMException("Error reading Content-ID from the Part."
-                            + e);
-                }
-            } catch (MessagingException e) {
-                throw new OMException(e);
-            }
-        }
-    }
-
-    /**
-     * @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;
-
-        boolean isSOAPPart = (partIndex == 0);
-        int threshhold = (fileCacheEnable) ? fileStorageThreshold : 0;
-
-        // Create a MIMEBodyPartInputStream that simulates a single stream for this MIME body part
-        MIMEBodyPartInputStream partStream =
-            new MIMEBodyPartInputStream(pushbackInStream,
-                                        boundary,
-                                        this,
-                                        PUSHBACK_SIZE);
-
-        // The PartFactory will determine which Part implementation is most appropriate.
-        Part part = PartFactory.createPart(getLifecycleManager(), partStream, 
-                                      isSOAPPart, 
-                                      threshhold, 
-                                      attachmentRepoDir, 
-                                      contentLength);  // content-length for the whole message
-        partIndex++;
-        return part;
+        return impl.getIncomingAttachmentsAsSingleStream();
     }
 }
\ No newline at end of file

Added: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/AttachmentsImpl.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/AttachmentsImpl.java?rev=1152609&view=auto
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/AttachmentsImpl.java (added)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/AttachmentsImpl.java Sun Jul 31 18:17:31 2011
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Set;
+
+import javax.activation.DataHandler;
+import javax.mail.internet.ContentType;
+
+import org.apache.axiom.attachments.lifecycle.LifecycleManager;
+import org.apache.axiom.om.OMException;
+
+abstract class AttachmentsImpl {
+    abstract ContentType getContentType();
+    abstract LifecycleManager getLifecycleManager();
+    abstract void setLifecycleManager(LifecycleManager manager);
+    abstract DataHandler getDataHandler(String contentID);
+    abstract void addDataHandler(String contentID, DataHandler dataHandler);
+    abstract void removeDataHandler(String blobContentID);
+    abstract InputStream getSOAPPartInputStream() throws OMException;
+    abstract String getSOAPPartContentID();
+    abstract String getSOAPPartContentType();
+    abstract IncomingAttachmentStreams getIncomingAttachmentStreams();
+    abstract Set getContentIDs(boolean fetchAll);
+    abstract Map getMap();
+    abstract long getContentLength() throws IOException;
+    abstract InputStream getIncomingAttachmentsAsSingleStream();
+}

Propchange: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/AttachmentsImpl.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/MIMEBodyPartInputStream.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/MIMEBodyPartInputStream.java?rev=1152609&r1=1152608&r2=1152609&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/MIMEBodyPartInputStream.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/MIMEBodyPartInputStream.java Sun Jul 31 18:17:31 2011
@@ -30,7 +30,7 @@ import java.io.PushbackInputStream;
 public class MIMEBodyPartInputStream extends InputStream {
     BoundaryPushbackInputStream bpis;
     PushbackInputStream inStream;
-    Attachments parent = null;
+    MIMEMessage parent = null;
     boolean done = false;
 
     /**
@@ -46,7 +46,7 @@ public class MIMEBodyPartInputStream ext
      * @param boundary
      * @param parent
      */
-    public MIMEBodyPartInputStream(PushbackInputStream inStream, byte[] boundary, Attachments parent) {
+    public MIMEBodyPartInputStream(PushbackInputStream inStream, byte[] boundary, MIMEMessage parent) {
         this (inStream, boundary, parent, boundary.length + 2);
     }
     
@@ -57,7 +57,7 @@ public class MIMEBodyPartInputStream ext
      * @param pushbacksize <= size of pushback buffer on inStream
      */
     public MIMEBodyPartInputStream(PushbackInputStream inStream,
-            byte[] boundary, Attachments parent, int pushbacksize) {
+            byte[] boundary, MIMEMessage parent, int pushbacksize) {
         bpis = new BoundaryPushbackInputStream(inStream, boundary, pushbacksize);
         this.inStream = inStream;
         this.parent = parent;

Added: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/MIMEMessage.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/MIMEMessage.java?rev=1152609&view=auto
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/MIMEMessage.java (added)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/MIMEMessage.java Sun Jul 31 18:17:31 2011
@@ -0,0 +1,444 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+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.attachments.impl.PartFactory;
+import org.apache.axiom.attachments.lifecycle.LifecycleManager;
+import org.apache.axiom.attachments.lifecycle.impl.LifecycleManagerImpl;
+import org.apache.axiom.om.OMException;
+import org.apache.axiom.om.util.DetachableInputStream;
+import org.apache.axiom.util.UIDGenerator;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+class MIMEMessage extends AttachmentsImpl {
+    private static final Log log = LogFactory.getLog(MIMEMessage.class);
+
+    /** <code>ContentType</code> of the MIME message */
+    private final ContentType contentType;
+    
+    private final int contentLength; // Content Length
+
+    /** Mime <code>boundary</code> which separates mime parts */
+    private final byte[] boundary;
+
+    /**
+     * <code>pushbackInStream</code> stores the reference to the incoming stream A PushbackStream
+     * has the ability to "push back" or "unread" one byte.
+     */
+    private final PushbackInputStream pushbackInStream;
+    private static final int PUSHBACK_SIZE = 4 * 1024;
+    private final DetachableInputStream filterIS;
+
+    /**
+     * Stores the Data Handlers of the already parsed Mime Body Parts in the order that the attachments
+     * occur in the message. This map is keyed using the content-ID's.
+     */
+    private final Map attachmentsMap = new LinkedHashMap();
+
+    /** <code>partIndex</code>- Number of Mime parts parsed */
+    private int partIndex = 0;
+
+    /** Container to hold streams for direct access */
+    private IncomingAttachmentStreams streams;
+
+    /** <code>boolean</code> Indicating if any streams have been directly requested */
+    private boolean streamsRequested;
+
+    /** <code>boolean</code> Indicating if any data handlers have been directly requested */
+    private boolean partsRequested;
+
+    /**
+     * <code>endOfStreamReached</code> flag which is to be set by MIMEBodyPartStream when MIME
+     * message terminator is found.
+     */
+    private boolean endOfStreamReached;
+
+    private String firstPartId;
+
+    private final boolean fileCacheEnable;
+
+    private final String attachmentRepoDir;
+
+    private final int fileStorageThreshold;
+    
+    private LifecycleManager manager;
+    
+    MIMEMessage(LifecycleManager manager, InputStream inStream, String contentTypeString, boolean fileCacheEnable,
+            String attachmentRepoDir, int fileStorageThreshold, int contentLength) throws OMException {
+        this.manager = manager;
+        this.contentLength = contentLength;
+        this.attachmentRepoDir = attachmentRepoDir;
+        this.fileCacheEnable = fileCacheEnable;
+        if (log.isDebugEnabled()) {
+            log.debug("Attachments contentLength=" + contentLength + ", contentTypeString=" + contentTypeString);
+        }
+        this.fileStorageThreshold = fileStorageThreshold;
+        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 "--".
+        try {
+            String boundaryParam = contentType.getParameter("boundary");
+            if (boundaryParam == null) {
+                throw new OMException("Content-type has no 'boundary' parameter");
+            }
+            // A MIME boundary only contains ASCII characters (see RFC2046)
+            this.boundary = ("--" + boundaryParam).getBytes("ascii");
+            if (log.isDebugEnabled()) {
+                log.debug("boundary=" + new String(this.boundary));
+            }
+        } catch (UnsupportedEncodingException e) {
+            throw new OMException(e);
+        }
+
+        // If the length is not known, install a TeeInputStream
+        // so that we can retrieve it later.
+        InputStream is = inStream;
+        if (contentLength <= 0) {
+            filterIS = new DetachableInputStream(inStream);
+            is = filterIS;
+        } else {
+            filterIS = null;
+        }
+        pushbackInStream = new PushbackInputStream(is,
+                                                   PUSHBACK_SIZE);
+
+        // 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 (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
+        getDataHandler(getSOAPPartContentID());
+
+        // Now reset partsRequested. SOAP part is a special case which is always 
+        // read beforehand, regardless of request.
+        partsRequested = false;
+    }
+
+    ContentType getContentType() {
+        return contentType;
+    }
+
+    LifecycleManager getLifecycleManager() {
+        if(manager == null) {
+            manager = new LifecycleManagerImpl();   
+        }
+        return manager;
+    }
+
+    void setLifecycleManager(LifecycleManager manager) {
+        this.manager = manager;
+    }
+
+    DataHandler getDataHandler(String contentID) {
+        do {
+            DataHandler dataHandler = (DataHandler)attachmentsMap.get(contentID);
+            if (dataHandler != null) {
+                return dataHandler;
+            }
+        } while (getNextPartDataHandler() != null);
+        return null;
+    }
+
+    void addDataHandler(String contentID, DataHandler dataHandler) {
+        attachmentsMap.put(contentID, dataHandler);
+    }
+    
+    void removeDataHandler(String blobContentID) {
+        do {
+            if (attachmentsMap.remove(blobContentID) != null) {
+                return;
+            }
+        } while (getNextPartDataHandler() != null);
+    }
+
+    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);
+        }
+    }
+
+    String getSOAPPartContentID() {
+        String rootContentID = contentType.getParameter("start");
+        if (log.isDebugEnabled()) {
+            log.debug("getSOAPPartContentID rootContentID=" + rootContentID);
+        }
+
+        // to handle the Start parameter not mentioned situation
+        if (rootContentID == null) {
+            if (partIndex == 0) {
+                getNextPartDataHandler();
+            }
+            rootContentID = firstPartId;
+        } else {
+            rootContentID = 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 (rootContentID.length() > 4
+                && "cid:".equalsIgnoreCase(rootContentID.substring(0, 4))) {
+            rootContentID = rootContentID.substring(4);
+        }
+        return rootContentID;
+    }
+    
+    String getSOAPPartContentType() {
+        String soapPartContentID = getSOAPPartContentID();
+        if (soapPartContentID == null) {
+            throw new OMException("Unable to determine the content ID of the SOAP part");
+        }
+        DataHandler soapPart = getDataHandler(soapPartContentID);
+        if (soapPart == null) {
+            throw new OMException("Unable to locate the SOAP part; content ID was " + soapPartContentID);
+        }
+        return soapPart.getContentType();
+    }
+    
+    IncomingAttachmentStreams getIncomingAttachmentStreams() {
+        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;
+    }
+
+    /**
+     * Force reading of all attachments.
+     */
+    private void fetchAllParts() {
+        while (getNextPartDataHandler() != null) {
+            // Just loop until getNextPartDataHandler returns null
+        }
+    }
+
+    Set getContentIDs(boolean fetchAll) {
+        if (fetchAll) {
+            fetchAllParts();
+        }
+        return attachmentsMap.keySet();
+    }
+    
+    Map getMap() {
+        fetchAllParts();
+        return Collections.unmodifiableMap(attachmentsMap);
+    }
+    
+    long getContentLength() throws IOException {
+        if (contentLength > 0) {
+            return contentLength;
+        } else {
+            // Ensure all parts are read
+            fetchAllParts();
+            // Now get the count from the filter
+            return filterIS.length();
+        }
+    }
+    
+    /**
+     * endOfStreamReached will be set to true if the message ended in MIME Style having "--" suffix
+     * with the last mime boundary
+     *
+     * @param value
+     */
+    void setEndOfStream(boolean value) {
+        this.endOfStreamReached = value;
+    }
+    
+    InputStream getIncomingAttachmentsAsSingleStream() {
+        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;
+
+        return this.pushbackInStream;
+    }
+    
+    /**
+     * @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 DataHandler getNextPartDataHandler() throws OMException {
+        if (endOfStreamReached) {
+            return null;
+        } else {
+            Part nextPart = getPart();
+            try {
+                long size = nextPart.getSize();
+                String partContentID;
+                DataHandler dataHandler;
+                try {
+                    partContentID = nextPart.getContentID();
+
+                    if (partContentID == null & partIndex == 1) {
+                        String id = "firstPart_" + UIDGenerator.generateContentId();
+                        firstPartId = id;
+                        if (size > 0) {
+                            dataHandler = nextPart.getDataHandler();
+                        } else {
+                            // Either the mime part is empty or the stream ended without having 
+                            // a MIME message terminator
+                            dataHandler = new DataHandler(new ByteArrayDataSource(new byte[]{}));
+                        }
+                        addDataHandler(id, dataHandler);
+                        return dataHandler;
+                    }
+                    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));
+
+                    }
+                    if (partIndex == 1) {
+                        firstPartId = partContentID;
+                    }
+                    if (attachmentsMap.containsKey(partContentID)) {
+                        throw new OMException(
+                                "Two MIME parts with the same Content-ID not allowed.");
+                    }
+                    if (size > 0) {
+                        dataHandler = nextPart.getDataHandler();
+                    } else {
+                        // Either the mime part is empty or the stream ended without having 
+                        // a MIME message terminator
+                        dataHandler = new DataHandler(new ByteArrayDataSource(new byte[]{}));
+                    }
+                    addDataHandler(partContentID, dataHandler);
+                    return dataHandler;
+                } catch (MessagingException e) {
+                    throw new OMException("Error reading Content-ID from the Part."
+                            + e);
+                }
+            } catch (MessagingException e) {
+                throw new OMException(e);
+            }
+        }
+    }
+
+    /**
+     * @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;
+
+        boolean isSOAPPart = (partIndex == 0);
+        int threshhold = (fileCacheEnable) ? fileStorageThreshold : 0;
+
+        // Create a MIMEBodyPartInputStream that simulates a single stream for this MIME body part
+        MIMEBodyPartInputStream partStream =
+            new MIMEBodyPartInputStream(pushbackInStream,
+                                        boundary,
+                                        this,
+                                        PUSHBACK_SIZE);
+
+        // The PartFactory will determine which Part implementation is most appropriate.
+        Part part = PartFactory.createPart(getLifecycleManager(), partStream, 
+                                      isSOAPPart, 
+                                      threshhold, 
+                                      attachmentRepoDir, 
+                                      contentLength);  // content-length for the whole message
+        partIndex++;
+        return part;
+    }
+}

Propchange: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/MIMEMessage.java
------------------------------------------------------------------------------
    svn:eol-style = native