Web ServicesUser Defined Exceptions: Improve Error Handling in Web Services

User Defined Exceptions: Improve Error Handling in Web Services

Exception handling is an important aspect in any software application development. An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions. When the exception occurs in a Web Service, it will be propagated to the client as a SOAP fault. This article will help you understand different types of exceptions that could be thrown inside the Web Service and how user defined exceptions can improve error handling in your Web Service.

Different Types of Exceptions

Exceptions could be thrown inside a Web Service for various reasons. The possible types of exceptions that could be thrown are RuntimeException, RemoteException, SOAPFaultException and User Defined Exception.

The Web Service developer might try throwing a RuntimeException such as NullPointerException or ArrayIndexOutOfBoundsException inside the Web Service. But, throwing RuntimeException inside the Web Service is considered to be a bad exercise because RuntimeException will always be converted into RemoteException at the client side. While the client is waiting to catch the RuntimeException after invoking a Web Service, he will get only the RemoteException instead of RuntimeException. Eventually, he cannot perform proper error handling for RuntimeException.

The problem with throwing RemoteException is that the different client side libraries will interpret this exception in different ways. It is not portable.

The appropriate exception that the Web Service application could throw is SOAPFaultException. The SOAPFaultException contains four parts: faultcode, faultstring, actor, and detail. This exception can give you the complete information about the error condition. The faultcode might tell you whether the error has happened because of the Server, Client, or something else. For example, when a client doesn’t provide security information such as username and password in the HTTP/SOAP header but the service mandates them, the pre-processing logic in the service implementation might obviously throw an authentication exception. This kind of error is considered to be a Client error. The faultstring contains the corresponding description of the error condition that has happened. This string message is human readable and the developer can easily debug the problem with the help of it. The detail element contains the actual exception message and its complete stack trace. Actually, the detail element is an instance of the javax.xml.soap.Detail class and can be created by using the javax.xml.soap.SOAPFactory.createDetail() API.

User-Defined Exceptions

Although SOAPFaultException gives necessary information that is needed to debug the problem, the Web Service client might want to do something more with a fault message that he had received. The one thing that he might want to do is to have a configurable and dynamic text description of the error that occurred. The other thing could be to have the internationalization support for the error messages. By providing text description in languages other than English, he could try addressing needs of all set of people. The list may grow more and more.

To achieve the previously stated goals, a user-defined exception with some specific format could be thrown in the service. Based on the exception that the client has received, he can decide what to do with fault message. The user-defined exception thrown inside the Web Service is directly mapped into wsdl:fault element in the Web Services Description Language (WSDL) of the Web Service. You could see the fault element as one of the child elements of operation element of the portType. The other two child elements of operation are input and output. The fault element, an optional field in the operation element, defines the abstract format of the error message that might be thrown by the Web Service. The wsdl:message element pertaining to WSDL:fault can contain only one message part, and the message part could be either a complexType or simple XMLType.

I propose here how a user-defined exception, MyCustomException, could be defined to better handle error messages. The MyCustomException contains an appropriate fault message that is as defined in the WSDL definition. The fault message contains the following three parts:

  • Error Code, containing a five-letter identifier plus a three-digit identifier

    For example, GENEX001 could mean a generic error, whereas AUTEX001 could mean an authentication error.

  • Text describing the fault, including substitution variables (mentioned as {0}, {1}, and so forth)

    For example, the corresponding text description for Error Code GENEX001 will be something like “Number that you have entered is {0}.”

  • A list of values corresponding to the substitution variables defined in the above text.

    For example, for the above case, the variable list will contain only one value (say 1) because the text description above contains only one substitution variable.

    After processing the text using relevant logic, the final text description would be “Number that you have entered is 1.”

With the above approach:

  • Faults can be identified, using the error code, without the need for applications or people to parse or understand the fault text (which may be in a unknown language).
  • Text description can be defined in many languages, and corresponding variables can be placed accordingly in a language-independent manner.
  • Internationalization support can be offered by the Services by providing alternate language forms of fault messages automatically by replacing the text description portion only.

Sample: Service, Exception Class, WSDL, and Client

In this section, you will see a sample for service, exception, WSDL, and client. These samples will use Weblogic implementation.

Service Implementation

This service takes two inputs: int number and String message. If the number is 1, SOAPFaultException will be thrown. If the number is 2, MyCustomException will be thrown; otherwise, the method will return successfully.

package WS.exception;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPFactory;
import javax.xml.soap.Detail;

