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 2008/09/27 01:10:12 UTC

svn commit: r699528 - in /webservices/commons/trunk/modules/axiom/modules: axiom-api/src/main/java/org/apache/axiom/attachments/impl/ axiom-api/src/main/java/org/apache/axiom/om/ axiom-api/src/main/java/org/apache/axiom/om/impl/ axiom-api/src/main/java...

Author: scheu
Date: Fri Sep 26 16:10:11 2008
New Revision: 699528

URL: http://svn.apache.org/viewvc?rev=699528&view=rev
Log:
WSCOMMONS-390
Contributor:Rich Scheuerle
Add an OMOutputFormat option that will write out "non-textual" attachments as base64.
A "textual" attachment is an attachment part that has one of the following:
  * a content-type of "text/" "attachment/xml" or "attachment/soap"
  * a content-type with a "charset" parameter.
  
A "non-textual" type is anything else (example: image/gif)

The new property is defined in OMOutputFormat:
public static final String USE_CTE_BASE64_FOR_NON_TEXTUAL_ATTACHMENTS = 
        "org.apache.axiom.om.OMFormat.use.cteBase64.forNonTextualAttachments";
        
I also included a representative test that writes and reads attachments with the property enabled and 
disabled.

Modified:
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/impl/PartOnMemoryEnhanced.java
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMOutputFormat.java
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/MIMEOutputUtils.java
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/MTOMXMLStreamWriter.java
    webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/util/CommonUtils.java
    webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/attachments/AttachmentsTest.java

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/impl/PartOnMemoryEnhanced.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/impl/PartOnMemoryEnhanced.java?rev=699528&r1=699527&r2=699528&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/impl/PartOnMemoryEnhanced.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/attachments/impl/PartOnMemoryEnhanced.java Fri Sep 26 16:10:11 2008
@@ -95,6 +95,8 @@
             InputStream is = ds.getInputStream();
             if (is instanceof BAAInputStream) {
                 ((BAAInputStream)is).writeTo(os);
+            } else {
+                BufferUtils.inputStream2OutputStream(is, os);
             }
         }
     }

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMOutputFormat.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMOutputFormat.java?rev=699528&r1=699527&r2=699528&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMOutputFormat.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/OMOutputFormat.java Fri Sep 26 16:10:11 2008
@@ -59,6 +59,19 @@
 
     public static final String ACTION_PROPERTY = "action";
     
+    // The value of this property is a Boolean.  
+    // A missing value indicates the default action, which is Boolean.FALSE
+    // If Boolean.TRUE, attachments that are "non textual" are written out with 
+    // a content-transfer-encoding type of base64.
+    // @See CommonUtils.isTextualPart for the textual part definition.
+    // 
+    // Example:
+    //   An attachment with a content-type of "image/gif" is a non-textual attachment.
+    //   An attachment with a content-type of "application/soap+xml" is an textual attachment
+    //
+    public static final String USE_CTE_BASE64_FOR_NON_TEXTUAL_ATTACHMENTS = 
+        "org.apache.axiom.om.OMFormat.use.cteBase64.forNonTextualAttachments";
+    
     HashMap map = null;  // Map of generic properties
 
 

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/MIMEOutputUtils.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/MIMEOutputUtils.java?rev=699528&r1=699527&r2=699528&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/MIMEOutputUtils.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/MIMEOutputUtils.java Fri Sep 26 16:10:11 2008
@@ -36,6 +36,7 @@
 import org.apache.axiom.om.OMException;
 import org.apache.axiom.om.OMOutputFormat;
 import org.apache.axiom.om.OMText;
+import org.apache.axiom.om.util.CommonUtils;
 import org.apache.axiom.soap.SOAP11Constants;
 import org.apache.axiom.soap.SOAP12Constants;
 import org.apache.commons.logging.Log;
@@ -114,6 +115,7 @@
         }
     }
     
