The situation in question is very special. Please see code below:
public void testReadUtf16() throws SOAPException, IOException {
String xml =
"" +
"<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://www.w3.org/2003/05/soap-envelope
">SOAP-ENV:Header/SOAP-ENV:Body<p:content SOAP-ENV:encodingStyle="http://
example.com/encoding" xmlns="some-uri"/></SOAP-ENV:Body></SOAP-ENV:Envelope>";
MessageFactory mFactory = MessageFactory.newInstance(SOAPConstants.SOAP_1_2_PROTOCOL);
SOAPMessage msg = mFactory.createMessage();
SOAPPart soapPart = msg.getSOAPPart();
soapPart.setContent(new StreamSource(new ByteArrayInputStream(xml.getBytes("utf-16"))));
// Try to access envelope
soapPart.getEnvelope();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
msg.writeTo(baos);
byte[] bs = baos.toByteArray();
String string = baos.toString("utf-16");
System.out.println(string);
System.out.println();
for (int i = 2; i < bs.length; i +=2)
{ System.out.printf("Character: %c %d - %d \n", string.charAt(i / 2 - 1), bs[i], bs[i + 1]); }
assertEquals((byte) 0xFE, bs[0]);
assertEquals((byte) 0xFF, bs[1]);
assertEquals((byte) 0x00, bs[2]);
assertTrue((byte) 0xFE != bs[80]);
assertTrue((byte) 0xFF != bs[81]);
}
The current SAAJ SI doesn't pass this test case. One critical point here is that if you comment out
"soapPart.getEnvelope();", then it passes. There are mainly two issues:
1. XMLDeclarationParser.parse method
Specifically,
if (utf16)
{ xmlDecl = new String(decl.getBytes(), "utf-16"); }
else
{ xmlDecl = decl; }
If utf16 is true, then xmlDec is set by decl. The problem is the set xmlDecl has a strange character
caused by BOM of decl getBytes(). Therefore,
if (utf16) { xmlDecl = new String(decl.getBytes(), "utf-16"); xmlDecl = xmlDecl.substring(xmlDecl.indexOf("<")); } else { xmlDecl = decl; }
solves the problem.
2. SOAPPartImp1_2(and 1_1)Impl.createEnvelopeFromSource()
envelop's character encoding is not set when XML declaration is set. First modify lookForXmlDecl to
return XMLDeclarationParser:
protected XMLDeclarationParser lookForXmlDecl() throws SOAPException {
...
return ev;
...
}
And createEnvelopeFromSource uses it:
protected Envelope createEnvelopeFromSource() throws SOAPException {
XMLDeclarationParser parser = lookForXmlDecl();
Source tmp = source;
source = null;
EnvelopeImpl envelope = (EnvelopeImpl)EnvelopeFactory.createEnvelope(tmp, this);
if (!envelope.getNamespaceURI().equals(SOAPConstants.URI_NS_SOAP_1_2_ENVELOPE))
{ log.severe("SAAJ0415.ver1_2.msg.invalid.soap1.2"); throw new SOAPException("InputStream does not represent a valid SOAP 1.2 Message"); }
if (!omitXmlDecl)
{ envelope.setOmitXmlDecl("no"); envelope.setXmlDecl(parser.getXmlDeclaration()); envelope.setCharsetEncoding(parser.getEncoding()); }
return envelope;
}
3. EnvelopeImp.output(OutputStream out)
When xmlDecl is not null, it is output by Writer, but the Writer is not reused. The aftermath is very
critical because BOM is rewriten between XML declaration and SOAP envelope. This problem can be
solved by the following change:
StreamResult result = new StreamResult(out);
if (xmlDecl != null)
{ OutputStreamWriter writer = new OutputStreamWriter(out, charset); writer.write(xmlDecl); writer.flush(); result = new StreamResult(writer); }
In conclusion, aforementioned solutions make the test case pass perfectly. I'm ready to submit patches
as well.
Environment
Operating System: All
Platform: All
Affected Versions
[current]