Showing posts with label fault. Show all posts
Showing posts with label fault. Show all posts

Wednesday, December 14, 2011

Assigning a value to $fault

I was trying to assign a value to $fault in a "Error Reporting" service, to see if the built-in "Report" action could parse the $fault and populate the right fields in the Error Report message.

This is a sample $fault:

<con:fault xmlns:con="http://www.bea.com/wli/sb/context">
  <con:errorCode>MYERROR</con:errorCode>
  <con:reason>MYMESSAGE</con:reason>
  <con:location>
    <con:node>PipelinePairNode1</con:node>
    <con:pipeline>PipelinePairNode1_request</con:pipeline>
    <con:stage>stage1</con:stage>
    <con:path>request-pipeline</con:path>
  </con:location>
</con:fault>


Unfortunately the $fault variable is meaningful only in the error handler.
If you try to use it in a normal message flow, you get:

Variable name validation failed: The variable "fault" cannot be used here

Sunday, October 23, 2011

Using Raise Error in OSB

If in your Message Flow you Raise Error:

Raise Error using error code [ MY_ERROR_CODE ] with error Message MY_ERROR_MESSAGE



the error handler will show in the
"errorCode" and "reason" respectively the
"error code and "error message"
that you have provided:

<con:fault xmlns:con="http://www.bea.com/wli/sb/context">
<con:errorCode>MY_ERROR_CODE</con:errorCode>
<con:reason>MY_ERROR_MESSAGE</con:reason>
<con:location>
<con:node>PipelinePairNode1</con:node>
<con:pipeline>PipelinePairNode1_request</con:pipeline>
<con:stage>stage1</con:stage>
<con:path>request-pipeline</con:path>
</con:location>
</con:fault>

both Service Error Handler and System Error Handler are invoked with the same fault, because in my Message Flow, the Service Error Handler doesn't handle the error:


If the Service Error Handler does a "reply with success", the same body as the request is returned in the response.
If the Service Error Handler does a "reply with error", the test console returns a message "The invocation resulted in an error: ."

Analyzing with SOAP UI (I create a Rest service and do a POST), if I show the RAW response I get:

HTTP/1.1 500 Internal Server Error
Date: Sun, 23 Oct 2011 21:45:50 GMT
Content-Length: 61
Content-Type: text/xml; charset=utf-8
X-Powered-By: Servlet/2.5 JSP/2.1

<?xml version="1.0" encoding="UTF-8"?>
<hello>pierre</hello>


in fact "Reply with Error" returns a HTTP 500



see also http://www.javamonamour.org/2011/07/throwing-exceptions-in-xquery-fnerror.html on how to raise an error in XQuery

Sunday, July 4, 2010

OSB: error handling, fault

First read the posts of my friend Jan and of Eric Elzinga (whom I have never had the pleasure to meet).

Here http://www.javamonamour.org/2010/04/soap-fault-in-osb.html I have taken some notes.

Here a full explanation on the topic:
http://download.oracle.com/docs/cd/E14571_01/doc.1111/e15867/modelingmessageflow.htm#i1040168


When a fault is handled in the fault handler, you have this:

$body

<env:Body xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
 <env:Fault>
  <faultcode>env:Server</faultcode>
  <faultstring/>
  <detail>
   <java:CacheStateException xmlns:java="java:com.acme.cache.commons">
    <java:CacheName>geo-data-geographic-area</java:CacheName>
    <java:Status>Not initialized</java:Status>
   </java:CacheStateException>
  </detail>
 </env:Fault>
</env:Body>


$fault

<con:fault xmlns:con="http://www.bea.com/wli/sb/context">
 <con:errorCode>BEA-380001</con:errorCode>
 <con:location>
  <con:node>RouteNodeToEJBBS</con:node>
  <con:path>response-pipeline</con:path>
 </con:location>
</con:fault>

Not necessarily "reason" and "details" are populated! Details should be retrieved from the $body variable. Reason is nowhere to be found.