+    
     /**
      * Invoked by MTOMXMLStreamWriter to write the SOAP Part and the attachements. 
      * 
@@ -132,6 +134,29 @@
                                 String contentId,
                                 String charSetEncoding, 
                                 String SOAPContentType) {
+        complete(outStream, xmlData, binaryNodeList, boundary,
+                 contentId, charSetEncoding, SOAPContentType, null);
+    }
+    /**
+     * Invoked by MTOMXMLStreamWriter to write the SOAP Part and the attachements. 
+     * 
+     * @param outStream OutputStream target
+     * @param bufferedXML String containing XML of SOAPPart
+     * @param binaryNodeList Text nodes with the attachment Data Handlers
+     * @param boundary Boundary String
+     * @param contentId Content-ID of SOAPPart
+     * @param charSetEncoding Character Encoding of SOAPPart
+     * @param SOAPContentType Content-Type of SOAPPart
+     * @param OMOutputFormat 
+     */
+    public static void complete(OutputStream outStream, 
+                                byte[] xmlData,
+                                LinkedList binaryNodeList, 
+                                String boundary, 
+                                String contentId,
+                                String charSetEncoding, 
+                                String SOAPContentType, 
+                                OMOutputFormat omOutputFormat) {
         try {
             if (isDebugEnabled) {
                 log.debug("Start: write the SOAPPart and the attachments");
@@ -166,7 +191,7 @@
                 OMText binaryNode = (OMText) binaryNodeIterator.next();
                 writeBodyPart(outStream, createMimeBodyPart(binaryNode
                         .getContentID(), (DataHandler) binaryNode
-                        .getDataHandler()), boundary);
+                        .getDataHandler(), omOutputFormat), boundary);
             }
             finishWritingMime(outStream);
             outStream.flush();
@@ -205,24 +230,53 @@
     }
 
     public static MimeBodyPart createMimeBodyPart(String contentID,
-                                                  DataHandler dataHandler)
+                                                  DataHandler dataHandler) 
             throws MessagingException {
+        return createMimeBodyPart(contentID, dataHandler, null);
+    }
+                                                  
+    public static MimeBodyPart createMimeBodyPart(String contentID,
+                                                  DataHandler dataHandler,
+                                                  OMOutputFormat omOutputFormat)
+           throws MessagingException {
+        String contentType = dataHandler.getContentType();
+        
+        // Get the content-transfer-encoding
+        String contentTransferEncoding = "binary";
+        if (dataHandler instanceof ConfigurableDataHandler) {
+            ConfigurableDataHandler configurableDataHandler = (ConfigurableDataHandler) dataHandler;
+            contentTransferEncoding = configurableDataHandler.getTransferEncoding();
+        }
+        
         if (isDebugEnabled) {
-            log.debug("Create MimeBodyPart for " + contentID);
+            log.debug("Create MimeBodyPart");
+            log.debug("  Content-ID = " + contentID);
+            log.debug("  Content-Type = " + contentType);
+            log.debug("  Content-Transfer-Encoding = " + contentTransferEncoding);
+        }
+        
+        boolean useCTEBase64 = omOutputFormat != null &&
+            Boolean.TRUE.equals(
+               omOutputFormat.getProperty(
+                   OMOutputFormat.USE_CTE_BASE64_FOR_NON_TEXTUAL_ATTACHMENTS));
+        if (useCTEBase64) {
+            if (!CommonUtils.isTextualPart(contentType) && 
+                "binary".equals(contentTransferEncoding)) {
+                if (isDebugEnabled) {
+                    log.debug(" changing Content-Transfer-Encoding from " + 
+                              contentTransferEncoding + " to base-64");
+                }
+                contentTransferEncoding = "base64";
+            }
+            
         }
-        String encoding = null;
+        
+        // Now create the mimeBodyPart for the datahandler and add the appropriate content headers
         MimeBodyPart mimeBodyPart = new MimeBodyPart();
         mimeBodyPart.setDataHandler(dataHandler);
         mimeBodyPart.addHeader("Content-ID", "<" + contentID + ">");
-        mimeBodyPart.addHeader("Content-Type", dataHandler.getContentType());
-        if (dataHandler instanceof ConfigurableDataHandler) {
-            ConfigurableDataHandler configurableDataHandler = (ConfigurableDataHandler) dataHandler;
-            encoding = configurableDataHandler.getTransferEncoding();
-        }
-        if (encoding == null) {
-            encoding = "binary";
-        }
-        mimeBodyPart.addHeader("Content-Transfer-Encoding", encoding);
+        mimeBodyPart.addHeader("Content-Type", contentType);
+        mimeBodyPart.addHeader("Content-Transfer-Encoding", contentTransferEncoding);
         return mimeBodyPart;
     }
 

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/MTOMXMLStreamWriter.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/MTOMXMLStreamWriter.java?rev=699528&r1=699527&r2=699528&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/MTOMXMLStreamWriter.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/impl/MTOMXMLStreamWriter.java Fri Sep 26 16:10:11 2008
@@ -184,7 +184,8 @@
                                          format.getMimeBoundary(),
                                          format.getRootContentId(),
                                          format.getCharSetEncoding(),
