Announcement Announcement Module
Collapse
No announcement yet.
Wanting to return the message from a custom Exception as a SOAP Fault string Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Wanting to return the message from a custom Exception as a SOAP Fault string

    I have wired the bean ...
    Code:
    <bean id="endpointExceptionResolver" class="org.springframework.ws.soap.endpoint.SoapFaultMappingExceptionResolver">
            <property name="defaultFault">
                <value>RECEIVER,Server error</value>
            </property>
            <property name="exceptionMappings">
            	<props>
            		<prop key="org.springframework.oxm.UnmarshallingException">SENDER,Invalid request</prop>
            		<prop key="org.springframework.oxm.ValidationFailureException">SENDER,Invalid request</prop>
            		<prop key="com.company.exception.DateNotFoundException">RECEIVER,Date not Found in Launch Calendar</prop>
                             <prop key="com.company.exception.LockdownException">RECEIVER,Site is in lockdown.</prop>
            		      	</props>
            </property>
        </bean>


    My goal is to get the message inside my custom exception objects and send that message back as the faultstring. How can I do this?

  • #2
    You could use the SimpleSoapExceptionResolver, which uses the exception message as a faultstring.

    Comment


    • #3
      I've worked with both SoapFaultMappingExceptionResolver and SimpleSoapExceptionResolver, and they do what they're supposed to do: turn an Exception into a fairly simple SOAP Fault. I've wanted to include more structure than a simple message in some Faults, though (It's all about the XML...) and it hasn't been as easy as I had hoped.

      Working with org.springframework.ws.soap.SoapFault and its friends (SoapFaultDetail, SoapFaultDetailElement) it looks like I can only add simple text elements to the Fault detail. I've played with setting the text content to a string containing the XML that I want to see in the fault details, but that has issues (and just seems wrong).

      Is there something I'm missing, like
      a) some other way to add structure to the details, or
      b) it's a Bad Thing(tm) to use anything but simple text details in a fault?

      Thanks for any help.

      Comment


      • #4
        Originally posted by wlsmith
        I've worked with both SoapFaultMappingExceptionResolver and SimpleSoapExceptionResolver, and they do what they're supposed to do: turn an Exception into a fairly simple SOAP Fault. I've wanted to include more structure than a simple message in some Faults, though (It's all about the XML...) and it hasn't been as easy as I had hoped.

        Working with org.springframework.ws.soap.SoapFault and its friends (SoapFaultDetail, SoapFaultDetailElement) it looks like I can only add simple text elements to the Fault detail. I've played with setting the text content to a string containing the XML that I want to see in the fault details, but that has issues (and just seems wrong).
        A SOAP 1.1 Fault has a qname code, a faultstring, an actor and a detail. You can add multiple detail elements, as you can see here. In Spring-WS, you can set these with setters. You might have to cast the SoapFault to a Soap11Fault though, if you need specific functionality. You can add a fault detail using addFaultDetail(), add specific entries using addFaultDetailEntry() and write XML to that using getResult().

        Originally posted by wlsmith
        Is there something I'm missing, like
        a) some other way to add structure to the details, or
        b) it's a Bad Thing(tm) to use anything but simple text details in a fault?
        Some SOAP stacks completely ignore the details, so it's not a good idea to put valuable information in it.

        Comment


        • #5
          Originally posted by poutsma
          You can add a fault detail using addFaultDetail(), add specific entries using addFaultDetailEntry() and write XML to that using getResult().
          Ah... So instead of SoapFaultDetailElement.addText() (which produces the simple text element), I would pull the Result from SoapFaultDetailElement.getResult(), figure out whether it's a DOM, SAX, or StreamResult, and push my details that way? Kind of painful, but it should work. Better than generating an XML string and adding it as the text.

          Originally posted by poutsma
          Some SOAP stacks completely ignore the details, so it's not a good idea to put valuable information in it.
          I had designed the XML contract for our services with a few Faults indicating alternate return values (plus the common Fault == Exception logic). For example, a job-submittal service that normally returns a job-ID but can produce a Fault if the job is invalid in some way. Some extra details on why the job was invalid are useful to the client, but not required. I had thought this was an OK thing to do, but the WS-I profiles aren't the easiest thing to absorb. In other words, I figured that if some SOAP client stack ignored the Fault details, then they were at fault, not my service

          I really like Spring-WS, especially the "focus on the XML" approach. Following that path leads to the occasional Fault with structured details. Does anybody have thoughts on the tradeoff between interoperability and Fault detail structure? How many of the common SOAP stacks discard the details element?

          Comment


          • #6
            Originally posted by wlsmith
            Ah... So instead of SoapFaultDetailElement.addText() (which produces the simple text element), I would pull the Result from SoapFaultDetailElement.getResult(), figure out whether it's a DOM, SAX, or StreamResult, and push my details that way? Kind of painful, but it should work. Better than generating an XML string and adding it as the text.
            Well, the idea would be to create some XML that you want to put into the detail, and use a Transformer to transform that into the Result. This might be a bit painful, but the Source/Result provide a valuable abstraction, which - among others - allow you to switch between a DOM-based model to a streaming StAX-model with just a configuration setting.


            Originally posted by wlsmith
            I had designed the XML contract for our services with a few Faults indicating alternate return values (plus the common Fault == Exception logic). For example, a job-submittal service that normally returns a job-ID but can produce a Fault if the job is invalid in some way. Some extra details on why the job was invalid are useful to the client, but not required. I had thought this was an OK thing to do, but the WS-I profiles aren't the easiest thing to absorb. In other words, I figured that if some SOAP client stack ignored the Fault details, then they were at fault, not my service
            You are right, but it is of little use being right when the majority is wrong. For instance, while the .NET framework does carry the details, its RPC-like model doesn't make it easier to get the details (though it is possible).

            Originally posted by wlsmith
            I really like Spring-WS, especially the "focus on the XML" approach. Following that path leads to the occasional Fault with structured details. Does anybody have thoughts on the tradeoff between interoperability and Fault detail structure? How many of the common SOAP stacks discard the details element?
            None that I know of discard them completely, it's just that most SOAP stack models don't actually focus on it. This mostly has to do with the whole Exception == SOAP Fault approach, which is wrong IMO.

            Comment


            • #7
              I was trying to use the SoapMessageUtils.addReceiverFaultResponse(...,...) but was unable to find it in the jar of the -m2 SNAPSHOT.

              My thought was to start with the code of the SimpleSoapExceptionResolver which uses this SoapMessageUtils.

              But replace this line
              Code:
              SoapMessageUtils.addReceiverFault(response, faultString);
              with this to return and use the SoapFault
              Code:
              SaopFault soapFault = SoapMessageUtils.addReceiverFault(response, faultString);
              with SoapFault object use the .addFaultDetail() to return a SoapFaultDetail object.

              With the SoapFaultDetail use the .addFaultDetailElement(QName) to return a SoapFaultDetailElement.

              With the SoapFaultDetailElement use the .getResult() to return a SaxResult.

              With the SaxResult use the .getHandler() to return a ContentHandler.

              With the ContentHandler use the .startElement and other methods to create something that ends up looks similiar to within the <soapenv:Fault>
              ....

              Code:
                        <detail>
                             <e:myfaultdetails xmlns:e="Some-URI">
                               <message>
                                 My application didn't work
                               </message>
                               <errorcode>
                                 1001
                               </errorcode>
                             </e:myfaultdetails>
                         </detail>
              Am I on the write track on this?

              Comment


              • #8
                Originally posted by TAOSBORNE
                I was trying to use the SoapMessageUtils.addReceiverFaultResponse(...,...) but was unable to find it in the jar of the -m2 SNAPSHOT.

                My thought was to start with the code of the SimpleSoapExceptionResolver which uses this SoapMessageUtils.

                But replace this line
                Code:
                SoapMessageUtils.addReceiverFault(response, faultString);
                with this to return and use the SoapFault
                Code:
                SaopFault soapFault = SoapMessageUtils.addReceiverFault(response, faultString);
                with SoapFault object use the .addFaultDetail() to return a SoapFaultDetail object.
                The addReceiverFault() method has been moved to the SoapBody itself, and has been renamed addServerOrReceiverFault(). It works the same as SoapMessageUtils used to.

                Originally posted by TAOSBORNE
                With the SoapFaultDetail use the .addFaultDetailElement(QName) to return a SoapFaultDetailElement.

                With the SoapFaultDetailElement use the .getResult() to return a SaxResult.

                With the SaxResult use the .getHandler() to return a ContentHandler.

                With the ContentHandler use the .startElement and other methods to create something that ends up looks similiar to within the <soapenv:Fault>
                ....

                Code:
                          <detail>
                               <e:myfaultdetails xmlns:e="Some-URI">
                                 <message>
                                   My application didn't work
                                 </message>
                                 <errorcode>
                                   1001
                                 </errorcode>
                               </e:myfaultdetails>
                           </detail>
                Am I on the write track on this?
                Well, it's not a good idea to cast the Result to a StaxResult directly. It's better to create a Source (DOMSource or StreamSource), and to transform that source to the result, like so:

                Code:
                SoapFaultDetail detail = fault.addFaultDetail();
                SoapFaultDetailElement el = detail.addFaultDetailElement("SomeUri", "myfaultdetails", "e");
                Source elSource = new StringSource("<message>My application didn't work</message><errorcode>1001</errorcode>");
                Transformer transformer = TransformerFactory.newInstance().newTransformer();
                transformer.transform(elSource, el.getResult());
                This way, you are not dependent on the underlying XML technology (DOM, StAX, etc).

                Comment


                • #9
                  Hi!

                  I want to be JWSDK2 and axis1 compatible, therefore i want to add a "fault bean", which i defined my my WSDL/XSL.

                  In your example Arjen, your are first adding a wrapper element (StringSource) and then you add some additional elements.

                  I want to add only one instance of my fault bean in the details element. In your approach, i have to call "faultDetail.addFaultDetailElement(QNAME)" to get an "SoapFaultDetailElement". I can't use that, because of the wrapper element which this method produces.
                  Is there any other way to get SoapFaultDetailElement?

                  I need the SoapFaultDetailElementit because of the required Result element one line later:
                  marshaller.marshal(faultType, faultDetailElement.getResult());

                  Cheers,

                  Ingo

                  PS.: My aim is this xml snippet:

                  <details> <!-- details element of a soap fault -->
                  <ns2:ObjectDoesNotExistFault> <!-- This is my marshalled bean -->
                  <ns2:mycode>XYZ</ns2:code>
                  <ns2:message>ABC</ns2:message>
                  </ns2:ObjectDoesNotExistFault>
                  </details>

                  Comment


                  • #10
                    I wanted to do essentially the same thing. My fault objects/beans are marshalled to XML just like all of the other objects passed through the WS interface. In essence, you need to do the following:
                    • Pull the top element of your fault and build a QName,
                    • Create/Add the SoapFaultDetailElement with the QName,
                    • Output each child element of your fault (e.g., <mycode>, <message>) to a String, and
                    • Insert each String into the detail element with a transform.
                    The following method inserts the marshalled XML into the message's fault (simply invoked from resolveExceptionInternal() in an extension of SoapFaultMappingExceptionResolver):
                    Code:
                    import javax.xml.namespace.QName;
                    import javax.xml.transform.Transformer;
                    import javax.xml.transform.TransformerException;
                    import javax.xml.transform.TransformerFactory;
                    
                    import org.jdom.Element;
                    import org.jdom.output.Format;
                    import org.jdom.output.XMLOutputter;
                    import org.springframework.ws.context.MessageContext;
                    import org.springframework.ws.soap.SoapFault;
                    import org.springframework.ws.soap.SoapFaultDetail;
                    import org.springframework.ws.soap.SoapFaultDetailElement;
                    import org.springframework.ws.soap.SoapMessage;
                    import org.springframework.ws.soap.context.SoapMessageContext;
                    import org.springframework.xml.transform.StringSource;
                    
                    protected void addFault(SoapMessageContext theSoapContext, 
                                            CommonFault theFault) 
                                                                  throws TransformerException {
                        SoapMessage response = theSoapContext.getSoapResponse();
                        String msg = theFault.getMessage();
                        SoapFault soapFault =
                            response.getSoapBody().addClientOrSenderFault(msg);
                    
                        // Create a fault <detail> and the single element under the detail.
                        // You can have multiple children of the <detail> element, but we're
                        // going for a single "payload" within the fault detail.
                    
                        SoapFaultDetail detail = soapFault.addFaultDetail();
                        Element jdomFault = theFault.marshal();
                        QName qname = new QName(jdomFault.getNamespaceURI(), 
                                                jdomFault.getName(),
                                                jdomFault.getNamespacePrefix());
                        SoapFaultDetailElement detailElt = detail.addFaultDetailElement(qname);
                    
                        // Now we need to transform the CONTENTS of the fault into the element
                        // we just created.  Problem is, we've got to skip the top-level element
                        // in the marshalled fault and do each of the child elements separately.
                        // Note: The "source" needs to be well-formed, so we can't do multiple
                        // children at once.
                    
                        // Note: Can't add attributes to the top-level Fault element.
                    
                        XMLOutputter out = new XMLOutputter(Format.getCompactFormat());
                        Transformer trn = TransformerFactory.newInstance().newTransformer();
                        for (Iterator it = jdomFault.getChildren().iterator(); it.hasNext(); ) {
                            String child = out.outputString((Element)it.next());
                            trn.transform(new StringSource(child), detailElt.getResult());
                        }
                    }
                    Imports are included so you can see where things are coming from. I use JDOM for marshalling, but the only trick is outputting the XML to a string to feed into the transformer as a StringSource. You would need a different approach depending on what you use to model your marshalled XML (DOM, SAX, etc).

                    Comment


                    • #11
                      Hi,

                      thank you very much for your code!
                      I think it really helps me a lot. But it's sad, that we have to add a QName, because this drives us to ugly code.
                      Maybe Arjen will add another method, if he reads this thread again.

                      I'll try the same like you did with JDOM, but with the standard marshaller object which is also used for MarshallingPayloadEndpoints.
                      I hope i get it work.

                      Thank you very much again,

                      Ingo

                      Comment


                      • #12
                        You're right, the code you will need to write is quite verbose. I will think about adding a new way to be able to use a marshaller to populate the detail entry.

                        Comment


                        • #13
                          I have a first version which is working, but not heavily tested.
                          "eimFault" is my application fault bean, which will be written to the <details> tag.

                          Code:
                          DOMResult domResult = new DOMResult();
                          try {
                            marshaller.marshal(eimFault, domResult);
                          } catch (XmlMappingException e) {
                            e.printStackTrace();
                          } catch (IOException e) {
                            e.printStackTrace();
                          }
                          
                          Node node = domResult.getNode();
                          Node faultNameNode = node.getFirstChild();
                          QName faultNameAsQName = new QName(faultNameNode.getNamespaceURI(), faultNameNode.getLocalName(), faultNameNode.getPrefix());
                          
                          NodeList faultNameChilds = faultNameNode.getChildNodes();
                          
                          // add qname which is the first element of the details element        
                          SoapFaultDetailElement faultDetailElement = faultDetail.addFaultDetailElement(faultNameAsQName);
                                  
                          try {
                            Transformer transformer = TransformerFactory.newInstance().newTransformer();
                          
                            // write all children to the details element
                            for(int i=0; i<faultNameChilds.getLength(); i++) {
                              Node child = faultNameChilds.item(i);
                              DOMSource childAsDOMSource = new DOMSource(child);
                          	        	
                              transformer.transform(childAsDOMSource, faultDetailElement.getResult());
                            }
                          } catch(TransformerConfigurationException confException) {
                            confException.printStackTrace();
                          } catch (TransformerException transformerException) {
                            transformerException.printStackTrace();
                          }
                          What do you think?
                          Any bad mistakes?

                          Cheers,

                          Ingo

                          Comment


                          • #14
                            I've added a getResult() to SoapFaultDetail, so that you can use a marshaller to directly marshal into the detail. The usage of addFaultDetailElement is no longer required.

                            Comment


                            • #15
                              step from tutorial

                              Hi,
                              Let say I'm a beginner and I want to use SoapFaultDetail...
                              I just did the tutorial and I'm using the AbstractJDomPayloadEndpoint.
                              For me everything start with protected Element invokeInternal(Element) how can I generate a fault from there... I'm new to spring and I find it difficult to identify bean required to manage my exception. OK let say I install an exception resolver and that give me access to a WebServiceMessage or SoapFaultDefinition how does that link to a SoapFault?

                              Thanks
                              mapospring

                              Comment

                              Working...
                              X