Announcement Announcement Module
Collapse
No announcement yet.
SoapFault Detail is limited to text strings in SpringWS Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • SoapFault Detail is limited to text strings in SpringWS

    My wsdl operation has a wsdl:fault defined as a complex type. Jaxb generates an object class for it, that can be marshaled.
    I would like springws to add it to org.springframework.ws.soap.SoapFaultDetail, in order to produce a fault message like this:
    Code:
    <soapenv:Envelope xmlns:soapenv="_2003/05/soap-envelope">
        <soapenv:Body>
            <soapenv:Fault>
                <soapenv:Code>
                    <soapenv:Value>soapenv:Receiver</soapenv:Value>
                </soapenv:Code>
                <soapenv:Reason>
                    <soapenv:Text xml:lang="en-US">NotFoundFaultException</soapenv:Text>
                </soapenv:Reason>
                <soapenv:Detail>
                    <ns2:NotFoundFault xmlns:ns2="_my/server/ws">
                        <ns2:workstation>1</ns2:workstation>
                    </ns2:NotFoundFault>
                </soapenv:Detail>
            </soapenv:Fault>
        </soapenv:Body>
    </soapenv:Envelope>
    Axis2 client can throw a custom exception mapped to the custom fault then.

    The Detail in soap envelope fault is defined as xs:any in soap-envelope schema.
    Do you plan to support mapping of exceptions to custom complex type soap faults in SpringWS?
    Petr

  • #2
    Hi

    I had the same problem, but fortunately Spring-WS allows for customization of generated faults.

    First, my endpoint class (class with @Endpoint annotation) has this method:
    Code:
       @PayloadRoot(localPart = "query-1", namespace = "http://at-tlumaczenia.pl/2009/test")
       public AJAXB2MarshalledResponse query1(AJAXB2MarshalledRequest request) throws AJAXB2MarshalledBusinessException, AJAXB2MarshalledGeneralException
       {
          return this.service.businessMethod(request);
       }
    Then there is a org.springframework.ws.soap.server.endpoint.Simple SoapExceptionResolver subclass defined in Spring's context:
    Code:
       <bean class="com.xyz.MyExceptionResolver">
          <property name="locale">
             <bean class="java.util.Locale">
                <constructor-arg value="pl" />
                <constructor-arg value="PL" />
             </bean>
          </property>
       </bean>
    Which looks like this:
    Code:
    public class MyExceptionResolver extends SimpleSoapExceptionResolver
    {
       @Autowired
       private Marshaller marshaller;
    
       /* (non-Javadoc)
        * @see org.springframework.ws.soap.server.endpoint.SimpleSoapExceptionResolver#customizeFault(org.springframework.ws.context.MessageContext, java.lang.Object, java.lang.Exception, org.springframework.ws.soap.SoapFault)
        */
       @Override
       protected void customizeFault(MessageContext messageContext, Object endpoint, Exception ex, SoapFault fault)
       {
          if (ex instanceof org.springframework.ws.WebServiceException) {
             SoapMessage response = (SoapMessage) messageContext.getResponse();
             Throwable rootEx = ex;
             while (rootEx.getCause() != null)
                rootEx = rootEx.getCause();
    
             String faultString = StringUtils.hasLength(rootEx.getMessage()) ? rootEx.getMessage() : rootEx.toString();
             SoapBody body = response.getSoapBody();
             fault = body.addClientOrSenderFault(rootEx.getClass().getName(), getLocale());
             fault.addFaultDetail();
             fault.getFaultDetail().addFaultDetailElement(new QName("http://at-tlumaczenia.pl/2009/test", "error")).addText(faultString);
    
             return;
          }
          
          try {
             fault.addFaultDetail();
             if (ex instanceof AJAXB2MarshalledBusinessException)
                this.marshaller.marshal(((AJAXB2MarshalledBusinessException)ex).getMyJAXB2MarshallableComplexMessage(), fault.getFaultDetail().getResult());
             else if (ex instanceof AJAXB2MarshalledGeneralException)
                fault.getFaultDetail().addFaultDetailElement(new QName("http://at-tlumaczenia.pl/2009/test", "error")).addText(((JAXB2MarshalledGeneralException)ex).getMyStringMessage());
             else if (ex instanceof NullPointerException) {
                StringWriter sw = new StringWriter();
                ex.printStackTrace(new PrintWriter(sw));
                fault.getFaultDetail().addFaultDetailElement(new QName("http://at-tlumaczenia.pl/2009/test", "error")).addText(sw.toString());
             } else
                fault.getFaultDetail().addFaultDetailElement(new QName("http://at-tlumaczenia.pl/2009/test", "error")).addText(ex.toString());
          } catch (Exception e) {
             log.error(e.getMessage(), e);
          }
       }
    }
    Though it doesn't look very simple, it really is - this resolver just deals with five types of exceptions. What you really want is these lines in your exception resolver:
    Code:
    fault.addFaultDetail();
       this.marshaller.marshal(((AJAXB2MarshalledBusinessException)ex).getMyJAXB2MarshallableComplexMessage(), fault.getFaultDetail().getResult());
    When I first saw Spring-WS it seemed lacking some features from Axi2 - particularily server-side method skeletons generated from WSDL's portType's and exceptions.

    First "feature" was only a myth - just look at generated Axis2 skeletons (nightmare) and my simple @Endpoint class with @PayloadRoot (or @SoapAction or WS-A @Action) annotated method. The "second" feature was resolved by the above code.

    A - for completenes, here's my AJAXB2MarshalledBusinessException class:
    Code:
    public class AJAXB2MarshalledBusinessException extends java.lang.Exception
    {
       private com.xyz.MyJAXB2MarshallableComplexMessage faultMessage;
    
       public AJAXB2MarshalledBusinessException()
       {
          super("AJAXB2MarshalledBusinessException");
       }
    
       public AJAXB2MarshalledBusinessException(String message)
       {
          super(message);
       }
    
       public AJAXB2MarshalledBusinessException(MyJAXB2MarshallableComplexMessage faultMessage)
       {
          this();
          this.faultMessage = faultMessage;
       }
    
       public com.xyz.MyJAXB2MarshallableComplexMessage faultMessage getFaultMessage()
       {
          return this.faultMessage;
       }
    
       public void setFaultMessage(com.xyz.MyJAXB2MarshallableComplexMessage faultMessage faultMessage)
       {
          this.faultMessage = faultMessage;
       }
    }
    Remember - code generation is double-sided sword. It saves you time at the beginning, but you have to deal with it forever. I don't use code generation in web services - these endpoints and exceptions are not so hard to hand write (though i like JAXB2 generated classes, which I don't have to fine-tune. And one more thing - at project's start everyone has lot of energy, so writing these simple Spring-WS classes is real pleasure

    regards
    Grzegorz Grzybek

    Comment

    Working...
    X