(see also here http://download.oracle.com/docs/cd/E13159_01/osb/docs10gr3/userguide/context.html#wp1051816 for $fault definition)


To access individual info in the fault:
$fault/ctx:errorCode/text()

Error codes are here http://download.oracle.com/docs/cd/E13159_01/osb/docs10gr3/consolehelp/errorcodes.html

NB:
the body uses the namespace http://schemas.xmlsoap.org/soap/envelope/
here http://schemas.xmlsoap.org/soap/envelope/ you can find the XSD for the env:Fault

the fault uses the namespace http://www.bea.com/wli/sb/context



If you terminate the Error Handler with "Reply with failure", you return a HTTP 500 status. In SOAPUI, in the response panel click in RAW, you will see "HTTP/1.1 500 Internal Server Error".

The ambiguity is that if you get a fault internal to OSB - rather than from an external service - the $body doesn't contain the fault, but rather the original request message. In this case the $fault will contain the info required:

<con:fault xmlns:con="http://www.bea.com/wli/sb/context">
 <con:errorCode>BEA-382513</con:errorCode>
 <con:reason>OSB Replace action failed updating variable "body": Error parsing XML: {err}XP0006: "text '
      '": bad value for type element {http://www.acme.com/schema/GeoServicePS/v1}getLocationsByLocationIds { {http://www.w3.
org/2001/XMLSchema}anyType }</con:reason>
 <con:location>
  <con:node>RouteNodeToEJBBS</con:node>
  <con:path>request-pipeline</con:path>
 </con:location>
</con:fault>

