Author: scheu
Date: Tue Sep 21 16:48:06 2010
New Revision: 999495

URL: http://svn.apache.org/viewvc?rev=999495&view=rev
Log:
Axis2-4826
Contributor: Rich Scheuerle
Summary:
  Wrote a test to isolate a problem with the marshaller embedding a BOM within 
a message.
  Provided a fix to the marshaling code to detect this situation and skip the 
BOM.

Added:
    
axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/datasource/jaxb/BOMOutputStreamFilter.java
Modified:
    
axis/axis2/java/core/trunk/modules/jaxws-integration/test/org/apache/axis2/jaxws/jaxb/string/JAXBStringUTF16Tests.java
    
axis/axis2/java/core/trunk/modules/jaxws-integration/test/org/apache/axis2/jaxws/provider/stringmsg/StringMessageProvider.java
    
axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/datasource/jaxb/JAXBDSContext.java

Modified: 
axis/axis2/java/core/trunk/modules/jaxws-integration/test/org/apache/axis2/jaxws/jaxb/string/JAXBStringUTF16Tests.java
URL: 
http://svn.apache.org/viewvc/axis/axis2/java/core/trunk/modules/jaxws-integration/test/org/apache/axis2/jaxws/jaxb/string/JAXBStringUTF16Tests.java?rev=999495&r1=999494&r2=999495&view=diff
==============================================================================
--- 
axis/axis2/java/core/trunk/modules/jaxws-integration/test/org/apache/axis2/jaxws/jaxb/string/JAXBStringUTF16Tests.java
 (original)
+++ 
axis/axis2/java/core/trunk/modules/jaxws-integration/test/org/apache/axis2/jaxws/jaxb/string/JAXBStringUTF16Tests.java
 Tue Sep 21 16:48:06 2010
