Are your Web services exceptions naked or covered?

Building a reliable SOA requires rigorous Web services exception handling and testing techniques

Web services—the foundation of service-oriented architecture (SOA)—are self-contained, modular applications that one can describe, publish, locate, and invoke over a network. Web services operate at a level of abstraction similar to the Internet. They are agnostic to operating system, hardware platform, communication protocol, or programming language and have blurred the boundaries between network devices, security products, applications, and other IT assets within an enterprise. Almost every IT asset now advertises its interface as a Web Services Description Language (WSDL) interface ready for SOAP/XML messaging. Using SOAP for system-to-system messaging and WSDL for interface description, IT professionals now have unprecedented flexibility in integrating IT assets across internal and external corporate domains. It is this flexibility of distributed computing provided by Web services that makes exception handling complex within a service-oriented architecture. In this article, we will explore exception handling and testing techniques, and their impact on Web services-based SOA.

Web services exceptions

An exception is shorthand for "exceptional event," defined as follows by the Java Tutorial: An exception is an event, which occurs during the execution of a program that disrupts the normal flow of the program's instructions.

Exceptions can happen when an error occurs in a method or a Web services operation. The error can be caused by program flaws or a lack of system resources. Typical problems include:

  • User input errors, where a user inadvertently types incorrect characters
  • Physical limitations, where a disk fills up or runs out of available memory
  • Device errors, where the hardware doesn't behave as expected, such as an unenabled portable USB key
  • Network errors, where an application server tries to connect to a remote database using Java Database Connectivity

The calling program or consumer must be notified that an error occurred in the invoked method or producer. This is where sophisticated, programmatic exception handling comes into play, with exceptions bubbled to the calling programs to enable recovery from the exception. Typically, an astute programmer anticipates exceptions and programmatically handles them; however, not all exceptions can be anticipated, and not all exceptions are recoverable. If an exception handler is not found, the runtime system can terminate and act unreliably.

Programming platforms such as Java and .Net have extensive and sophisticated exception handling capabilities. Developers can construct a rich set of error handling patterns using conditional if-then-else constructs and try-catch-finally blocks. Using such exception handling patterns, developers can reduce the risk of drastic program crashes or abrupt, unanticipated program termination and add robustness to an application.

Within a Web services-based distributed architecture, exception handling must address additional requirements. Exceptions now have to be communicated independent of operating systems, programming languages, and applications. The exceptions must be presented to the consumer such that they are readily interpreted, and if the exception is recoverable, the consumer can seamlessly respond to the exception thrown by a Web services producer that may be located anywhere within, or external to, the corporate IT boundaries. The communication of such exceptions must occur with corporate security in mind as well. If exceptions are not handled rigorously, carefully, and deliberately, a producer Web service runs the risk of exposing details of internal IT assets to the consumer or leaking sensitive corporate information in stack traces.

Bottom line: For externally facing Web services exposed to a large number of public consumers, only recoverable exceptions should be communicated. All other exceptions should be controlled, cleansed, or suppressed.

Test setup

To explore exception handling for Web services, we built a simple Java class with numerous similar methods that divide two numbers. The methods differed in their ability to handle exceptions, starting with a method that completely lacked exception handling to one that had explicit exception handling constructs for many exception scenarios. As shown in Figure 1, we used the following components for setting up exception handling tests:

  1. BEA WebLogic Portal 9.2 is an application server for Java-based application development. The installer includes BEA Workshop for WebLogic Platform, an IDE for rapidly building Web services. We chose to use WebLogic because of our familiarity with the product, its popularity across the industry, and its ease-of-use in building Web services.
  2. Crosscheck Networks SOAPSonar is a Web services testing client that consumes a WSDL and generates functional, performance, interoperability, and vulnerability tests for a target Web service.
Web services exception handling setup

In the subsequent sections, we will explore the different exception handling techniques and the impact of calling the methods remotely from Web services clients such as SOAPSonar, and then look at how they behave under positive and negative tests as well as harsh boundary condition tests generated from SOAPSonar.

Naked Web service

Our first Web service has no protection against adverse conditions. The underlying method, as shown in Listing 1, takes in a couple of double inputs, divides the dividend by the divisor, and returns a resultant double value.

Listing 1. Naked Java method, nakedDivide(…) without exception handling

 package mathservice;
import javax.jws.*;

@WebService
public class DivideWS {

   @WebMethod
   public double nakedDivide(double dividend, double divisor) 
   {
        return dividend/divisor;    
   }
}

Test results: A few positive and negative tests quickly reveal the behavior of the nakedDivide(…) Web method:

  • Positive tests show that the Web service divides the input numbers as expected
  • When a zero divisor is entered, the application server handles it elegantly and returns an INF value
  • If nothing is entered in the dividend, or divisor, a detailed stack trace with list information about the XML processing internal, such as serializer, SOAP server protocol handler, and implementation platform is exposed

In general, stack trace information is useless to the Web services consumer; there isn't much the consumer can do to recover from such a generic and verbose exception. It's not as if a simple set of document return codes have been sent to the client for an elegant recovery from a divide-by-zero or null-value exception. Not only is the stack trace information sent to the client useless and programmatically in-actionable, it also exposes a serious risk of passing implementation details to external consumers that may be used for further exploits.

