Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

How to add information to a SOAP fault message with EJB 3 based web services

DZone's Guide to

How to add information to a SOAP fault message with EJB 3 based web services

· Java Zone
Free Resource

Never build auth again! The Okta Developer Platform makes it simple to implement authentication, authorization, MFA and more in Java applications. Get started with the free API.

Are you building a Java web service based on EJB3?
Do you need to return a more significant message to your web service clients other that just the exception message or even worst the recurring javax.transaction.TransactionRolledbackException?
Well if the answer is YES to the above questions then keep reading...

The code in this article has been tested with JBoss 5.1.0 but it should (!?) work on other EJB containers as well
  1. Create a base application exception that will be extended by all the other exception, I will refer to it as MyApplicationBaseException .
    This exception contains a list of UserMessage, again a class I created with some messages and locale information
  2. You need to create a javax.xml.ws.handler.soap.SOAPHandler < SOAPMessageContext > implementation. Mine looks like this
    	
    import java.util.Set;
    import javax.xml.bind.JAXBContext;
    import javax.xml.bind.JAXBException;
    import javax.xml.bind.Marshaller;
    import javax.xml.namespace.QName;
    import javax.xml.soap.SOAPException;
    import javax.xml.soap.SOAPFault;
    import javax.xml.soap.SOAPMessage;
    import javax.xml.ws.handler.MessageContext;
    import javax.xml.ws.handler.soap.SOAPHandler;
    import javax.xml.ws.handler.soap.SOAPMessageContext;
     
    import org.apache.commons.lang.exception.ExceptionUtils;
     
    public class SoapExceptionHandler implements SOAPHandler<SOAPMessageContext> {
     private transient Logger logger = ServiceLogFactory.getLogger(SoapExceptionHandler.class);
     
     @Override
     public void close(MessageContext context) { }
     
     @Override
     public boolean handleFault(SOAPMessageContext context) {
      try {
       boolean outbound = (Boolean) context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY);
     
       if (outbound) {
        logger.info("Processing " + context + " for exceptions");
        SOAPMessage msg = ((SOAPMessageContext) context).getMessage();
        SOAPFault fault = msg.getSOAPBody().getFault();
        // Retrives the exception from the context
        Exception ex = (Exception) context.get("exception");
        if (ex != null) {
         // Add a fault to the body if not there already
         if (fault == null) {
          fault = msg.getSOAPBody().addFault();
         }
         // Get my exception
         int indexOfType = ExceptionUtils.indexOfType(ex, MyApplicationBaseException.class);
         if (indexOfType != -1) {
          ex = (MyApplicationBaseException)ExceptionUtils.getThrowableList(ex).get(indexOfType);
          MyApplicationBaseException myEx = (AmsException) ex;
          fault.setFaultString(myEx.getMessage());
          try {
           JAXBContext jaxContext = JAXBContext.newInstance(UserMessages.class);
           Marshaller marshaller = jaxContext.createMarshaller();
           //Add the UserMessage xml as a fault detail. Detail interface extends Node
           marshaller.marshal(amsEx.getUserMessages(), fault.addDetail());
          } catch (JAXBException e) {
           throw new RuntimeException("Can't marshall the user message ", e);
          }
         }else {
          logger.info("This is not an AmsException");
         }
        }else {
         logger.warn("No exception found in the webServiceContext");
        }
       }
     
      } catch (SOAPException e) {
       logger.warn("Error when trying to access the soap message", e);
      }
      return true;
     }
     
     @Override
     public boolean handleMessage(SOAPMessageContext context) {
      return true;
     }
     
     @Override
     public Set<QName> getHeaders() {
      return null;
     }
      
      
    }
  3. Now that you have the exception handler you need to register this SoapHandler with the EJB. To do that you'll need to create an Xml file in your class path and add an annotation to the EJB implementation class.
    The xml file :
    <?xml version="1.0" encoding="UTF-8"?>
       <jws:handler-chains xmlns:jws="http://java.sun.com/xml/ns/javaee">
     
         <jws:handler-chain>
        <jws:handler>
          <jws:handler-name>ExceptionHandler</jws:handler-name>
          <jws:handler-class>com.mycompany.utilities.ExceptionHandler</jws:handler-class>
        </jws:handler>
         </jws:handler-chain>
     
       </jws:handler-chains>

    and the EJB with annotation will be

    import javax.jws.HandlerChain;
       
      @Local(MyService.class)
      @Stateless
      @HandlerChain(file = "soapHandler.xml")
      @Interceptors( { MyApplicationInterceptor.class })
      @SOAPBinding(style = SOAPBinding.Style.RPC)
      @WebService(endpointInterface = "com.mycompany.services.myservice", targetNamespace = "http://myservice.services.mycompany.com")
      public final class MyServiceImpl implements MyService {
       
      // service implementation
       
      }

  4. To make sure all my exceptions have proper messages and that the exception is set in the SOAPMessageContext I use an Interceptor to wrap all the service methods and transform any exception to an instance of MyApplicationException
    The interceptor has a single method
    @AroundInvoke
      private Object setException(InvocationContext ic) throws Exception {
       Object toReturn = null;
       try {
        toReturn = ic.proceed();
       } catch (Exception e) {
        logger.error("Exception during the request processing.", e);
        //converts any exception to MyApplicationException
        e = MyApplicationExceptionHandler.getMyApplicationException(e);
        if (context != null && context.getMessageContext() != null) {
         context.getMessageContext().put("exception", e);
        }
        throw e;
       }
       return toReturn;
      }
  5. That's it! You're done.

From http://www.devinprogress.info/2011/02/how-to-add-information-to-soap-fault.html

Build and launch faster with Okta’s user management API. Register today for the free forever developer edition!

Topics:

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}