public class ExceptionService{
   public String echo(int number, String message)
          throws MyCustomException{
      System.out.println("Number: "+number+", Message: " + message);
      switch(number){
         case 2:
            throw new MyCustomException(
            "GENEX001",
            "Number that you have entered is {0}",
            new String[]{String.valueOf(number)}
               );
         case 1:
            Detail detail = null;
            try{
               detail = SOAPFactory.newInstance().createDetail();
               //add error message here
               detail.addTextNode(
                  "Choice 1 means SOAPFaultException");
            }catch(SOAPException ex){
               throw new MyCustomException(
                  "SEREX001",
                  "Error while creating detail element",
                  new String(){});
            }

            throw new javax.xml.rpc.soap.SOAPFaultException(
               new javax.xml.namespace.QName("env.Server"),
               "Number that you have entered is "+number,
               "NO ACTOR",
               detail);
      }

      return "SUCCESS";
   }
}

MyCustomException class

The pseudo implementation for the custom exception is given below:

package WS.exception;
public class MyCustomException extends Exception{
   private String   errorCode;
   private String   errorDescription;
   private String[] variables;

   public MyCustomException(String errorCode,
                            String errorDescription,
                            String[] variables){
      this.errorCode = errorCode;
      this.errorDescription = errorDescription;
      this.variables = variables;
   }
   //all setter and getter methods need to be implemented here
}

WSDL Fault

The description of wsdl:fault is given below:

  <xsd:schema xmlns_xsd="http://www.w3.org/2001/XMLSchema"
    xmlns_stns="java:WS.exception"
    elementFormDefault="qualified"
    attributeFormDefault="qualified"
    targetNamespace="java:WS.exception">
   <xsd:import namespace="java:language_builtins.lang">
   </xsd:import>
   <xsd:element type="stns:MyCustomException"
     name="MyCustomException">
   </xsd:element>
   <xsd:complexType name="MyCustomException">
    <xsd:sequence>
     <xsd:element type="xsd:string"
       name="errorCode"
       minOccurs="1"
       nillable="true"
       maxOccurs="1">
     </xsd:element>
     <xsd:element type="xsd:string"
       name="errorDescription"
       minOccurs="1"
       nillable="true"
       maxOccurs="1">
     </xsd:element>
     <xsd:element xmlns_tp="java:language_builtins.lang"
       type="tp:ArrayOfString"
       name="variables"
       minOccurs="1"
       nillable="true"
       maxOccurs="1">
     </xsd:element>
    </xsd:sequence>
   </xsd:complexType>
</xsd:schema>

<message name="MyCustomException">
  <part  xmlns_partns="java:WS.exception"
    type="partns:MyCustomException"
    name="MyCustomException">
  </part>
 </message>

<portType name="ESPort">
  <operation name="echo">
   <input message="tns:echo">
   </input>
   <output message="tns:echoResponse">
   </output>
   <fault name="MyCustomException"
     message="tns:MyCustomException">
   </fault>
  </operation>
 </portType>

Client Implementation

Web Services can be invoked using two approaches:

  1. Using stubs
  2. Using Dynamic Invocation Interface (DII)

Following is the client implementation using DII:

import java.util.*;
import javax.xml.namespace.QName;
import javax.xml.rpc.*;
import javax.xml.rpc.soap.SOAPFaultException;

public class ExceptionService{
   public static void main(String[] args) throws Exception{
     System.setProperty(
         ServiceFactory.SERVICEFACTORY_PROPERTY,
         "weblogic.webservice.core.rpc.ServiceFactoryImpl");

      ServiceFactory factory = ServiceFactory.newInstance();
      Service service = factory.createService(new QName(""));
      Call call = service.createCall();

      call.addParameter("intVal",
                        new QName("http://www.w3.org/2001/XMLSchema",
                        "int"), ParameterMode.IN);
      call.addParameter("string",
         new QName("http://www.w3.org/2001/XMLSchema", "string"),
         ParameterMode.IN);
      call.setOperationName(new QName("http://tempuri.org", "echo"));
      call.setReturnType(
         new QName("http://www.w3.org/2001/XMLSchema", "string"));
      call.setTargetEndpointAddress("http://localhost/exception/ES");
      Object[] argArray = {new Integer(args[0]), "how are you"};
      Object resultObj = null;
      try{
         resultObj = call.invoke(argArray);
      }catch(SOAPFaultException fault){
         fault.printStackTrace();
         resultObj = "ERROR Caught";

         System.out.println("Fault Detail : "+ fault.getDetail());
         System.out.println("Fault Actor : "+ fault.getFaultActor());
         System.out.println("Fault String: "+ fault.getFaultString());
      }

      System.out.println("Result: "+resultObj);
   }
}

