Welcome!

Java Authors: Jason Dolinger, Miko Matsumura, Walter H. Pinson, III, Liz McMillan, Maureen O'Gara

Related Topics: Java

Java: Article

A Strategy for Aspect-Oriented Error Handling

Bringing new challenges to development

Aspect-Oriented Programming (AOP) is a new, thought-provoking architecture paradigm still in its youth. One of AOP's primary goals is to improve the development of object-oriented systems by refactoring related lines of code that are typically found spread among classes (and are therefore difficult to maintain).

These blocks of related code represent functional "aspects" of the system, which now can be written in a single place and then "woven" into the target application. Logging the start and end of all method calls, securing method calls, and handling thrown exceptions are all commonly found aspects. While AOP provides an interesting and effective methodology for refactoring aspects out of code, how to implement these aspects is still left up to the developer. This article discusses a strategy for building a more easily maintainable, compartmentalized exception handing subsystem using a declarative chain of responsibility pattern and some concepts from aspect-oriented development. Many of the concepts discussed here have been implemented in an open source project called "Prob-lo-Matic" (http://problomatic.sourceforge.net).

As a consultant working on a wide spectrum of projects for various companies, I've found that subsystems specifically designed for exception handling (an aspect found in every project) can be strangely rare in the business world. There are few if any vendor products available that are "generic, full-featured exception handling suites." When exception-handling frameworks are home grown, as they often are, they usually contain a lot of difficult logic (causing more problems than they fix). There seems to be a wide variety of monitoring software that act as excellent exception detection and alerting tools but, in practice, few frameworks that provide application developers with structured, robust error-correction tools for use within their software packages.

For simple error handling, such as checking some variables or attempting to access an I/O device, the Java try/catch/finally system works fine. When an error occurs, there is usually no alternative but to handle the low-level exception inline and return some status to the calling stack frame. However, in modern distributed systems, there are more intricate requirements for error handling. Robust applications are required to retry failed steps, notify components of failures, and produce logging statements. First-rate application design demands a generic, pluggable exception-handling framework for our distributed systems. A declarative chain of responsibility pattern can act as the foundation for building such a system, and AOP can give us a powerful non-invasive way to integrate such a system with our code.

Chain of Responsibility (a.k.a. Chain of Command)
The chain of responsibility pattern's intent is to "avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request" (Gamma, et al. Design Patterns). Essentially, a message object is passed through a chain of objects implementing a common interface. The interface includes methods to chain the handler together, i.e., get- and setSuccessor methods:

public interface Handler {
public void handle(Object message);
public void setSuccessor(Handler successor);
public Handler getSuccessor();
}

Implementations of this interface use the successor property to pass the message through the chain, as in:

public class HandlerImplementation {
public void handle(Object message) {

// local handling code here

if (getSuccessor()!=null) {
successor.handle(message);
}
}
}

Handlers are acquired by the application in some fashion (more on that later), and execution is branched to the handle's handle() method. In the case of an exception-handling framework, we wish to construct an object that encapsulates all of the information about the state of our application when an error occurred, as well as information about what to do for specific errors. For example, if we have implemented a handler to retry database calls it's necessary to provide information as to where the secondary database instances are. To do so, it's useful to create an object tree based on a message interface that's understandable by all handlers.

The Problem Interface
An important part of using the chain of responsibility pattern is standardizing the interface of the message passed down the links. If a wrapper around the underlying exception, error or application-generated warning is applied, information can be shared among nodes. An example interface could be:

public interface Problem {
public void setAttribute(String name, Object obj);
public Object getAttribute[(String name);
public boolean hasAttribute(String name);
}

Implementations of Problem could be created for specific recurring situations, such as:

public class DatabaseAccessProblem implements Problem { ... }
public class ProblemInRequestProcessor implements Problem { ... }
public class SecurityProblem implements Problem { ... }

Each specific implementation can store information that allows for some logic to occur. Coupled with a decla-rative handler chain (see below), a framework evolves that allows for robust, extensible generic exception processing subsystems to be designed and quickly adapted to any project.

Declarative Programming and Prob-lo-Matic
Declarative programming refers to the practice of refactoring application logic out of the code and into a reference file. An excellent example of a declarative framework is the Spring Framework (www.springframework.org/), which allows developers to move dependencies out of code and into XML files (the pattern on which the framework is based is called "dependency injection" or "inversion of control"). A declarative approach facilitates quick system maintenance and the ability to "hot swap" features of a deployed application.

Using the inversion of control pattern makes code very flexible and is the method used by Prob-lo-Matic to configure the problem handler chains. Any number of formats could be used to store this information; for example, here is a Prob-lo-Matic configuration file, which is XML (see Listing 1).

About Dan Stieglitz

Dan is an independent software consultant in New York. He specializes in designing and developing distributed applications in Java and J2EE.

Comments (0)

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.