@@ -10,6 +10,7 @@ import javax.xml.ws.WebServiceException;
 
 public class JAXBStringUTF16Tests extends AbstractTestCase {
     String axisEndpoint = 
"http://localhost:6060/axis2/services/JAXBStringService.JAXBStringPortTypeImplPort";;
+    String axis2ProviderEndpoint = 
"http://localhost:6060/axis2/services/StringMessageProviderService.StringMessageProviderPort";;
 
     public static Test suite() {
         return getTestSetup(new TestSuite(JAXBStringUTF16Tests.class));
@@ -22,6 +23,12 @@ public class JAXBStringUTF16Tests extend
     private void runTest16(String value, String value1) {
         runTestWithUTF16(value, value1);
     }
+    
+    public void testSimpleString16BOM() throws Exception {
+        // Call the Axis2 StringMessageProvider which has a check to ensure
+        // that the BOM for UTF-16 is not written inside the message.
+        runTestWithEncoding("a simple string", "a simple string", "UTF-16", 
axis2ProviderEndpoint);
+    }
 
     public void testSimpleString16() throws Exception {
         runTest16("a simple string");
@@ -76,13 +83,15 @@ public class JAXBStringUTF16Tests extend
     private void runTestWithUTF16(String input, String output) {
         runTestWithEncoding(input, output, "UTF-16");
     }
-
     private void runTestWithEncoding(String input, String output, String 
encoding) {
+        runTestWithEncoding(input, output, encoding, axisEndpoint);
+    }
+    private void runTestWithEncoding(String input, String output, String 
encoding, String endpoint) {
         TestLogger.logger.debug("Test : " + getName());
         try {
             JAXBStringPortType myPort = (new 
JAXBStringService()).getJAXBStringPort();
             BindingProvider p = (BindingProvider) myPort;
-            
p.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, 
axisEndpoint);
+            
p.getRequestContext().put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, endpoint);
 
             if (encoding != null) {
                 
p.getRequestContext().put(org.apache.axis2.Constants.Configuration.CHARACTER_SET_ENCODING,
 encoding);

Modified: 
axis/axis2/java/core/trunk/modules/jaxws-integration/test/org/apache/axis2/jaxws/provider/stringmsg/StringMessageProvider.java
URL: 
http://svn.apache.org/viewvc/axis/axis2/java/core/trunk/modules/jaxws-integration/test/org/apache/axis2/jaxws/provider/stringmsg/StringMessageProvider.java?rev=999495&r1=999494&r2=999495&view=diff
==============================================================================
--- 
axis/axis2/java/core/trunk/modules/jaxws-integration/test/org/apache/axis2/jaxws/provider/stringmsg/StringMessageProvider.java
 (original)
+++ 
axis/axis2/java/core/trunk/modules/jaxws-integration/test/org/apache/axis2/jaxws/provider/stringmsg/StringMessageProvider.java
 Tue Sep 21 16:48:06 2010
@@ -23,12 +23,15 @@ import org.apache.axis2.jaxws.TestLogger
 
 import javax.xml.ws.BindingType;
 import javax.xml.ws.Provider;
+import javax.xml.ws.Service;
+import javax.xml.ws.ServiceMode;
 import javax.xml.ws.WebServiceProvider;
 import javax.xml.ws.soap.SOAPBinding;
 import javax.xml.ws.http.HTTPBinding;
 
 @WebServiceProvider(serviceName="StringMessageProviderService")
 @BindingType(SOAPBinding.SOAP11HTTP_BINDING)
+...@servicemode(value=Service.Mode.MESSAGE)
 public class StringMessageProvider implements Provider<String> {
     private static String responseGood = "<?xml version='1.0' 
encoding='utf-8'?><soapenv:Envelope 
xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\";><soapenv:Header 
/><soapenv:Body><provider><message>request 
processed</message></provider></soapenv:Body></soapenv:Envelope>";
     private static String responseBad  = "<?xml version='1.0' 
encoding='utf-8'?><soapenv:Envelope 
xmlns:soapenv=\"http://schemas.xmlsoap.org/soap/envelope/\";><soapenv:Header 
/><soapenv:Body><provider><message>ERROR:null request 
received</message><provider></soapenv:Body></soapenv:Envelope>";
@@ -39,6 +42,22 @@ public class StringMessageProvider imple
             TestLogger.logger.debug(">> StringMessageProvider received a new 
request");
             TestLogger.logger.debug(">> request [" + str + "]");
             
+            // Make sure there are no extra characters (like a BOM) between 
the Body tag and the operation element
+            if (str.contains("echo")) {
+                if (str.contains("Body><echo")) {
+                    // Good data...replace the echo with echoResponse
+                    TestLogger.logger.debug("Valid");
+                    str = str.replaceAll("echo", "echoResponse");
+                    str = str.replaceAll("arg", "response");
+                    return str;
+                   
+                } else {
+                    TestLogger.logger.debug("Bad Data detected after the SOAP 
body");
+                    // Bad data...simply return the bad response..this will 
cause an exception on the client
+                    return responseBad;
+                }
+            }
+
             return responseGood;
         }
         TestLogger.logger.debug(">> ERROR:null request received");

Added: 
axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/datasource/jaxb/BOMOutputStreamFilter.java
URL: 
http://svn.apache.org/viewvc/axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/datasource/jaxb/BOMOutputStreamFilter.java?rev=999495&view=auto
==============================================================================
--- 
axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/datasource/jaxb/BOMOutputStreamFilter.java
 (added)
+++ 
axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/datasource/jaxb/BOMOutputStreamFilter.java
 Tue Sep 21 16:48:06 2010
@@ -0,0 +1,112 @@
+/*
+ * 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.axis2.datasource.jaxb;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+/**
+ * Strip off the BOM when serializing an embedded JAXB object.
+ */
+public class BOMOutputStreamFilter extends FilterOutputStream {
+
+    private static final Log log = 
LogFactory.getLog(BOMOutputStreamFilter.class);
+    int count = 0;
+    int bomLength = 2;
+    
+    
+    /**
+     * Create a BOMOutputStreamFilter to remove the BOM (Byte Order Mark)
+     * @param encoding
+     * @param out
+     */
+    public BOMOutputStreamFilter(String encoding, OutputStream out) {
+        super(out);
+        if (encoding == null || encoding.equalsIgnoreCase("UTF-8")) {
+            bomLength = 0;
+        } else if (encoding.equalsIgnoreCase("UTF-16") || 
+                   encoding.equalsIgnoreCase("UTF-16LE") || 
+                   encoding.equalsIgnoreCase("UTF-16LE")) {
+            bomLength = 2;  // FF FE or FE FF
+        } else if (encoding.equalsIgnoreCase("UTF-32")) {
+            // Currently not valid for SOAP...adding for completeness
+            bomLength = 4;  // 00 00 FE FF or FF FE 00 00 
+        } else {
+            
+            bomLength = 0;
+            if (log.isDebugEnabled()) {
+                log.debug("Don't know the BOM length for " + encoding + ". 
assuming zero.");
+            }
+        }
+        if (log.isDebugEnabled()) {
+            log.debug("encoding = " + encoding);
+            log.debug("expected BOM length = " + bomLength);
+        }
+    }
+
+
+    @Override
+    public void write(int b) throws IOException {
+        // Don't write the first two characters because they represent the BOM
+        if (count >= bomLength) {
+            out.write(b);
+        } else {
+            if (b == 0 ||  // 0x00
+                b == -1 || // 0xFF
+                b == -2) { // 0xFE
+                // skip...this is a BOM character
+                if (log.isDebugEnabled()) {
+                    log.debug("Skipping BOM character " + b);
+                }
+            } else {
+                out.write(b);
+            }
+        }
+        count++;
+    }
+    
+    @Override
+    public void write(byte[] b) throws IOException {
+        // Delegate to our 3 argument write method
+        this.write(b, 0, b.length);
+    }
+    
+    
+    @Override
+    public void write(byte[] b, int off, int len) throws IOException {
+        // Delegate the first couple of bytes to our 
+        // single argument write constructor
+        while (count < bomLength && len > 0) {
+            this.write(b[off]);
+            off++;
+            len--;
+        }
+        
+        // Delegate the remaining bytes to the target output stream
+        if (len > 0) {
+            out.write(b, off, len);
+            count = count + len;
+        }
+    }
+}
+

Modified: 
axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/datasource/jaxb/JAXBDSContext.java
URL: 
http://svn.apache.org/viewvc/axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/datasource/jaxb/JAXBDSContext.java?rev=999495&r1=999494&r2=999495&view=diff
==============================================================================
--- 
axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/datasource/jaxb/JAXBDSContext.java
 (original)
+++ 
axis/axis2/java/core/trunk/modules/jaxws/src/org/apache/axis2/datasource/jaxb/JAXBDSContext.java
 Tue Sep 21 16:48:06 2010
@@ -45,6 +45,7 @@ import javax.xml.bind.JAXBContext;
 import javax.xml.bind.JAXBElement;
 import javax.xml.bind.JAXBException;
 import javax.xml.bind.Marshaller;
+import javax.xml.bind.PropertyException;
 import javax.xml.bind.Unmarshaller;
 import javax.xml.bind.attachment.AttachmentMarshaller;
 import javax.xml.bind.attachment.AttachmentUnmarshaller;
@@ -479,7 +480,7 @@ public class JAXBDSContext {
                 // XMLStreamWriter. 
                 // Take advantage of this optimization if there is an output 
stream.
                 try {
-                    OutputStream os = (optimize) ? getOutputStream(writer) : 
null;
+                    OutputStream os = (optimize) ? getOutputStream(writer,m) : 
null;
                     if (os != null) {
                         if (DEBUG_ENABLED) {
                             log.debug("Invoking marshalByElement.  " +
@@ -524,9 +525,11 @@ public class JAXBDSContext {
     /**
      * If the writer is backed by an OutputStream, then return the OutputStream
      * @param writer
+     * @param Marshaller
      * @return OutputStream or null
      */
-    private static OutputStream getOutputStream(XMLStreamWriter writer) throws 
XMLStreamException {
+    private static OutputStream getOutputStream(XMLStreamWriter writer, 
+                Marshaller m) throws XMLStreamException {
         if (log.isDebugEnabled()) {
             log.debug("XMLStreamWriter is " + writer);
         }
@@ -543,6 +546,23 @@ public class JAXBDSContext {
                 log.debug("OutputStream accessible from XMLStreamWriterWithOS 
is " + os);
             }
         }
+        if (os != null) {
+            String marshallerEncoding = null;
+            try {
+                marshallerEncoding = (String) 
m.getProperty(Marshaller.JAXB_ENCODING);
+            } catch (PropertyException e) {
+                if (DEBUG_ENABLED) {
+                    log.debug("Could not query JAXB_ENCODING..Continuing. " + 
e);
+                }
+            }
+            if (marshallerEncoding != null && 
!marshallerEncoding.equalsIgnoreCase("UTF-8")) {
+                if (DEBUG_ENABLED) {
+                    log.debug("Wrapping output stream to remove BOM");
+                }
+                os = new BOMOutputStreamFilter(marshallerEncoding, os);
+            }
+        }
+
         return os;
     }
     
@@ -951,7 +971,7 @@ public class JAXBDSContext {
                     }
 
                     // If the output stream is available, marshal directly to 
it
-                    OutputStream os = (optimize) ? getOutputStream(writer) : 
null;
+                    OutputStream os = (optimize) ? getOutputStream(writer, m) 
: null;
                     if (os == null){ 
                         if (DEBUG_ENABLED) {
                             log.debug("Invoking marshalByType.  " +


Reply via email to