Web services programming tips and tricks: Exception Handling with JAX-RPC

Throw the right exception from the service endpoint

Explicitly declaring faults in WSDL operations, like explicitly declaring exceptions in Java methods, is good programming practice. This tip first examines the exception behavior in the absence of wsdl:fault. It then focuses on how a wsdl:fault is mapped to a checked Java exception and how a JAX-RPC runtime handles this checked exception.

Share:

Ping Wang, Software Engineer, IBM

Ping Wang is one of the developers of the IBM WebSphere Web services engine. Previously, he was a developer for WebSphere System Management and focused on how to manage distributed processes using JMX (Java Management eXtension). You can contact Ping at pacific at us.ibm.com



Russell Butek (butek@us.ibm.com), Software Engineer, IBM

Russell Butek is one of the developers of the IBM WebSphere Web services engine. He is also an IBM representative on the JAX-RPC Java Specification Request (JSR) expert group. He was involved in the implementation of Apache's AXIS SOAP engine, driving AXIS 1.0 to comply with JAX-RPC 1.0. Previously, he was a developer of the IBM CORBA ORB and an IBM representative on a number of OMG task forces: the portable interceptor task force (of which he was chair), the core task force, and the interoperability task force. You can contact Russell at butek at us.ibm.com.



06 February 2004

Also available in Japanese

In the SOAP Web services world, a fault flows from the server to the client in the form of SOAP fault. A SOAP fault consists of the faultcode, faultstring, and optional fault actor and detail. The JAX-RPC specification defines various rules about how to map from a Java exception to a SOAP fault (server side) and from the SOAP fault back to the Java exception (client side).