Defensive Web service

The defensive Web service defensiveDivide(…), as shown in Listing 2, builds on the previous method nakedDivide(…) by adding a simple check to see whether the divisor is zero. This ensures that a divide-by-zero error does not occur. The method returns 0 if the divisor is zero so the calling program can recover from an inappropriate zero divisor input. In this method, the programmer has anticipated a boundary condition and defensively coded to handle it.

Listing 2. Defensive Java method, defensiveDivide(…) with divisor !=0 check

 @WebMethod
   public double defensiveDivide(double dividend, double divisor) 
   {
      if (divisor != 0)
         return dividend/divisor;
      else
         return 0;  
   }

Test results: A few positive and negative tests quickly reveal the behavior of the defensiveDivide(…) Web method:

  • Positive tests show that the Web service divides the input numbers as expected
  • When a zero divisor is entered, unlike nakedDivide(…), which returns an INF value for infinity after attempting the divide, defensiveDivide(…) returns an explicit programmer-defined 0.0 error value without even attempting the divide operation
  • If nothing is entered in the dividend, or divisor, as in the case of nakedDivide(), a detailed stack trace with list information about the XML processing internal, such as serializer, SOAP server protocol handler, and implementation platform is exposed

The test results show that although the divide-by-zero scenario is properly anticipated and handled, not much is done for defensive error handling beyond that. The programmer, however, makes the defensiveDivide() more efficient than nakedDivide() by avoiding a computation with bad data. Anticipation means program efficiency. For non-numeric characters or nulls, the method reveals its internals, much like its naked counterpart. In the following section, the method is tightened for further exception handling.

Container-safe Web service

As shown in Listing 3, the divide method is modified to take in string values as inputs, rather than double values as expected in the previous examples. This enables the developer to programmatically control the appropriateness of the input values, instead of letting the method throw a runtime exception that is handled by the container. Letting the container handle exceptions removes control from the programmer, especially for anticipated edge conditions that the programmer can address.

The string dividend and divisor are first converted to doubles in a try-catch block. If the conversion fails, the catch block catches the exception and returns a zero value. After a successful conversion, the divisor is checked for a zero value and if the value is not zero, the operation successfully occurs.

Listing 3. Container-safe Java method, defensiveStringDivide(…) with string inputs

 @WebMethod
   public double defensiveStringDivide(String dividend, String divisor) 
   {

      double dDividend;
      double dDivisor;

      try
      {
         dDividend = Double.parseDouble(dividend);
         dDivisor = Double.parseDouble(divisor);
      }
      catch (Exception e)
      {
         return 0;
      }


      if (dDivisor != 0)
         return dDividend/dDivisor;
      else
         return 0;
   }

Test results: A few positive and negative tests quickly reveal the behavior of the defensiveStringDivide(…) Web method:

  • Positive tests show that the Web service divides the input numbers as expected.
  • When a zero divisor is entered, like defensiveDivide(…), the method behaves elegantly without sending a SOAP fault with stack traces. The method elegantly returns a zero value to the Web services consumer.
  • If nothing is entered in the dividend or divisor, or a non-numeric character is entered, the method attempts to convert it and fails. The catch block handles the exception and returns a zero value.

The defensiveStringDivide() method is tighter than both previous methods. This method explicitly protects against arbitrary non-numeric values being entered as inputs. It controls the exception handling and prevents stack traces from being generated from non-numeric inputs. Through defensive coding, the programmer prevents information from leaking via the container's exception handling constructs. Using such defensive techniques lowers the risk of extracting program, parser, and container internals over a Web services channel.

Web services exception handling best practices

Exception handling across distributed systems is complex and challenging and requires significant thought. With SOAP fault constructs available for standard-based fault communication, mechanisms for exception communication and handling are available across program, hardware, and system boundaries. However, with such networks-based fault communication facilities, the burden of properly coding exception handling increases. Without disciplined and deliberate Web services exception handling, one can end up exposing internal component details to external consumers, which, at a minimum, could be a source of embarrassment, or far more significantly, such information leaks can add to corporate liability. Some of the best practices for handling exceptions within an SOA are as follows:

  • Only exceptions that can be used by the consumer should be thrown.
  • All irrecoverable exceptions for externally facing Web services should be cleansed or suppressed.
  • Web services developers should anticipate exceptions and provide proper handling mechanisms for all anticipated exceptions through try-catch and if-then-else type constructs.
  • Security administrators should centralize Web services flows by using SOA/Web services gateways so that all stack traces traveling over SOAP are inspected and controlled before SOAP messages leave corporate boundaries. Internal Web services may not have as strict requirements as externally facing Web services in what can be seen in a SOAP fault.
  • Web services developers should use a combination of try-catch and if-then-else statements for exception handling. Try-catch should be used as more general-purpose exception handling constructs, where not everything is anticipated. Keep in mind that overuse of try-catch can add a performance hit.
  • Rigorous automated boundary condition testing is crucial to anticipate and expose error conditions in a complex and distributed Web services-based SOA.
1 2 Page
Join the discussion
Be the first to comment on this article. Our Commenting Policies
See more