When you generate stubs using the Weblogic clientgen utility, you would be able to catch MyCustomeException itself on the client side. The sample code is given below:

package WS.exception;
import java.text.MessageFormat;
public class Client {
   public static void main(String[] args ) throws Exception{
      System.setProperty(
      "javax.xml.rpc.ServiceFactory",
      "weblogic.webservice.core.rpc.ServiceFactoryImpl");

      ES_Impl ws = new ES_Impl(args[0]);
      ESPort port  = ws.getESPort();

      String returnVal = null;
      try{
         returnVal = port.echo(Integer.parseInt(args[1]),
                               "A for Apple");
   }catch(MyCustomException ex){
      System.out.println("MyCustomException occurred:");
      //ex.printStackTrace();
      String errorCode = ex.getErrorCode();
      String errorDescription = ex.getErrorDescription();
      String[] variables = ex.getVariables();

      String resolvedErrorMsg = MessageFormat.format(
                                errorDescription, variables);
      if(errorCode.startsWith("AUT")){
         //Authentication error: do something
      }else if(errorCode.startsWith("MSG")){
         //SOAP Message error: do something
      }else{
         //General error: do something
      }
   }catch(Exception ex){
      System.out.println("One of the other Exceptions occurred:");
      ex.printStackTrace();
   }
   System.out.println(returnVal);
   }
}

In the program above, after catching MyCustomException, you can get the error code, text, and variables. Based on the error, you can segregate the type of error that had happened. If you want, you can use the text description that comes with the excerption. Otherwise, you can fetch the text description which in other language from a database, or a disk file that is associated with the error code. You can store the error code and description as a name-value pair in a disk file or a database. This will really help you manage internationalization of the error message. Also, MessageFormat class will help you get the dynamic error message.

SOAP Response 1 (when a SOAPFaultException is thrown)

Following is the SOAP response that I got while the service had thrown SOAPFaultException.

<env:Envelope xmlns_env="http://schemas.xmlsoap.org/soap/envelope/"
              xmlns_xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns_soapenc="http://schemas.xmlsoap.org/soap/encoding/"
              xmlns_xsd="http://www.w3.org/2001/XMLSchema">
   <env:Body>
      <env:Fault xmlns_fault="">
         <faultcode>fault:env.Server</faultcode>
         <faultstring>Number that you have entered is 1</faultstring>
         <faultactor>NO ACTOR</faultactor>
         <detail>Choice 1 means SOAPFaultException</detail>
      </env:Fault>
   </env:Body></env:Envelope>

SOAP Response 2 (when MyCustomException is thrown)

Following is the SOAP response that I got while the service had thrown MyCustomException.

<env:Envelope xmlns_env="http://schemas.xmlsoap.org/soap/envelope/"
              xmlns_xsi="http://www.w3.org/2001/XMLSchema-instance"
              xmlns_soapenc="http://schemas.xmlsoap.org/soap/encoding/"
              xmlns_xsd="http://www.w3.org/2001/XMLSchema">
   <env:Body>
      <env:Fault>
         <faultcode>env:Server</faultcode>
         <faultstring>Service specific exception:
                      WS.exception.MyCustomException</faultstring>
         <detail>
            <MyCustomException xmlns_n1="java:WS.exception"
                               xsi_type="n1:MyCustomException">
               <errorCode xsi_type="xsd:string">GENEX001</errorCode>
               <errorDescription xsi_type="xsd:string">
                  Number that you have entered is {0}
               </errorDescription>
               <variables soapenc_arrayType="xsd:string[1]">
                  <string xsi_type="xsd:string">2</string>
               </variables>
            </MyCustomException>
         </detail>
      </env:Fault>
   </env:Body></env:Envelope>

Conclusion

This article has discussed various exceptions in Web Service and how a user-defined exception could be used for effective error handling in Web Services.

About the Author

Ayyappan Gandhirajan holds a Bachelor’s degree in Electronics & Communication Engineering from MK University, India and is pursuing a Master’s in Software Systems from BITS, Pilani. He has been working as an Associate System Analyst for Hewlett-Packard, Bangalore. He has more than six years of software experience involving Web services, WS Security, and J2EE technologies. He is currently involved in Web services orchestration and access controllers. He can be reached at ayyappan.gandhirajan@hp.com or G_Ayyapparaj@yahoo.com.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories