Announcement Announcement Module
Collapse
No announcement yet.
SoapFault Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • 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
    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,

    Comment


    • #3
      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

      Comment


      • #4
        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,

        Comment


        • #5
          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

          Comment


          • #6
            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,

            Comment


            • #7
              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

              Comment


              • #8
                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.

                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,

                Comment


                • #9
                  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

                  Comment


                  • #10
                    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.

                    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 .

                    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.

                    Comment


                    • #11
                      Hi Arjen,

                      i just try to understand the new soap api.
                      In M1, i was able to write
                      Code:
                      SoapBody body = response.getSoapBody();
                      body.addFault( [....] );
                      In the new SOAP API of M2, there are 4 methods:
                      Code:
                      body.addMustUnderstandFault()
                      body.addVersionMismatchFault()
                      body.addClientOrSenderFault()
                      body.addServerOrReceiverFault()
                      But how can i add a custom fault code?

                      I just saw in subversion your comment: "custom code, only supported for SOAP 1.1".
                      Does my application needs to use SOAP 1.1 if i want to use custom fault codes?
                      What are the consequences?
                      What are the disadvantages/drawbacks?

                      Cheers,

                      Ingo

                      Comment


                      • #12
                        Yeah, the API has changed so that it reflects the common denominator between SOAP 1.1 and SOAP 1.2. SOAP 1.1 supports custom faults codes, which SOAP 1.2 does not. In SOAP 1.2, you can only add you custom fault subcode to one of the standard error codes (MustUnderstand, VersionMismatch, Sender, or Receiver, see addSubcode in Soap12Fault). So you have to cast the SoapBody to Soap11Body, and then you can add your fault.

                        Basically, you probably want to use SOAP 1.1 anyway. SOAP 1.2 is not supported by many clients yet. Unless you know you really need it, it's best to use SOAP 1.1.

                        Comment


                        • #13
                          Basically, you probably want to use SOAP 1.1 anyway. SOAP 1.2 is not supported by many clients yet. Unless you know you really need it, it's best to use SOAP 1.1.
                          Ok, but i can switch to SOAP 1.2 if i really need that in a year?


                          Is it possible to switch my SWS-application to SOAP1.2?
                          I see your type cast, but where is it defined that SWS uses SOAP 1.1?
                          Code:
                           if (soapBody instanceof Soap11Body) {
                                          Soap11Body soap11Body = (Soap11Body) soapBody;
                          I don't need it, but i need to satisfy my curiosity.

                          Ingo

                          Comment


                          • #14
                            The MessageContextFactories basically detect what the version of the incoming message is, and uses that also for the request message, so it does it dynamically.

                            Note that you will need SAAJ 1.3 (saaj.dev.java.net) to support SOAP 1.2. Then, you can set a property on the SaajSoapMessageContextFactory (soapProtocol) to set the version statically.

                            Which reminds me, I should add a similar property to Axiom.

                            Comment

                            Working...
                            X