-                                         SOAPContentType);
+                                         SOAPContentType, 
+                                         format);
                 bufferedXML.close();
                 bufferedXML = null;
             } catch (UnsupportedEncodingException e) {

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/util/CommonUtils.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/util/CommonUtils.java?rev=699528&r1=699527&r2=699528&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/util/CommonUtils.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-api/src/main/java/org/apache/axiom/om/util/CommonUtils.java Fri Sep 26 16:10:11 2008
@@ -156,4 +156,32 @@
         return logStream.getLength();
     }
     
+    /** 
+     * A "textual part" has one or more of the following criteria
+     *   1) a content-type that start with "text"
+     *      "application/xml" or "application/soap"
+     *   2) has a charset parameter on the content-type.
+     *
+     * Example:
+     *   An part with a content-type of "image/gif" is a non-textual attachment.
+     *   An part with a content-type of "application/soap+xml" is an textual attachment
+     *
+     * @param contentType
+     * @return true if text, false otherwise
+     */
+    public static boolean isTextualPart(String contentType) {
+        String ct = contentType.trim();
+        if (ct.startsWith("text/") ||
+            ct.startsWith("application/soap") ||
+            ct.startsWith("application/xml")) {
+            // REVIEW: What about content-type with a type of "message"
+            return true;
+        } 
+        
+        if (ct.contains("charset")) {
+            return true;
+        }
+        return false;
+                        
+    }
 }

Modified: webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/attachments/AttachmentsTest.java
URL: http://svn.apache.org/viewvc/webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/attachments/AttachmentsTest.java?rev=699528&r1=699527&r2=699528&view=diff
==============================================================================
--- webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/attachments/AttachmentsTest.java (original)
+++ webservices/commons/trunk/modules/axiom/modules/axiom-tests/src/test/java/org/apache/axiom/attachments/AttachmentsTest.java Fri Sep 26 16:10:11 2008
@@ -20,9 +20,16 @@
 package org.apache.axiom.attachments;
 
 import org.apache.axiom.om.AbstractTestCase;
+import org.apache.axiom.om.OMElement;
+import org.apache.axiom.om.OMOutputFormat;
+import org.apache.axiom.om.impl.MTOMXMLStreamWriter;
+import org.apache.axiom.om.impl.builder.XOPAwareStAXOMBuilder;
+import org.apache.axiom.om.util.CommonUtils;
 
 import javax.activation.DataHandler;
 import javax.activation.FileDataSource;
+
+import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
@@ -41,8 +48,16 @@
     String img1FileName = "mtom/img/test.jpg";
     String img2FileName = "mtom/img/test2.jpg";
 
+    String boundary = "MIMEBoundaryurn:uuid:A3ADBAEE51A1A87B2A11443668160701";
+    String start= "0.urn:uuid:A3ADBAEE51A1A87B2A11443668160702@apache.org";
     String contentTypeString =
-            "multipart/related; boundary=\"MIMEBoundaryurn:uuid:A3ADBAEE51A1A87B2A11443668160701\"; type=\"application/xop+xml\"; start=\"<0....@apache.org>\"; start-info=\"application/soap+xml\"; charset=UTF-8;action=\"mtomSample\"";
+                        "multipart/related; " +
+                        "boundary=\"" + boundary + "\"; " +
+                        "type=\"application/xop+xml\"; " +
+                        "start=\"<" + start +">\"; " +
+                        "start-info=\"application/soap+xml\"; " +
+                        "charset=UTF-8;" +
+                        "action=\"mtomSample\"";
 
     public void testMIMEHelper() {
     }
@@ -154,6 +169,100 @@
         is = attachments.getSOAPPartInputStream();
         while (is.read() != -1) ;  
     }
