Andrew Dinn [
http://community.jboss.org/people/adinn] replied to the discussion
"Continuing problem with XTS WS-T tests in AS trunk/CXF 2.2.9"
To view the discussion, visit:
http://community.jboss.org/message/548969#548969
--------------------------------------------------------------
'll follow up with progress on the results of making the changes
I described above.
Hmm, more regress than progress I am afraid.
The latest CXF stack wil dispatch from the client end the soap fault I am sending via the
SoapFaultService . However, it will not deliver the fault to the target endpoint. This is
not because the endpoint is masquerading as a SoapFaultService. Even if I were to install
a pucker endpoint defined by my SoapFault service WSDL the current CXF would not deliver
the message. Th eproblme is that the incoming message is actually a SoapFault and the
incoming message chain assumes, invalidly, that it must relate to an outgoing request and
ends up tripping over its own knickers with a null pointer exception.
The message gets received and routed to the relevant endpoint handler. Here is the stack
trace for the exception which is causing mesage deliver to blow up:
java.lang.NullPointerException
at
org.apache.cxf.interceptor.InFaultChainInitiatorObserver.addToChain(InFaultChainInitiatorObserver.java:62)
at
org.apache.cxf.interceptor.InFaultChainInitiatorObserver.initializeInterceptors(InFaultChainInitiatorObserver.java:59)
at
org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:95)
at
org.apache.cxf.jaxws.handler.soap.SOAPHandlerInterceptor.handleMessage(SOAPHandlerInterceptor.java:142)
at
org.apache.cxf.jaxws.handler.soap.SOAPHandlerInterceptor.handleMessage(SOAPHandlerInterceptor.java:71)
at
org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:243)
at
org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:110)
at
org.apache.cxf.transport.servlet.ServletDestination.invoke(ServletDestination.java:98)
at
org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:423)
at
org.jboss.wsf.stack.cxf.ServletControllerExt.invoke(ServletControllerExt.java:172)
at
org.jboss.wsf.stack.cxf.RequestHandlerImpl.handleHttpRequest(RequestHandlerImpl.java:57)
at org.jboss.wsf.stack.cxf.CXFServletExt.invoke(CXFServletExt.java:130)
at
org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:179)
at
org.apache.cxf.transport.servlet.AbstractHTTPServlet.doPost(AbstractHTTPServlet.java:103)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:754)
at
org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:159)
at
org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:324)
at
org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:242)
at
org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275)
at
org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at
org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:181)
at
org.jboss.modcluster.catalina.CatalinaContext$RequestListenerValve.event(CatalinaContext.java:285)
at
org.jboss.modcluster.catalina.CatalinaContext$RequestListenerValve.invoke(CatalinaContext.java:261)
at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:88)
at
org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:93)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at
org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at
org.jboss.web.tomcat.service.request.ActiveRequestResponseCacheValve.invoke(ActiveRequestResponseCacheValve.java:53)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:362)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877)
at
org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:653)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:951)
at java.lang.Thread.run(Thread.java:619)
So, things go all right up to the point where the SOAPHandlerInterceptor.handleMessage
gets invoked. It calls handleMessageInternal which calls createProtocolMessageContext. The
latter looks for incoming reference parameters and then checks to see if the message body
contains a soap fault.
protected MessageContext createProtocolMessageContext(SoapMessage
message) {
SOAPMessageContextImpl sm = new SOAPMessageContextImpl(message);
Exchange exch = message.getExchange();
setupBindingOperationInfo(exch, sm);
SOAPMessage msg = sm.getMessage();
try {
List<SOAPElement> params = new ArrayList<SOAPElement>();
message.put(MessageContext.REFERENCE_PARAMETERS, params);
SOAPHeader head = msg.getSOAPHeader();
if (head != null) {
Iterator<Node> it = CastUtils.cast(head.getChildElements());
while (it != null && it.hasNext()) {
Node nd = it.next();
if (nd instanceof SOAPElement) {
SOAPElement el = (SOAPElement)nd;
if (el.hasAttributeNS(Names.WSA_NAMESPACE_NAME,
"IsReferenceParameter")
&&
("1".equals(el.getAttributeNS(Names.WSA_NAMESPACE_NAME,
"IsReferenceParameter"))
||
Boolean.parseBoolean(el.getAttributeNS(Names.WSA_NAMESPACE_NAME,
"IsReferenceParameter")))) {
params.add(el);
}
}
}
}
if (msg.getSOAPPart().getEnvelope().getBody() != null
&& msg.getSOAPPart().getEnvelope().getBody().hasFault()) {
return null;
}
} catch (SOAPException e) {
throw new Fault(e);
}
return sm;
}
Since the incoming message is indeed a soap fault it returns null rather than a soap
message context. Now this would be appropriate if the message was a fault being delivered
as part of a RPC MEP or on the outbound side of a OneWay MEP exchange indicating a
delivery problem. However, this is not the case. This fault is the payload of an incoming
OneWay MEP message.
Because createProtocolMessageContext returns null handleMessageInternal returns true
without processing the message any further.
private boolean handleMessageInternal(SoapMessage message) {
MessageContext context = createProtocolMessageContext(message);
if (context == null) {
return true;
}
. . .
handleMessage goes on to set up an XMLStreamReader on the SOAPMessage and runs
SAAJInterceptor.replaceHeaders as per any incoming message and then, because it assumes
this is a fault rather than a normal payload, aborts the current interceptor chain and
runs the endpoint's incoming fault observer onMessage routine which is where we enter
the exception stack trace shown above.
public void handleMessage(SoapMessage message) {
if (binding.getHandlerChain().isEmpty()) {
return;
}
if (getInvoker(message).getProtocolHandlers().isEmpty()) {
return;
}
checkUnderstoodHeaders(message);
if (getInvoker(message).isOutbound()) {
if (!chainAlreadyContainsSAAJ(message)) {
SAAJ_OUT.handleMessage(message);
}
message.getInterceptorChain().add(ending);
} else {
boolean isFault = handleMessageInternal(message);
SOAPMessage msg = message.getContent(SOAPMessage.class);
if (msg != null) {
XMLStreamReader xmlReader = createXMLStreamReaderFromSOAPMessage(msg);
message.setContent(XMLStreamReader.class, xmlReader);
// replace headers
try {
SAAJInInterceptor.replaceHeaders(msg, message);
} catch (SOAPException e) {
e.printStackTrace();
}
}
if (isFault) {
Endpoint ep = message.getExchange().get(Endpoint.class);
message.getInterceptorChain().abort();
if (ep.getInFaultObserver() != null) {
ep.getInFaultObserver().onMessage(message);
}
}
}
}
The observer is an InFaultChainInitiatorObserver whose onMessage method is inherited from
super AbstractFaultChainInitiatorObserver. This runs to the point where it calls
initializeInterceptors which eventually calls addToChain. The null pointer exception
happens here
private void addToChain(PhaseInterceptorChain chain, Message m) {
Collection<InterceptorProvider> providers
= CastUtils.cast((Collection<?>)m.get(Message.INTERCEPTOR_PROVIDERS));
// <= blow up!
if (providers != null) {
for (InterceptorProvider p : providers) {
chain.add(p.getInFaultInterceptors());
}
}
Collection<Interceptor> is =
CastUtils.cast((Collection<?>)m.get(Message.FAULT_IN_INTERCEPTORS));
if (is != null) {
chain.add(is);
}
}
It appears that m.get(Message.INTERCEPTOR_PROVIDERS) returns null. I cannot actually find
any place in the CXF source where this value is ever put so I don't really know if
this is the only thing which is wrong. Anyway, I think the fact that we have now switched
to fautl processing rather than delivering an incoming fault message is the root cause of
the problem here. I think createProtocolMessageContext needs to be changed as follows:
protected MessageContext createProtocolMessageContext(SoapMessage
message) {
SOAPMessageContextImpl sm = new SOAPMessageContextImpl(message);
. . .
if (msg.getSOAPPart().getEnvelope().getBody() != null
&& msg.getSOAPPart().getEnvelope().getBody().hasFault()
&& !(isRequestor(msg) && isInbound())) { // <== allow
incoming fault request
return null;
}
} catch (SOAPException e) {
throw new Fault(e);
}
return sm;
}
Sorry but I'm going to have to raise a Blocker JIRA for this on Jonathan's
instructions. It is not possible for the update to CXF to go out in the product if it
breaks XTS -- at least not without a decision from on high.
--------------------------------------------------------------
Reply to this message by going to Community
[
http://community.jboss.org/message/548969#548969]
Start a new discussion in JBoss Web Services CXF at Community
[
http://community.jboss.org/choose-container!input.jspa?contentType=1&...]