martes, 11 de marzo de 2014

...customize SOAP Fault in SwitchYard

Sometimes it may be necessary to customize the returned Fault to the client, to be consistent with the specification/contract you have for your service.
By default, SwitchYard may wrap every exception/Fault occurred in the engine (Bus, Pipeline, Handlers,...) with a SOAPFault maybe containing a SwitchYardException.

As faultcode may be customize by any service contract, or the Fault Actor:
"Applications that do not act as the ultimate destination of the SOAP message MUST include the faultactor element in a SOAP Fault element. The ultimate destination of a message MAY use the faultactor element to indicate explicitly that it generated the fault", or any other part of the SoapFault.

To do this, there might be many possibilities, but one that I have proved to work is, creating a Custom MessageComposer, to handle this.

I will show a very basic example to demonstrate how to do this.

I have done an example service that returns a DownloaderException when timeout is less than 0, as you can see in the picture below.




1. Create your Custom MessageComposer. You can extend SOAPMessageComposer to use all of it's functionality. This class is not intended for subclassing, so depending on the needs, there may be a need to overwrite a lot of stuff. For this case in particular, there is no need.
public class CustomFaultMessageComposer extends SOAPMessageComposer {

 @Override
 public SOAPBindingData decompose(Exchange exchange, SOAPBindingData target)
   throws Exception {
  SOAPBindingData data = target;
  try{
   data = super.decompose(exchange, target);
  }catch(Exception e){
   data.getSOAPMessage().getSOAPBody().addFault(new QName("100"), "ERROR in transformation");
  }
        try {
            getContextMapper().mapTo(exchange.getContext(), data);
        } catch (Exception ex) {
            throw SOAPMessages.MESSAGES.failedToMapContextPropertiesToSOAPMessage(ex);
        }
  
        return data;  
 }
}

2. Add your Custom MessageComposer to the binding:
    <sca:service name="MyServicePortType" promote="MyServiceBean/MyService">
      <sca:interface .wsdl="" interface="MyService.wsdl#wsdl.porttype(MyServicePortType)">
      <soap:binding .soap="" name="soap1">
        <soap:messagecomposer class="com.example.switchyard.soap.CustomFaultMessageComposer" unwrapped="true">
        ...
      </soap:messagecomposer></soap:binding>
    </sca:interface>

3. Deploy your application and make it throw an Exception.



Probably there are more things to check for, as MessageComposers are applied in InboundHandlers, there may be exceptions/errors that happen outside the scope of what can be controlled by the MessageComposers and may need to be controlled in some other place, like a CXF Interceptor. But I haven't been able to make it work there.

I hope this is handy for anybody.

This post is inspired by a discussion in SwitchYard forum.

Grab the code

1 comentario:

Tùng Trần dijo...

Hi Jorge, I come from SwitchYard forum. https://developer.jboss.org/thread/249761

I'm trying to figure out how to do exception handling with RESTEasy binding. At the very first time, I thought Message Composer is a silver bullet for this but I think I was wrong. The decompose() method invocation take places before JAXB bind the response message content to XML.

Additionally, the REST method's return type is immutable during the exchange, so even if I change the content type of the response message to a "Error DTO", then a class cast exception will occur.

My REST interface's method:
/*
some JAX-RS annotations
*/
public BookList getBooks()

My decompose method's code fragment:
if (exchange.getState().equals(ExchangeState.FAULT)) {

exchange.getMessage().setContent(new BaseDTO("345", "Something wrong inside..."));

}

Could you give me some idea for this?
Thanks