Announcement Announcement Module
Collapse
No announcement yet.
Is it possible to throw custom WSDL faults? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Is it possible to throw custom WSDL faults?

    We would like to define our own custom faults in our WSDL- mainly just to differentiate the types of errors that are occurring rather than providing any complex structure on the fault.
    So far we have only been able get Spring-WS to send SOAP faults (i.e <soapenv:fault/> elements) back to the client - we cannot get it to return our custom faults.
    The relevant pieces of WSDL looks like this:

    ...
    <types...
    <schema...
    <xsd:element name="authenticatonFault" type="xsd:string"/>
    ...
    <message name="authenticatonFault">
    <part name="authenticatonFault" element="tns:authenticatonFault"/>
    </message>

    <portType...
    <operation...
    <fault message="tns:authenticatonFault" name="authenticatonFault"/>

    <binding...
    <operation...
    <fault name="authenticatonFault">
    <soap:fault name="authenticatonFault" use="literal"/>
    </fault>

    We have defined the following spring bean:
    <bean id="endpointExceptionResolver" class="org.springframework.ws.soap.endpoint.SoapFa ultMappingExceptionResolver">
    <property name="defaultFault">
    <value>RECEIVER,Server error</value>
    </property>
    <property name="exceptionMappings">
    <props>
    <prop key="com.whatever.AuthenticatonException">{http://www.whatever.com/ws/ourNS}ns1:authenticationFault,Invalid login</prop>

    </props>
    </property>
    </bean>

    We have looked at forum entries, and cannot see any example of someone else trying to use custom wsdl faults, although the SoapFaultDefinition editor seems to suggest that you can supply the qname of your own fault.
    Can anyone tell us if this is currently possible with Spring-WS m2 or is it in the plan, or are we maiking some error?

    Thanks,
    Konrad.

  • #2
    A WSDL fault is stored in a SOAP fault as a detail entry. Apart from this detail, the soap fault has a fault code, and fault string, both of which you can customize. For details how to accomplish this, see this thread. It basically involves creating your own EndpointExceptionResolver, which sets the detail entry.

    Some people has requested an easier way to use a marshaller for converting an object into a detail entry, and i'm looking into that.

    Comment


    • #3
      Thanks ...

      Thanks ... unfortunately I was looking for this easier solution using marshaller.
      Regards
      Konrad

      Comment


      • #4
        Solved ...

        We solved it by making our own AbstractEndpointExceptionResolver listed below (almost same as SoapFaultMappingExceptionResolver, just lines 66 - 67 were added):
        This code just adds an empty element as a child of detail (i.e. the exception fields are not being serialized out), but mostly this is a marker exception for our case. This QNamed element is sufficient for our (XFire) client to serialize the fault to the correct generated exception.

        And we are on track

        Not sure if it is the best solution - but it works for us.

        Code:
        package com.mycompany.webservice;
        
        import org.springframework.ws.context.MessageContext;
        import org.springframework.ws.endpoint.AbstractEndpointExceptionResolver;
        import org.springframework.ws.soap.SoapBody;
        import org.springframework.ws.soap.SoapFaultDetail;
        import org.springframework.ws.soap.SoapMessage;
        import org.springframework.ws.soap.context.SoapMessageContext;
        import org.springframework.ws.soap.endpoint.SoapFaultDefinition;
        import org.springframework.ws.soap.endpoint.SoapFaultDefinitionEditor;
        import org.springframework.ws.soap.soap11.Soap11Body;
        
        import java.util.*;
        
        public class MyEndpointExceptionResolver extends AbstractEndpointExceptionResolver
        {
            private Properties exceptionMappings;
        
            private SoapFaultDefinition defaultFault;
        
            public void setExceptionMappings(Properties mappings)
            {
                exceptionMappings = mappings;
            }
        
            public void setDefaultFault(SoapFaultDefinition defaultFault)
            {
                this.defaultFault = defaultFault;
            }
        
            protected boolean resolveExceptionInternal(MessageContext messageContext, Object endpoint, Exception ex)
            {
                if (!(messageContext instanceof SoapMessageContext))
                {
                    throw new IllegalArgumentException("SoapFaultMappingExceptionResolver requires a SoapMessageContext");
                }
                SoapFaultDefinition definition = getFaultDefinition(ex);
                if (definition == null)
                {
                    return false;
                }
                SoapMessageContext soapContext = (SoapMessageContext) messageContext;
                SoapMessage response = soapContext.getSoapResponse();
                SoapBody soapBody = response.getSoapBody();
        
                if (SoapFaultDefinition.SERVER.equals(definition.getFaultCode()) ||
                        SoapFaultDefinition.RECEIVER.equals(definition.getFaultCode()))
                {
                    soapBody.addServerOrReceiverFault(definition.getFaultStringOrReason(), definition.getLocale());
                }
                else if (SoapFaultDefinition.CLIENT.equals(definition.getFaultCode()) ||
                        SoapFaultDefinition.SENDER.equals(definition.getFaultCode()))
                {
                    soapBody.addClientOrSenderFault(definition.getFaultStringOrReason(), definition.getLocale());
                }
                else
                {
                    // custom code, only supported for SOAP 1.1
                    if (soapBody instanceof Soap11Body)
                    {
                        Soap11Body soap11Body = (Soap11Body) soapBody;
                        soap11Body.addFault(definition.getFaultCode(), definition.getFaultStringOrReason(),
                                definition.getLocale());
                    }
        
                    SoapFaultDetail soapFaultDetail = soapBody.getFault().addFaultDetail();
                    soapFaultDetail.addFaultDetailElement(definition.getFaultCode());
                }
        
                return true;
            }
        
            private SoapFaultDefinition getFaultDefinition(Exception ex)
            {
                SoapFaultDefinition definition = null;
                if (exceptionMappings != null)
                {
                    String definitionText = null;
                    int deepest = Integer.MAX_VALUE;
                    for (Enumeration names = exceptionMappings.propertyNames(); names.hasMoreElements();)
                    {
                        String exceptionMapping = (String) names.nextElement();
                        int depth = getDepth(exceptionMapping, ex);
                        if (depth >= 0 && depth < deepest)
                        {
                            deepest = depth;
                            definitionText = exceptionMappings.getProperty(exceptionMapping);
                        }
                    }
                    if (definitionText != null)
                    {
                        SoapFaultDefinitionEditor editor = new SoapFaultDefinitionEditor();
                        editor.setAsText(definitionText);
                        definition = (SoapFaultDefinition) editor.getValue();
                    }
                }
                if (definition != null || defaultFault == null)
                {
                    return definition;
                }
        
                return defaultFault;
            }
        
            public int getDepth(String exceptionMapping, Exception ex)
            {
                return getDepth(exceptionMapping, ex.getClass(), 0);
            }
        
            private int getDepth(String exceptionMapping, Class exceptionClass, int depth)
            {
                if (exceptionClass.getName().contains(exceptionMapping))
                {
                    return depth;
                }
                if (exceptionClass.equals(Throwable.class))
                {
                    return -1;
                }
                return getDepth(exceptionMapping, exceptionClass.getSuperclass(), depth + 1);
            }
        }
        Best regards
        Konrad

        Comment


        • #5
          Small correction:
          Code:
                      // custom code, only supported for SOAP 1.1
                      if (soapBody instanceof Soap11Body)
                      {
                          Soap11Body soap11Body = (Soap11Body) soapBody;
                          soap11Body.addFault(definition.getFaultCode(), definition.getFaultStringOrReason(),
                                  definition.getLocale());
                          if (definition.getFaultCode() != null)
                          {
                              SoapFaultDetail soapFaultDetail = soapBody.getFault().addFaultDetail();
                              soapFaultDetail.addFaultDetailElement(definition.getFaultCode());
                          }
                      }

          Comment


          • #6
            Hi,

            This is a lot easier now. If you just need to create an custom element to differentiate between faults, you can do something like this:

            Create an annotation:
            Code:
            import java.lang.annotation.*;
            
            @Target(ElementType.TYPE)
            @Retention(RetentionPolicy.RUNTIME)
            @Documented
            @Inherited
            public @interface CustomSoapFaultDetailElement{
            	String namespace();
            	String localName();
            }
            Create an exception resolver
            Code:
            public class CustomSoapFaultDetailAnnotationExceptionResolver extends SoapFaultAnnotationExceptionResolver {
                @Override
                protected void customizeFault(Object endpoint, Exception ex, SoapFault fault) {
                	CustomSoapFaultDetailElement faultDetailAnnotation = ex.getClass().getAnnotation(CustomSoapFaultDetailElement.class);
                    if (faultDetailAnnotation != null) {
                        SoapFaultDetail faultDetail = fault.addFaultDetail();
                        faultDetail.addFaultDetailElement(new QName(faultDetailAnnotation.namespace(), faultDetailAnnotation.localName()));
                    }
                }
            }
            Then add the new annotation and the Spring SoapFault annotation to your exceptions:

            Code:
            @SoapFault(...)
            @CustomSoapFaultDetailElement(namespace="...", localName="...")
            public class MoonInWrongPhaseException extends Exception {}

            Comment


            • #7
              Spring API for handling soap messages since SoapMessageContext has been removed ?

              Would like tio use WSDL Faults but in spring-ws 1.50 understand the resultant soap fault message does not (in this release) populate the <detail> element in fault the WSDL Fault element structure (as is desired here).

              Interested to run this custom exception resolver class above but find in my 1.5.0 spring env that SoapMessageContext has been removed from spring release now ?, I am not familiar with the replacement approach, can anyone pls point me in the right direction here, thanks !

              Comment


              • #8
                No way to populate the Detail with XML?

                So from my understanding of this discussion and other threads on this forum, there is no way to add any XML to the detail of the SOAP fault, correct?

                My application currently defines complex objects that we serialize into XML (multiple levels of XML) and put it into the detail element for the client to process. Looking at the SoapDetail API, I can only add elements at the top level and these elements only support attributes or text.

                Is there no way to serialize a complex object into the fault detail? I'm willing to do my own marshalling in my resolver but I need a way to stick that information into the SoapDetail.

                -mike

                Comment


                • #9
                  Solution: Custom detail XML for complex objects

                  I read through some more of the documentation and the examples and hacked my way to a solution. In the end it turned out pretty nice. The key is just directly writing to the SoapDetail Result rather than using the addDetailElement call. Here's my code:

                  Code:
                        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);
                  I hope that helps someone. It would be nice if this was in the exception handling documentation of Spring WS.

                  Thanks,
                  -mike

                  Comment

                  Working...
                  X