The rule for a "universal error handler" could be:
merge whatever information comes from $body (if it's a env:Fault) and from $fault, to build a meaningful env:Fault object to return to the caller with a HTTP 500 status.


This can be used to merge env:Fault and ctx:fault into a meaningful env:Fault:

xquery version "1.0" encoding "Cp1252";
xquery version "1.0" encoding "Cp1252";
(:: pragma  parameter="$theBody" type="xs:anyType" ::)
(:: pragma  parameter="$theFault" type="xs:anyType" ::)
(:: pragma  type="xs:anyType" ::)

declare namespace xf = "http://tempuri.org/GEO_OSB_MARIA_EJBProxyProject/XQ/generateFault/";
declare namespace env = "http://schemas.xmlsoap.org/soap/envelope/";
declare namespace con = "http://www.bea.com/wli/sb/context";

declare function xf:generateFault($theBody as element(*),
    $theFault as element(*))
    as element(*) {
  
   
    {fn:concat($theBody/env:Fault/faultcode/text(), ' ', $theFault/con:errorCode/text())}
    {fn:concat($theBody/env:Fault/faultstring/text(), ' ' , $theFault/con:reason/text())}
    
    {$theBody/env:Fault/detail/*} { $theFault/con:location} 
   
  
  
};

declare variable $theBody as element(*) external;
declare variable $theFault as element(*) external;

xf:generateFault($theBody, $theFault) 



Using Axis2 as a client, during unmarshalling of the response Axis will generate a org.apache.axis2.AxisFault Java exception upon reception of a env:Fault in the body;
reason, code and details are populated.
This is optional and can be disabled.
All this takes place in Axis2 kernel, org.apache.axis2.util.Utils.getInboundFaultFromMessageContext(...)


When unittesting for SOAP Fault, assert that an AxisFault exception is generated.

Wednesday, May 19, 2010

Web Services faults caught from a Java client

If your WS returns you a fault, in Java this will be mapped to a

javax.xml.ws.soap.SOAPFaultException

which is filled with a detailedMessage 

Failed to invoke end component com.acme.dbaccess.CompanyDBWS (POJO), operation=insertCompany
 -> Failed to invoke method
 -> UNABLE_TO_EXECUTE_SQL


and a fault (name is SOAP-ENV:Fault) with QName {http://schemas.xmlsoap.org/soap/envelope/}Fault

Friday, May 7, 2010

Exception Handling in Web Services.... AKA Web Services suck - big time.

I have a WebService with a WebMethod

@WebMethod
public void updateCompany(Company company) throws CompanyException


where
public class CompanyException extends Exception


this generates this WSDL:

    <s0:operation name="updateCompany" parameterOrder="parameters">
      <s0:input message="s1:updateCompany"/>
      <s0:output message="s1:updateCompanyResponse"/>
      <s0:fault message="s1:CompanyException" name="CompanyException"/>
    </s0:operation>

______________________

If I have 2 exceptions:
public void updateCompany(Company company) throws CompanyException, NamingException

I get 2 faults
      <s0:fault message="s1:CompanyException" name="CompanyException"/>
      <s0:fault message="s1:NamingException" name="NamingException"/>

______________________

If I add the annotation javax.xml.ws.WebFault:
@WebFault(name="companyFault")

before the public class CompanyException extends Exception, the WSDL SHOULD become

    <s0:operation name="updateCompany" parameterOrder="parameters">
      <s0:input message="s1:updateCompany"/>
      <s0:output message="s1:updateCompanyResponse"/>
      <s0:fault message="s1:companyFault" name="companyFault"/>
    </s0:operation>

or something like that.... but unfortunately with WebLogic this doesn't seem to affect the WSDL!

______________________

Anyhow, if you construct the CompanyException without invoking the super(String message) constructor,
you will get this:

<env:Body>

<env:Fault>
<faultcode>env:Server</faultcode>
<faultstring/>

<detail>
<com:string xsi:nil="true"/>
</detail>
</env:Fault>
</env:Body>




otherwise, if you do super(message),and you invoke

Java:
throw new CompanyException("UNABLE_TO_EXECUTE_SQL")

SOAP Fault:

<env:Fault>
<faultcode>env:Server</faultcode>
<faultstring>UNABLE_TO_EXECUTE_SQL</faultstring>

<detail>
<com:string>UNABLE_TO_EXECUTE_SQL</com:string>
</detail>
</env:Fault>
</env:Body>


______________________

If your exception extends WebServiceException, AND you do super(message), you get something really exciting:


<env:Body>

<env:Fault>
<faultcode>env:Server</faultcode>

<faultstring>
Failed to invoke end component com.acme.dbaccess.CompanyDBWS (POJO), operation=insertCompany
 -> Failed to invoke method
 -> UNABLE_TO_EXECUTE_SQL
</faultstring>

<detail>

<bea_fault:stacktrace>
com.acme.dbaccess.CompanyException: UNABLE_TO_EXECUTE_SQL

    at com.acme.dbaccess.CompanyDBWS.insertCompany(CompanyDBWS.java:81)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
..................
    at weblogic.work.ExecuteThread.run(ExecuteThread.java:173)

</bea_fault:stacktrace>
</detail>
</env:Fault>
</env:Body>



Better still if you use the super(message, Throwable) so you get also the stacktrace of the original exception!

YET the fault generated is not very usable.... the faultstring is very dirty and the faultcode useless....
I need to find a better way of doing this...


______________________


Now, if your service throws an Unchecked Exception (like NullPointerException), you will still get something decent:

Java:
throw new NullPointerException("I am a NPE") ;

SOAP Fault:

<env:Envelope>

<env:Body>
<env:Fault>
<faultcode>env:Server</faultcode>
<faultstring>
Failed to invoke end component com.acme.dbaccess.CompanyDBWS (POJO), operation=updateCompany
 -> Failed to invoke method
 -> I am a NPE
</faultstring>
<detail>
<bea_fault:stacktrace>
java.lang.NullPointerException: I am a NPE

    at com.acme.dbaccess.CompanyDBWS.updateCompany(CompanyDBWS.java:25)

    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
..............
    at weblogic.work.ExecuteThread.run(ExecuteThread.java:173)

</bea_fault:stacktrace>
</detail>
</env:Fault>
</env:Body>
</env:Envelope>


_____________________________

If I extend CompanyException from SOAPException, I get this fault:

<env:Envelope>
<env:Body>
<env:Fault>
<faultcode>env:Server</faultcode>
<faultstring>UNABLE_TO_UPDATE</faultstring>
<detail>
<java:CompanyException/>
</detail>
</env:Fault>
</env:Body>
</env:Envelope>

that is, the Fault message is cleary readable (UNABLE_TO_UPDATE) but I have lost the Stacktrace.

_____________________________

On the whole, my impression is that Exception (Fault) generation and handling is, as everything else in WS, very poorly specified and implemented. Compare it to the level of technology in Java and you will only cry and feel lost in hyperspace with WS.

Let's face it, when you come from a Java background, you feel that Web Service technology has been designed by a bunch of fat old drunkards in a brothel running wildly after some young cheerful ladies in pink pajamas... two organs require a lot of blood: the brain and the penis, and we can only operate one at a time.

_______________

It is very educational to look at how a SOAP call is executed inside WebLogic:

    JavaClassComponent.invoke(String, Object[], MessageContext) line: 124   
    ComponentHandler.handleRequest(MessageContext) line: 84   
    HandlerIterator.handleRequest(MessageContext, int) line: 141   
    ServerDispatcher.dispatch() line: 114   
    WsSkel.invoke(Connection, WsPort) line: 80   
    SoapProcessor.handlePost(BaseWSServlet, HttpServletRequest, HttpServletResponse) line: 66   
    SoapProcessor.process(HttpServletRequest, HttpServletResponse, BaseWSServlet) line: 44   
    BaseWSServlet$AuthorizedInvoke.run() line: 285   
    WebappWSServlet(BaseWSServlet).service(HttpServletRequest, HttpServletResponse) line: 169   
    WebappWSServlet(HttpServlet).service(ServletRequest, ServletResponse) line: 820   
    StubSecurityHelper$ServletServiceAction.run() line: 227   
    StubSecurityHelper.invokeServlet(ServletRequest, HttpServletRequest, ServletRequestImpl, ServletResponse, HttpServletResponse, Servlet) line: 125   
    ServletStubImpl.execute(ServletRequest, ServletResponse, FilterChainImpl) line: 292   
    ServletStubImpl.execute(ServletRequest, ServletResponse) line: 175   
    WebAppServletContext$ServletInvocationAction.run() line: 3498   
    AuthenticatedSubject.doAs(AbstractSubject, PrivilegedAction) line: 321   
    SecurityManager.runAs(AuthenticatedSubject, AuthenticatedSubject, PrivilegedAction) line: not available   
    WebAppServletContext.securedExecute(HttpServletRequest, HttpServletResponse, boolean) line: 2180   
    WebAppServletContext.execute(ServletRequestImpl, ServletResponseImpl) line: 2086   
    ServletRequestImpl.run() line: 1406   
    ExecuteThread.execute(Runnable) line: 201   
    ExecuteThread.run() line: 173   

MOST LIKELY it's the SoapProcessor who maps the Java Exception to the SOAP Fault.... ah, if only I had the source code!
_______________

JAX-WS specs define at least 4 exceptions (see http://www.ibm.com/developerworks/webservices/library/ws-jaxws-faults/index.html):

SOAPFaultException
javax.xml.ws.WebServiceException
ExecutionException
javax.xml.soap.SOAPException (one is redefined also in XMLBeans)


all this is simply ridiculous. Things have seriously gone out of control in WS technology.   

This post http://io.typepad.com/eben_hewitt_on_java/2009/07/using-soap-faults-and-exceptions-in-java-jaxws-web-services.html  is excellent, yet I keep HATING this technology. I am a strong believer of Convention Over Configuration and when I see all the verbosity in WS it makes me mad.

If builders built buildings the way programmers wrote programs, then the first woodpecker that came along would destroy civilization. (Weinberg's Second Law)


Perfection is achieved, not when there is nothing more to add, but when there is nothing left to take away. (Antoine de Saint-Exupery, French writer)





Sunday, May 2, 2010

How to extract a fault stacktrace from the SOAP fault using Java

It's..... COMPLICATED!
ps.txt contains the entire fault.... you can adapt to read from a String...

package com.integration.xpath;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;

import javax.xml.XMLConstants;
import javax.xml.namespace.NamespaceContext;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class XPathRunner {
    public static void main(String[] args) throws ParserConfigurationException, SAXException, IOException, XPathExpressionException {
        doFault();       
    }

    private static void doFault() throws ParserConfigurationException,
    SAXException, IOException, XPathExpressionException {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true); // never forget this!
        DocumentBuilder builder = factory.newDocumentBuilder();
        File f = new File("st.txt");
        Document doc = builder.parse(f);
        XPathFactory xpathFactory = XPathFactory.newInstance();
        XPath xpath = xpathFactory.newXPath();
        xpath.setNamespaceContext(new PersonalNamespaceContext());
       
        XPathExpression expr = xpath.compile("//bea_fault:stacktrace/text()");
        Object result = expr.evaluate(doc, XPathConstants.NODESET);
        NodeList nodes = (NodeList) result;
        for (int i = 0; i < nodes.getLength(); i++) {
            Node item = nodes.item(i);
            System.out.println(item.getNodeValue());
        }
    }

  
   
    public static class PersonalNamespaceContext implements NamespaceContext {

        public String getNamespaceURI(String prefix) {
            if (prefix == null) throw new NullPointerException("Null prefix");
            else if ("env".equals(prefix)) return "http://schemas.xmlsoap.org/soap/envelope/";
            else if ("bea_fault".equals(prefix)) return "http://www.bea.com/servers/wls70/webservice/fault/1.0.0";
            else if ("xml".equals(prefix)) return XMLConstants.XML_NS_URI;
            return XMLConstants.NULL_NS_URI;
        }

        // This method isn't necessary for XPath processing.
        public String getPrefix(String uri) {
            throw new UnsupportedOperationException();
        }

        // This method isn't necessary for XPath processing either.
        public Iterator getPrefixes(String uri) {
            throw new UnsupportedOperationException();
        }

    }   
}

thank you to http://www.ibm.com/developerworks/library/x-javaxpathapi.html

Recoverable vs. Unrecoverable System Faults

My overall problem is:

I invoke an external service and receive a fault.
How do I distinguish between a System Fault and a Business Fault?
If another process is updating a DB table, locking the data so I go in timeout.... is this a Business Fault or a System Fault?

In case of any fault:
- Should I retry after some time, with the same parameters?
- Should I retry after some time, tweaking some parameters?
- Should I simply give up?


Another example:
BEA-380002  means "unable to connect".... it the series of endpoints configured for the Business Service has been exhausted, and the number of retries configured is finished, then I should simply give up.

Anyway one should go over this http://download.oracle.com/docs/cd/E11036_01/alsb30/messages/alsb/kernel/l10n/TransportKernel.html list of transport errors and tell me if we can retry them or not.


OSB gives you a very limited set of policies on a Business Service: 
retry count
retry interval
retry application errors (i.e. soap faults, this is a new feature in 3.0).


Reading Oracle SOA Suite Developer's Guide (Error Handling chapter) I learn that:
Oracle BPM has a Fault Management Framework,
where you can define FaultPolicy elements in a XML file,
with Fault Condition and Actions. The Fault Condition classifies a Fault Type, and the Action defines how to handle this specific Fault Type.

The Condition is based on FaultName (as in the WSDL "fault" clause) and FaultCode (as from the $fault/faultcode element). It can also be expressed with a test clause where you extract part of the fault message using XPath expression and check its content, as in:
<test>$fault.payload/tns:fault/tns:code="380002"</test>

The Action can be: retry, humanIntervention, rethrow, abort, replayScope, or javaAction.
Each Action type iscomplemented by a specific parameter set (see XSD after).

The whole enchilada is explained here: 

You provide a fault-policies.xml file and a fault-bindings.xml file. In the fault-bindings you define which policies apply to each process.

Some more concepts are expressed here http://download.oracle.com/docs/cd/E12839_01/integration.1111/e10224/med_faulthandling.htm,  this link contains also a very precious XSD of  policies and bindings.


A quick link to the book