15

My Spring Web Service Client has this custom resolver to catch SoapFaults:

public class MySoapFaultResolver extends SoapFaultMessageResolver implements FaultMessageResolver 
{
    private static Logger logger = Logger.getLogger( MySoapFaultResolver.class);

    @Override
    public void resolveFault(WebServiceMessage wsm) throws IOException 
    {
        logger.debug("entering");

//      SOAPMessage soapMessage = (SOAPMessage) wsm;   cant cast to this
        SoapMessage soapMessage = (SoapMessage) wsm;

        if( soapMessage == null) {
            logger.debug("soapMessage is null");
        } else {
            logger.debug("soapMessage is not null");
            QName om_fc = soapMessage.getFaultCode();
            String om_frs = soapMessage.getFaultReason();
            logger.debug("om_fc:" + om_fc);
            logger.debug("om_frs:" + om_frs);
            if( soapMessage.getSoapBody() == null) {
                logger.debug("soap body is null");               
            } else {
                logger.debug("soap body is not null"); 
                SoapBody sb = soapMessage.getSoapBody();
                logger.debug( sb.toString() ); // prints org.springframework.ws.soap.saaj.SaajSoap11Body@1d23bf4
                QName sb_name = sb.getName();
                logger.debug("sb_name:" + sb_name);
                Iterator<QName> iter_attr_sb = sb.getAllAttributes();
                while( iter_attr_sb.hasNext()) {
                    QName qname = iter_attr_sb.next();
                    String qname_valu = sb.getAttributeValue(qname);
                    logger.debug("attribute: " + qname + ":" + qname_valu);
                }
                if( sb.hasFault()) {
                    logger.debug("soap body has fault");
                    SoapFault sff = sb.getFault();
                    QName fc = sff.getFaultCode();
                    String fsr = sff.getFaultStringOrReason();
                    logger.debug("fc:" + fc);
                    logger.debug("fsr:" + fsr);
                    Iterator<QName> iter_attr = sff.getAllAttributes();
                    while( iter_attr.hasNext()) {
                        QName qname = iter_attr.next();
                        String qname_valu = sff.getAttributeValue(qname);
                        logger.debug("attribute: " + qname + ":" + qname_valu);
                    }
                    if( sff.getFaultDetail() == null) {
                        logger.debug("fault has no details");
                    } else {
                        logger.debug("fault has details");
                        SoapFaultDetail faultDetail = sff.getFaultDetail();
                        Iterator<SoapFaultDetailElement> detailEntries = faultDetail.getDetailEntries();
                        while( detailEntries.hasNext()) {
                            SoapFaultDetailElement detailElement = detailEntries.next();
                            logger.debug("Found SoapFaultDetailElement name:" + detailElement.getName());
                        }
                    }
                } else {
                    logger.debug("soap body does not have fault");    
                }
            }
        }
        logger.debug("exiting");

        SoapFaultClientException sfce = new SoapFaultClientException( soapMessage);
        throw new IOException( "cursesfoiledagain", sfce);
    }

which gets executed when this fault comes back from the service:

<soap:Envelope>
  <soap:Body>
    <soap:Fault>
      <faultcode>soap:Server</faultcode>
      <faultstring>Could not open connection; nested exception is org.hibernate.exception.GenericJDBCException: Could not open connection</faultstring>
    </soap:Fault>
  </soap:Body>
</soap:Envelope>

and writes this to the logfile:

MySoapFaultResolver-resolveFault] - entering
MySoapFaultResolver-resolveFault] - soapMessage is not null
MySoapFaultResolver-resolveFault] - om_fc:null
MySoapFaultResolver-resolveFault] - om_frs:null
MySoapFaultResolver-resolveFault] - soap body is not null
MySoapFaultResolver-resolveFault] - org.springframework.ws.soap.saaj.SaajSoap11Body@1d23bf4
MySoapFaultResolver-resolveFault] - sb_name:{http://schemas.xmlsoap.org/soap/envelope/}Body
MySoapFaultResolver-resolveFault] - soap body does not have fault
MySoapFaultResolver-resolveFault] - exiting

I am baffled by why the code isn't finding the SoapFault within the SoapBody. Can anyone shed some light on this? --appended-- Intriguing. I also own the webservice that's throwing this error:

