This has been really frustrating for me so I'll try explain what I know and don't now. Please make sure to see my updates at the bottom of the post.
First, we have a war that uses Spring-WS to process messages. We also have a client that uses WebServiceTemplate to send messages. We deploy on Tomcat 7.
In our current dev environment, the Tomcat 7 server is using JRE 6u24. Locally, I'm running the same version when I use the client to send a SOAP message. Everything works as expected.
When I make my local JRE 7u04, I get a 500 response and the server sees the following error:
[2012-05-05 20:19:13,179] DEBUG - org.springframework.web.servlet.FrameworkServlet.processRequest(671) | Could not complete request
org.springframework.ws.soap.saaj.SaajSoapEnvelopeException: Could not access envelope: Unable to create envelope from given source: ; nested exception is com.sun.xml.messaging.saaj.SOAPExceptionImpl: Unable to create envelope from given source:
at org.springframework.ws.soap.saaj.SaajSoapMessage.getSaajVersion(SaajSoapMessage.java:260)
at org.springframework.ws.soap.saaj.SaajSoapMessage.getImplementation(SaajSoapMessage.java:342)
at org.springframework.ws.soap.saaj.SaajSoapMessage.<init>(SaajSoapMessage.java:117)
at org.springframework.ws.soap.saaj.SaajSoapMessageFactory.createWebServiceMessage(SaajSoapMessageFactory.java:184)
at org.springframework.ws.soap.saaj.SaajSoapMessageFactory.createWebServiceMessage(SaajSoapMessageFactory.java:58)
at org.springframework.ws.transport.AbstractWebServiceConnection.receive(AbstractWebServiceConnection.java:90)
at org.springframework.ws.transport.support.WebServiceMessageReceiverObjectSupport.handleConnection(WebServiceMessageReceiverObjectSupport.java:86)
at org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter.handle(WebServiceMessageReceiverHandlerAdapter.java:57)
at org.springframework.ws.transport.http.MessageDispatcherServlet.doService(MessageDispatcherServlet.java:222)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:560)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:641)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:304)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:684)
at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:471)
at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:402)
at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:329)
at org.tuckey.web.filters.urlrewrite.NormalRewrittenUrl.doRewrite(NormalRewrittenUrl.java:195)
at org.tuckey.web.filters.urlrewrite.RuleChain.handleRewrite(RuleChain.java:159)
at org.tuckey.web.filters.urlrewrite.RuleChain.doRules(RuleChain.java:141)
at org.tuckey.web.filters.urlrewrite.UrlRewriter.processRequest(UrlRewriter.java:90)
at org.tuckey.web.filters.urlrewrite.UrlRewriteFilter.doFilter(UrlRewriteFilter.java:417)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:224)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:169)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:168)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:929)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:405)
at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:964)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:515)
at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:302)
at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
at java.lang.Thread.run(Thread.java:662)
Caused by: com.sun.xml.messaging.saaj.SOAPExceptionImpl: Unable to create envelope from given source:
at com.sun.xml.messaging.saaj.soap.EnvelopeFactory.createEnvelope(EnvelopeFactory.java:127)
at com.sun.xml.messaging.saaj.soap.ver1_2.SOAPPart1_2Impl.createEnvelopeFromSource(SOAPPart1_2Impl.java:84)
at com.sun.xml.messaging.saaj.soap.SOAPPartImpl.getEnvelope(SOAPPartImpl.java:150)
at org.springframework.ws.soap.saaj.support.SaajUtils.getSaajVersion(SaajUtils.java:155)
at org.springframework.ws.soap.saaj.SaajSoapMessage.getSaajVersion(SaajSoapMessage.java:257)
... 39 more
Caused by: javax.xml.transform.TransformerException: org.xml.sax.SAXParseException: The markup in the document preceding the root element must be well-formed.
at org.apache.xalan.transformer.TransformerIdentityImpl.transform(TransformerIdentityImpl.java:501)
at com.sun.xml.messaging.saaj.util.transform.EfficientStreamingTransformer.transform(EfficientStreamingTransformer.java:414)
at com.sun.xml.messaging.saaj.soap.EnvelopeFactory.createEnvelope(EnvelopeFactory.java:118)
... 43 more
Caused by: org.xml.sax.SAXParseException: The markup in the document preceding the root element must be well-formed.
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1231)
at com.sun.org.apache.xerces.internal.jaxp.SAXParserImpl$JAXPSAXParser.parse(SAXParserImpl.java:522)
at org.xml.sax.helpers.XMLFilterImpl.parse(XMLFilterImpl.java:333)
at org.apache.xalan.transformer.TransformerIdentityImpl.transform(TransformerIdentityImpl.java:484)
... 45 more
I've went through a lot of potential issues. However, I think I've narrowed it down to being a SAAJ issue in some respect.
In the SaajSoapMessageFactory class for Spring-WS there is a method that begins with:
public SaajSoapMessage createWebServiceMessage(InputStream inputStream) throws IOException {
MimeHeaders mimeHeaders = parseMimeHeaders(inputStream);
try {
inputStream = checkForUtf8ByteOrderMark(inputStream);
SOAPMessage saajMessage = messageFactory.createMessage(mimeHeaders, inputStream);
postProcess(saajMessage);
return new SaajSoapMessage(saajMessage, langAttributeOnSoap11FaultString, messageFactory);
}
When I send using the client with JRE 6, the inputStream AFTER the createMessage call begins like:
[60, 101, 110, 118, 58, 69, 110, 118, 101, 108, 111, 112, 101, 32, 120, 109, 108, 110, 115, 58, 101, 110, 118, 61, 34, 104, 116
Which converts to: <env:Envelope
... etc.
However, when I send using the client with JRE 7, the inputStream after the createMessage call begins like:
[101, 110, 118, 58, 69, 110, 118, 101, 108, 111, 112, 101, 32, 120, 109, 108, 110, 115, 58, 101, 110, 118, 61, 34, 104, 116
As you can see the first byte, the left angle bracket, has gone missing. I have been trying to debug down into it but I lack the com.sun.xml.internal sources to really do much effectivey.
Also of note, in JRE 6, the PushbackInputStream AFTER the checkForUtf8ByteOrderMark call actually looks like the final byte array:
[60, 101, 110, 118, 58, 69, 110, 118, 101, 108, 111, 112, 101, 32, 120, 109, 108, 110, 115, 58, 101, 110, 118, 61, 34, 104, 116,
etc
But when sending using JRE 7, it looks like this:
[60, 0, 0, 0, 0, 0, 0, 0, 0
, etc
Now, the method itself seems to follow the same flow, verifying that there is no BOM and then putting back what it took off to verify it (at least, so it appears to me).
So, I have two questions:
1) Does anyone know what's going on? Is there anything here that's ringing a bell?
2) How can I get the source for those com.sun.xml.internal packages?
I should note, I can run this locally on Tomcat in Eclipse and not seem to have any issues, but I know there's some funky class loading stuff probably going on there.
Update 1:
I was able to associate some source to the code and got into this method in SOAPPart1_2Impl:
public void setContent(Source source) throws SOAPException {
try {
if (source instanceof StreamSource) {
InputStream is = ((StreamSource) source).getInputStream();
Reader rdr = ((StreamSource) source).getReader();
if (is != null) {
this.source = new JAXMStreamSource(is);
} else if (rdr != null) {
this.source = new JAXMStreamSource(rdr);
} else {
log.severe("SAAJ0544.soap.no.valid.reader.for.src");
throw new SOAPExceptionImpl("Source does not have a valid Reader or InputStream");
}
}
Now, it takes the first path of setting the InputStream. What's very interesting is that when this is done, source has a ByteInputStream that begins as such:
[60, 0, 0, 101, 110, 118, 58, 69, 110, 118, 101, 108, 111, 112, 101, 32, 120, 109, 108, 110, 115, 58, 101,
etc
I'm very confused by this. I'll will try to figure out what exactly happens to those firs three characters -- maybe there is some side effect to the checkForUtf8ByteOrderMark method.
Update 2:
Okay, now I'm really getting somewhere. I looked into that JAXMStreamSource class and that leads to a ByteOutputStream class which has a method:
public void write(InputStream in) throws IOException {
if (in instanceof ByteArrayInputStream) {
int size = in.available();
ensureCapacity(size);
count += in.read(buf,count,size);
return;
}
while(true) {
int cap = buf.length-count;
int sz = in.read(buf,count,cap);
if(sz<0) return; // hit EOS
count += sz;
if(cap==sz)
// the buffer filled up. double the buffer
ensureCapacity(count);
}
}
On the line "int sz = in.read(buf,count,cap);" is where the two zeros after the 60 gets added. I'm still trying to figure out why and why a client, of all things, using 1.7 could have possibly had this effect.