+    
+    public void testWritingBinaryAttachments() throws Exception {
+
+        // Read in message: SOAPPart and 2 image attachments
+        File f = getTestResourceFile(inMimeFileName);
+        InputStream inStream = new FileInputStream(f);
+        Attachments attachments = new Attachments(inStream, contentTypeString);
+        
+        attachments.getSOAPPartInputStream();
+
+        String[] contentIDs = attachments.getAllContentIDs();
+        
+        OMOutputFormat oof = new OMOutputFormat();
+        oof.setDoOptimize(true);
+        oof.setMimeBoundary(boundary);
+        oof.setRootContentId(start);
+        
+        // Write out the message
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        MTOMXMLStreamWriter writer = new MTOMXMLStreamWriter(baos, oof);
+        
+        XOPAwareStAXOMBuilder builder = 
+            new XOPAwareStAXOMBuilder(attachments.getSOAPPartInputStream(),
+                                      attachments);
+        OMElement om = builder.getDocumentElement();
+        om.serialize(writer);
+        String outNormal = baos.toString();
+        
+        assertTrue(!outNormal.contains("base64"));
+        
+        // Now do it again but use base64 content-type-encoding for 
+        // binary attachments
+        baos = new ByteArrayOutputStream();
+        oof.setProperty(OMOutputFormat.USE_CTE_BASE64_FOR_NON_TEXTUAL_ATTACHMENTS, 
+                        Boolean.TRUE);
+        writer = new MTOMXMLStreamWriter(baos, oof);
+        builder = 
+            new XOPAwareStAXOMBuilder(attachments.getSOAPPartInputStream(),
+                                      attachments);
+        om = builder.getDocumentElement();
+        om.serialize(writer);
+        String outBase64 = baos.toString();
+        
+        
+        // Do a quick check to see if the data is base64 and is
+        // writing base64 compliant code.
+        assertTrue(outBase64.contains("base64"));
+        assertTrue(outBase64.contains("GBgcGBQgHBwcJCQgKDBQNDAsL"));
+        
+        // Now read the data back in
+        InputStream is = new ByteArrayInputStream(outBase64.getBytes());
+        Attachments attachments2 = new Attachments(is, contentTypeString);
+        
+        // Now write it back out with binary...
+        baos = new ByteArrayOutputStream();
+        oof.setProperty(OMOutputFormat.USE_CTE_BASE64_FOR_NON_TEXTUAL_ATTACHMENTS, 
+                        Boolean.FALSE);
+        writer = new MTOMXMLStreamWriter(baos, oof);
+        builder = 
+            new XOPAwareStAXOMBuilder(attachments2.getSOAPPartInputStream(),
+                                      attachments2);
+        om = builder.getDocumentElement();
+        om.serialize(writer);
+        String outBase64ToNormal = baos.toString();
+        
+        assertTrue(!outBase64ToNormal.contains("base64"));
+        
+        // Now do it again but use base64 content-type-encoding for 
+        // binary attachments
+        baos = new ByteArrayOutputStream();
+        oof.setProperty(OMOutputFormat.USE_CTE_BASE64_FOR_NON_TEXTUAL_ATTACHMENTS, 
+                        Boolean.TRUE);
+        writer = new MTOMXMLStreamWriter(baos, oof);
+        builder = 
+            new XOPAwareStAXOMBuilder(attachments2.getSOAPPartInputStream(),
+                                      attachments2);
+        om = builder.getDocumentElement();
+        om.serialize(writer);
+        String outBase64ToBase64 = baos.toString();
+        
+        // Do a quick check to see if the data is base64 and is
+        // writing base64 compliant code.
+        assertTrue(outBase64ToBase64.contains("base64"));
+        assertTrue(outBase64ToBase64.contains("GBgcGBQgHBwcJCQgKDBQNDAsL"));
+        
+        // Some quick verifications of the isTextualPart logic
+        assertTrue(CommonUtils.isTextualPart("text/xml"));
+        assertTrue(CommonUtils.isTextualPart("application/xml"));
+        assertTrue(CommonUtils.isTextualPart("application/soap+xml"));
+        assertTrue(CommonUtils.isTextualPart("foo/bar; charset=UTF-8"));
+        assertTrue(!CommonUtils.isTextualPart("image/gif"));
+        
+    }
+    
 
     private void compareStreams(InputStream data, InputStream expected) throws Exception {
         byte[] dataArray = this.getStreamAsByteArray(data, -1);