public class MyOutSoapFaultInterceptor extends AbstractSoapInterceptor
{
    private static Logger logger = Logger.getLogger( MyOutSoapFaultInterceptor.class);

    public MyOutSoapFaultInterceptor()
    {
        super(Phase.MARSHAL);
    }

    @Override
    public void handleMessage( SoapMessage message) throws Fault
    {
        logger.debug("entering");

        Exception e = message.getContent( Exception.class);
        if( e == null) {
            logger.debug("e is null");
        } else {
            logger.debug("e is not null");
            logger.debug("e.getCause:" + e.getCause());
            logger.debug("e.getMessage:" + e.getMessage());
            if( e instanceof Fault) {
                logger.debug("e is instanceOf Fault");
                Fault f = (Fault) message.getContent( Fault.class);
                SoapFault sf = SoapFault.createFault((Fault) e, message.getVersion());
                logger.debug("sf is not null");
                logger.debug("sf.getCause:" + sf.getCause());
                logger.debug("sf.getMessage:" + sf.getMessage());
                logger.debug("sf.getStatusCode:" + sf.getStatusCode());
                logger.debug("sf.getCode:" + sf.getCode());
                FormsEndpointFault newFault = new FormsEndpointFault();
                newFault.setCode( sf.getStatusCode());
                newFault.setMessage( sf.getMessage());
                if( sf.hasDetails()) {
                    logger.debug("sf has details");    
                    Element eee = sf.getDetail();
                    if( eee.hasAttributes()) {
                        NamedNodeMap nnm = eee.getAttributes();
                        logger.debug("sf.details has " + nnm.getLength() + " attributes");
                        for( int ii = 0 ; ii < nnm.getLength() ; ii++ ) {
                            Node nnode = nnm.item(ii); //WARNING Nodes are recursive structures
                            logger.debug(" attribute node value:" + nnode.getNodeValue());
                        }
                    }
                    if( eee.hasChildNodes()) {
                        NodeList nl = eee.getChildNodes();
                        logger.debug("sf.details has " + nl.getLength() + " child nodes");
                        for( int ii = 0 ; ii < nl.getLength() ; ii++ ) {
                            Node nnode = nl.item(ii);
                            logger.debug(" child node value:" + nnode.getNodeValue());
                        }
                    }
                } else {
                    logger.debug("sf has no details");                
                }
            }
        }
        logger.debug("exiting");
    }

and here's what it logs:

handleMessage] - entering
handleMessage] - e is not null
handleMessage] - e.getCause:org.springframework.orm.jpa.JpaSystemException: Could not open connection; nested exception is org.hibernate.exception.GenericJDBCException: Could not open connection
handleMessage] - e.getMessage:Could not open connection; nested exception is org.hibernate.exception.GenericJDBCException: Could not open connection
handleMessage] - e is instanceOf Fault
handleMessage] - sf is not null
handleMessage] - sf.getCause:org.springframework.orm.jpa.JpaSystemException: Could not open connection; nested exception is org.hibernate.exception.GenericJDBCException: Could not open connection
handleMessage] - sf.getMessage:Could not open connection; nested exception is org.hibernate.exception.GenericJDBCException: Could not open connection
handleMessage] - sf.getStatusCode:500
handleMessage] - sf.getCode:Could not open connection; nested exception is org.hibernate.exception.GenericJDBCException: Could not open connection
handleMessage] - sf has no details
handleMessage] - exiting

-/appended--

So here we see that the fault code is set to 500 in the fault before it leaves the service but doesn't appear in the browser. TIA,

Still-learning Stev

user1201168
  • 415
  • 6
  • 19
  • You log "fc:", but in your logfile "om_fc:" is reported; the sam with "frs:". Are you sure that this code and this logfile fit together? – Uwe Allner Dec 16 '15 at 08:15
  • Yes. om_fc and om_frs and fc and fsr are all unique. fc and fsr never get logged because sb.hasFault() returns false as proven by "soap body does not have fault" as seen in logfile. – user1201168 Dec 16 '15 at 21:38
  • I suggest you debbug your code and verify what exacly do "sb.hasFault()" do. It may have at least an indication of what to do – Plínio Pantaleão Dec 22 '15 at 17:23
  • There is something so weird about your response xml message, it lacks the namespace definition. However, SAAJ should complain about it missing when trying to build the SOAPMessage. But this doesn't happen. Would you, trace the exchanged messages, level `TRACE` for `org.springframework.ws.client.MessageTracing` and give the exact output ? – Laabidi Raissi Dec 23 '15 at 23:33
  • I will give this a try and post the results, Thank You for your input. – user1201168 Dec 25 '15 at 00:23

