diff --git a/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapMessageFactory.java b/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapMessageFactory.java index d36f1510..98270aa2 100644 --- a/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapMessageFactory.java +++ b/core/src/main/java/org/springframework/ws/soap/saaj/SaajSoapMessageFactory.java @@ -16,8 +16,10 @@ package org.springframework.ws.soap.saaj; +import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.PushbackInputStream; import java.util.Iterator; import java.util.StringTokenizer; import javax.xml.soap.MessageFactory; @@ -146,21 +148,9 @@ public class SaajSoapMessageFactory implements SoapMessageFactory, InitializingB } public WebServiceMessage createWebServiceMessage(InputStream inputStream) throws IOException { - MimeHeaders mimeHeaders = new MimeHeaders(); - if (inputStream instanceof TransportInputStream) { - TransportInputStream transportInputStream = (TransportInputStream) inputStream; - for (Iterator headerNames = transportInputStream.getHeaderNames(); headerNames.hasNext();) { - String headerName = (String) headerNames.next(); - for (Iterator headerValues = transportInputStream.getHeaders(headerName); headerValues.hasNext();) { - String headerValue = (String) headerValues.next(); - StringTokenizer tokenizer = new StringTokenizer(headerValue, ","); - while (tokenizer.hasMoreTokens()) { - mimeHeaders.addHeader(headerName, tokenizer.nextToken().trim()); - } - } - } - } + MimeHeaders mimeHeaders = parseMimeHeaders(inputStream); try { + inputStream = checkForUtf8ByteOrderMark(inputStream); return new SaajSoapMessage(messageFactory.createMessage(mimeHeaders, inputStream)); } catch (SOAPException ex) { @@ -182,6 +172,42 @@ public class SaajSoapMessageFactory implements SoapMessageFactory, InitializingB } } + private MimeHeaders parseMimeHeaders(InputStream inputStream) throws IOException { + MimeHeaders mimeHeaders = new MimeHeaders(); + if (inputStream instanceof TransportInputStream) { + TransportInputStream transportInputStream = (TransportInputStream) inputStream; + for (Iterator headerNames = transportInputStream.getHeaderNames(); headerNames.hasNext();) { + String headerName = (String) headerNames.next(); + for (Iterator headerValues = transportInputStream.getHeaders(headerName); headerValues.hasNext();) { + String headerValue = (String) headerValues.next(); + StringTokenizer tokenizer = new StringTokenizer(headerValue, ","); + while (tokenizer.hasMoreTokens()) { + mimeHeaders.addHeader(headerName, tokenizer.nextToken().trim()); + } + } + } + } + return mimeHeaders; + } + + /** + * Checks for the UTF-8 Byte Order Mark, and removes it if present. The SAAJ RI cannot cope with these BOMs. + * + * @see SWS-393 + * @see UTF-8 BOMs + */ + private InputStream checkForUtf8ByteOrderMark(InputStream inputStream) throws IOException { + PushbackInputStream pushbackInputStream = new PushbackInputStream(new BufferedInputStream(inputStream), 3); + byte[] bom = new byte[3]; + if (pushbackInputStream.read(bom) != -1) { + // check for the UTF-8 BOM, and remove it if there. See SWS-393 + if (!(bom[0] == (byte) 0xEF && bom[1] == (byte) 0xBB && bom[2] == (byte) 0xBF)) { + pushbackInputStream.unread(bom); + } + } + return pushbackInputStream; + } + public String toString() { StringBuffer buffer = new StringBuffer("SaajSoapMessageFactory["); buffer.append(SaajUtils.getSaajVersionString()); diff --git a/core/src/test/java/org/springframework/ws/soap/soap11/AbstractSoap11MessageFactoryTestCase.java b/core/src/test/java/org/springframework/ws/soap/soap11/AbstractSoap11MessageFactoryTestCase.java index 0c6c67c3..ca1fd971 100644 --- a/core/src/test/java/org/springframework/ws/soap/soap11/AbstractSoap11MessageFactoryTestCase.java +++ b/core/src/test/java/org/springframework/ws/soap/soap11/AbstractSoap11MessageFactoryTestCase.java @@ -39,7 +39,7 @@ public abstract class AbstractSoap11MessageFactoryTestCase extends AbstractSoapM public void testCreateSoapMessageNoAttachment() throws Exception { InputStream is = AbstractSoap11MessageFactoryTestCase.class.getResourceAsStream("soap11.xml"); - final Properties headers = new Properties(); + Properties headers = new Properties(); headers.setProperty("Content-Type", "text/xml"); String soapAction = "http://springframework.org/spring-ws/Action"; headers.setProperty("SOAPAction", soapAction); @@ -112,5 +112,35 @@ public abstract class AbstractSoap11MessageFactoryTestCase extends AbstractSoapM assertNotNull("No attachment read", attachment); } + public void testCreateSoapMessageUtf8ByteOrderMark() throws Exception { + InputStream is = AbstractSoap11MessageFactoryTestCase.class.getResourceAsStream("soap11-utf8-bom.xml"); + Properties headers = new Properties(); + headers.setProperty("Content-Type", "text/xml; charset=UTF-8"); + TransportInputStream tis = new MockTransportInputStream(is, headers); + + SoapMessage message = (SoapMessage) messageFactory.createWebServiceMessage(tis); + assertEquals("Invalid soap version", SoapVersion.SOAP_11, message.getVersion()); + } + + public void testCreateSoapMessageUtf16BigEndianByteOrderMark() throws Exception { + InputStream is = AbstractSoap11MessageFactoryTestCase.class.getResourceAsStream("soap11-utf16-be-bom.xml"); + Properties headers = new Properties(); + headers.setProperty("Content-Type", "text/xml; charset=UTF-16"); + TransportInputStream tis = new MockTransportInputStream(is, headers); + + SoapMessage message = (SoapMessage) messageFactory.createWebServiceMessage(tis); + assertEquals("Invalid soap version", SoapVersion.SOAP_11, message.getVersion()); + } + + public void testCreateSoapMessageUtf16LittleEndianByteOrderMark() throws Exception { + InputStream is = AbstractSoap11MessageFactoryTestCase.class.getResourceAsStream("soap11-utf16-le-bom.xml"); + Properties headers = new Properties(); + headers.setProperty("Content-Type", "text/xml; charset=UTF-16"); + TransportInputStream tis = new MockTransportInputStream(is, headers); + + SoapMessage message = (SoapMessage) messageFactory.createWebServiceMessage(tis); + assertEquals("Invalid soap version", SoapVersion.SOAP_11, message.getVersion()); + } + } diff --git a/core/src/test/resources/org/springframework/ws/soap/soap11/soap11-utf16-be-bom.xml b/core/src/test/resources/org/springframework/ws/soap/soap11/soap11-utf16-be-bom.xml new file mode 100644 index 00000000..1517963e Binary files /dev/null and b/core/src/test/resources/org/springframework/ws/soap/soap11/soap11-utf16-be-bom.xml differ diff --git a/core/src/test/resources/org/springframework/ws/soap/soap11/soap11-utf16-le-bom.xml b/core/src/test/resources/org/springframework/ws/soap/soap11/soap11-utf16-le-bom.xml new file mode 100644 index 00000000..454ee138 Binary files /dev/null and b/core/src/test/resources/org/springframework/ws/soap/soap11/soap11-utf16-le-bom.xml differ diff --git a/core/src/test/resources/org/springframework/ws/soap/soap11/soap11-bom.xml b/core/src/test/resources/org/springframework/ws/soap/soap11/soap11-utf8-bom.xml similarity index 68% rename from core/src/test/resources/org/springframework/ws/soap/soap11/soap11-bom.xml rename to core/src/test/resources/org/springframework/ws/soap/soap11/soap11-utf8-bom.xml index 1fba6b1f..fa22ad85 100644 --- a/core/src/test/resources/org/springframework/ws/soap/soap11/soap11-bom.xml +++ b/core/src/test/resources/org/springframework/ws/soap/soap11/soap11-utf8-bom.xml @@ -1,6 +1,5 @@  - + DIS