Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to create custom soap fault message using spring web service

Tags:

spring-ws

I'm trying to write a web service using spring web service library. I'm able to configure my endpoints successfully and it is working fine, but I'm facing some issues with the exception mappings.

I'm able to map exceptions using @SoapFault and SoapFaultAnnotationExceptionResolver but the wsdl definition is as follows

<xsd:schema elementFormDefault="qualified" targetNamespace="http://abc.com/soap/">
    <xsd:complexType name="ServiceException">
        <xsd:sequence>
            <xsd:element name="message" nillable="true" type="xsd:string"/>
        </xsd:sequence>
    </xsd:complexType>

    <xsd:complexType name="ValidationException">
        <xsd:complexContent>
            <xsd:extension base="tns:ServiceException">
                <xsd:sequence/>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>

    <xsd:complexType name="InternalException">
        <xsd:complexContent>
            <xsd:extension base="tns:ServiceException">
                <xsd:sequence/>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>

    <xsd:complexType name="AuthenticationException">
        <xsd:complexContent>
            <xsd:extension base="tns:ServiceException">
                <xsd:sequence/>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>

    <xsd:complexType name="LoginInput">
        <xsd:sequence>
            <xsd:element minOccurs="1" maxOccurs="1" name="UserName" nillable="false" type="xsd:string"/>
            <xsd:element minOccurs="1" maxOccurs="1" name="PassWord" nillable="false" type="xsd:string"/>
        </xsd:sequence>
    </xsd:complexType>

    <xsd:complexType name="LoginOutput">
        <xsd:sequence>
            <xsd:element minOccurs="1" maxOccurs="1" name="ValidTo" nillable="false" type="xsd:dateTime"/>
        </xsd:sequence>
    </xsd:complexType>

    <xsd:element name="login" type="tns:LoginInput"/>
    <xsd:element name="loginResponse" type="tns:LoginOutput"/>

    <xsd:element name="ValidationException" type="tns:ValidationException"/>
    <xsd:element name="InternalException" type="tns:InternalException"/>
    <xsd:element name="AuthenticationException" type="tns:AuthenticationException"/>
</xsd:schema>

<message name="LoginRequest">
    <part name="login" element="tns:login"/>
</message>

<message name="LoginResponse">
    <part name="loginResponse" element="tns:loginResponse"/>
</message>

<message name="ValidationException">
    <part name="ValidationException" element="tns:ValidationException"/>
</message>

<message name="InternalException">
    <part name="InternalException" element="tns:InternalException"/>
</message>

<message name="AuthenticationException">
    <part name="AuthenticationException" element="tns:AuthenticationException"/>
</message>

<portType name="ServicePortType">
    <operation name="Login">
        <input message="tns:LoginRequest"/>
        <output message="tns:LoginResponse"/>
        <fault name="ValidationException" message="tns:ValidationException"/>
        <fault name="InternalException" message="tns:InternalException"/>
        <fault name="AuthenticationException" message="tns:AuthenticationException"/>
    </operation>
</portType>

<binding name="ServiceBinding" type="tns:ServicePortType">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="Login">
        <soap:operation soapAction="urn://Service#Login"/>
        <input>
            <soap:body use="literal"/>
        </input>
        <output>
            <soap:body use="literal"/>
        </output>
        <fault name="ValidationException">
            <soap:fault name="ValidationException" use="literal"/>
        </fault>
        <fault name="InternalException">
            <soap:fault name="InternalException" use="literal"/>
        </fault>
        <fault name="AuthenticationException">
            <soap:fault name="AuthenticationException" use="literal"/>
        </fault>
    </operation>
</binding>

How can I write a exception handling for this service definition?

Thank you

like image 683
Arun P Johny Avatar asked Nov 08 '10 04:11

Arun P Johny


People also ask

How do you handle SOAP fault in spring boots?

One way is writing your custom interceptor which implements Spring WS's ClientInterceptor interface. You should override handleFault method to handle SOAP faults with your custom logic. Then you need to register your custom Interceptor class as an interceptor at your SOAP client config class.

