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. " +