4 Answers4

5

I've dealt with a similar issue before. In the end the problem was the HTTP status code.

In case of a SOAP error while processing the request, the SOAP HTTP server MUST issue an HTTP 500 "Internal Server Error" response and include a SOAP message in the response containing a SOAP Fault element (see section 4.4) indicating the SOAP processing error.

http://www.w3.org/TR/2000/NOTE-SOAP-20000508/

Not having the correct HTTP status code caused my client library (Metro/Glassfish) to ignore the fault section entirely.

Note that this is for Soap 1.1, however I believe that for Soap 1.2 this is different again, if this is relevant to you then you can read the specs but it looks like you're using 1.1.

Sebastiaan van den Broek
  • 5,818
  • 7
  • 40
  • 73
  • Intriguing. I do not see a fault code in the xml returned by the web service. But I also own the webservice and looking in the logs for my outbound CXF FaultInterceptor I do see where f.getStatusCode does return 500. So I think we're on the right track - the problem may lie not with the client resolver but with what the service interceptor is shlepping out. I will investigate. Thx for your help. Film at 11. – user1201168 Dec 16 '15 at 23:44
  • @user1201168 any luck with this? I'm curious about the case. – Sebastiaan van den Broek Dec 20 '15 at 11:02
  • in tinkering with the interceptor on the service I've discovered 1) turning interceptors off yields a soapfault as shown, 2) turning interceptors on and not throwing anything explicity yields a soapfault as shown, 3) turning interceptors on and throwing an explicit fault with something like throw new Fault ( new MyCustomException) yielded no soapfault at all but rather a incorrect 200-Ok response. Using SoapUI greatly speeds up the testing of what's coming back from the service. Am going to put this on the back burner until I can get my client interceptors to trigger. Thanks for your help. – user1201168 Dec 28 '15 at 18:31
3

The soap fault you are returning is missing the definition namespace which could be the reason that your code is failing to find the fault.

if( sb.hasFault()) {
   logger.debug("soap body has fault");
   ...

You may want to try including this as

<soap:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
Mike Murphy
  • 1,006
  • 8
  • 16
1

Internally, method sb.hasFault() gets all the child elements of SoapBody looking for one named Fault and defined with the URI http://schemas.xmlsoap.org/soap/envelope/. Somehow it's not finding it.

However, you can extract the information from the SoapBody object with something like this:

DOMSource source = (DOMSource)sb.getPayloadSource();
Node fault = source.getNode().getChildNodes().item(0);

String faultcode = fault.getChildNodes().item(0)  // "faultcode"
                        .getChildNodes().item(0)  // text node inside "faultcode" 
                            .getNodeValue();
String faultstring = fault.getChildNodes().item(1)  // "faultstring"
                        .getChildNodes().item(0)  // text node inside "faultstring" 
                            .getNodeValue();

Or by trying to cast the child of SoapBody directly:

SoapFault sff = null;
DOMSource source = (DOMSource)sb.getPayloadSource();
Node fault = source.getNode().getChildNodes().item(0);
if(fault instanceof SoapFault) {
    sff = (SoapFault)fault;
}
Esteban Herrera
  • 2,263
  • 2
  • 23
  • 32
  • Gracias a tu ayuda pude navegar por el arbol con algo semejante: Source ver = pr.getSource(); NodeList lstNode = ((DOMSource) ver).getNode().getChildNodes(); //TODO para reccorer automaticamente a futuro Node data = (Node) lstNode.item(0); – Juan Ignacio Liska May 14 '19 at 21:50
0

Falta un algoritmo para recorrer el árbol pero seria algo similar a esto:

faultDetail.getDetailEntries().forEachRemaining(

                        pr -> {
                            Source ver  = pr.getSource();
                            NodeList lstNode = ((DOMSource) ver).getNode().getChildNodes();

                            Node data = (Node) lstNode.item(0);