How do I set the SOAP fault code in Java?

The following line of code creates a SOAPFault object and adds it to body. SOAPFault fault = body. addFault(); The SOAPFault interface provides convenience methods that create an element, add the new element to the SOAPFault object, and add a text node, all in one operation.

What is a SOAP fault message?

A SOAP fault is an error in a SOAP (Simple Object Access Protocol) communication resulting from incorrect message format, header-processing problems, or incompatibility between applications.


1 Answers

After some more search I found this from spring source forum.

SoapMessage response = (SoapMessage) messageContext.getResponse();
SoapBody soapBody = response.getSoapBody();

SoapFault soapFault =
soapBody.addClientOrSenderFault(ex.getMessage(), Locale.ENGLISH);

SoapFaultDetail faultDetail = soapFault.addFaultDetail();
Result result = faultDetail.getResult();

// My detail XML object
InvalidArgumentFault fault = new InvalidArgumentFault();
fault.setErrorCode("Custom Error Code");
fault.setOpsMessage("This is the ops message");
fault.setSystemMessage("This is the system message");

// Marshal the detail. We have to use the ObjectFactory which isn't
// marshaller agnostic because the detail element doesn't have an
// XmlRootElement tag as required by JAXB.
ObjectFactory of = new ObjectFactory();
mMarshaller.marshal(of.createInvalidArgumentFault( fault), result);

UPDATED

This a complete sample implementations I'm using,

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;

import javax.xml.transform.Result;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.oxm.Marshaller;
import org.springframework.ws.context.MessageContext;
import org.springframework.ws.server.endpoint.AbstractEndpointExceptionResolver;
import org.springframework.ws.soap.SoapBody;
import org.springframework.ws.soap.SoapFault;
import org.springframework.ws.soap.SoapFaultDetail;
import org.springframework.ws.soap.SoapMessage;

import org.apache.commons.lang.StringUtils;

public class CustomSoapFaultDetailAnnotationExceptionResolver extends
        AbstractEndpointExceptionResolver implements ApplicationContextAware {
    private static Log log = LogFactory
            .getLog(CustomSoapFaultDetailAnnotationExceptionResolver.class);

    private Collection<Marshaller> marshallers;

    private Map<Class<? extends Object>, Marshaller> marshallerMap = new HashMap<Class<? extends Object>, Marshaller>();

    public CustomSoapFaultDetailAnnotationExceptionResolver() {
        setWarnLogCategory(getClass().getCanonicalName());
    }

    @Override
    protected boolean resolveExceptionInternal(MessageContext messageContext,
            Object endpoint, Exception ex) {
        boolean resolved = false;

        try {
            CustomSoapFaultDetails annotation = ex.getClass().getAnnotation(
                    CustomSoapFaultDetails.class);
            if (annotation != null) {

                Method m = ex.getClass().getMethod("getFaultInfo",
                        new Class[] {});
                Object fault = m.invoke(ex, new Object[] {});

                SoapMessage response = (SoapMessage) messageContext
                        .getResponse();
                SoapBody soapBody = response.getSoapBody();

                SoapFault soapFault = soapBody
                        .addClientOrSenderFault(
                                StringUtils.isBlank(ex.getMessage()) ? "server exception"
                                        : ex.getMessage(), Locale.ENGLISH);

                SoapFaultDetail faultDetail = soapFault.addFaultDetail();
                Result result = faultDetail.getResult();

                if (marshallerMap.containsKey(fault.getClass())) {
                    marshallerMap.get(fault.getClass()).marshal(fault, result);
                    resolved = true;
                } else {
                    for (Marshaller marshaller : marshallers) {
                        try {
                            marshaller.marshal(fault, result);
                            marshallerMap.put(fault.getClass(), marshaller);
                            resolved = true;
                            break;
                        } catch (Exception e) {
                            // Ignore error
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error(e.toString(), e);
        }
        return resolved;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.marshallers = applicationContext.getBeansOfType(Marshaller.class)
                .values();
    }
}
like image 163
Arun P Johny Avatar answered Dec 07 '22 22:12

Arun P Johny