There are four types of exceptions that can be thrown from the server.

  • java.rmi.RemoteException
  • java.lang.RuntimeException
  • javax.xml.rpc.soap.SOAPFaultException (a special, subclass of RuntimeException)
  • a checked, user-defined exception (mapped from the WSDL's wsdl:fault construct)

The client side will receive one of the following types of exceptions. Note that the client can not catch any RuntimeException other than SOAPFaultException.

  • java.rmi.RemoteException
  • javax.xml.rpc.soap.SOAPFaultException
  • a checked, user-defined exception

This article first discusses the expected behaviour on the client when the server throws various exceptions, and then emphasizes the use of checked exceptions.

RemoteException

JAX-RPC requires that all remote methods in a service endpoint interface (SEI) throw the standard java.rmi.RemoteException. This allows exceptions which arise from communications or runtime difficulties to propagate back to the caller. However, there is no portable means to send specific subclasses of RemoteException.

The application itself could also throw a RemoteException. However, since there is no portable means of sending specific RemoteExceptions, the client cannot catch specific RemoteExceptions. For a given SOAP fault returned from the server, different client-side JAX-RPC runtimes may have different interpretations and generate different RemoteExceptions. Because of this interoperability problem, the application should avoid throwing RemoteExceptions.


RuntimeException

When a problem occurs in a server-side JAX-RPC runtime which results in a RuntimeException being thrown (for example, NullPointerException), that exception will propagate back to the client, but it will do so as a SOAP fault. The client runtime will map SOAP fault to either RemoteException or SOAPFaultException (described below). Therefore, a service endpoint should not throw a RuntimeException expecting the client to always catch that RuntimeException because the client may receive a RemoteException instead.


SOAPFaultException

There is one special RuntimeException: javax.xml.rpc.soap.SOAPFaultException. SOAPFaultException is more descriptive than a RuntimeException and dictates the exact SOAP fault message which flows to the client. In other words, whoever throws this fault, whether the runtime or the application, controls the SOAP fault response. Therefore, how to map the SOAP fault to an appropriate exception really depends on the content of SOAPFaultException, it may be mapped to SOAPFaultException, RemoteException or even a checked user exception. SOAPFaultException is often used by JAX-RPC handlers. A JAX-RPC application itself normally should avoid throwing the SOAPFaultException.


Checked user exception

A good programming practice often involves explicitly defining checked user exceptions as part of the interface contract. In the JAX-RPC world, programmers need to first define wsdl:faults as part of a wsdl:operation. A wsdl:operation allows multiple wsdl:fault elements, just like a Java method allows multiple exceptions. Each wsdl:fault is mapped to a user exception as part of the SEI. In most cases, Java exceptions do not have complicated data structures; similarly for wsdl:fault, the schema definition referenced by the wsdl:fault is often straightforward. Nevertheless, it's still very important for programmers to think over which kind of exceptions are expected to be thrown, and then define appropriate wsdl:faults.

Mapping rules

Unlike wsdl:input and wsdl:output, the message referenced by wsdl:fault is only allowed a single message part which could refer to a simple type or a complex type. If the part element has a type attribute, then you can tell directly whether the type is simple (for example, xsd:int, xsd:string, etc.) or complex. If the part element has an element attribute, then you have to step to the element to see whether the type is simple or complex.

Mapping rules for simple types

For a simple type, the Java exception name is mapped from the name attribute of the wsdl:message element. The wsdl:part name is mapped to a getter method and a parameter in the constructor of the Java exception. For example, the fault information in the WSDL in Listing 1 maps to the Java language exception in Listing 2.

Listing 1. WSDL definition with a simple fault
<definitions ...>

  <message name="empty"/>
  <message name="InsufficientFundsFault">
    <part name="balance" type="xsd:int"/>
  </message>

  <portType name="Bank">
    <operation name="throwException">
      <input message="tns:empty"/>
      <output message="tns:empty"/>
      <fault name="fault" message="tns:InsufficientFundFault"/>
    </operation>
  </portType>
  ...
</definitions>
Listing 2. Java exception from the fault in Listing 1
public class InsufficientFundFault extends java.lang.Exception {
    private int balance;
    public int getBalance() {
        return this.balance;
    }

    public InsufficientFundFault() {
    }

    public InsufficientFundFault(int balance) {
        this.balance = balance;
    }
}

Mapping rules for complex types

For complexTypes, the Java exception name is mapped from the name of the complexType (or the name of the element if the fault message's part refers to an element). Each element inside the complexType is mapped to a parameter in the constructor of the Java exception and a getter method. Note that, unlike beans, there is no setter method. The only way to set such a field is through the exception constructor. For example, the fault information in the WSDL in Listing 3 maps to the Java language exception in Listing 4.

Listing 3. WSDL definition for a complex fault
<definitions ...>

  <types>
    <schema xmlns="http://www.w3.org/2001/XMLSchema">
      <element name="InsufficientFundFault">
        <complexType>
          <sequence>
            <element name="balance" type="xsd:int"/>
            <element name="requestedFund" type="xsd:int"/>
          </sequence>
        </complexType>
      </element>
    </schema>
  </types>
			 
  <message name="empty"/>
  <message name="withdrawRequest">
    <part name="amount" type="xsd:int"/>
  </message>
  <message name="InsufficientFundFaultMessage">
    <part name="fault" element="tns:InsufficientFundFault"/>
  </message>

  <portType name="Bank">
    <operation name="withdraw">
      <input message="tns:withdrawRequest"/>
      <output message="tns:empty"/>
      <fault name="fault" message="tns:InsufficientFundFaultMessage"/>
    </operation>
  </portType>

  ...
</definitions>
Listing 4: Java exception from the fault in Listing 3
    public class InsufficientFundFault 
    	extends java.lang.Exception 
    	implements java.io.Serializable {
    private int balance;
    private int requestedFund;

    public InsufficientFundFault(
           int balance,
           int requestedFund) {
        this.balance = balance;
        this.requestedFund = requestedFund;
    }

    public int getBalance() {
        return balance;
    }

    public int getRequestedFund() {
        return requestedFund;
    }
}

Mapping rules for fault inheritance

Suppose you want to define a subclass of InsufficientFundFault, called AccountInsufficientFundFault, to carry the account number of the requesting client (see Listing 5). Your application can throw this subclass exception and your client would receive this exception. Very straightforward.

Listing 5: WSDL faults with inheritance
  <wsdl ...>
    <types>
    <schema targetNamespace="http://example" ...>
      ...

      <complexType name="InsufficientFundFaultType">
        <sequence>
          <element name="balance" type="xsd:int"/>
          <element name="requestedFund" type="xsd:int"/>
        </sequence>
      </complexType>

      <complexType name="AccountInsufficientFundFaultType">
        <complexContent>
           <extension base="tns:InsufficientFundFaultType">
             <sequence>
               <element name="account" type="xsd:string"/>
             </sequence>
           </extension>
        </complexContent>
      </complexType> 
			     		    
      <element name="InsufficientFundFault" 
               type="tns:InsufficientFundFaultType"/>
      ...
    </schema>
    </types>
			 
    <message name="AccountInsufficientFundFaultMessage">
      <part element="tns:AccountInsufficientFundFault" name="fault"/>
    </message>
			
    <operation name="withdraw">
      <input message="tns:withdrawRequest" name="withdrawRequest"/>
      <output message="tns:withdrawResponse" name="withdrawResponse"/>
      <fault message="tns:InsufficientFundFaultMessage" 
                name="InsufficientFundFault"/>
    </operation>
    ...
  </wsdl>

By doing this, the method "withdraw" of the generated SEI stays the same (see Listing 6). One item to note, however, is that any subclass of InsufficientFundFault must appear in the WSDL file for the client to receive that exception. You cannot create a new subclass of InsufficientFundFault in the server-side Java component without doing the same in the WSDL file. WSDL is a declarative language. Every possible fault that a service can throw must be explicitly defined in the XML, otherwise the client will not know about it and will not be able to receive it.

Listing 6: Java SEI mapped from the above WSDL definition
  public interface Bank extends java.rmi.Remote {
    public boolean withdraw(java.lang.String account, int amount) 
        throws java.rmi.RemoteException, 
               example.InsufficientFundFaultType;
  }

SOAP fault content

The JAX-RPC runtime catches a user exception and serializes it to XML data based on the schema definition referenced by the message part of the wsdl:fault. Such XML data is used to fill in the content of the detail element of the SOAP fault. Listing 7 is the SOAP message for the complex InsufficientFundFault example. The SOAP message for a simpleType fault is similar except that the detail section is different.

Listing 7: SOAP Fault example
  <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
    <soapenv:Body>
      <soapenv:Fault>
        <faultcode >...</faultcode>
        <faultstring>...</faultstring>
        <detail>
          <InsufficientFundFault xmlns="http://example">
            <balance>1000</balance>
            <requestedFund>2000</requestedFund>
          </InsufficientFundFault>
        </detail>
      </soapenv:Fault>
    </soapenv:Body>
  </soapenv:Envelope>

The key thing here is that the detail section carries the content which must match the schema definition referenced by wsdl:fault. In this way, the client runtime will know which user exception it should be mapped to. Also note that the SOAP fault does not carry the exception stack trace as you normally expect for the Java exception; therefore, a Web services client should not expect to see the stack trace originating from the server side.


Summary

It is good programming practice to introduce user-defined faults. Using RemoteExceptions or RuntimeExceptions is not only too general, there is also no guarantee that every vendor will handle these in the same manner.

Once you decide to introduce user-defined faults, you must decide what kinds of faults to use -- faults of simple types, faults of complexTypes, or an inheritance tree of faults -- and you must understand how those faults map to Java programming artifacts.


Download

DescriptionNameSize
Source code for this articleDoNotLeaveThisLink.zip40KB

Resources

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into SOA and web services on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=SOA and web services, XML
ArticleID=11880
ArticleTitle=Web services programming tips and tricks: Exception Handling with JAX-RPC
publish-date=02062004