You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commons-dev@ws.apache.org by sc...@apache.org on 2007/07/11 21:51:55 UTC

svn commit: r555379 - /webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/Attachments.java

Author: scheu
Date: Wed Jul 11 12:51:52 2007
New Revision: 555379

URL: http://svn.apache.org/viewvc?view=rev&rev=555379
Log:
JIRA AXIS2-2946
Contributor: Rich Scheuerle
Added code to use the message content-length as a heuristic to improve attachment performance.
The content-length is used instead of building large internal byte buffers.
Also added a method to the Attachments class to get the attachments in order.
This can be useful in SWA "raw" situations.  In such scenarios the xml does not have a reference cid
to the attachment.  Thus the receiver may wish to view the attachments in the order that they appeared
on the wire.  One use case is JAX-WS RPC/LIT support with SWA attachments.

Modified:
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/Attachments.java

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?view=diff&rev=555379&r1=555378&r2=555379
==============================================================================
--- 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 Wed Jul 11 12:51:52 2007
@@ -1,529 +1,609 @@
-/*
- * 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 org.apache.axiom.om.OMException;
-import org.apache.axiom.om.impl.MTOMConstants;
-import org.apache.axiom.om.util.UUIDGenerator;
-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.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PushbackInputStream;
-import java.io.UnsupportedEncodingException;
-import java.util.Set;
-import java.util.TreeMap;
-
-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;
-    int PUSHBACK_SIZE = 1000;
-
-    /**
-     * <code>attachmentsMap</code> stores the Data Handlers of the already parsed Mime Body Parts.
-     * This ordered Map is keyed using the content-ID's.
-     */
-    TreeMap attachmentsMap;
-
-    /** <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 */
-    private boolean streamsRequested = false;
-
-    /** <code>boolean</code> Indicating if any data handlers have been directly requested */
-    private boolean partsRequested = false;
-
-    /**
-     * <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 boolean noStreams = false;
-
-    private String firstPartId;
-
-    private boolean fileCacheEnable;
-
-    private String attachmentRepoDir;
-
-    private int fileStorageThreshold;
-
-    protected static Log log = LogFactory.getLog(Attachments.class);
-
-
-    /**
-     * 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;
-        }
-        attachmentsMap = new TreeMap();
-        try {
-            contentType = new ContentType(contentTypeString);
-        } catch (ParseException e) {
-            throw new OMException(
-                    "Invalid Content Type Field in the Mime Message"
-                    , e);
-        }
-        // REVIEW: This conversion is hard-coded to UTF-8.
-        // The complete solution is to respect the charset setting of the message.
-        // However this may cause problems in BoundaryDelimittedStream and other
-        // lower level classes.
-
-        // Boundary always have the prefix "--".
-        try {
-            String encoding = contentType.getParameter("charset");
-            if(encoding == null || encoding.length()==0){
-                encoding = "UTF-8";
-            }
-            this.boundary = ("--" + contentType.getParameter("boundary"))
-                    .getBytes(encoding);
-        } catch (UnsupportedEncodingException e) {
-            throw new OMException(e);
-        }
-
-        // do we need to wrap InputStream from a BufferedInputStream before
-        // wrapping from PushbackStream
-        pushbackInStream = new PushbackInputStream(inStream,
-                                                   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 ((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
-        getDataHandler(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);
-    }
-
-    /**
-     * Use this constructor when instantiating this to store the attachments set programatically
-     * through the SwA API.
-     */
-    public Attachments() {
-        attachmentsMap = new TreeMap();
-        noStreams = true;
-    }
-
-    /**
-     * @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 if ((MTOMConstants.SWA_TYPE_12).equalsIgnoreCase(applicationType)) {
-                this.applicationType = MTOMConstants.SWA_TYPE_12;
-            } else {
-                throw new OMException(
-                        "Invalid Application type. Support available for MTOM & SwA only.");
-            }
-        }
-        return this.applicationType;
-    }
-
-    /**
-     * Checks 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.
-     *
-     * @param blobContentID (without the surrounding angle brackets and "cid:" prefix)
-     * @return The DataHandler of the mime part referred by the Content-Id or *null* if the mime
-     *         part referred by the content-id does not exist
-     */
-    public DataHandler getDataHandler(String blobContentID) {
-        DataHandler dataHandler;
-        if (attachmentsMap.containsKey(blobContentID)) {
-            dataHandler = (DataHandler) attachmentsMap.get(blobContentID);
-            return dataHandler;
-        } else if (!noStreams) {
-            //This loop will be terminated by the Exceptions thrown if the Mime
-            // part searching was not found
-            while ((dataHandler = this.getNextPartDataHandler()) != null) {
-                if (attachmentsMap.containsKey(blobContentID)) {
-                    dataHandler = (DataHandler) attachmentsMap.get(blobContentID);
-                    return dataHandler;
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Programatically adding an SOAP with Attachments(SwA) Attachment. These attachments will get
-     * serialized only if SOAP with Attachments is enabled.
-     *
-     * @param contentID
-     * @param dataHandler
-     */
-    public void addDataHandler(String contentID, DataHandler dataHandler) {
-        attachmentsMap.put(contentID, dataHandler);
-    }
-
-    /**
-     * Removes the DataHandler corresponding to the given contenID. If it is not present, then
-     * trying to find it calling the getNextPart() till the required part is found.
-     *
-     * @param blobContentID
-     */
-    public void removeDataHandler(String blobContentID) {
-        DataHandler dataHandler;
-        if (attachmentsMap.containsKey(blobContentID)) {
-            attachmentsMap.remove(blobContentID);
-        } else if (!noStreams) {
-            //This loop will be terminated by the Exceptions thrown if the Mime
-            // part searching was not found
-            while ((dataHandler = this.getNextPartDataHandler()) != null) {
-                if (attachmentsMap.containsKey(blobContentID)) {
-                    attachmentsMap.remove(blobContentID);
-                }
-            }
-        }
-    }
-
-    /**
-     * @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;
-        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 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
-     */
-    public String getSOAPPartContentID() {
-        String rootContentID = contentType.getParameter("start");
-
-        // 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 ("cid".equalsIgnoreCase(rootContentID.substring(0, 3))) {
-            rootContentID = rootContentID.substring(4);
-        }
-        return rootContentID;
-    }
-
-    public String getSOAPPartContentType() {
-        if (!noStreams) {
-            DataHandler soapPart = getDataHandler(getSOAPPartContentID());
-            return soapPart.getContentType();
-        } else {
-            throw new OMException(
-                    "The attachments map was created programatically. Unsupported operation.");
-        }
-    }
-
-    /**
-     * 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.");
-        }
-        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;
-    }
-
-    public String[] getAllContentIDs() {
-        Set keys = getContentIDSet();
-        return (String[]) keys.toArray(new String[keys.size()]);
-    }
-
-    public Set getContentIDSet() {
-        DataHandler dataHandler;
-        while (!noStreams) {
-            dataHandler = this.getNextPartDataHandler();
-            if (dataHandler == null) {
-                break;
-            }
-        }
-        return attachmentsMap.keySet();
-    }
-
-    /**
-     * endOfStreamReached will be set to true if the message ended in MIME Style having "--" suffix
-     * with the last mime boundary
-     *
-     * @param value
-     */
-    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 DataHandler getNextPartDataHandler() throws OMException {
-        if (endOfStreamReached) {
-            return null;
-        }
-        Part nextPart;
-        nextPart = getPart();
-        if (nextPart == null) {
-            return null;
-        } else
-            try {
-                if (nextPart.getSize() > 0) {
-                    String partContentID;
-                    try {
-                        partContentID = nextPart.getContentID();
-
-                        if (partContentID == null & partIndex == 1) {
-                            String id = "firstPart_" + UUIDGenerator.getUUID();
-                            attachmentsMap.put(id, nextPart.getDataHandler());
-                            firstPartId = id;
-                            return nextPart.getDataHandler();
-                        }
-                        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 (attachmentsMap.containsKey(partContentID)) {
-                            throw new OMException(
-                                    "Two MIME parts with the same Content-ID not allowed.");
-                        }
-                        attachmentsMap.put(partContentID, nextPart.getDataHandler());
-                        return nextPart.getDataHandler();
-                    } catch (MessagingException e) {
-                        throw new OMException("Error reading Content-ID from the Part."
-                                + e);
-                    }
-                } // This will take care if stream ended without having MIME
-                // message terminator
-                else {
-                    return null;
-                }
-            } 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;
-
-        Part part;
-
-        try {
-            if (fileCacheEnable) {
-                try {
-                    MIMEBodyPartInputStream partStream;
-                    byte[] buffer = new byte[fileStorageThreshold];
-                    partStream = new MIMEBodyPartInputStream(pushbackInStream,
-                                                             boundary, this, PUSHBACK_SIZE);
-                    int count = 0;
-                    do {
-                        int len;
-                        int off = 0;
-                        int rem = fileStorageThreshold;
-                        while ((len = partStream.read(buffer, off, rem)) > 0) {
-                            off = off + len;
-                            rem = rem - len;
-                        }
-                        count += off;
-                    } while (partStream.available() > 0);
-
-                    if (count == fileStorageThreshold) {
-                        PushbackFilePartInputStream filePartStream =
-                                new PushbackFilePartInputStream(
-                                        partStream, buffer);
-                        part = new PartOnFile(filePartStream, attachmentRepoDir);
-                    } else {
-                        ByteArrayInputStream byteArrayInStream = new ByteArrayInputStream(buffer,
-                                                                                          0, count);
-                        part = new PartOnMemory(byteArrayInStream);
-                    }
-                } catch (Exception e) {
-                    throw new OMException("Error creating temporary File.", e);
-                }
-            } else {
-                MIMEBodyPartInputStream partStream;
-                partStream = new MIMEBodyPartInputStream(pushbackInStream,
-                                                         boundary, this, PUSHBACK_SIZE);
-                part = new PartOnMemory(partStream);
-            }
-
-        } catch (MessagingException e) {
-            throw new OMException(e);
-        }
-        partIndex++;
-        return part;
-    }
+/*
+ * 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 org.apache.axiom.om.OMException;
+import org.apache.axiom.om.impl.MTOMConstants;
+import org.apache.axiom.om.util.UUIDGenerator;
+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.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeMap;
+
+public class Attachments {
+
+    /** <code>ContentType</code> of the MIME message */
+    ContentType contentType;
+    
+    int contentLength; // Content Length
+
+    /** 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;
+    int PUSHBACK_SIZE = 1000;
+
+    /**
+     * <code>attachmentsMap</code> stores the Data Handlers of the already parsed Mime Body Parts.
+     * This ordered Map is keyed using the content-ID's.
+     */
+    TreeMap attachmentsMap;
+    
+    /**
+     * <code>cids</code> stores the content ids in the order that the attachments
+     * occur in the message
+     */
+    ArrayList cids = new ArrayList(); 
+
+    /** <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 */
+    private boolean streamsRequested = false;
+
+    /** <code>boolean</code> Indicating if any data handlers have been directly requested */
+    private boolean partsRequested = false;
+
+    /**
+     * <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 boolean noStreams = false;
+
+    private String firstPartId;
+
+    private boolean fileCacheEnable;
+
+    private String attachmentRepoDir;
+
+    private int fileStorageThreshold;
+
+    protected static Log log = LogFactory.getLog(Attachments.class);
+
+
+    /**
+     * 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(inStream, contentTypeString, fileCacheEnable, attachmentRepoDir, fileThreshold, 0);
+    }
+        
+        /**
+     * 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
+     * @param fileThreshold
+     * @param contentLength
+     * @throws OMException
+     */
+    public Attachments(InputStream inStream, String contentTypeString, boolean fileCacheEnable,
+            String attachmentRepoDir, String fileThreshold, int contentLength) throws OMException {
+        this.contentLength = contentLength;
+        this.attachmentRepoDir = attachmentRepoDir;
+        this.fileCacheEnable = fileCacheEnable;
+        if (fileThreshold != null && (!"".equals(fileThreshold))) {
+            this.fileStorageThreshold = Integer.parseInt(fileThreshold);
+        } else {
+            this.fileStorageThreshold = 1;
+        }
+        attachmentsMap = new TreeMap();
+        try {
+            contentType = new ContentType(contentTypeString);
+        } catch (ParseException e) {
+            throw new OMException(
+                    "Invalid Content Type Field in the Mime Message"
+                    , e);
+        }
+        // REVIEW: This conversion is hard-coded to UTF-8.
+        // The complete solution is to respect the charset setting of the message.
+        // However this may cause problems in BoundaryDelimittedStream and other
+        // lower level classes.
+
+        // Boundary always have the prefix "--".
+        try {
+            String encoding = contentType.getParameter("charset");
+            if(encoding == null || encoding.length()==0){
+                encoding = "UTF-8";
+            }
+            this.boundary = ("--" + contentType.getParameter("boundary"))
+                    .getBytes(encoding);
+        } catch (UnsupportedEncodingException e) {
+            throw new OMException(e);
+        }
+
+        // do we need to wrap InputStream from a BufferedInputStream before
+        // wrapping from PushbackStream
+        pushbackInStream = new PushbackInputStream(inStream,
+                                                   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 ((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
+        getDataHandler(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);
+    }
+
+    /**
+     * Use this constructor when instantiating this to store the attachments set programatically
+     * through the SwA API.
+     */
+    public Attachments() {
+        attachmentsMap = new TreeMap();
+        noStreams = true;
+    }
+
+    /**
+     * @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 if ((MTOMConstants.SWA_TYPE_12).equalsIgnoreCase(applicationType)) {
+                this.applicationType = MTOMConstants.SWA_TYPE_12;
+            } else {
+                throw new OMException(
+                        "Invalid Application type. Support available for MTOM & SwA only.");
+            }
+        }
+        return this.applicationType;
+    }
+
+    /**
+     * Checks 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.
+     *
+     * @param blobContentID (without the surrounding angle brackets and "cid:" prefix)
+     * @return The DataHandler of the mime part referred by the Content-Id or *null* if the mime
+     *         part referred by the content-id does not exist
+     */
+    public DataHandler getDataHandler(String blobContentID) {
+        DataHandler dataHandler;
+        if (attachmentsMap.containsKey(blobContentID)) {
+            dataHandler = (DataHandler) attachmentsMap.get(blobContentID);
+            return dataHandler;
+        } else if (!noStreams) {
+            //This loop will be terminated by the Exceptions thrown if the Mime
+            // part searching was not found
+            while ((dataHandler = this.getNextPartDataHandler()) != null) {
+                if (attachmentsMap.containsKey(blobContentID)) {
+                    dataHandler = (DataHandler) attachmentsMap.get(blobContentID);
+                    return dataHandler;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Programatically adding an SOAP with Attachments(SwA) Attachment. These attachments will get
+     * serialized only if SOAP with Attachments is enabled.
+     *
+     * @param contentID
+     * @param dataHandler
+     */
+    public void addDataHandler(String contentID, DataHandler dataHandler) {
+        attachmentsMap.put(contentID, dataHandler);
+        if (!cids.contains(contentID)) {
+            cids.add(contentID);
+        }
+    }
+
+    /**
+     * Removes the DataHandler corresponding to the given contenID. If it is not present, then
+     * trying to find it calling the getNextPart() till the required part is found.
+     *
+     * @param blobContentID
+     */
+    public void removeDataHandler(String blobContentID) {
+        DataHandler dataHandler;
+        if (attachmentsMap.containsKey(blobContentID)) {
+            attachmentsMap.remove(blobContentID);
+        } else if (!noStreams) {
+            //This loop will be terminated by the Exceptions thrown if the Mime
+            // part searching was not found
+            while ((dataHandler = this.getNextPartDataHandler()) != null) {
+                if (attachmentsMap.containsKey(blobContentID)) {
+                    attachmentsMap.remove(blobContentID);
+                }
+            }
+        }
+        if (!cids.contains(blobContentID)) {
+            cids.remove(blobContentID);
+        }
+    }
+
+    /**
+     * @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;
+        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 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
+     */
+    public String getSOAPPartContentID() {
+        String rootContentID = contentType.getParameter("start");
+
+        // 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 ("cid".equalsIgnoreCase(rootContentID.substring(0, 3))) {
+            rootContentID = rootContentID.substring(4);
+        }
+        return rootContentID;
+    }
+
+    public String getSOAPPartContentType() {
+        if (!noStreams) {
+            DataHandler soapPart = getDataHandler(getSOAPPartContentID());
+            return soapPart.getContentType();
+        } else {
+            throw new OMException(
+                    "The attachments map was created programatically. Unsupported operation.");
+        }
+    }
+
+    /**
+     * 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.");
+        }
+        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;
+    }
+
+    public String[] getAllContentIDs() {
+        Set keys = getContentIDSet();
+        return (String[]) keys.toArray(new String[keys.size()]);
+    }
+
+    public Set getContentIDSet() {
+        DataHandler dataHandler;
+        while (!noStreams) {
+            dataHandler = this.getNextPartDataHandler();
+            if (dataHandler == null) {
+                break;
+            }
+        }
+        return attachmentsMap.keySet();
+    }
+    
+    /**
+     * @return List of content ids in order of appearance in message
+     */
+    public List getContentIDList() {
+        return cids;
+    }
+
+    /**
+     * endOfStreamReached will be set to true if the message ended in MIME Style having "--" suffix
+     * with the last mime boundary
+     *
+     * @param value
+     */
+    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 DataHandler getNextPartDataHandler() throws OMException {
+        if (endOfStreamReached) {
+            return null;
+        }
+        Part nextPart;
+        nextPart = getPart();
+        if (nextPart == null) {
+            return null;
+        } else
+            try {
+                if (nextPart.getSize() > 0) {
+                    String partContentID;
+                    try {
+                        partContentID = nextPart.getContentID();
+
+                        if (partContentID == null & partIndex == 1) {
+                            String id = "firstPart_" + UUIDGenerator.getUUID();
+                            firstPartId = id;
+                            addDataHandler(id, nextPart.getDataHandler());
+                            return nextPart.getDataHandler();
+                        }
+                        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 (attachmentsMap.containsKey(partContentID)) {
+                            throw new OMException(
+                                    "Two MIME parts with the same Content-ID not allowed.");
+                        }
+                        addDataHandler(partContentID, nextPart.getDataHandler());
+                        return nextPart.getDataHandler();
+                    } catch (MessagingException e) {
+                        throw new OMException("Error reading Content-ID from the Part."
+                                + e);
+                    }
+                } // This will take care if stream ended without having MIME
+                // message terminator
+                else {
+                    return null;
+                }
+            } 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;
+
+        Part part;
+
+        try {
+            if (fileCacheEnable) {
+                try {
+                    if (contentLength != 0 &&
+                            contentLength <= fileStorageThreshold) {
+                        // Since the content-length is less than the threshold, we can safely 
+                        // store it in memory.
+                        if (log.isDebugEnabled()) {
+                            log.debug("Creating an Attachment Part (in memory)");
+                        }
+                        MIMEBodyPartInputStream partStream =
+                            new MIMEBodyPartInputStream(pushbackInStream,
+                                                        boundary,
+                                                        this,
+                                                        PUSHBACK_SIZE);
+                        part = new PartOnMemory(partStream);
+                    } else if (contentLength != 0 && 
+                            contentLength > fileStorageThreshold * 2) {  
+                        if (log.isDebugEnabled()) {
+                            log.debug("Creating an Attachment Part (in a temporary file in " + attachmentRepoDir + ")");
+                        }
+                        // The content-length is much bigger than the threshold, then always
+                        // store the attachments in a file.  This prevents unnecessary buffering.
+                        // REVIEW Arbitrary heuristic.  Does this need to be fine-tuned.
+                        MIMEBodyPartInputStream partStream =
+                            new MIMEBodyPartInputStream(pushbackInStream,
+                                                        boundary,
+                                                        this,
+                                                        PUSHBACK_SIZE);
+                        PushbackFilePartInputStream filePartStream =
+                            new PushbackFilePartInputStream(partStream, new byte[0]);
+                        part = new PartOnFile(filePartStream, attachmentRepoDir);
+                        
+                    } else {
+                        if (log.isDebugEnabled()) {
+                            log.debug("Buffering attachment part to determine if it should be stored in memory");
+                        }
+                        // Read chunks of data to determine the size
+                        // of the attachment.  This can wasteful because we need to gc the buffers.
+                        // TODO We could look at the content-length of the individual attachment; however
+                        // this is seldom provided.
+                        
+                        MIMEBodyPartInputStream partStream;
+                        byte[] buffer = new byte[fileStorageThreshold];
+                        partStream =
+                            new MIMEBodyPartInputStream(pushbackInStream,
+                                                        boundary,
+                                                        this,
+                                                        PUSHBACK_SIZE);
+                        int count = 0;
+                        int value;
+                        count = partStream.read(buffer);
+                        
+                        if (count == fileStorageThreshold) {
+                            if (log.isDebugEnabled()) {
+                                log.debug("The calculated attachment size is " + count + ". Storing Part in file.");
+                            }
+                            PushbackFilePartInputStream filePartStream =
+                                new PushbackFilePartInputStream(partStream, buffer);
+                            part = new PartOnFile(filePartStream, attachmentRepoDir);
+                        } else {
+                            if (log.isDebugEnabled()) {
+                                log.debug("The calculated attachment size is " + count + ". Storing Part in memory.");
+                            }
+                            ByteArrayInputStream byteArrayInStream =
+                                new ByteArrayInputStream(buffer, 0, count);
+                            part = new PartOnMemory(byteArrayInStream);
+                        }
+                    } 
+                } catch (Exception e) {
+                    throw new OMException("Error creating temporary File.", e);
+                }
+            } else {
+                MIMEBodyPartInputStream partStream;
+                partStream =
+                        new MIMEBodyPartInputStream(pushbackInStream, boundary, this, PUSHBACK_SIZE);
+                part = new PartOnMemory(partStream);
+            }
+
+        } catch (MessagingException e) {
+            throw new OMException(e);
+        }
+        partIndex++;
+        return part;
+    }
 }



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@ws.apache.org
For additional commands, e-mail: commons-dev-help@ws.apache.org


Re: svn commit: r555379 - /webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/Attachments.java

Posted by R J Scheuerle Jr <sc...@us.ibm.com>.
Greetings Thilina,

partStream.read(buffer) will read the number of available bytes.

So you are correct that this could fail.  Here is the scenario that would
cause a failure:

    The incoming message is chunked and the transport layer code does not
block until the the full message is received.

This is a valid scnenario, but I am not sure that we have tests for this.
(FYI, IBM currently does not send chunked messages, which
is why I did not catch this case.)

BOTTOM LINE: I will change the code today.  Thanks for the review.

Rich Scheuerle
IBM Web Services
Apache Axis2 (scheu@apache.org)
512-838-5115  (IBM TL 678-5115)


"Thilina Gunarathne" <cs...@gmail.com> wrote on 07/12/2007 08:34:10 AM:

> >http://svn.apache.
> org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-
> api/src/main/java/org/apache/axiom/attachments/Attachments.java?
> r1=555378&r2=555379&pathrev=555379&view=diff&diff_format=l
> It works...
>
> Rich,
> Are you confident about replacing the following with one
> partStream.read(buffer);
>
> (Line 492 - 501 in the earlier code)..
>                     do {
>                         int len;
>                         int off = 0;
>                         int rem = fileStorageThreshold;
>                         while ((len = partStream.read(buffer, off,
> rem)) > 0) {
>                             off = off + len;
>                             rem = rem - len;
>                         }
>                         count += off;
>                     } while (partStream.available() > 0);
>
> AFAIK partStream.read(buffer) will readup only the data available at
> that moment and it does not guarantee that the buffer will be filled
> even if the size of the incoming data are larger than the buffer.. In
> that situation there is a possibility of not caching a large
> attachment... Please correct me if I'm wrong..
>
> thanks,
> Thilina
>
> > thanks,
> > dims
> >
> > On 7/12/07, Thilina Gunarathne <cs...@gmail.com> wrote:
> > > Hi,
> > > Can we have a cleaner patch of the specific changes please... It
would
> > > be really nice if you can revert this and commit the only the changed
> > > lines without all the formatting changes...
> > >
> > > We are on the verge of a release of Axiom and Axis2 (even the RC's
> > > have gone).. In that case I think we need to be much more careful
when
> > >  committing stuff...
> > >
> > > thanks,
> > > Thilina
> > >
> > > > Also added a method to the Attachments class to get the
> attachments in order.
> > > AFAIK
> > > > This can be useful in SWA "raw" situations.  In such scenarios
> the xml does not have a reference cid
> > > > to the attachment.  Thus the receiver may wish to view the
> attachments in the order that they appeared
> > > > on the wire.  One use case is JAX-WS RPC/LIT support with SWA
> attachments.
> > > >
> > > > Modified:
> > > >     webservices/commons/trunk/modules/axiom/modules/axiom-
> api/src/main/java/org/apache/axiom/attachments/Attachments.java
> > > >
> > > > 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?
> view=diff&rev=555379&r1=555378&r2=555379
> > > >
>
==============================================================================

> > > > --- 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 Wed
> Jul 11 12:51:52 2007
> > > > @@ -1,529 +1,609 @@
> > > > -/*
> > > > - * 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 org.apache.axiom.om.OMException;
> > > > -import org.apache.axiom.om.impl.MTOMConstants;
> > > > -import org.apache.axiom.om.util.UUIDGenerator;
> > > > -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.ByteArrayInputStream;
> > > > -import java.io.IOException;
> > > > -import java.io.InputStream;
> > > > -import java.io.PushbackInputStream;
> > > > -import java.io.UnsupportedEncodingException;
> > > > -import java.util.Set;
> > > > -import java.util.TreeMap;
> > > > -
> > > > -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;
> > > > -    int PUSHBACK_SIZE = 1000;
> > > > -
> > > > -    /**
> > > > -     * <code>attachmentsMap</code> stores the Data Handlers
> of the already parsed Mime Body Parts.
> > > > -     * This ordered Map is keyed using the content-ID's.
> > > > -     */
> > > > -    TreeMap attachmentsMap;
> > > > -
> > > > -    /** <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 */
> > > > -    private boolean streamsRequested = false;
> > > > -
> > > > -    /** <code>boolean</code> Indicating if any data handlers
> have been directly requested */
> > > > -    private boolean partsRequested = false;
> > > > -
> > > > -    /**
> > > > -     * <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 boolean noStreams = false;
> > > > -
> > > > -    private String firstPartId;
> > > > -
> > > > -    private boolean fileCacheEnable;
> > > > -
> > > > -    private String attachmentRepoDir;
> > > > -
> > > > -    private int fileStorageThreshold;
> > > > -
> > > > -    protected static Log log = LogFactory.getLog
(Attachments.class);
> > > > -
> > > > -
> > > > -    /**
> > > > -     * 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;
> > > > -        }
> > > > -        attachmentsMap = new TreeMap();
> > > > -        try {
> > > > -            contentType = new ContentType(contentTypeString);
> > > > -        } catch (ParseException e) {
> > > > -            throw new OMException(
> > > > -                    "Invalid Content Type Field in the Mime
Message"
> > > > -                    , e);
> > > > -        }
> > > > -        // REVIEW: This conversion is hard-coded to UTF-8.
> > > > -        // The complete solution is to respect the charset
> setting of the message.
> > > > -        // However this may cause problems in
> BoundaryDelimittedStream and other
> > > > -        // lower level classes.
> > > > -
> > > > -        // Boundary always have the prefix "--".
> > > > -        try {
> > > > -            String encoding = contentType.getParameter("charset");
> > > > -            if(encoding == null || encoding.length()==0){
> > > > -                encoding = "UTF-8";
> > > > -            }
> > > > -            this.boundary = ("--" + contentType.
> getParameter("boundary"))
> > > > -                    .getBytes(encoding);
> > > > -        } catch (UnsupportedEncodingException e) {
> > > > -            throw new OMException(e);
> > > > -        }
> > > > -
> > > > -        // do we need to wrap InputStream from a
> BufferedInputStream before
> > > > -        // wrapping from PushbackStream
> > > > -        pushbackInStream = new PushbackInputStream(inStream,
> > > > -                                                   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 ((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
> > > > -        getDataHandler(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);
> > > > -    }
> > > > -
> > > > -    /**
> > > > -     * Use this constructor when instantiating this to store
> the attachments set programatically
> > > > -     * through the SwA API.
> > > > -     */
> > > > -    public Attachments() {
> > > > -        attachmentsMap = new TreeMap();
> > > > -        noStreams = true;
> > > > -    }
> > > > -
> > > > -    /**
> > > > -     * @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 if ((MTOMConstants.SWA_TYPE_12).
> equalsIgnoreCase(applicationType)) {
> > > > -                this.applicationType = MTOMConstants.SWA_TYPE_12;
> > > > -            } else {
> > > > -                throw new OMException(
> > > > -                        "Invalid Application type. Support
> available for MTOM & SwA only.");
> > > > -            }
> > > > -        }
> > > > -        return this.applicationType;
> > > > -    }
> > > > -
> > > > -    /**
> > > > -     * Checks 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.
> > > > -     *
> > > > -     * @param blobContentID (without the surrounding angle
> brackets and "cid:" prefix)
> > > > -     * @return The DataHandler of the mime part referred by
> the Content-Id or *null* if the mime
> > > > -     *         part referred by the content-id does not exist
> > > > -     */
> > > > -    public DataHandler getDataHandler(String blobContentID) {
> > > > -        DataHandler dataHandler;
> > > > -        if (attachmentsMap.containsKey(blobContentID)) {
> > > > -            dataHandler = (DataHandler) attachmentsMap.
> get(blobContentID);
> > > > -            return dataHandler;
> > > > -        } else if (!noStreams) {
> > > > -            //This loop will be terminated by the Exceptions
> thrown if the Mime
> > > > -            // part searching was not found
> > > > -            while ((dataHandler = this.
> getNextPartDataHandler()) != null) {
> > > > -                if (attachmentsMap.containsKey(blobContentID)) {
> > > > -                    dataHandler = (DataHandler)
> attachmentsMap.get(blobContentID);
> > > > -                    return dataHandler;
> > > > -                }
> > > > -            }
> > > > -        }
> > > > -        return null;
> > > > -    }
> > > > -
> > > > -    /**
> > > > -     * Programatically adding an SOAP with Attachments(SwA)
> Attachment. These attachments will get
> > > > -     * serialized only if SOAP with Attachments is enabled.
> > > > -     *
> > > > -     * @param contentID
> > > > -     * @param dataHandler
> > > > -     */
> > > > -    public void addDataHandler(String contentID, DataHandler
> dataHandler) {
> > > > -        attachmentsMap.put(contentID, dataHandler);
> > > > -    }
> > > > -
> > > > -    /**
> > > > -     * Removes the DataHandler corresponding to the given
> contenID. If it is not present, then
> > > > -     * trying to find it calling the getNextPart() till the
> required part is found.
> > > > -     *
> > > > -     * @param blobContentID
> > > > -     */
> > > > -    public void removeDataHandler(String blobContentID) {
> > > > -        DataHandler dataHandler;
> > > > -        if (attachmentsMap.containsKey(blobContentID)) {
> > > > -            attachmentsMap.remove(blobContentID);
> > > > -        } else if (!noStreams) {
> > > > -            //This loop will be terminated by the Exceptions
> thrown if the Mime
> > > > -            // part searching was not found
> > > > -            while ((dataHandler = this.
> getNextPartDataHandler()) != null) {
> > > > -                if (attachmentsMap.containsKey(blobContentID)) {
> > > > -                    attachmentsMap.remove(blobContentID);
> > > > -                }
> > > > -            }
> > > > -        }
> > > > -    }
> > > > -
> > > > -    /**
> > > > -     * @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;
> > > > -        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 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
> > > > -     */
> > > > -    public String getSOAPPartContentID() {
> > > > -        String rootContentID = contentType.getParameter("start");
> > > > -
> > > > -        // 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 ("cid".equalsIgnoreCase(rootContentID.substring(0, 3)))
{
> > > > -            rootContentID = rootContentID.substring(4);
> > > > -        }
> > > > -        return rootContentID;
> > > > -    }
> > > > -
> > > > -    public String getSOAPPartContentType() {
> > > > -        if (!noStreams) {
> > > > -            DataHandler soapPart =
> getDataHandler(getSOAPPartContentID());
> > > > -            return soapPart.getContentType();
> > > > -        } else {
> > > > -            throw new OMException(
> > > > -                    "The attachments map was created
> programatically. Unsupported operation.");
> > > > -        }
> > > > -    }
> > > > -
> > > > -    /**
> > > > -     * 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.");
> > > > -        }
> > > > -        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;
> > > > -    }
> > > > -
> > > > -    public String[] getAllContentIDs() {
> > > > -        Set keys = getContentIDSet();
> > > > -        return (String[]) keys.toArray(new String[keys.size()]);
> > > > -    }
> > > > -
> > > > -    public Set getContentIDSet() {
> > > > -        DataHandler dataHandler;
> > > > -        while (!noStreams) {
> > > > -            dataHandler = this.getNextPartDataHandler();
> > > > -            if (dataHandler == null) {
> > > > -                break;
> > > > -            }
> > > > -        }
> > > > -        return attachmentsMap.keySet();
> > > > -    }
> > > > -
> > > > -    /**
> > > > -     * endOfStreamReached will be set to true if the message
> ended in MIME Style having "--" suffix
> > > > -     * with the last mime boundary
> > > > -     *
> > > > -     * @param value
> > > > -     */
> > > > -    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 DataHandler getNextPartDataHandler() throws
OMException {
> > > > -        if (endOfStreamReached) {
> > > > -            return null;
> > > > -        }
> > > > -        Part nextPart;
> > > > -        nextPart = getPart();
> > > > -        if (nextPart == null) {
> > > > -            return null;
> > > > -        } else
> > > > -            try {
> > > > -                if (nextPart.getSize() > 0) {
> > > > -                    String partContentID;
> > > > -                    try {
> > > > -                        partContentID = nextPart.getContentID();
> > > > -
> > > > -                        if (partContentID == null & partIndex ==
1) {
> > > > -                            String id = "firstPart_" +
> UUIDGenerator.getUUID();
> > > > -                            attachmentsMap.put(id, nextPart.
> getDataHandler());
> > > > -                            firstPartId = id;
> > > > -                            return nextPart.getDataHandler();
> > > > -                        }
> > > > -                        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 (attachmentsMap.
> containsKey(partContentID)) {
> > > > -                            throw new OMException(
> > > > -                                    "Two MIME parts with the
> same Content-ID not allowed.");
> > > > -                        }
> > > > -                        attachmentsMap.put(partContentID,
> nextPart.getDataHandler());
> > > > -                        return nextPart.getDataHandler();
> > > > -                    } catch (MessagingException e) {
> > > > -                        throw new OMException("Error reading
> Content-ID from the Part."
> > > > -                                + e);
> > > > -                    }
> > > > -                } // This will take care if stream ended
> without having MIME
> > > > -                // message terminator
> > > > -                else {
> > > > -                    return null;
> > > > -                }
> > > > -            } 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;
> > > > -
> > > > -        Part part;
> > > > -
> > > > -        try {
> > > > -            if (fileCacheEnable) {
> > > > -                try {
> > > > -                    MIMEBodyPartInputStream partStream;
> > > > -                    byte[] buffer = new byte
[fileStorageThreshold];
> > > > -                    partStream = new
> MIMEBodyPartInputStream(pushbackInStream,
> > > > -
> boundary, this, PUSHBACK_SIZE);
> > > > -                    int count = 0;
> > > > -                    do {
> > > > -                        int len;
> > > > -                        int off = 0;
> > > > -                        int rem = fileStorageThreshold;
> > > > -                        while ((len = partStream.read(buffer,
> off, rem)) > 0) {
> > > > -                            off = off + len;
> > > > -                            rem = rem - len;
> > > > -                        }
> > > > -                        count += off;
> > > > -                    } while (partStream.available() > 0);
> > > > -
> > > > -                    if (count == fileStorageThreshold) {
> > > > -                        PushbackFilePartInputStream filePartStream
=
> > > > -                                new PushbackFilePartInputStream(
> > > > -                                        partStream, buffer);
> > > > -                        part = new PartOnFile(filePartStream,
> attachmentRepoDir);
> > > > -                    } else {
> > > > -                        ByteArrayInputStream
> byteArrayInStream = new ByteArrayInputStream(buffer,
> > > > -                                                             0,
count);
> > > > -                        part = new PartOnMemory
(byteArrayInStream);
> > > > -                    }
> > > > -                } catch (Exception e) {
> > > > -                    throw new OMException("Error creating
> temporary File.", e);
> > > > -                }
> > > > -            } else {
> > > > -                MIMEBodyPartInputStream partStream;
> > > > -                partStream = new
> MIMEBodyPartInputStream(pushbackInStream,
> > > > -
> boundary, this, PUSHBACK_SIZE);
> > > > -                part = new PartOnMemory(partStream);
> > > > -            }
> > > > -
> > > > -        } catch (MessagingException e) {
> > > > -            throw new OMException(e);
> > > > -        }
> > > > -        partIndex++;
> > > > -        return part;
> > > > -    }
> > > > +/*
> > > > + * 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 org.apache.axiom.om.OMException;
> > > > +import org.apache.axiom.om.impl.MTOMConstants;
> > > > +import org.apache.axiom.om.util.UUIDGenerator;
> > > > +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.ByteArrayInputStream;
> > > > +import java.io.IOException;
> > > > +import java.io.InputStream;
> > > > +import java.io.PushbackInputStream;
> > > > +import java.io.UnsupportedEncodingException;
> > > > +import java.util.ArrayList;
> > > > +import java.util.List;
> > > > +import java.util.Set;
> > > > +import java.util.TreeMap;
> > > > +
> > > > +public class Attachments {
> > > > +
> > > > +    /** <code>ContentType</code> of the MIME message */
> > > > +    ContentType contentType;
> > > > +
> > > > +    int contentLength; // Content Length
> > > > +
> > > > +    /** 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;
> > > > +    int PUSHBACK_SIZE = 1000;
> > > > +
> > > > +    /**
> > > > +     * <code>attachmentsMap</code> stores the Data Handlers
> of the already parsed Mime Body Parts.
> > > > +     * This ordered Map is keyed using the content-ID's.
> > > > +     */
> > > > +    TreeMap attachmentsMap;
> > > > +
> > > > +    /**
> > > > +     * <code>cids</code> stores the content ids in the order
> that the attachments
> > > > +     * occur in the message
> > > > +     */
> > > > +    ArrayList cids = new ArrayList();
> > > > +
> > > > +    /** <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 */
> > > > +    private boolean streamsRequested = false;
> > > > +
> > > > +    /** <code>boolean</code> Indicating if any data handlers
> have been directly requested */
> > > > +    private boolean partsRequested = false;
> > > > +
> > > > +    /**
> > > > +     * <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 boolean noStreams = false;
> > > > +
> > > > +    private String firstPartId;
> > > > +
> > > > +    private boolean fileCacheEnable;
> > > > +
> > > > +    private String attachmentRepoDir;
> > > > +
> > > > +    private int fileStorageThreshold;
> > > > +
> > > > +    protected static Log log = LogFactory.getLog
(Attachments.class);
> > > > +
> > > > +
> > > > +    /**
> > > > +     * 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(inStream, contentTypeString, fileCacheEnable,
> attachmentRepoDir, fileThreshold, 0);
> > > > +    }
> > > > +
> > > > +        /**
> > > > +     * 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
> > > > +     * @param fileThreshold
> > > > +     * @param contentLength
> > > > +     * @throws OMException
> > > > +     */
> > > > +    public Attachments(InputStream inStream, String
> contentTypeString, boolean fileCacheEnable,
> > > > +            String attachmentRepoDir, String fileThreshold,
> int contentLength) throws OMException {
> > > > +        this.contentLength = contentLength;
> > > > +        this.attachmentRepoDir = attachmentRepoDir;
> > > > +        this.fileCacheEnable = fileCacheEnable;
> > > > +        if (fileThreshold != null && (!"".equals(fileThreshold)))
{
> > > > +            this.fileStorageThreshold = Integer.
> parseInt(fileThreshold);
> > > > +        } else {
> > > > +            this.fileStorageThreshold = 1;
> > > > +        }
> > > > +        attachmentsMap = new TreeMap();
> > > > +        try {
> > > > +            contentType = new ContentType(contentTypeString);
> > > > +        } catch (ParseException e) {
> > > > +            throw new OMException(
> > > > +                    "Invalid Content Type Field in the Mime
Message"
> > > > +                    , e);
> > > > +        }
> > > > +        // REVIEW: This conversion is hard-coded to UTF-8.
> > > > +        // The complete solution is to respect the charset
> setting of the message.
> > > > +        // However this may cause problems in
> BoundaryDelimittedStream and other
> > > > +        // lower level classes.
> > > > +
> > > > +        // Boundary always have the prefix "--".
> > > > +        try {
> > > > +            String encoding = contentType.getParameter("charset");
> > > > +            if(encoding == null || encoding.length()==0){
> > > > +                encoding = "UTF-8";
> > > > +            }
> > > > +            this.boundary = ("--" + contentType.
> getParameter("boundary"))
> > > > +                    .getBytes(encoding);
> > > > +        } catch (UnsupportedEncodingException e) {
> > > > +            throw new OMException(e);
> > > > +        }
> > > > +
> > > > +        // do we need to wrap InputStream from a
> BufferedInputStream before
> > > > +        // wrapping from PushbackStream
> > > > +        pushbackInStream = new PushbackInputStream(inStream,
> > > > +                                                   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 ((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
> > > > +        getDataHandler(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);
> > > > +    }
> > > > +
> > > > +    /**
> > > > +     * Use this constructor when instantiating this to store
> the attachments set programatically
> > > > +     * through the SwA API.
> > > > +     */
> > > > +    public Attachments() {
> > > > +        attachmentsMap = new TreeMap();
> > > > +        noStreams = true;
> > > > +    }
> > > > +
> > > > +    /**
> > > > +     * @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 if ((MTOMConstants.SWA_TYPE_12).
> equalsIgnoreCase(applicationType)) {
> > > > +                this.applicationType = MTOMConstants.SWA_TYPE_12;
> > > > +            } else {
> > > > +                throw new OMException(
> > > > +                        "Invalid Application type. Support
> available for MTOM & SwA only.");
> > > > +            }
> > > > +        }
> > > > +        return this.applicationType;
> > > > +    }
> > > > +
> > > > +    /**
> > > > +     * Checks 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.
> > > > +     *
> > > > +     * @param blobContentID (without the surrounding angle
> brackets and "cid:" prefix)
> > > > +     * @return The DataHandler of the mime part referred by
> the Content-Id or *null* if the mime
> > > > +     *         part referred by the content-id does not exist
> > > > +     */
> > > > +    public DataHandler getDataHandler(String blobContentID) {
> > > > +        DataHandler dataHandler;
> > > > +        if (attachmentsMap.containsKey(blobContentID)) {
> > > > +            dataHandler = (DataHandler) attachmentsMap.
> get(blobContentID);
> > > > +            return dataHandler;
> > > > +        } else if (!noStreams) {
> > > > +            //This loop will be terminated by the Exceptions
> thrown if the Mime
> > > > +            // part searching was not found
> > > > +            while ((dataHandler = this.
> getNextPartDataHandler()) != null) {
> > > > +                if (attachmentsMap.containsKey(blobContentID)) {
> > > > +                    dataHandler = (DataHandler)
> attachmentsMap.get(blobContentID);
> > > > +                    return dataHandler;
> > > > +                }
> > > > +            }
> > > > +        }
> > > > +        return null;
> > > > +    }
> > > > +
> > > > +    /**
> > > > +     * Programatically adding an SOAP with Attachments(SwA)
> Attachment. These attachments will get
> > > > +     * serialized only if SOAP with Attachments is enabled.
> > > > +     *
> > > > +     * @param contentID
> > > > +     * @param dataHandler
> > > > +     */
> > > > +    public void addDataHandler(String contentID, DataHandler
> dataHandler) {
> > > > +        attachmentsMap.put(contentID, dataHandler);
> > > > +        if (!cids.contains(contentID)) {
> > > > +            cids.add(contentID);
> > > > +        }
> > > > +    }
> > > > +
> > > > +    /**
> > > > +     * Removes the DataHandler corresponding to the given
> contenID. If it is not present, then
> > > > +     * trying to find it calling the getNextPart() till the
> required part is found.
> > > > +     *
> > > > +     * @param blobContentID
> > > > +     */
> > > > +    public void removeDataHandler(String blobContentID) {
> > > > +        DataHandler dataHandler;
> > > > +        if (attachmentsMap.containsKey(blobContentID)) {
> > > > +            attachmentsMap.remove(blobContentID);
> > > > +        } else if (!noStreams) {
> > > > +            //This loop will be terminated by the Exceptions
> thrown if the Mime
> > > > +            // part searching was not found
> > > > +            while ((dataHandler = this.
> getNextPartDataHandler()) != null) {
> > > > +                if (attachmentsMap.containsKey(blobContentID)) {
> > > > +                    attachmentsMap.remove(blobContentID);
> > > > +                }
> > > > +            }
> > > > +        }
> > > > +        if (!cids.contains(blobContentID)) {
> > > > +            cids.remove(blobContentID);
> > > > +        }
> > > > +    }
> > > > +
> > > > +    /**
> > > > +     * @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;
> > > > +        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 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
> > > > +     */
> > > > +    public String getSOAPPartContentID() {
> > > > +        String rootContentID = contentType.getParameter("start");
> > > > +
> > > > +        // 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 ("cid".equalsIgnoreCase(rootContentID.substring(0, 3)))
{
> > > > +            rootContentID = rootContentID.substring(4);
> > > > +        }
> > > > +        return rootContentID;
> > > > +    }
> > > > +
> > > > +    public String getSOAPPartContentType() {
> > > > +        if (!noStreams) {
> > > > +            DataHandler soapPart =
> getDataHandler(getSOAPPartContentID());
> > > > +            return soapPart.getContentType();
> > > > +        } else {
> > > > +            throw new OMException(
> > > > +                    "The attachments map was created
> programatically. Unsupported operation.");
> > > > +        }
> > > > +    }
> > > > +
> > > > +    /**
> > > > +     * 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.");
> > > > +        }
> > > > +        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;
> > > > +    }
> > > > +
> > > > +    public String[] getAllContentIDs() {
> > > > +        Set keys = getContentIDSet();
> > > > +        return (String[]) keys.toArray(new String[keys.size()]);
> > > > +    }
> > > > +
> > > > +    public Set getContentIDSet() {
> > > > +        DataHandler dataHandler;
> > > > +        while (!noStreams) {
> > > > +            dataHandler = this.getNextPartDataHandler();
> > > > +            if (dataHandler == null) {
> > > > +                break;
> > > > +            }
> > > > +        }
> > > > +        return attachmentsMap.keySet();
> > > > +    }
> > > > +
> > > > +    /**
> > > > +     * @return List of content ids in order of appearance in
message
> > > > +     */
> > > > +    public List getContentIDList() {
> > > > +        return cids;
> > > > +    }
> > > > +
> > > > +    /**
> > > > +     * endOfStreamReached will be set to true if the message
> ended in MIME Style having "--" suffix
> > > > +     * with the last mime boundary
> > > > +     *
> > > > +     * @param value
> > > > +     */
> > > > +    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 DataHandler getNextPartDataHandler() throws
OMException {
> > > > +        if (endOfStreamReached) {
> > > > +            return null;
> > > > +        }
> > > > +        Part nextPart;
> > > > +        nextPart = getPart();
> > > > +        if (nextPart == null) {
> > > > +            return null;
> > > > +        } else
> > > > +            try {
> > > > +                if (nextPart.getSize() > 0) {
> > > > +                    String partContentID;
> > > > +                    try {
> > > > +                        partContentID = nextPart.getContentID();
> > > > +
> > > > +                        if (partContentID == null & partIndex ==
1) {
> > > > +                            String id = "firstPart_" +
> UUIDGenerator.getUUID();
> > > > +                            firstPartId = id;
> > > > +                            addDataHandler(id, nextPart.
> getDataHandler());
> > > > +                            return nextPart.getDataHandler();
> > > > +                        }
> > > > +                        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 (attachmentsMap.
> containsKey(partContentID)) {
> > > > +                            throw new OMException(
> > > > +                                    "Two MIME parts with the
> same Content-ID not allowed.");
> > > > +                        }
> > > > +                        addDataHandler(partContentID,
> nextPart.getDataHandler());
> > > > +                        return nextPart.getDataHandler();
> > > > +                    } catch (MessagingException e) {
> > > > +                        throw new OMException("Error reading
> Content-ID from the Part."
> > > > +                                + e);
> > > > +                    }
> > > > +                } // This will take care if stream ended
> without having MIME
> > > > +                // message terminator
> > > > +                else {
> > > > +                    return null;
> > > > +                }
> > > > +            } 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;
> > > > +
> > > > +        Part part;
> > > > +
> > > > +        try {
> > > > +            if (fileCacheEnable) {
> > > > +                try {
> > > > +                    if (contentLength != 0 &&
> > > > +                            contentLength <= fileStorageThreshold)
{
> > > > +                        // Since the content-length is less
> than the threshold, we can safely
> > > > +                        // store it in memory.
> > > > +                        if (log.isDebugEnabled()) {
> > > > +                            log.debug("Creating an Attachment
> Part (in memory)");
> > > > +                        }
> > > > +                        MIMEBodyPartInputStream partStream =
> > > > +                            new
> MIMEBodyPartInputStream(pushbackInStream,
> > > > +                                                        boundary,
> > > > +                                                        this,
> > > > +
PUSHBACK_SIZE);
> > > > +                        part = new PartOnMemory(partStream);
> > > > +                    } else if (contentLength != 0 &&
> > > > +                            contentLength > fileStorageThreshold *
2) {
> > > > +                        if (log.isDebugEnabled()) {
> > > > +                            log.debug("Creating an Attachment
> Part (in a temporary file in " + attachmentRepoDir + ")");
> > > > +                        }
> > > > +                        // The content-length is much bigger
> than the threshold, then always
> > > > +                        // store the attachments in a file.
> This prevents unnecessary buffering.
> > > > +                        // REVIEW Arbitrary heuristic.  Does
> this need to be fine-tuned.
> > > > +                        MIMEBodyPartInputStream partStream =
> > > > +                            new
> MIMEBodyPartInputStream(pushbackInStream,
> > > > +                                                        boundary,
> > > > +                                                        this,
> > > > +
PUSHBACK_SIZE);
> > > > +                        PushbackFilePartInputStream filePartStream
=
> > > > +                            new
> PushbackFilePartInputStream(partStream, new byte[0]);
> > > > +                        part = new PartOnFile(filePartStream,
> attachmentRepoDir);
> > > > +
> > > > +                    } else {
> > > > +                        if (log.isDebugEnabled()) {
> > > > +                            log.debug("Buffering attachment
> part to determine if it should be stored in memory");
> > > > +                        }
> > > > +                        // Read chunks of data to determine the
size
> > > > +                        // of the attachment.  This can
> wasteful because we need to gc the buffers.
> > > > +                        // TODO We could look at the content-
> length of the individual attachment; however
> > > > +                        // this is seldom provided.
> > > > +
> > > > +                        MIMEBodyPartInputStream partStream;
> > > > +                        byte[] buffer = new byte
[fileStorageThreshold];
> > > > +                        partStream =
> > > > +                            new
> MIMEBodyPartInputStream(pushbackInStream,
> > > > +                                                        boundary,
> > > > +                                                        this,
> > > > +
PUSHBACK_SIZE);
> > > > +                        int count = 0;
> > > > +                        int value;
> > > > +                        count = partStream.read(buffer);
> > > > +
> > > > +                        if (count == fileStorageThreshold) {
> > > > +                            if (log.isDebugEnabled()) {
> > > > +                                log.debug("The calculated
> attachment size is " + count + ". Storing Part in file.");
> > > > +                            }
> > > > +                            PushbackFilePartInputStream
> filePartStream =
> > > > +                                new
> PushbackFilePartInputStream(partStream, buffer);
> > > > +                            part = new
> PartOnFile(filePartStream, attachmentRepoDir);
> > > > +                        } else {
> > > > +                            if (log.isDebugEnabled()) {
> > > > +                                log.debug("The calculated
> attachment size is " + count + ". Storing Part in memory.");
> > > > +                            }
> > > > +                            ByteArrayInputStream byteArrayInStream
=
> > > > +                                new
> ByteArrayInputStream(buffer, 0, count);
> > > > +                            part = new PartOnMemory
(byteArrayInStream);
> > > > +                        }
> > > > +                    }
> > > > +                } catch (Exception e) {
> > > > +                    throw new OMException("Error creating
> temporary File.", e);
> > > > +                }
> > > > +            } else {
> > > > +                MIMEBodyPartInputStream partStream;
> > > > +                partStream =
> > > > +                        new
> MIMEBodyPartInputStream(pushbackInStream, boundary, this, PUSHBACK_SIZE);
> > > > +                part = new PartOnMemory(partStream);
> > > > +            }
> > > > +
> > > > +        } catch (MessagingException e) {
> > > > +            throw new OMException(e);
> > > > +        }
> > > > +        partIndex++;
> > > > +        return part;
> > > > +    }
> > > >  }
> > > >
> > > >
> > > >
> > > >
---------------------------------------------------------------------
> > > > To unsubscribe, e-mail: commons-dev-unsubscribe@ws.apache.org
> > > > For additional commands, e-mail: commons-dev-help@ws.apache.org
> > > >
> > > >
> > >
> > >
> > > --
> > > Thilina Gunarathne  -  http://www.wso2.com -
http://thilinag.blogspot.com
> > >
> > > ---------------------------------------------------------------------
> > > To unsubscribe, e-mail: commons-dev-unsubscribe@ws.apache.org
> > > For additional commands, e-mail: commons-dev-help@ws.apache.org
> > >
> > >
> >
> >
> > --
> > Davanum Srinivas :: http://davanum.wordpress.com
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: commons-dev-unsubscribe@ws.apache.org
> > For additional commands, e-mail: commons-dev-help@ws.apache.org
> >
> >
>
>
> --
> Thilina Gunarathne  -  http://www.wso2.com - http://thilinag.blogspot.com

Re: svn commit: r555379 - /webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/Attachments.java

Posted by Thilina Gunarathne <cs...@gmail.com>.
>http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/Attachments.java?r1=555378&r2=555379&pathrev=555379&view=diff&diff_format=l
It works...

Rich,
Are you confident about replacing the following with one
partStream.read(buffer);

(Line 492 - 501 in the earlier code)..
                    do {
                        int len;
                        int off = 0;
                        int rem = fileStorageThreshold;
                        while ((len = partStream.read(buffer, off, rem)) > 0) {
                            off = off + len;
                            rem = rem - len;
                        }
                        count += off;
                    } while (partStream.available() > 0);

AFAIK partStream.read(buffer) will readup only the data available at
that moment and it does not guarantee that the buffer will be filled
even if the size of the incoming data are larger than the buffer.. In
that situation there is a possibility of not caching a large
attachment... Please correct me if I'm wrong..

thanks,
Thilina

> thanks,
> dims
>
> On 7/12/07, Thilina Gunarathne <cs...@gmail.com> wrote:
> > Hi,
> > Can we have a cleaner patch of the specific changes please... It would
> > be really nice if you can revert this and commit the only the changed
> > lines without all the formatting changes...
> >
> > We are on the verge of a release of Axiom and Axis2 (even the RC's
> > have gone).. In that case I think we need to be much more careful when
> >  committing stuff...
> >
> > thanks,
> > Thilina
> >
> > > Also added a method to the Attachments class to get the attachments in order.
> > AFAIK
> > > This can be useful in SWA "raw" situations.  In such scenarios the xml does not have a reference cid
> > > to the attachment.  Thus the receiver may wish to view the attachments in the order that they appeared
> > > on the wire.  One use case is JAX-WS RPC/LIT support with SWA attachments.
> > >
> > > Modified:
> > >     webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/Attachments.java
> > >
> > > 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?view=diff&rev=555379&r1=555378&r2=555379
> > > ==============================================================================
> > > --- 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 Wed Jul 11 12:51:52 2007
> > > @@ -1,529 +1,609 @@
> > > -/*
> > > - * 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 org.apache.axiom.om.OMException;
> > > -import org.apache.axiom.om.impl.MTOMConstants;
> > > -import org.apache.axiom.om.util.UUIDGenerator;
> > > -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.ByteArrayInputStream;
> > > -import java.io.IOException;
> > > -import java.io.InputStream;
> > > -import java.io.PushbackInputStream;
> > > -import java.io.UnsupportedEncodingException;
> > > -import java.util.Set;
> > > -import java.util.TreeMap;
> > > -
> > > -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;
> > > -    int PUSHBACK_SIZE = 1000;
> > > -
> > > -    /**
> > > -     * <code>attachmentsMap</code> stores the Data Handlers of the already parsed Mime Body Parts.
> > > -     * This ordered Map is keyed using the content-ID's.
> > > -     */
> > > -    TreeMap attachmentsMap;
> > > -
> > > -    /** <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 */
> > > -    private boolean streamsRequested = false;
> > > -
> > > -    /** <code>boolean</code> Indicating if any data handlers have been directly requested */
> > > -    private boolean partsRequested = false;
> > > -
> > > -    /**
> > > -     * <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 boolean noStreams = false;
> > > -
> > > -    private String firstPartId;
> > > -
> > > -    private boolean fileCacheEnable;
> > > -
> > > -    private String attachmentRepoDir;
> > > -
> > > -    private int fileStorageThreshold;
> > > -
> > > -    protected static Log log = LogFactory.getLog(Attachments.class);
> > > -
> > > -
> > > -    /**
> > > -     * 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;
> > > -        }
> > > -        attachmentsMap = new TreeMap();
> > > -        try {
> > > -            contentType = new ContentType(contentTypeString);
> > > -        } catch (ParseException e) {
> > > -            throw new OMException(
> > > -                    "Invalid Content Type Field in the Mime Message"
> > > -                    , e);
> > > -        }
> > > -        // REVIEW: This conversion is hard-coded to UTF-8.
> > > -        // The complete solution is to respect the charset setting of the message.
> > > -        // However this may cause problems in BoundaryDelimittedStream and other
> > > -        // lower level classes.
> > > -
> > > -        // Boundary always have the prefix "--".
> > > -        try {
> > > -            String encoding = contentType.getParameter("charset");
> > > -            if(encoding == null || encoding.length()==0){
> > > -                encoding = "UTF-8";
> > > -            }
> > > -            this.boundary = ("--" + contentType.getParameter("boundary"))
> > > -                    .getBytes(encoding);
> > > -        } catch (UnsupportedEncodingException e) {
> > > -            throw new OMException(e);
> > > -        }
> > > -
> > > -        // do we need to wrap InputStream from a BufferedInputStream before
> > > -        // wrapping from PushbackStream
> > > -        pushbackInStream = new PushbackInputStream(inStream,
> > > -                                                   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 ((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
> > > -        getDataHandler(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);
> > > -    }
> > > -
> > > -    /**
> > > -     * Use this constructor when instantiating this to store the attachments set programatically
> > > -     * through the SwA API.
> > > -     */
> > > -    public Attachments() {
> > > -        attachmentsMap = new TreeMap();
> > > -        noStreams = true;
> > > -    }
> > > -
> > > -    /**
> > > -     * @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 if ((MTOMConstants.SWA_TYPE_12).equalsIgnoreCase(applicationType)) {
> > > -                this.applicationType = MTOMConstants.SWA_TYPE_12;
> > > -            } else {
> > > -                throw new OMException(
> > > -                        "Invalid Application type. Support available for MTOM & SwA only.");
> > > -            }
> > > -        }
> > > -        return this.applicationType;
> > > -    }
> > > -
> > > -    /**
> > > -     * Checks 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.
> > > -     *
> > > -     * @param blobContentID (without the surrounding angle brackets and "cid:" prefix)
> > > -     * @return The DataHandler of the mime part referred by the Content-Id or *null* if the mime
> > > -     *         part referred by the content-id does not exist
> > > -     */
> > > -    public DataHandler getDataHandler(String blobContentID) {
> > > -        DataHandler dataHandler;
> > > -        if (attachmentsMap.containsKey(blobContentID)) {
> > > -            dataHandler = (DataHandler) attachmentsMap.get(blobContentID);
> > > -            return dataHandler;
> > > -        } else if (!noStreams) {
> > > -            //This loop will be terminated by the Exceptions thrown if the Mime
> > > -            // part searching was not found
> > > -            while ((dataHandler = this.getNextPartDataHandler()) != null) {
> > > -                if (attachmentsMap.containsKey(blobContentID)) {
> > > -                    dataHandler = (DataHandler) attachmentsMap.get(blobContentID);
> > > -                    return dataHandler;
> > > -                }
> > > -            }
> > > -        }
> > > -        return null;
> > > -    }
> > > -
> > > -    /**
> > > -     * Programatically adding an SOAP with Attachments(SwA) Attachment. These attachments will get
> > > -     * serialized only if SOAP with Attachments is enabled.
> > > -     *
> > > -     * @param contentID
> > > -     * @param dataHandler
> > > -     */
> > > -    public void addDataHandler(String contentID, DataHandler dataHandler) {
> > > -        attachmentsMap.put(contentID, dataHandler);
> > > -    }
> > > -
> > > -    /**
> > > -     * Removes the DataHandler corresponding to the given contenID. If it is not present, then
> > > -     * trying to find it calling the getNextPart() till the required part is found.
> > > -     *
> > > -     * @param blobContentID
> > > -     */
> > > -    public void removeDataHandler(String blobContentID) {
> > > -        DataHandler dataHandler;
> > > -        if (attachmentsMap.containsKey(blobContentID)) {
> > > -            attachmentsMap.remove(blobContentID);
> > > -        } else if (!noStreams) {
> > > -            //This loop will be terminated by the Exceptions thrown if the Mime
> > > -            // part searching was not found
> > > -            while ((dataHandler = this.getNextPartDataHandler()) != null) {
> > > -                if (attachmentsMap.containsKey(blobContentID)) {
> > > -                    attachmentsMap.remove(blobContentID);
> > > -                }
> > > -            }
> > > -        }
> > > -    }
> > > -
> > > -    /**
> > > -     * @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;
> > > -        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 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
> > > -     */
> > > -    public String getSOAPPartContentID() {
> > > -        String rootContentID = contentType.getParameter("start");
> > > -
> > > -        // 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 ("cid".equalsIgnoreCase(rootContentID.substring(0, 3))) {
> > > -            rootContentID = rootContentID.substring(4);
> > > -        }
> > > -        return rootContentID;
> > > -    }
> > > -
> > > -    public String getSOAPPartContentType() {
> > > -        if (!noStreams) {
> > > -            DataHandler soapPart = getDataHandler(getSOAPPartContentID());
> > > -            return soapPart.getContentType();
> > > -        } else {
> > > -            throw new OMException(
> > > -                    "The attachments map was created programatically. Unsupported operation.");
> > > -        }
> > > -    }
> > > -
> > > -    /**
> > > -     * 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.");
> > > -        }
> > > -        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;
> > > -    }
> > > -
> > > -    public String[] getAllContentIDs() {
> > > -        Set keys = getContentIDSet();
> > > -        return (String[]) keys.toArray(new String[keys.size()]);
> > > -    }
> > > -
> > > -    public Set getContentIDSet() {
> > > -        DataHandler dataHandler;
> > > -        while (!noStreams) {
> > > -            dataHandler = this.getNextPartDataHandler();
> > > -            if (dataHandler == null) {
> > > -                break;
> > > -            }
> > > -        }
> > > -        return attachmentsMap.keySet();
> > > -    }
> > > -
> > > -    /**
> > > -     * endOfStreamReached will be set to true if the message ended in MIME Style having "--" suffix
> > > -     * with the last mime boundary
> > > -     *
> > > -     * @param value
> > > -     */
> > > -    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 DataHandler getNextPartDataHandler() throws OMException {
> > > -        if (endOfStreamReached) {
> > > -            return null;
> > > -        }
> > > -        Part nextPart;
> > > -        nextPart = getPart();
> > > -        if (nextPart == null) {
> > > -            return null;
> > > -        } else
> > > -            try {
> > > -                if (nextPart.getSize() > 0) {
> > > -                    String partContentID;
> > > -                    try {
> > > -                        partContentID = nextPart.getContentID();
> > > -
> > > -                        if (partContentID == null & partIndex == 1) {
> > > -                            String id = "firstPart_" + UUIDGenerator.getUUID();
> > > -                            attachmentsMap.put(id, nextPart.getDataHandler());
> > > -                            firstPartId = id;
> > > -                            return nextPart.getDataHandler();
> > > -                        }
> > > -                        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 (attachmentsMap.containsKey(partContentID)) {
> > > -                            throw new OMException(
> > > -                                    "Two MIME parts with the same Content-ID not allowed.");
> > > -                        }
> > > -                        attachmentsMap.put(partContentID, nextPart.getDataHandler());
> > > -                        return nextPart.getDataHandler();
> > > -                    } catch (MessagingException e) {
> > > -                        throw new OMException("Error reading Content-ID from the Part."
> > > -                                + e);
> > > -                    }
> > > -                } // This will take care if stream ended without having MIME
> > > -                // message terminator
> > > -                else {
> > > -                    return null;
> > > -                }
> > > -            } 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;
> > > -
> > > -        Part part;
> > > -
> > > -        try {
> > > -            if (fileCacheEnable) {
> > > -                try {
> > > -                    MIMEBodyPartInputStream partStream;
> > > -                    byte[] buffer = new byte[fileStorageThreshold];
> > > -                    partStream = new MIMEBodyPartInputStream(pushbackInStream,
> > > -                                                             boundary, this, PUSHBACK_SIZE);
> > > -                    int count = 0;
> > > -                    do {
> > > -                        int len;
> > > -                        int off = 0;
> > > -                        int rem = fileStorageThreshold;
> > > -                        while ((len = partStream.read(buffer, off, rem)) > 0) {
> > > -                            off = off + len;
> > > -                            rem = rem - len;
> > > -                        }
> > > -                        count += off;
> > > -                    } while (partStream.available() > 0);
> > > -
> > > -                    if (count == fileStorageThreshold) {
> > > -                        PushbackFilePartInputStream filePartStream =
> > > -                                new PushbackFilePartInputStream(
> > > -                                        partStream, buffer);
> > > -                        part = new PartOnFile(filePartStream, attachmentRepoDir);
> > > -                    } else {
> > > -                        ByteArrayInputStream byteArrayInStream = new ByteArrayInputStream(buffer,
> > > -                                                                                          0, count);
> > > -                        part = new PartOnMemory(byteArrayInStream);
> > > -                    }
> > > -                } catch (Exception e) {
> > > -                    throw new OMException("Error creating temporary File.", e);
> > > -                }
> > > -            } else {
> > > -                MIMEBodyPartInputStream partStream;
> > > -                partStream = new MIMEBodyPartInputStream(pushbackInStream,
> > > -                                                         boundary, this, PUSHBACK_SIZE);
> > > -                part = new PartOnMemory(partStream);
> > > -            }
> > > -
> > > -        } catch (MessagingException e) {
> > > -            throw new OMException(e);
> > > -        }
> > > -        partIndex++;
> > > -        return part;
> > > -    }
> > > +/*
> > > + * 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 org.apache.axiom.om.OMException;
> > > +import org.apache.axiom.om.impl.MTOMConstants;
> > > +import org.apache.axiom.om.util.UUIDGenerator;
> > > +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.ByteArrayInputStream;
> > > +import java.io.IOException;
> > > +import java.io.InputStream;
> > > +import java.io.PushbackInputStream;
> > > +import java.io.UnsupportedEncodingException;
> > > +import java.util.ArrayList;
> > > +import java.util.List;
> > > +import java.util.Set;
> > > +import java.util.TreeMap;
> > > +
> > > +public class Attachments {
> > > +
> > > +    /** <code>ContentType</code> of the MIME message */
> > > +    ContentType contentType;
> > > +
> > > +    int contentLength; // Content Length
> > > +
> > > +    /** 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;
> > > +    int PUSHBACK_SIZE = 1000;
> > > +
> > > +    /**
> > > +     * <code>attachmentsMap</code> stores the Data Handlers of the already parsed Mime Body Parts.
> > > +     * This ordered Map is keyed using the content-ID's.
> > > +     */
> > > +    TreeMap attachmentsMap;
> > > +
> > > +    /**
> > > +     * <code>cids</code> stores the content ids in the order that the attachments
> > > +     * occur in the message
> > > +     */
> > > +    ArrayList cids = new ArrayList();
> > > +
> > > +    /** <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 */
> > > +    private boolean streamsRequested = false;
> > > +
> > > +    /** <code>boolean</code> Indicating if any data handlers have been directly requested */
> > > +    private boolean partsRequested = false;
> > > +
> > > +    /**
> > > +     * <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 boolean noStreams = false;
> > > +
> > > +    private String firstPartId;
> > > +
> > > +    private boolean fileCacheEnable;
> > > +
> > > +    private String attachmentRepoDir;
> > > +
> > > +    private int fileStorageThreshold;
> > > +
> > > +    protected static Log log = LogFactory.getLog(Attachments.class);
> > > +
> > > +
> > > +    /**
> > > +     * 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(inStream, contentTypeString, fileCacheEnable, attachmentRepoDir, fileThreshold, 0);
> > > +    }
> > > +
> > > +        /**
> > > +     * 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
> > > +     * @param fileThreshold
> > > +     * @param contentLength
> > > +     * @throws OMException
> > > +     */
> > > +    public Attachments(InputStream inStream, String contentTypeString, boolean fileCacheEnable,
> > > +            String attachmentRepoDir, String fileThreshold, int contentLength) throws OMException {
> > > +        this.contentLength = contentLength;
> > > +        this.attachmentRepoDir = attachmentRepoDir;
> > > +        this.fileCacheEnable = fileCacheEnable;
> > > +        if (fileThreshold != null && (!"".equals(fileThreshold))) {
> > > +            this.fileStorageThreshold = Integer.parseInt(fileThreshold);
> > > +        } else {
> > > +            this.fileStorageThreshold = 1;
> > > +        }
> > > +        attachmentsMap = new TreeMap();
> > > +        try {
> > > +            contentType = new ContentType(contentTypeString);
> > > +        } catch (ParseException e) {
> > > +            throw new OMException(
> > > +                    "Invalid Content Type Field in the Mime Message"
> > > +                    , e);
> > > +        }
> > > +        // REVIEW: This conversion is hard-coded to UTF-8.
> > > +        // The complete solution is to respect the charset setting of the message.
> > > +        // However this may cause problems in BoundaryDelimittedStream and other
> > > +        // lower level classes.
> > > +
> > > +        // Boundary always have the prefix "--".
> > > +        try {
> > > +            String encoding = contentType.getParameter("charset");
> > > +            if(encoding == null || encoding.length()==0){
> > > +                encoding = "UTF-8";
> > > +            }
> > > +            this.boundary = ("--" + contentType.getParameter("boundary"))
> > > +                    .getBytes(encoding);
> > > +        } catch (UnsupportedEncodingException e) {
> > > +            throw new OMException(e);
> > > +        }
> > > +
> > > +        // do we need to wrap InputStream from a BufferedInputStream before
> > > +        // wrapping from PushbackStream
> > > +        pushbackInStream = new PushbackInputStream(inStream,
> > > +                                                   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 ((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
> > > +        getDataHandler(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);
> > > +    }
> > > +
> > > +    /**
> > > +     * Use this constructor when instantiating this to store the attachments set programatically
> > > +     * through the SwA API.
> > > +     */
> > > +    public Attachments() {
> > > +        attachmentsMap = new TreeMap();
> > > +        noStreams = true;
> > > +    }
> > > +
> > > +    /**
> > > +     * @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 if ((MTOMConstants.SWA_TYPE_12).equalsIgnoreCase(applicationType)) {
> > > +                this.applicationType = MTOMConstants.SWA_TYPE_12;
> > > +            } else {
> > > +                throw new OMException(
> > > +                        "Invalid Application type. Support available for MTOM & SwA only.");
> > > +            }
> > > +        }
> > > +        return this.applicationType;
> > > +    }
> > > +
> > > +    /**
> > > +     * Checks 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.
> > > +     *
> > > +     * @param blobContentID (without the surrounding angle brackets and "cid:" prefix)
> > > +     * @return The DataHandler of the mime part referred by the Content-Id or *null* if the mime
> > > +     *         part referred by the content-id does not exist
> > > +     */
> > > +    public DataHandler getDataHandler(String blobContentID) {
> > > +        DataHandler dataHandler;
> > > +        if (attachmentsMap.containsKey(blobContentID)) {
> > > +            dataHandler = (DataHandler) attachmentsMap.get(blobContentID);
> > > +            return dataHandler;
> > > +        } else if (!noStreams) {
> > > +            //This loop will be terminated by the Exceptions thrown if the Mime
> > > +            // part searching was not found
> > > +            while ((dataHandler = this.getNextPartDataHandler()) != null) {
> > > +                if (attachmentsMap.containsKey(blobContentID)) {
> > > +                    dataHandler = (DataHandler) attachmentsMap.get(blobContentID);
> > > +                    return dataHandler;
> > > +                }
> > > +            }
> > > +        }
> > > +        return null;
> > > +    }
> > > +
> > > +    /**
> > > +     * Programatically adding an SOAP with Attachments(SwA) Attachment. These attachments will get
> > > +     * serialized only if SOAP with Attachments is enabled.
> > > +     *
> > > +     * @param contentID
> > > +     * @param dataHandler
> > > +     */
> > > +    public void addDataHandler(String contentID, DataHandler dataHandler) {
> > > +        attachmentsMap.put(contentID, dataHandler);
> > > +        if (!cids.contains(contentID)) {
> > > +            cids.add(contentID);
> > > +        }
> > > +    }
> > > +
> > > +    /**
> > > +     * Removes the DataHandler corresponding to the given contenID. If it is not present, then
> > > +     * trying to find it calling the getNextPart() till the required part is found.
> > > +     *
> > > +     * @param blobContentID
> > > +     */
> > > +    public void removeDataHandler(String blobContentID) {
> > > +        DataHandler dataHandler;
> > > +        if (attachmentsMap.containsKey(blobContentID)) {
> > > +            attachmentsMap.remove(blobContentID);
> > > +        } else if (!noStreams) {
> > > +            //This loop will be terminated by the Exceptions thrown if the Mime
> > > +            // part searching was not found
> > > +            while ((dataHandler = this.getNextPartDataHandler()) != null) {
> > > +                if (attachmentsMap.containsKey(blobContentID)) {
> > > +                    attachmentsMap.remove(blobContentID);
> > > +                }
> > > +            }
> > > +        }
> > > +        if (!cids.contains(blobContentID)) {
> > > +            cids.remove(blobContentID);
> > > +        }
> > > +    }
> > > +
> > > +    /**
> > > +     * @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;
> > > +        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 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
> > > +     */
> > > +    public String getSOAPPartContentID() {
> > > +        String rootContentID = contentType.getParameter("start");
> > > +
> > > +        // 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 ("cid".equalsIgnoreCase(rootContentID.substring(0, 3))) {
> > > +            rootContentID = rootContentID.substring(4);
> > > +        }
> > > +        return rootContentID;
> > > +    }
> > > +
> > > +    public String getSOAPPartContentType() {
> > > +        if (!noStreams) {
> > > +            DataHandler soapPart = getDataHandler(getSOAPPartContentID());
> > > +            return soapPart.getContentType();
> > > +        } else {
> > > +            throw new OMException(
> > > +                    "The attachments map was created programatically. Unsupported operation.");
> > > +        }
> > > +    }
> > > +
> > > +    /**
> > > +     * 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.");
> > > +        }
> > > +        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;
> > > +    }
> > > +
> > > +    public String[] getAllContentIDs() {
> > > +        Set keys = getContentIDSet();
> > > +        return (String[]) keys.toArray(new String[keys.size()]);
> > > +    }
> > > +
> > > +    public Set getContentIDSet() {
> > > +        DataHandler dataHandler;
> > > +        while (!noStreams) {
> > > +            dataHandler = this.getNextPartDataHandler();
> > > +            if (dataHandler == null) {
> > > +                break;
> > > +            }
> > > +        }
> > > +        return attachmentsMap.keySet();
> > > +    }
> > > +
> > > +    /**
> > > +     * @return List of content ids in order of appearance in message
> > > +     */
> > > +    public List getContentIDList() {
> > > +        return cids;
> > > +    }
> > > +
> > > +    /**
> > > +     * endOfStreamReached will be set to true if the message ended in MIME Style having "--" suffix
> > > +     * with the last mime boundary
> > > +     *
> > > +     * @param value
> > > +     */
> > > +    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 DataHandler getNextPartDataHandler() throws OMException {
> > > +        if (endOfStreamReached) {
> > > +            return null;
> > > +        }
> > > +        Part nextPart;
> > > +        nextPart = getPart();
> > > +        if (nextPart == null) {
> > > +            return null;
> > > +        } else
> > > +            try {
> > > +                if (nextPart.getSize() > 0) {
> > > +                    String partContentID;
> > > +                    try {
> > > +                        partContentID = nextPart.getContentID();
> > > +
> > > +                        if (partContentID == null & partIndex == 1) {
> > > +                            String id = "firstPart_" + UUIDGenerator.getUUID();
> > > +                            firstPartId = id;
> > > +                            addDataHandler(id, nextPart.getDataHandler());
> > > +                            return nextPart.getDataHandler();
> > > +                        }
> > > +                        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 (attachmentsMap.containsKey(partContentID)) {
> > > +                            throw new OMException(
> > > +                                    "Two MIME parts with the same Content-ID not allowed.");
> > > +                        }
> > > +                        addDataHandler(partContentID, nextPart.getDataHandler());
> > > +                        return nextPart.getDataHandler();
> > > +                    } catch (MessagingException e) {
> > > +                        throw new OMException("Error reading Content-ID from the Part."
> > > +                                + e);
> > > +                    }
> > > +                } // This will take care if stream ended without having MIME
> > > +                // message terminator
> > > +                else {
> > > +                    return null;
> > > +                }
> > > +            } 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;
> > > +
> > > +        Part part;
> > > +
> > > +        try {
> > > +            if (fileCacheEnable) {
> > > +                try {
> > > +                    if (contentLength != 0 &&
> > > +                            contentLength <= fileStorageThreshold) {
> > > +                        // Since the content-length is less than the threshold, we can safely
> > > +                        // store it in memory.
> > > +                        if (log.isDebugEnabled()) {
> > > +                            log.debug("Creating an Attachment Part (in memory)");
> > > +                        }
> > > +                        MIMEBodyPartInputStream partStream =
> > > +                            new MIMEBodyPartInputStream(pushbackInStream,
> > > +                                                        boundary,
> > > +                                                        this,
> > > +                                                        PUSHBACK_SIZE);
> > > +                        part = new PartOnMemory(partStream);
> > > +                    } else if (contentLength != 0 &&
> > > +                            contentLength > fileStorageThreshold * 2) {
> > > +                        if (log.isDebugEnabled()) {
> > > +                            log.debug("Creating an Attachment Part (in a temporary file in " + attachmentRepoDir + ")");
> > > +                        }
> > > +                        // The content-length is much bigger than the threshold, then always
> > > +                        // store the attachments in a file.  This prevents unnecessary buffering.
> > > +                        // REVIEW Arbitrary heuristic.  Does this need to be fine-tuned.
> > > +                        MIMEBodyPartInputStream partStream =
> > > +                            new MIMEBodyPartInputStream(pushbackInStream,
> > > +                                                        boundary,
> > > +                                                        this,
> > > +                                                        PUSHBACK_SIZE);
> > > +                        PushbackFilePartInputStream filePartStream =
> > > +                            new PushbackFilePartInputStream(partStream, new byte[0]);
> > > +                        part = new PartOnFile(filePartStream, attachmentRepoDir);
> > > +
> > > +                    } else {
> > > +                        if (log.isDebugEnabled()) {
> > > +                            log.debug("Buffering attachment part to determine if it should be stored in memory");
> > > +                        }
> > > +                        // Read chunks of data to determine the size
> > > +                        // of the attachment.  This can wasteful because we need to gc the buffers.
> > > +                        // TODO We could look at the content-length of the individual attachment; however
> > > +                        // this is seldom provided.
> > > +
> > > +                        MIMEBodyPartInputStream partStream;
> > > +                        byte[] buffer = new byte[fileStorageThreshold];
> > > +                        partStream =
> > > +                            new MIMEBodyPartInputStream(pushbackInStream,
> > > +                                                        boundary,
> > > +                                                        this,
> > > +                                                        PUSHBACK_SIZE);
> > > +                        int count = 0;
> > > +                        int value;
> > > +                        count = partStream.read(buffer);
> > > +
> > > +                        if (count == fileStorageThreshold) {
> > > +                            if (log.isDebugEnabled()) {
> > > +                                log.debug("The calculated attachment size is " + count + ". Storing Part in file.");
> > > +                            }
> > > +                            PushbackFilePartInputStream filePartStream =
> > > +                                new PushbackFilePartInputStream(partStream, buffer);
> > > +                            part = new PartOnFile(filePartStream, attachmentRepoDir);
> > > +                        } else {
> > > +                            if (log.isDebugEnabled()) {
> > > +                                log.debug("The calculated attachment size is " + count + ". Storing Part in memory.");
> > > +                            }
> > > +                            ByteArrayInputStream byteArrayInStream =
> > > +                                new ByteArrayInputStream(buffer, 0, count);
> > > +                            part = new PartOnMemory(byteArrayInStream);
> > > +                        }
> > > +                    }
> > > +                } catch (Exception e) {
> > > +                    throw new OMException("Error creating temporary File.", e);
> > > +                }
> > > +            } else {
> > > +                MIMEBodyPartInputStream partStream;
> > > +                partStream =
> > > +                        new MIMEBodyPartInputStream(pushbackInStream, boundary, this, PUSHBACK_SIZE);
> > > +                part = new PartOnMemory(partStream);
> > > +            }
> > > +
> > > +        } catch (MessagingException e) {
> > > +            throw new OMException(e);
> > > +        }
> > > +        partIndex++;
> > > +        return part;
> > > +    }
> > >  }
> > >
> > >
> > >
> > > ---------------------------------------------------------------------
> > > To unsubscribe, e-mail: commons-dev-unsubscribe@ws.apache.org
> > > For additional commands, e-mail: commons-dev-help@ws.apache.org
> > >
> > >
> >
> >
> > --
> > Thilina Gunarathne  -  http://www.wso2.com - http://thilinag.blogspot.com
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: commons-dev-unsubscribe@ws.apache.org
> > For additional commands, e-mail: commons-dev-help@ws.apache.org
> >
> >
>
>
> --
> Davanum Srinivas :: http://davanum.wordpress.com
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: commons-dev-unsubscribe@ws.apache.org
> For additional commands, e-mail: commons-dev-help@ws.apache.org
>
>


-- 
Thilina Gunarathne  -  http://www.wso2.com - http://thilinag.blogspot.com

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@ws.apache.org
For additional commands, e-mail: commons-dev-help@ws.apache.org


Re: svn commit: r555379 - /webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/Attachments.java

Posted by Davanum Srinivas <da...@gmail.com>.
Thilina,

If you are in a hurry to review the changes see here for diff w/o
whitespace changes:
http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/Attachments.java?r1=555378&r2=555379&pathrev=555379&view=diff&diff_format=l

thanks,
dims

On 7/12/07, Thilina Gunarathne <cs...@gmail.com> wrote:
> Hi,
> Can we have a cleaner patch of the specific changes please... It would
> be really nice if you can revert this and commit the only the changed
> lines without all the formatting changes...
>
> We are on the verge of a release of Axiom and Axis2 (even the RC's
> have gone).. In that case I think we need to be much more careful when
>  committing stuff...
>
> thanks,
> Thilina
>
> > Also added a method to the Attachments class to get the attachments in order.
> AFAIK
> > This can be useful in SWA "raw" situations.  In such scenarios the xml does not have a reference cid
> > to the attachment.  Thus the receiver may wish to view the attachments in the order that they appeared
> > on the wire.  One use case is JAX-WS RPC/LIT support with SWA attachments.
> >
> > Modified:
> >     webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/Attachments.java
> >
> > 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?view=diff&rev=555379&r1=555378&r2=555379
> > ==============================================================================
> > --- 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 Wed Jul 11 12:51:52 2007
> > @@ -1,529 +1,609 @@
> > -/*
> > - * 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 org.apache.axiom.om.OMException;
> > -import org.apache.axiom.om.impl.MTOMConstants;
> > -import org.apache.axiom.om.util.UUIDGenerator;
> > -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.ByteArrayInputStream;
> > -import java.io.IOException;
> > -import java.io.InputStream;
> > -import java.io.PushbackInputStream;
> > -import java.io.UnsupportedEncodingException;
> > -import java.util.Set;
> > -import java.util.TreeMap;
> > -
> > -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;
> > -    int PUSHBACK_SIZE = 1000;
> > -
> > -    /**
> > -     * <code>attachmentsMap</code> stores the Data Handlers of the already parsed Mime Body Parts.
> > -     * This ordered Map is keyed using the content-ID's.
> > -     */
> > -    TreeMap attachmentsMap;
> > -
> > -    /** <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 */
> > -    private boolean streamsRequested = false;
> > -
> > -    /** <code>boolean</code> Indicating if any data handlers have been directly requested */
> > -    private boolean partsRequested = false;
> > -
> > -    /**
> > -     * <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 boolean noStreams = false;
> > -
> > -    private String firstPartId;
> > -
> > -    private boolean fileCacheEnable;
> > -
> > -    private String attachmentRepoDir;
> > -
> > -    private int fileStorageThreshold;
> > -
> > -    protected static Log log = LogFactory.getLog(Attachments.class);
> > -
> > -
> > -    /**
> > -     * 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;
> > -        }
> > -        attachmentsMap = new TreeMap();
> > -        try {
> > -            contentType = new ContentType(contentTypeString);
> > -        } catch (ParseException e) {
> > -            throw new OMException(
> > -                    "Invalid Content Type Field in the Mime Message"
> > -                    , e);
> > -        }
> > -        // REVIEW: This conversion is hard-coded to UTF-8.
> > -        // The complete solution is to respect the charset setting of the message.
> > -        // However this may cause problems in BoundaryDelimittedStream and other
> > -        // lower level classes.
> > -
> > -        // Boundary always have the prefix "--".
> > -        try {
> > -            String encoding = contentType.getParameter("charset");
> > -            if(encoding == null || encoding.length()==0){
> > -                encoding = "UTF-8";
> > -            }
> > -            this.boundary = ("--" + contentType.getParameter("boundary"))
> > -                    .getBytes(encoding);
> > -        } catch (UnsupportedEncodingException e) {
> > -            throw new OMException(e);
> > -        }
> > -
> > -        // do we need to wrap InputStream from a BufferedInputStream before
> > -        // wrapping from PushbackStream
> > -        pushbackInStream = new PushbackInputStream(inStream,
> > -                                                   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 ((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
> > -        getDataHandler(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);
> > -    }
> > -
> > -    /**
> > -     * Use this constructor when instantiating this to store the attachments set programatically
> > -     * through the SwA API.
> > -     */
> > -    public Attachments() {
> > -        attachmentsMap = new TreeMap();
> > -        noStreams = true;
> > -    }
> > -
> > -    /**
> > -     * @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 if ((MTOMConstants.SWA_TYPE_12).equalsIgnoreCase(applicationType)) {
> > -                this.applicationType = MTOMConstants.SWA_TYPE_12;
> > -            } else {
> > -                throw new OMException(
> > -                        "Invalid Application type. Support available for MTOM & SwA only.");
> > -            }
> > -        }
> > -        return this.applicationType;
> > -    }
> > -
> > -    /**
> > -     * Checks 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.
> > -     *
> > -     * @param blobContentID (without the surrounding angle brackets and "cid:" prefix)
> > -     * @return The DataHandler of the mime part referred by the Content-Id or *null* if the mime
> > -     *         part referred by the content-id does not exist
> > -     */
> > -    public DataHandler getDataHandler(String blobContentID) {
> > -        DataHandler dataHandler;
> > -        if (attachmentsMap.containsKey(blobContentID)) {
> > -            dataHandler = (DataHandler) attachmentsMap.get(blobContentID);
> > -            return dataHandler;
> > -        } else if (!noStreams) {
> > -            //This loop will be terminated by the Exceptions thrown if the Mime
> > -            // part searching was not found
> > -            while ((dataHandler = this.getNextPartDataHandler()) != null) {
> > -                if (attachmentsMap.containsKey(blobContentID)) {
> > -                    dataHandler = (DataHandler) attachmentsMap.get(blobContentID);
> > -                    return dataHandler;
> > -                }
> > -            }
> > -        }
> > -        return null;
> > -    }
> > -
> > -    /**
> > -     * Programatically adding an SOAP with Attachments(SwA) Attachment. These attachments will get
> > -     * serialized only if SOAP with Attachments is enabled.
> > -     *
> > -     * @param contentID
> > -     * @param dataHandler
> > -     */
> > -    public void addDataHandler(String contentID, DataHandler dataHandler) {
> > -        attachmentsMap.put(contentID, dataHandler);
> > -    }
> > -
> > -    /**
> > -     * Removes the DataHandler corresponding to the given contenID. If it is not present, then
> > -     * trying to find it calling the getNextPart() till the required part is found.
> > -     *
> > -     * @param blobContentID
> > -     */
> > -    public void removeDataHandler(String blobContentID) {
> > -        DataHandler dataHandler;
> > -        if (attachmentsMap.containsKey(blobContentID)) {
> > -            attachmentsMap.remove(blobContentID);
> > -        } else if (!noStreams) {
> > -            //This loop will be terminated by the Exceptions thrown if the Mime
> > -            // part searching was not found
> > -            while ((dataHandler = this.getNextPartDataHandler()) != null) {
> > -                if (attachmentsMap.containsKey(blobContentID)) {
> > -                    attachmentsMap.remove(blobContentID);
> > -                }
> > -            }
> > -        }
> > -    }
> > -
> > -    /**
> > -     * @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;
> > -        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 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
> > -     */
> > -    public String getSOAPPartContentID() {
> > -        String rootContentID = contentType.getParameter("start");
> > -
> > -        // 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 ("cid".equalsIgnoreCase(rootContentID.substring(0, 3))) {
> > -            rootContentID = rootContentID.substring(4);
> > -        }
> > -        return rootContentID;
> > -    }
> > -
> > -    public String getSOAPPartContentType() {
> > -        if (!noStreams) {
> > -            DataHandler soapPart = getDataHandler(getSOAPPartContentID());
> > -            return soapPart.getContentType();
> > -        } else {
> > -            throw new OMException(
> > -                    "The attachments map was created programatically. Unsupported operation.");
> > -        }
> > -    }
> > -
> > -    /**
> > -     * 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.");
> > -        }
> > -        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;
> > -    }
> > -
> > -    public String[] getAllContentIDs() {
> > -        Set keys = getContentIDSet();
> > -        return (String[]) keys.toArray(new String[keys.size()]);
> > -    }
> > -
> > -    public Set getContentIDSet() {
> > -        DataHandler dataHandler;
> > -        while (!noStreams) {
> > -            dataHandler = this.getNextPartDataHandler();
> > -            if (dataHandler == null) {
> > -                break;
> > -            }
> > -        }
> > -        return attachmentsMap.keySet();
> > -    }
> > -
> > -    /**
> > -     * endOfStreamReached will be set to true if the message ended in MIME Style having "--" suffix
> > -     * with the last mime boundary
> > -     *
> > -     * @param value
> > -     */
> > -    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 DataHandler getNextPartDataHandler() throws OMException {
> > -        if (endOfStreamReached) {
> > -            return null;
> > -        }
> > -        Part nextPart;
> > -        nextPart = getPart();
> > -        if (nextPart == null) {
> > -            return null;
> > -        } else
> > -            try {
> > -                if (nextPart.getSize() > 0) {
> > -                    String partContentID;
> > -                    try {
> > -                        partContentID = nextPart.getContentID();
> > -
> > -                        if (partContentID == null & partIndex == 1) {
> > -                            String id = "firstPart_" + UUIDGenerator.getUUID();
> > -                            attachmentsMap.put(id, nextPart.getDataHandler());
> > -                            firstPartId = id;
> > -                            return nextPart.getDataHandler();
> > -                        }
> > -                        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 (attachmentsMap.containsKey(partContentID)) {
> > -                            throw new OMException(
> > -                                    "Two MIME parts with the same Content-ID not allowed.");
> > -                        }
> > -                        attachmentsMap.put(partContentID, nextPart.getDataHandler());
> > -                        return nextPart.getDataHandler();
> > -                    } catch (MessagingException e) {
> > -                        throw new OMException("Error reading Content-ID from the Part."
> > -                                + e);
> > -                    }
> > -                } // This will take care if stream ended without having MIME
> > -                // message terminator
> > -                else {
> > -                    return null;
> > -                }
> > -            } 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;
> > -
> > -        Part part;
> > -
> > -        try {
> > -            if (fileCacheEnable) {
> > -                try {
> > -                    MIMEBodyPartInputStream partStream;
> > -                    byte[] buffer = new byte[fileStorageThreshold];
> > -                    partStream = new MIMEBodyPartInputStream(pushbackInStream,
> > -                                                             boundary, this, PUSHBACK_SIZE);
> > -                    int count = 0;
> > -                    do {
> > -                        int len;
> > -                        int off = 0;
> > -                        int rem = fileStorageThreshold;
> > -                        while ((len = partStream.read(buffer, off, rem)) > 0) {
> > -                            off = off + len;
> > -                            rem = rem - len;
> > -                        }
> > -                        count += off;
> > -                    } while (partStream.available() > 0);
> > -
> > -                    if (count == fileStorageThreshold) {
> > -                        PushbackFilePartInputStream filePartStream =
> > -                                new PushbackFilePartInputStream(
> > -                                        partStream, buffer);
> > -                        part = new PartOnFile(filePartStream, attachmentRepoDir);
> > -                    } else {
> > -                        ByteArrayInputStream byteArrayInStream = new ByteArrayInputStream(buffer,
> > -                                                                                          0, count);
> > -                        part = new PartOnMemory(byteArrayInStream);
> > -                    }
> > -                } catch (Exception e) {
> > -                    throw new OMException("Error creating temporary File.", e);
> > -                }
> > -            } else {
> > -                MIMEBodyPartInputStream partStream;
> > -                partStream = new MIMEBodyPartInputStream(pushbackInStream,
> > -                                                         boundary, this, PUSHBACK_SIZE);
> > -                part = new PartOnMemory(partStream);
> > -            }
> > -
> > -        } catch (MessagingException e) {
> > -            throw new OMException(e);
> > -        }
> > -        partIndex++;
> > -        return part;
> > -    }
> > +/*
> > + * 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 org.apache.axiom.om.OMException;
> > +import org.apache.axiom.om.impl.MTOMConstants;
> > +import org.apache.axiom.om.util.UUIDGenerator;
> > +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.ByteArrayInputStream;
> > +import java.io.IOException;
> > +import java.io.InputStream;
> > +import java.io.PushbackInputStream;
> > +import java.io.UnsupportedEncodingException;
> > +import java.util.ArrayList;
> > +import java.util.List;
> > +import java.util.Set;
> > +import java.util.TreeMap;
> > +
> > +public class Attachments {
> > +
> > +    /** <code>ContentType</code> of the MIME message */
> > +    ContentType contentType;
> > +
> > +    int contentLength; // Content Length
> > +
> > +    /** 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;
> > +    int PUSHBACK_SIZE = 1000;
> > +
> > +    /**
> > +     * <code>attachmentsMap</code> stores the Data Handlers of the already parsed Mime Body Parts.
> > +     * This ordered Map is keyed using the content-ID's.
> > +     */
> > +    TreeMap attachmentsMap;
> > +
> > +    /**
> > +     * <code>cids</code> stores the content ids in the order that the attachments
> > +     * occur in the message
> > +     */
> > +    ArrayList cids = new ArrayList();
> > +
> > +    /** <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 */
> > +    private boolean streamsRequested = false;
> > +
> > +    /** <code>boolean</code> Indicating if any data handlers have been directly requested */
> > +    private boolean partsRequested = false;
> > +
> > +    /**
> > +     * <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 boolean noStreams = false;
> > +
> > +    private String firstPartId;
> > +
> > +    private boolean fileCacheEnable;
> > +
> > +    private String attachmentRepoDir;
> > +
> > +    private int fileStorageThreshold;
> > +
> > +    protected static Log log = LogFactory.getLog(Attachments.class);
> > +
> > +
> > +    /**
> > +     * 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(inStream, contentTypeString, fileCacheEnable, attachmentRepoDir, fileThreshold, 0);
> > +    }
> > +
> > +        /**
> > +     * 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
> > +     * @param fileThreshold
> > +     * @param contentLength
> > +     * @throws OMException
> > +     */
> > +    public Attachments(InputStream inStream, String contentTypeString, boolean fileCacheEnable,
> > +            String attachmentRepoDir, String fileThreshold, int contentLength) throws OMException {
> > +        this.contentLength = contentLength;
> > +        this.attachmentRepoDir = attachmentRepoDir;
> > +        this.fileCacheEnable = fileCacheEnable;
> > +        if (fileThreshold != null && (!"".equals(fileThreshold))) {
> > +            this.fileStorageThreshold = Integer.parseInt(fileThreshold);
> > +        } else {
> > +            this.fileStorageThreshold = 1;
> > +        }
> > +        attachmentsMap = new TreeMap();
> > +        try {
> > +            contentType = new ContentType(contentTypeString);
> > +        } catch (ParseException e) {
> > +            throw new OMException(
> > +                    "Invalid Content Type Field in the Mime Message"
> > +                    , e);
> > +        }
> > +        // REVIEW: This conversion is hard-coded to UTF-8.
> > +        // The complete solution is to respect the charset setting of the message.
> > +        // However this may cause problems in BoundaryDelimittedStream and other
> > +        // lower level classes.
> > +
> > +        // Boundary always have the prefix "--".
> > +        try {
> > +            String encoding = contentType.getParameter("charset");
> > +            if(encoding == null || encoding.length()==0){
> > +                encoding = "UTF-8";
> > +            }
> > +            this.boundary = ("--" + contentType.getParameter("boundary"))
> > +                    .getBytes(encoding);
> > +        } catch (UnsupportedEncodingException e) {
> > +            throw new OMException(e);
> > +        }
> > +
> > +        // do we need to wrap InputStream from a BufferedInputStream before
> > +        // wrapping from PushbackStream
> > +        pushbackInStream = new PushbackInputStream(inStream,
> > +                                                   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 ((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
> > +        getDataHandler(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);
> > +    }
> > +
> > +    /**
> > +     * Use this constructor when instantiating this to store the attachments set programatically
> > +     * through the SwA API.
> > +     */
> > +    public Attachments() {
> > +        attachmentsMap = new TreeMap();
> > +        noStreams = true;
> > +    }
> > +
> > +    /**
> > +     * @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 if ((MTOMConstants.SWA_TYPE_12).equalsIgnoreCase(applicationType)) {
> > +                this.applicationType = MTOMConstants.SWA_TYPE_12;
> > +            } else {
> > +                throw new OMException(
> > +                        "Invalid Application type. Support available for MTOM & SwA only.");
> > +            }
> > +        }
> > +        return this.applicationType;
> > +    }
> > +
> > +    /**
> > +     * Checks 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.
> > +     *
> > +     * @param blobContentID (without the surrounding angle brackets and "cid:" prefix)
> > +     * @return The DataHandler of the mime part referred by the Content-Id or *null* if the mime
> > +     *         part referred by the content-id does not exist
> > +     */
> > +    public DataHandler getDataHandler(String blobContentID) {
> > +        DataHandler dataHandler;
> > +        if (attachmentsMap.containsKey(blobContentID)) {
> > +            dataHandler = (DataHandler) attachmentsMap.get(blobContentID);
> > +            return dataHandler;
> > +        } else if (!noStreams) {
> > +            //This loop will be terminated by the Exceptions thrown if the Mime
> > +            // part searching was not found
> > +            while ((dataHandler = this.getNextPartDataHandler()) != null) {
> > +                if (attachmentsMap.containsKey(blobContentID)) {
> > +                    dataHandler = (DataHandler) attachmentsMap.get(blobContentID);
> > +                    return dataHandler;
> > +                }
> > +            }
> > +        }
> > +        return null;
> > +    }
> > +
> > +    /**
> > +     * Programatically adding an SOAP with Attachments(SwA) Attachment. These attachments will get
> > +     * serialized only if SOAP with Attachments is enabled.
> > +     *
> > +     * @param contentID
> > +     * @param dataHandler
> > +     */
> > +    public void addDataHandler(String contentID, DataHandler dataHandler) {
> > +        attachmentsMap.put(contentID, dataHandler);
> > +        if (!cids.contains(contentID)) {
> > +            cids.add(contentID);
> > +        }
> > +    }
> > +
> > +    /**
> > +     * Removes the DataHandler corresponding to the given contenID. If it is not present, then
> > +     * trying to find it calling the getNextPart() till the required part is found.
> > +     *
> > +     * @param blobContentID
> > +     */
> > +    public void removeDataHandler(String blobContentID) {
> > +        DataHandler dataHandler;
> > +        if (attachmentsMap.containsKey(blobContentID)) {
> > +            attachmentsMap.remove(blobContentID);
> > +        } else if (!noStreams) {
> > +            //This loop will be terminated by the Exceptions thrown if the Mime
> > +            // part searching was not found
> > +            while ((dataHandler = this.getNextPartDataHandler()) != null) {
> > +                if (attachmentsMap.containsKey(blobContentID)) {
> > +                    attachmentsMap.remove(blobContentID);
> > +                }
> > +            }
> > +        }
> > +        if (!cids.contains(blobContentID)) {
> > +            cids.remove(blobContentID);
> > +        }
> > +    }
> > +
> > +    /**
> > +     * @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;
> > +        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 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
> > +     */
> > +    public String getSOAPPartContentID() {
> > +        String rootContentID = contentType.getParameter("start");
> > +
> > +        // 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 ("cid".equalsIgnoreCase(rootContentID.substring(0, 3))) {
> > +            rootContentID = rootContentID.substring(4);
> > +        }
> > +        return rootContentID;
> > +    }
> > +
> > +    public String getSOAPPartContentType() {
> > +        if (!noStreams) {
> > +            DataHandler soapPart = getDataHandler(getSOAPPartContentID());
> > +            return soapPart.getContentType();
> > +        } else {
> > +            throw new OMException(
> > +                    "The attachments map was created programatically. Unsupported operation.");
> > +        }
> > +    }
> > +
> > +    /**
> > +     * 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.");
> > +        }
> > +        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;
> > +    }
> > +
> > +    public String[] getAllContentIDs() {
> > +        Set keys = getContentIDSet();
> > +        return (String[]) keys.toArray(new String[keys.size()]);
> > +    }
> > +
> > +    public Set getContentIDSet() {
> > +        DataHandler dataHandler;
> > +        while (!noStreams) {
> > +            dataHandler = this.getNextPartDataHandler();
> > +            if (dataHandler == null) {
> > +                break;
> > +            }
> > +        }
> > +        return attachmentsMap.keySet();
> > +    }
> > +
> > +    /**
> > +     * @return List of content ids in order of appearance in message
> > +     */
> > +    public List getContentIDList() {
> > +        return cids;
> > +    }
> > +
> > +    /**
> > +     * endOfStreamReached will be set to true if the message ended in MIME Style having "--" suffix
> > +     * with the last mime boundary
> > +     *
> > +     * @param value
> > +     */
> > +    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 DataHandler getNextPartDataHandler() throws OMException {
> > +        if (endOfStreamReached) {
> > +            return null;
> > +        }
> > +        Part nextPart;
> > +        nextPart = getPart();
> > +        if (nextPart == null) {
> > +            return null;
> > +        } else
> > +            try {
> > +                if (nextPart.getSize() > 0) {
> > +                    String partContentID;
> > +                    try {
> > +                        partContentID = nextPart.getContentID();
> > +
> > +                        if (partContentID == null & partIndex == 1) {
> > +                            String id = "firstPart_" + UUIDGenerator.getUUID();
> > +                            firstPartId = id;
> > +                            addDataHandler(id, nextPart.getDataHandler());
> > +                            return nextPart.getDataHandler();
> > +                        }
> > +                        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 (attachmentsMap.containsKey(partContentID)) {
> > +                            throw new OMException(
> > +                                    "Two MIME parts with the same Content-ID not allowed.");
> > +                        }
> > +                        addDataHandler(partContentID, nextPart.getDataHandler());
> > +                        return nextPart.getDataHandler();
> > +                    } catch (MessagingException e) {
> > +                        throw new OMException("Error reading Content-ID from the Part."
> > +                                + e);
> > +                    }
> > +                } // This will take care if stream ended without having MIME
> > +                // message terminator
> > +                else {
> > +                    return null;
> > +                }
> > +            } 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;
> > +
> > +        Part part;
> > +
> > +        try {
> > +            if (fileCacheEnable) {
> > +                try {
> > +                    if (contentLength != 0 &&
> > +                            contentLength <= fileStorageThreshold) {
> > +                        // Since the content-length is less than the threshold, we can safely
> > +                        // store it in memory.
> > +                        if (log.isDebugEnabled()) {
> > +                            log.debug("Creating an Attachment Part (in memory)");
> > +                        }
> > +                        MIMEBodyPartInputStream partStream =
> > +                            new MIMEBodyPartInputStream(pushbackInStream,
> > +                                                        boundary,
> > +                                                        this,
> > +                                                        PUSHBACK_SIZE);
> > +                        part = new PartOnMemory(partStream);
> > +                    } else if (contentLength != 0 &&
> > +                            contentLength > fileStorageThreshold * 2) {
> > +                        if (log.isDebugEnabled()) {
> > +                            log.debug("Creating an Attachment Part (in a temporary file in " + attachmentRepoDir + ")");
> > +                        }
> > +                        // The content-length is much bigger than the threshold, then always
> > +                        // store the attachments in a file.  This prevents unnecessary buffering.
> > +                        // REVIEW Arbitrary heuristic.  Does this need to be fine-tuned.
> > +                        MIMEBodyPartInputStream partStream =
> > +                            new MIMEBodyPartInputStream(pushbackInStream,
> > +                                                        boundary,
> > +                                                        this,
> > +                                                        PUSHBACK_SIZE);
> > +                        PushbackFilePartInputStream filePartStream =
> > +                            new PushbackFilePartInputStream(partStream, new byte[0]);
> > +                        part = new PartOnFile(filePartStream, attachmentRepoDir);
> > +
> > +                    } else {
> > +                        if (log.isDebugEnabled()) {
> > +                            log.debug("Buffering attachment part to determine if it should be stored in memory");
> > +                        }
> > +                        // Read chunks of data to determine the size
> > +                        // of the attachment.  This can wasteful because we need to gc the buffers.
> > +                        // TODO We could look at the content-length of the individual attachment; however
> > +                        // this is seldom provided.
> > +
> > +                        MIMEBodyPartInputStream partStream;
> > +                        byte[] buffer = new byte[fileStorageThreshold];
> > +                        partStream =
> > +                            new MIMEBodyPartInputStream(pushbackInStream,
> > +                                                        boundary,
> > +                                                        this,
> > +                                                        PUSHBACK_SIZE);
> > +                        int count = 0;
> > +                        int value;
> > +                        count = partStream.read(buffer);
> > +
> > +                        if (count == fileStorageThreshold) {
> > +                            if (log.isDebugEnabled()) {
> > +                                log.debug("The calculated attachment size is " + count + ". Storing Part in file.");
> > +                            }
> > +                            PushbackFilePartInputStream filePartStream =
> > +                                new PushbackFilePartInputStream(partStream, buffer);
> > +                            part = new PartOnFile(filePartStream, attachmentRepoDir);
> > +                        } else {
> > +                            if (log.isDebugEnabled()) {
> > +                                log.debug("The calculated attachment size is " + count + ". Storing Part in memory.");
> > +                            }
> > +                            ByteArrayInputStream byteArrayInStream =
> > +                                new ByteArrayInputStream(buffer, 0, count);
> > +                            part = new PartOnMemory(byteArrayInStream);
> > +                        }
> > +                    }
> > +                } catch (Exception e) {
> > +                    throw new OMException("Error creating temporary File.", e);
> > +                }
> > +            } else {
> > +                MIMEBodyPartInputStream partStream;
> > +                partStream =
> > +                        new MIMEBodyPartInputStream(pushbackInStream, boundary, this, PUSHBACK_SIZE);
> > +                part = new PartOnMemory(partStream);
> > +            }
> > +
> > +        } catch (MessagingException e) {
> > +            throw new OMException(e);
> > +        }
> > +        partIndex++;
> > +        return part;
> > +    }
> >  }
> >
> >
> >
> > ---------------------------------------------------------------------
> > To unsubscribe, e-mail: commons-dev-unsubscribe@ws.apache.org
> > For additional commands, e-mail: commons-dev-help@ws.apache.org
> >
> >
>
>
> --
> Thilina Gunarathne  -  http://www.wso2.com - http://thilinag.blogspot.com
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: commons-dev-unsubscribe@ws.apache.org
> For additional commands, e-mail: commons-dev-help@ws.apache.org
>
>


-- 
Davanum Srinivas :: http://davanum.wordpress.com

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@ws.apache.org
For additional commands, e-mail: commons-dev-help@ws.apache.org


Re: svn commit: r555379 - /webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/Attachments.java

Posted by Thilina Gunarathne <cs...@gmail.com>.
Hi,
Can we have a cleaner patch of the specific changes please... It would
be really nice if you can revert this and commit the only the changed
lines without all the formatting changes...

We are on the verge of a release of Axiom and Axis2 (even the RC's
have gone).. In that case I think we need to be much more careful when
 committing stuff...

thanks,
Thilina

> Also added a method to the Attachments class to get the attachments in order.
AFAIK
> This can be useful in SWA "raw" situations.  In such scenarios the xml does not have a reference cid
> to the attachment.  Thus the receiver may wish to view the attachments in the order that they appeared
> on the wire.  One use case is JAX-WS RPC/LIT support with SWA attachments.
>
> Modified:
>     webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/Attachments.java
>
> 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?view=diff&rev=555379&r1=555378&r2=555379
> ==============================================================================
> --- 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 Wed Jul 11 12:51:52 2007
> @@ -1,529 +1,609 @@
> -/*
> - * 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 org.apache.axiom.om.OMException;
> -import org.apache.axiom.om.impl.MTOMConstants;
> -import org.apache.axiom.om.util.UUIDGenerator;
> -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.ByteArrayInputStream;
> -import java.io.IOException;
> -import java.io.InputStream;
> -import java.io.PushbackInputStream;
> -import java.io.UnsupportedEncodingException;
> -import java.util.Set;
> -import java.util.TreeMap;
> -
> -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;
> -    int PUSHBACK_SIZE = 1000;
> -
> -    /**
> -     * <code>attachmentsMap</code> stores the Data Handlers of the already parsed Mime Body Parts.
> -     * This ordered Map is keyed using the content-ID's.
> -     */
> -    TreeMap attachmentsMap;
> -
> -    /** <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 */
> -    private boolean streamsRequested = false;
> -
> -    /** <code>boolean</code> Indicating if any data handlers have been directly requested */
> -    private boolean partsRequested = false;
> -
> -    /**
> -     * <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 boolean noStreams = false;
> -
> -    private String firstPartId;
> -
> -    private boolean fileCacheEnable;
> -
> -    private String attachmentRepoDir;
> -
> -    private int fileStorageThreshold;
> -
> -    protected static Log log = LogFactory.getLog(Attachments.class);
> -
> -
> -    /**
> -     * 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;
> -        }
> -        attachmentsMap = new TreeMap();
> -        try {
> -            contentType = new ContentType(contentTypeString);
> -        } catch (ParseException e) {
> -            throw new OMException(
> -                    "Invalid Content Type Field in the Mime Message"
> -                    , e);
> -        }
> -        // REVIEW: This conversion is hard-coded to UTF-8.
> -        // The complete solution is to respect the charset setting of the message.
> -        // However this may cause problems in BoundaryDelimittedStream and other
> -        // lower level classes.
> -
> -        // Boundary always have the prefix "--".
> -        try {
> -            String encoding = contentType.getParameter("charset");
> -            if(encoding == null || encoding.length()==0){
> -                encoding = "UTF-8";
> -            }
> -            this.boundary = ("--" + contentType.getParameter("boundary"))
> -                    .getBytes(encoding);
> -        } catch (UnsupportedEncodingException e) {
> -            throw new OMException(e);
> -        }
> -
> -        // do we need to wrap InputStream from a BufferedInputStream before
> -        // wrapping from PushbackStream
> -        pushbackInStream = new PushbackInputStream(inStream,
> -                                                   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 ((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
> -        getDataHandler(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);
> -    }
> -
> -    /**
> -     * Use this constructor when instantiating this to store the attachments set programatically
> -     * through the SwA API.
> -     */
> -    public Attachments() {
> -        attachmentsMap = new TreeMap();
> -        noStreams = true;
> -    }
> -
> -    /**
> -     * @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 if ((MTOMConstants.SWA_TYPE_12).equalsIgnoreCase(applicationType)) {
> -                this.applicationType = MTOMConstants.SWA_TYPE_12;
> -            } else {
> -                throw new OMException(
> -                        "Invalid Application type. Support available for MTOM & SwA only.");
> -            }
> -        }
> -        return this.applicationType;
> -    }
> -
> -    /**
> -     * Checks 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.
> -     *
> -     * @param blobContentID (without the surrounding angle brackets and "cid:" prefix)
> -     * @return The DataHandler of the mime part referred by the Content-Id or *null* if the mime
> -     *         part referred by the content-id does not exist
> -     */
> -    public DataHandler getDataHandler(String blobContentID) {
> -        DataHandler dataHandler;
> -        if (attachmentsMap.containsKey(blobContentID)) {
> -            dataHandler = (DataHandler) attachmentsMap.get(blobContentID);
> -            return dataHandler;
> -        } else if (!noStreams) {
> -            //This loop will be terminated by the Exceptions thrown if the Mime
> -            // part searching was not found
> -            while ((dataHandler = this.getNextPartDataHandler()) != null) {
> -                if (attachmentsMap.containsKey(blobContentID)) {
> -                    dataHandler = (DataHandler) attachmentsMap.get(blobContentID);
> -                    return dataHandler;
> -                }
> -            }
> -        }
> -        return null;
> -    }
> -
> -    /**
> -     * Programatically adding an SOAP with Attachments(SwA) Attachment. These attachments will get
> -     * serialized only if SOAP with Attachments is enabled.
> -     *
> -     * @param contentID
> -     * @param dataHandler
> -     */
> -    public void addDataHandler(String contentID, DataHandler dataHandler) {
> -        attachmentsMap.put(contentID, dataHandler);
> -    }
> -
> -    /**
> -     * Removes the DataHandler corresponding to the given contenID. If it is not present, then
> -     * trying to find it calling the getNextPart() till the required part is found.
> -     *
> -     * @param blobContentID
> -     */
> -    public void removeDataHandler(String blobContentID) {
> -        DataHandler dataHandler;
> -        if (attachmentsMap.containsKey(blobContentID)) {
> -            attachmentsMap.remove(blobContentID);
> -        } else if (!noStreams) {
> -            //This loop will be terminated by the Exceptions thrown if the Mime
> -            // part searching was not found
> -            while ((dataHandler = this.getNextPartDataHandler()) != null) {
> -                if (attachmentsMap.containsKey(blobContentID)) {
> -                    attachmentsMap.remove(blobContentID);
> -                }
> -            }
> -        }
> -    }
> -
> -    /**
> -     * @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;
> -        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 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
> -     */
> -    public String getSOAPPartContentID() {
> -        String rootContentID = contentType.getParameter("start");
> -
> -        // 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 ("cid".equalsIgnoreCase(rootContentID.substring(0, 3))) {
> -            rootContentID = rootContentID.substring(4);
> -        }
> -        return rootContentID;
> -    }
> -
> -    public String getSOAPPartContentType() {
> -        if (!noStreams) {
> -            DataHandler soapPart = getDataHandler(getSOAPPartContentID());
> -            return soapPart.getContentType();
> -        } else {
> -            throw new OMException(
> -                    "The attachments map was created programatically. Unsupported operation.");
> -        }
> -    }
> -
> -    /**
> -     * 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.");
> -        }
> -        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;
> -    }
> -
> -    public String[] getAllContentIDs() {
> -        Set keys = getContentIDSet();
> -        return (String[]) keys.toArray(new String[keys.size()]);
> -    }
> -
> -    public Set getContentIDSet() {
> -        DataHandler dataHandler;
> -        while (!noStreams) {
> -            dataHandler = this.getNextPartDataHandler();
> -            if (dataHandler == null) {
> -                break;
> -            }
> -        }
> -        return attachmentsMap.keySet();
> -    }
> -
> -    /**
> -     * endOfStreamReached will be set to true if the message ended in MIME Style having "--" suffix
> -     * with the last mime boundary
> -     *
> -     * @param value
> -     */
> -    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 DataHandler getNextPartDataHandler() throws OMException {
> -        if (endOfStreamReached) {
> -            return null;
> -        }
> -        Part nextPart;
> -        nextPart = getPart();
> -        if (nextPart == null) {
> -            return null;
> -        } else
> -            try {
> -                if (nextPart.getSize() > 0) {
> -                    String partContentID;
> -                    try {
> -                        partContentID = nextPart.getContentID();
> -
> -                        if (partContentID == null & partIndex == 1) {
> -                            String id = "firstPart_" + UUIDGenerator.getUUID();
> -                            attachmentsMap.put(id, nextPart.getDataHandler());
> -                            firstPartId = id;
> -                            return nextPart.getDataHandler();
> -                        }
> -                        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 (attachmentsMap.containsKey(partContentID)) {
> -                            throw new OMException(
> -                                    "Two MIME parts with the same Content-ID not allowed.");
> -                        }
> -                        attachmentsMap.put(partContentID, nextPart.getDataHandler());
> -                        return nextPart.getDataHandler();
> -                    } catch (MessagingException e) {
> -                        throw new OMException("Error reading Content-ID from the Part."
> -                                + e);
> -                    }
> -                } // This will take care if stream ended without having MIME
> -                // message terminator
> -                else {
> -                    return null;
> -                }
> -            } 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;
> -
> -        Part part;
> -
> -        try {
> -            if (fileCacheEnable) {
> -                try {
> -                    MIMEBodyPartInputStream partStream;
> -                    byte[] buffer = new byte[fileStorageThreshold];
> -                    partStream = new MIMEBodyPartInputStream(pushbackInStream,
> -                                                             boundary, this, PUSHBACK_SIZE);
> -                    int count = 0;
> -                    do {
> -                        int len;
> -                        int off = 0;
> -                        int rem = fileStorageThreshold;
> -                        while ((len = partStream.read(buffer, off, rem)) > 0) {
> -                            off = off + len;
> -                            rem = rem - len;
> -                        }
> -                        count += off;
> -                    } while (partStream.available() > 0);
> -
> -                    if (count == fileStorageThreshold) {
> -                        PushbackFilePartInputStream filePartStream =
> -                                new PushbackFilePartInputStream(
> -                                        partStream, buffer);
> -                        part = new PartOnFile(filePartStream, attachmentRepoDir);
> -                    } else {
> -                        ByteArrayInputStream byteArrayInStream = new ByteArrayInputStream(buffer,
> -                                                                                          0, count);
> -                        part = new PartOnMemory(byteArrayInStream);
> -                    }
> -                } catch (Exception e) {
> -                    throw new OMException("Error creating temporary File.", e);
> -                }
> -            } else {
> -                MIMEBodyPartInputStream partStream;
> -                partStream = new MIMEBodyPartInputStream(pushbackInStream,
> -                                                         boundary, this, PUSHBACK_SIZE);
> -                part = new PartOnMemory(partStream);
> -            }
> -
> -        } catch (MessagingException e) {
> -            throw new OMException(e);
> -        }
> -        partIndex++;
> -        return part;
> -    }
> +/*
> + * 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 org.apache.axiom.om.OMException;
> +import org.apache.axiom.om.impl.MTOMConstants;
> +import org.apache.axiom.om.util.UUIDGenerator;
> +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.ByteArrayInputStream;
> +import java.io.IOException;
> +import java.io.InputStream;
> +import java.io.PushbackInputStream;
> +import java.io.UnsupportedEncodingException;
> +import java.util.ArrayList;
> +import java.util.List;
> +import java.util.Set;
> +import java.util.TreeMap;
> +
> +public class Attachments {
> +
> +    /** <code>ContentType</code> of the MIME message */
> +    ContentType contentType;
> +
> +    int contentLength; // Content Length
> +
> +    /** 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;
> +    int PUSHBACK_SIZE = 1000;
> +
> +    /**
> +     * <code>attachmentsMap</code> stores the Data Handlers of the already parsed Mime Body Parts.
> +     * This ordered Map is keyed using the content-ID's.
> +     */
> +    TreeMap attachmentsMap;
> +
> +    /**
> +     * <code>cids</code> stores the content ids in the order that the attachments
> +     * occur in the message
> +     */
> +    ArrayList cids = new ArrayList();
> +
> +    /** <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 */
> +    private boolean streamsRequested = false;
> +
> +    /** <code>boolean</code> Indicating if any data handlers have been directly requested */
> +    private boolean partsRequested = false;
> +
> +    /**
> +     * <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 boolean noStreams = false;
> +
> +    private String firstPartId;
> +
> +    private boolean fileCacheEnable;
> +
> +    private String attachmentRepoDir;
> +
> +    private int fileStorageThreshold;
> +
> +    protected static Log log = LogFactory.getLog(Attachments.class);
> +
> +
> +    /**
> +     * 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(inStream, contentTypeString, fileCacheEnable, attachmentRepoDir, fileThreshold, 0);
> +    }
> +
> +        /**
> +     * 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
> +     * @param fileThreshold
> +     * @param contentLength
> +     * @throws OMException
> +     */
> +    public Attachments(InputStream inStream, String contentTypeString, boolean fileCacheEnable,
> +            String attachmentRepoDir, String fileThreshold, int contentLength) throws OMException {
> +        this.contentLength = contentLength;
> +        this.attachmentRepoDir = attachmentRepoDir;
> +        this.fileCacheEnable = fileCacheEnable;
> +        if (fileThreshold != null && (!"".equals(fileThreshold))) {
> +            this.fileStorageThreshold = Integer.parseInt(fileThreshold);
> +        } else {
> +            this.fileStorageThreshold = 1;
> +        }
> +        attachmentsMap = new TreeMap();
> +        try {
> +            contentType = new ContentType(contentTypeString);
> +        } catch (ParseException e) {
> +            throw new OMException(
> +                    "Invalid Content Type Field in the Mime Message"
> +                    , e);
> +        }
> +        // REVIEW: This conversion is hard-coded to UTF-8.
> +        // The complete solution is to respect the charset setting of the message.
> +        // However this may cause problems in BoundaryDelimittedStream and other
> +        // lower level classes.
> +
> +        // Boundary always have the prefix "--".
> +        try {
> +            String encoding = contentType.getParameter("charset");
> +            if(encoding == null || encoding.length()==0){
> +                encoding = "UTF-8";
> +            }
> +            this.boundary = ("--" + contentType.getParameter("boundary"))
> +                    .getBytes(encoding);
> +        } catch (UnsupportedEncodingException e) {
> +            throw new OMException(e);
> +        }
> +
> +        // do we need to wrap InputStream from a BufferedInputStream before
> +        // wrapping from PushbackStream
> +        pushbackInStream = new PushbackInputStream(inStream,
> +                                                   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 ((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
> +        getDataHandler(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);
> +    }
> +
> +    /**
> +     * Use this constructor when instantiating this to store the attachments set programatically
> +     * through the SwA API.
> +     */
> +    public Attachments() {
> +        attachmentsMap = new TreeMap();
> +        noStreams = true;
> +    }
> +
> +    /**
> +     * @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 if ((MTOMConstants.SWA_TYPE_12).equalsIgnoreCase(applicationType)) {
> +                this.applicationType = MTOMConstants.SWA_TYPE_12;
> +            } else {
> +                throw new OMException(
> +                        "Invalid Application type. Support available for MTOM & SwA only.");
> +            }
> +        }
> +        return this.applicationType;
> +    }
> +
> +    /**
> +     * Checks 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.
> +     *
> +     * @param blobContentID (without the surrounding angle brackets and "cid:" prefix)
> +     * @return The DataHandler of the mime part referred by the Content-Id or *null* if the mime
> +     *         part referred by the content-id does not exist
> +     */
> +    public DataHandler getDataHandler(String blobContentID) {
> +        DataHandler dataHandler;
> +        if (attachmentsMap.containsKey(blobContentID)) {
> +            dataHandler = (DataHandler) attachmentsMap.get(blobContentID);
> +            return dataHandler;
> +        } else if (!noStreams) {
> +            //This loop will be terminated by the Exceptions thrown if the Mime
> +            // part searching was not found
> +            while ((dataHandler = this.getNextPartDataHandler()) != null) {
> +                if (attachmentsMap.containsKey(blobContentID)) {
> +                    dataHandler = (DataHandler) attachmentsMap.get(blobContentID);
> +                    return dataHandler;
> +                }
> +            }
> +        }
> +        return null;
> +    }
> +
> +    /**
> +     * Programatically adding an SOAP with Attachments(SwA) Attachment. These attachments will get
> +     * serialized only if SOAP with Attachments is enabled.
> +     *
> +     * @param contentID
> +     * @param dataHandler
> +     */
> +    public void addDataHandler(String contentID, DataHandler dataHandler) {
> +        attachmentsMap.put(contentID, dataHandler);
> +        if (!cids.contains(contentID)) {
> +            cids.add(contentID);
> +        }
> +    }
> +
> +    /**
> +     * Removes the DataHandler corresponding to the given contenID. If it is not present, then
> +     * trying to find it calling the getNextPart() till the required part is found.
> +     *
> +     * @param blobContentID
> +     */
> +    public void removeDataHandler(String blobContentID) {
> +        DataHandler dataHandler;
> +        if (attachmentsMap.containsKey(blobContentID)) {
> +            attachmentsMap.remove(blobContentID);
> +        } else if (!noStreams) {
> +            //This loop will be terminated by the Exceptions thrown if the Mime
> +            // part searching was not found
> +            while ((dataHandler = this.getNextPartDataHandler()) != null) {
> +                if (attachmentsMap.containsKey(blobContentID)) {
> +                    attachmentsMap.remove(blobContentID);
> +                }
> +            }
> +        }
> +        if (!cids.contains(blobContentID)) {
> +            cids.remove(blobContentID);
> +        }
> +    }
> +
> +    /**
> +     * @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;
> +        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 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
> +     */
> +    public String getSOAPPartContentID() {
> +        String rootContentID = contentType.getParameter("start");
> +
> +        // 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 ("cid".equalsIgnoreCase(rootContentID.substring(0, 3))) {
> +            rootContentID = rootContentID.substring(4);
> +        }
> +        return rootContentID;
> +    }
> +
> +    public String getSOAPPartContentType() {
> +        if (!noStreams) {
> +            DataHandler soapPart = getDataHandler(getSOAPPartContentID());
> +            return soapPart.getContentType();
> +        } else {
> +            throw new OMException(
> +                    "The attachments map was created programatically. Unsupported operation.");
> +        }
> +    }
> +
> +    /**
> +     * 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.");
> +        }
> +        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;
> +    }
> +
> +    public String[] getAllContentIDs() {
> +        Set keys = getContentIDSet();
> +        return (String[]) keys.toArray(new String[keys.size()]);
> +    }
> +
> +    public Set getContentIDSet() {
> +        DataHandler dataHandler;
> +        while (!noStreams) {
> +            dataHandler = this.getNextPartDataHandler();
> +            if (dataHandler == null) {
> +                break;
> +            }
> +        }
> +        return attachmentsMap.keySet();
> +    }
> +
> +    /**
> +     * @return List of content ids in order of appearance in message
> +     */
> +    public List getContentIDList() {
> +        return cids;
> +    }
> +
> +    /**
> +     * endOfStreamReached will be set to true if the message ended in MIME Style having "--" suffix
> +     * with the last mime boundary
> +     *
> +     * @param value
> +     */
> +    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 DataHandler getNextPartDataHandler() throws OMException {
> +        if (endOfStreamReached) {
> +            return null;
> +        }
> +        Part nextPart;
> +        nextPart = getPart();
> +        if (nextPart == null) {
> +            return null;
> +        } else
> +            try {
> +                if (nextPart.getSize() > 0) {
> +                    String partContentID;
> +                    try {
> +                        partContentID = nextPart.getContentID();
> +
> +                        if (partContentID == null & partIndex == 1) {
> +                            String id = "firstPart_" + UUIDGenerator.getUUID();
> +                            firstPartId = id;
> +                            addDataHandler(id, nextPart.getDataHandler());
> +                            return nextPart.getDataHandler();
> +                        }
> +                        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 (attachmentsMap.containsKey(partContentID)) {
> +                            throw new OMException(
> +                                    "Two MIME parts with the same Content-ID not allowed.");
> +                        }
> +                        addDataHandler(partContentID, nextPart.getDataHandler());
> +                        return nextPart.getDataHandler();
> +                    } catch (MessagingException e) {
> +                        throw new OMException("Error reading Content-ID from the Part."
> +                                + e);
> +                    }
> +                } // This will take care if stream ended without having MIME
> +                // message terminator
> +                else {
> +                    return null;
> +                }
> +            } 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;
> +
> +        Part part;
> +
> +        try {
> +            if (fileCacheEnable) {
> +                try {
> +                    if (contentLength != 0 &&
> +                            contentLength <= fileStorageThreshold) {
> +                        // Since the content-length is less than the threshold, we can safely
> +                        // store it in memory.
> +                        if (log.isDebugEnabled()) {
> +                            log.debug("Creating an Attachment Part (in memory)");
> +                        }
> +                        MIMEBodyPartInputStream partStream =
> +                            new MIMEBodyPartInputStream(pushbackInStream,
> +                                                        boundary,
> +                                                        this,
> +                                                        PUSHBACK_SIZE);
> +                        part = new PartOnMemory(partStream);
> +                    } else if (contentLength != 0 &&
> +                            contentLength > fileStorageThreshold * 2) {
> +                        if (log.isDebugEnabled()) {
> +                            log.debug("Creating an Attachment Part (in a temporary file in " + attachmentRepoDir + ")");
> +                        }
> +                        // The content-length is much bigger than the threshold, then always
> +                        // store the attachments in a file.  This prevents unnecessary buffering.
> +                        // REVIEW Arbitrary heuristic.  Does this need to be fine-tuned.
> +                        MIMEBodyPartInputStream partStream =
> +                            new MIMEBodyPartInputStream(pushbackInStream,
> +                                                        boundary,
> +                                                        this,
> +                                                        PUSHBACK_SIZE);
> +                        PushbackFilePartInputStream filePartStream =
> +                            new PushbackFilePartInputStream(partStream, new byte[0]);
> +                        part = new PartOnFile(filePartStream, attachmentRepoDir);
> +
> +                    } else {
> +                        if (log.isDebugEnabled()) {
> +                            log.debug("Buffering attachment part to determine if it should be stored in memory");
> +                        }
> +                        // Read chunks of data to determine the size
> +                        // of the attachment.  This can wasteful because we need to gc the buffers.
> +                        // TODO We could look at the content-length of the individual attachment; however
> +                        // this is seldom provided.
> +
> +                        MIMEBodyPartInputStream partStream;
> +                        byte[] buffer = new byte[fileStorageThreshold];
> +                        partStream =
> +                            new MIMEBodyPartInputStream(pushbackInStream,
> +                                                        boundary,
> +                                                        this,
> +                                                        PUSHBACK_SIZE);
> +                        int count = 0;
> +                        int value;
> +                        count = partStream.read(buffer);
> +
> +                        if (count == fileStorageThreshold) {
> +                            if (log.isDebugEnabled()) {
> +                                log.debug("The calculated attachment size is " + count + ". Storing Part in file.");
> +                            }
> +                            PushbackFilePartInputStream filePartStream =
> +                                new PushbackFilePartInputStream(partStream, buffer);
> +                            part = new PartOnFile(filePartStream, attachmentRepoDir);
> +                        } else {
> +                            if (log.isDebugEnabled()) {
> +                                log.debug("The calculated attachment size is " + count + ". Storing Part in memory.");
> +                            }
> +                            ByteArrayInputStream byteArrayInStream =
> +                                new ByteArrayInputStream(buffer, 0, count);
> +                            part = new PartOnMemory(byteArrayInStream);
> +                        }
> +                    }
> +                } catch (Exception e) {
> +                    throw new OMException("Error creating temporary File.", e);
> +                }
> +            } else {
> +                MIMEBodyPartInputStream partStream;
> +                partStream =
> +                        new MIMEBodyPartInputStream(pushbackInStream, boundary, this, PUSHBACK_SIZE);
> +                part = new PartOnMemory(partStream);
> +            }
> +
> +        } catch (MessagingException e) {
> +            throw new OMException(e);
> +        }
> +        partIndex++;
> +        return part;
> +    }
>  }
>
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: commons-dev-unsubscribe@ws.apache.org
> For additional commands, e-mail: commons-dev-help@ws.apache.org
>
>


-- 
Thilina Gunarathne  -  http://www.wso2.com - http://thilinag.blogspot.com

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@ws.apache.org
For additional commands, e-mail: commons-dev-help@ws.apache.org