Page 1 of 2 12 LastLast
Results 1 to 10 of 14

Thread: SoapFault

  1. #1
    Join Date
    Mar 2006
    Location
    Germany, Karlsruhe
    Posts
    157

    Default SoapFault

    Hi,

    WS-I says, that dot-subcodes are not allowed as error codes. That's why subcode elements exist.

    It would be nice if i could add subcodes to a SoapFault.

    I need that because i want to create custom exceptions codes (for example Client.AuthFail).
    A client ws technology like Axis should get my faultcode and throw an exception (something like AuthFailException).

    Cheers,

    Ingo

  2. #2
    Join Date
    Jul 2005
    Location
    Rotterdam, the Netherlands
    Posts
    1,562

    Default

    Quote Originally Posted by res1st
    WS-I says, that dot-subcodes are not allowed as error codes. That's why subcode elements exist.

    It would be nice if i could add subcodes to a SoapFault.

    I need that because i want to create custom exceptions codes (for example Client.AuthFail).
    A client ws technology like Axis should get my faultcode and throw an exception (something like AuthFailException).
    Well, fault subcodes only exists in SOAP 1.2, not SOAP 1.1. In SOAP 1.1, you can add a faultcode with your own namespace, and that's perfectly fine. You are right about the dot-subcodes though: don't use them (see the examples at http://www.ws-i.org/Profiles/BasicPr...m_Fault_Codes).

    The fact of the matter is that SOAP 1.1 and SOAP 1.2 are different on so many accounts, that it's hard to define a common interface. This is especially true for the faults. SOAP 1.1 allows you to define a single custom fault code as a QName, whereas SOAP 1.2 does not. Instead SOAP 1.2 allows you to use multiple custom fault subcodes, while the fault codes can only be Sender, Receiver, MustUnderstand, etc. I guess the W3C guys never heard of backwards compatibility.

    There are four possible solutions to this:

    1. Define specific SoapFault interfaces for SOAP 1.1 and SOAP 1.2. I don't like this solution that much, because it basically requires instanceofs everywhere.
    2. Combine SOAP 1.1 and 1.2 methods in the SoapFault interface, and ignore methods which are not suitable for the SOAP version in use. This is the approach Axiom takes: internally, Axiom uses a SOAP 1.2 model, and basically changes that on the fly to SOAP 1.1 when saving (though not without error). I don't like this approach, because there's no way to tell whether the methods you call have any effect.
    3. Combine SOAP 1.1 and 1.2 methods in the SoapFault interface, and throw exceptions when an method is called that is not suitable for the SOAP version in use. This is the approach SAAJ 1.3 takes. I don't like this approach either, because it requires you to catch exceptions everywhere.
    4. Expose a single SoapFault interface, which defines the common denominator. If using SOAP 1.1, define a custom <faultcode>, if 1.2 define a custom <SubCode>. This is the approach I've picked for SWS. You lose some of the power (i.e. you cannot add multiple subcodes in SOAP 1.2), but at least you have a single, consistent interface.


    I'm currently working on SAAJ 1.3 support, so this is stuff is very much on my mind. That said, I'm open for suggestions on this. If you require some missing functionality, just say so.

    Cheers,
    Arjen Poutsma

    Spring Web Services Dev Lead
    Please read the FAQ

  3. #3
    Join Date
    Mar 2006
    Location
    Germany, Karlsruhe
    Posts
    157

    Default

    Hi Arjen,

    that fault handling isn't very well documentated(jax-ws, axis, ...). Please let my describe my idea to you.
    That's the important part of my schema:
    Code:
    <xsd:simpleType name="ExceptionCode">
    		<xsd:restriction base="xsd:string">
    			<xsd:enumeration value = "DATAOBJECT_DOES_NOT_EXIST" />
                             [...]
    		</xsd:restriction>
    	</xsd:simpleType>
    
    	<xsd:complexType name="DataAccessFault">
    		<xsd:sequence>
    			<xsd:element name="code"    type="tns:ExceptionCode"/>
    			<xsd:element name="message" type="xsd:string" />
    		</xsd:sequence>
    	</xsd:complexType>
    
    	<xsd:element name="ObjectDoesNotExistFault" type="tns:ObjectDoesNotExistFaultType"/>
    
    	<xsd:complexType name="ObjectDoesNotExistFaultType">
    		<xsd:complexContent>
    			<xsd:extension base="tns:DataAccessFault"/>
    		</xsd:complexContent>
    	</xsd:complexType>
    I added to my wsdl a fault element, of course.
    Code:
    <wsdl:operation name="get">
      		<wsdl:input		name="getRequest"	message="tns:getRequest"/>
      		<wsdl:output	name="getResponse"	message="tns:getResponse"/>
      		<wsdl:fault		name="ObjectDoesNotExistFault" message="tns:ObjectDoesNotExistFault"/>
      	</wsdl:operation>
    Two possible strategies: I could use a new Exception which says what gone wrong (ObjectDoesNotExistFault) or i use an "generic exception" and add a constant. In my example above, i used both.

    If i use a technology like axis or xfire to generate client stubs, they can throw my ObjectDoesNotExistFault exception(in axis it's an AxisFault which is a RemoteException).

    My Server technology SWS+JAXB2 also creating this exceptions. I simply throw it.
    Your nice SoapFaultMappingExceptionResolver (or my own one) get the exception. Now comes the important part...

    • How should i convert my ObjectDoesNotExistFault exception on my server into a ObjectDoesNotExistFault exception on the client?
    • Should i use the constant as a subcode (Soap 1.2) value or should i add something to the details section?
    • I have to put the right information in the soap fault element and all client technologies should understand it and throw an exception. Hot should the xml message look like?
    • Do you have any experiance on that? That would be really great...


    Ingo

  4. #4
    Join Date
    Jul 2005
    Location
    Rotterdam, the Netherlands
    Posts
    1,562

    Default

    First, a bit of architectural advice. A SOAP Fault is not the same as an exception, both on server and client-side. A SOAP 1.1 fault has a QName faultcode, a faultString, and possibly a role and details elements. These elements don't map directly to an exception. That's why the EndpointExceptionResolver exists: it allows you to map your application exceptions to Soap Fault messages. An additional benefit is that you create a layer between your inner exceptions and your faults, allowing the two to change independently. Just like you don't want to expose your domain objects, you don't want to expose your exceptions. Once you have exposed them, you basically cannot change them that easily any more.

    If you really want to let people use your Web service in a RPC-like fashion, you should play by the rules of the JAX-RCP or JAX-WS specs. This basically means (in SOAP 1.1) that you should create a fault with:

    • a soapenv:Server faultCode,
    • a faultString that is equal to the exception's message
    • a fault details element that contains the JavaBean properties of the exception


    Note that this is basically what the SimpleSoapExceptionResolver does, except for the last bullet, which is hard without a marshaller.

    Thus, in the example you gave, that means you should put the ObjectDoesNotExistFault in the details element, not the (sub)code.

    Cheers,
    Arjen Poutsma

    Spring Web Services Dev Lead
    Please read the FAQ

  5. #5
    Join Date
    Mar 2006
    Location
    Germany, Karlsruhe
    Posts
    157

    Default

    Thank you very much Arjen, you are really a great help.
    Your are right, server and client exception shouldn't be mixed.

    My aim in client code generation is something like this:
    Code:
     service.call(...)
    } catch (LoginFault ex) { 
     // The LoginFault derives from AxisFault
     ExceptionCode exCode = ex.getExceptionCode();
     if (exCode == ExceptionCode.FUNCTIONALITY_NOT_ENABLED ||
         exCode==ExceptionCode.INVALID_LOGIN ||
         exCode==ExceptionCode.TRIAL_EXPIRED ) { 
       System.out.println("Please make sure that you have a valid user id and password."); 
     } else { 
        System.out.println(ex.getExceptionCode());
        System.out.println("An unexpected error:" + ex.getMessage()); 
      } 
    } catch (RemoteException ex) { 
      System.out.println("An unexpected error:" + ex.getMessage()); 
    }
    As you can see, i have an LoginFault with constants. Nearly the same as my example before.

    I simply doesn't find information about how that works in Axis or XFire on client side.
    My defined xml schema fault content will be placed in the details element. I think this is the correct way. But how does Axis or XFire know, that it should generate a LoginFault an not a generic AxisFault? Somewhere i have to place a link to "{myFaultNamespac}LoginFault" in my Soap fault body. Do you know where?
    The ideal way would be a solution which works in xfire as well as in axis or dotNet.

    Ingo

  6. #6
    Join Date
    Jul 2005
    Location
    Rotterdam, the Netherlands
    Posts
    1,562

    Default

    Well, the SOAP thing to do is not to define QNames for your errors instead of an error code property, and let the client handle that. The client code would look like this:

    Code:
    service.call(...)
    } catch (AxisFault ex) { 
      QName faultCode = ex.getFaultCode();
      if (FaultCodes.FUNCTIONALITY_NOT_ENABLED.equals(faultCode) ||
         FaultCodes.INVALID_LOGIN.equals(faultCode) ||
         FaultCodes.TRIAL_EXPIRED.equals(faultCode)) { 
       System.out.println("Please make sure that you have a valid user id and password."); 
     } else { 
        System.out.println(faultCode);
        System.out.println("An unexpected error:" + ex.getMessage()); 
      } 
    } catch (RemoteException ex) { 
      System.out.println("An unexpected error:" + ex.getMessage()); 
    }
    This basically works under any soap client and that's pretty easy to setup using the SoapFaultMappingExceptionResolver.

    If you really want a custom exception, I suggest reading the jax-rpc spec at http://java.sun.com/xml/downloads/ja...l#jaxrpcspec11, section 4.3.6.

    Note that .NET does not have checked exceptions, and as far as I know completely ignores the <wsdl:fault/> element.

    Cheers,
    Arjen Poutsma

    Spring Web Services Dev Lead
    Please read the FAQ

  7. #7
    Join Date
    Mar 2006
    Location
    Germany, Karlsruhe
    Posts
    157

    Default

    Well, the SOAP thing to do is not to define QNames for your errors instead of an error code property, and let the client handle that.
    Code:
    service.call(...)
    } catch (AxisFault ex) { 
      QName faultCode = ex.getFaultCode();
      if (FaultCodes.FUNCTIONALITY_NOT_ENABLED.equals(faultCode) ||
         FaultCodes.INVALID_LOGIN.equals(faultCode) ||
         FaultCodes.TRIAL_EXPIRED.equals(faultCode)) { 
       System.out.println("Please make sure that you have a valid user id and password."); 
     } else { 
        System.out.println(faultCode);
        System.out.println("An unexpected error:" + ex.getMessage()); 
      } 
    } catch (RemoteException ex) { 
      System.out.println("An unexpected error:" + ex.getMessage()); 
    }
    Sorry, what do you mean?
    ex.getFaultCode() returns the soap fault code, that needs to be Client or Server. If i return a custom fault code (like you did), than it wouldn't be compatible with WS-I.

    Someone told me once that web services are an easy technology...

    Ingo

  8. #8
    Join Date
    Jul 2005
    Location
    Rotterdam, the Netherlands
    Posts
    1,562

    Default

    Quote Originally Posted by res1st
    Sorry, what do you mean?
    ex.getFaultCode() returns the soap fault code, that needs to be Client or Server. If i return a custom fault code (like you did), than it wouldn't be compatible with WS-I.
    No, returning a custom fault code is in compliance with WS-I, as long as you don't use the dot notation. Read this: http://www.ws-i.org/Profiles/BasicPr...om_Fault_Codes.

    R1004: When an ENVELOPE contains a faultcode element, the content of that element SHOULD be either one of the fault codes defined in SOAP 1.1 (supplying additional information if necessary in the detail element), or a Qname whose namespace is controlled by the fault's specifying authority (in that order of preference).
    Look at the first 'Correct' example. They use a custom fault code there.

    Quote Originally Posted by res1st
    Someone told me once that web services are an easy technology...
    I never said that . It's not without reason that the W3C dropped the "Simple" from the SOAP acronym in SOAP 1.2. With SWS, I try to make it as simple as possible though, without losing any power.

    Cheers,
    Arjen Poutsma

    Spring Web Services Dev Lead
    Please read the FAQ

  9. #9
    Join Date
    Mar 2006
    Location
    Germany, Karlsruhe
    Posts
    157

    Thumbs up Funny little post icons ;)

    No, returning a custom fault code is in compliance with WS-I, as long as you don't use the dot notation. Read this: http://www.ws-i.org/Profiles/BasicPr...om_Fault_Codes.
    You right, sorry.

    If i'm using your SoapFaultMappingExceptionResolver, i looked into the source code and learned that my fault code in the applicationContext should look like this.
    Code:
    "{http://my.cas.de}myfault:DATAOBJECT_DOES_NOT_EXIST, MyFaultStringMessage"
    Is this correct?

    If i return my fault, i doesn't see a logging message of the validating interceptor. Something like this:
    Code:
    endpoint.PayloadValidatingInterceptor  - Response message validated
    Why are soap fault messages not validated? Is there any reason?

    I never said that . It's not without reason that the W3C dropped the "Simple" from the SOAP acronym in SOAP 1.2. With SWS, I try to make it as simple as possible though, without losing any power.
    You are really doing a good job.

    I have another questions regarding SoapFaultMappingExceptionResolver.
    I doesn't like it that it only returns predefined strings.
    If my business logic throws an exception with an useful exception message, the client will never see it. Is there any trick? Or should i write a new SoapFaultMappingExceptionResolver which (for example) copy all exception properties to the detail tag.

    Cheers,

    Ingo

  10. #10
    Join Date
    Jul 2005
    Location
    Rotterdam, the Netherlands
    Posts
    1,562

    Default

    Quote Originally Posted by res1st
    If i'm using your SoapFaultMappingExceptionResolver, i looked into the source code and learned that my fault code in the applicationContext should look like this.
    Code:
    "{http://my.cas.de}myfault:DATAOBJECT_DOES_NOT_EXIST, MyFaultStringMessage"
    Is this correct?
    That's correct. The format is documented here.

    Quote Originally Posted by res1st
    If i return my fault, i doesn't see a logging message of the validating interceptor. Something like this:
    Code:
    endpoint.PayloadValidatingInterceptor  - Response message validated
    Why are soap fault messages not validated? Is there any reason?
    Fault are not validated because they consist of elements in your schema and elements in the SOAP schema. This makes it harder to validate. Also, the fault codes are not defined in the WSDL, only the detail can be. So theoretically, I could validate the detail. If you really want that, create an issue .

    Quote Originally Posted by res1st
    I have another questions regarding SoapFaultMappingExceptionResolver.
    I doesn't like it that it only returns predefined strings.
    If my business logic throws an exception with an useful exception message, the client will never see it. Is there any trick? Or should i write a new SoapFaultMappingExceptionResolver which (for example) copy all exception properties to the detail tag.
    There is no trick (yet). So that basically means write your own for now. You could also use the SimpleSoapFaultExceptionResolver. This takes the
    exception message, but you lose the code, since that's fixed to soap-env:Server (in SOAP 1.1).

    Note that the interface for creating SoapFaults will change somewhat either today or tomorrow. The current API allows you to define faults which are invalid in SOAP 1.2. The problem is that SOAP 1.2 doesn't allow you to define custom faults at the top-level, only as subcode of a set of fixed codes. See http://www.idealliance.org/papers/xm...tml#faultcodes for more info.

    The new API, which I will check in shortly, will only allow to create messages which are valid in either SOAP 1.1 of SOAP 1.2. Let me know how that works out for you.
    Arjen Poutsma

    Spring Web Services Dev Lead
    Please read the FAQ

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •