|

Chained Exceptions in Java
By Richard G. Baldwin
Go to page: 1 2 3 4 Next
Java Programming Notes # 57
Preface
The recently released JavaTM 2 SDK, Standard Edition Version
1.4.0 contains a number of new features. This article explains how
to use one of those new features: the cause facility otherwise known
as chained exceptions.
Here is how Sun describes the
cause facility:
"A throwable ... can contain a cause: another throwable
that caused this throwable to get thrown. The cause facility is new in
release 1.4. It is also known as the chained exception facility, as the
cause can, itself, have a cause, and so on, leading to a "chain" of exceptions,
each caused by another..."
Sun goes on to say:
"This new facility provides a common API to record the fact
that one exception caused another, to access causative exceptions, and
to access the entire "causal chain" as part of the standard stack backtrace,
ensuring that preexisting programs will provide this information with no
additional effort on the part of their authors."
In this lesson, I well teach you how to use the chained exception
facility that is now available.
Viewing tip
You may find it useful to open another copy of this lesson in a separate
browser window. That will make it easier for you to scroll back and
forth among the different listings and figures while you are reading about
them.
Supplementary material
I recommend that you also study the other lessons in my extensive collection
of online Java tutorials. You will find those lessons published at
Gamelan.com.
However, as of the date of this writing, Gamelan doesn't maintain a consolidated
index of my Java tutorial lessons, and sometimes they are difficult to
locate there. You will find a consolidated index at
Baldwin's
Java Programming Tutorials.
Discussion
and Sample Code
Introduction
The exception-handling capability of Java makes it possible for you
to:
-
Monitor for exceptional conditions within your program
-
Transfer control to special exception-handling code, (which you design),
if an exceptional condition occurs
The basic concept
This is accomplished using the keywords:
try, catch, throw,
throws,
and finally. The basic concept is as follows:
-
You try to execute the statements contained within a block of code.
-
If you detect an exceptional condition within that block, you throw
an exception object of a specific type.
-
You catch and process the exception object using code that you have
designed.
-
You optionally execute a block of code, designated by finally,
which needs to be executed whether or not an exception occurs.
Exceptions in code written by others
There are also situations where you don't write the code to throw
the exception object. Rather, the code in a method that you call
throws an exception, or you attempt to execute some code that causes the
runtime system to throw an exception (such as integer divide by zero).
When an exceptional condition causes an exception to be thrown,
that exception is represented by an object instantiated from the class
named Throwable or one of its subclasses.
Throwable constructors and methods
All errors and exceptions are subclasses of the
Throwable class.
As of JDK 1.4.0, the Throwable class provides four constructors
and about a dozen methods. The four constructors are shown in Figure
1.
Throwable()
Throwable(String message)
Throwable(String message,
Throwable cause)
Throwable(Throwable cause)
Figure 1
|
The first two constructors have been in Java for a very long time.
Basically, these two constructors allow you to construct an exception object
with or without a String message encapsulated in the object.
New to JDK 1.4
The last two constructors (highlighted in boldface) are new to
JDK 1.4. These two constructors are provided to support the cause
facility, otherwise known as chained exceptions.
Catch blocks can throw exceptions
According to Sun, "It is common for Java code to catch one exception
and throw another." Typically, this involves the use of code
such as that shown in Figure 2.
try{
//...
}//end try block
catch(OneExceptionType e){
throw new AnotherExceptionType();
}//end catch block
Figure 2 |
A problem with this has always been that information about the first
exception has generally been lost when the second exception was thrown.
This did not work well insofar as debugging is concerned.
Homebrew and non-standard schemes have been
in use
As a result, many developers have developed homebrew schemes for implementing
chained exceptions, wherein they wrapped information about the first exception
inside the information encapsulated in the object thrown inside the catch
block a higher level. This has led to non-standard approaches, some
of which worked better than others.
Also, while referring to the standard class libraries, Sun has this
to say:
"Prior to release 1.4, there were many throwables that had
their own non-standard exception chaining mechanisms ... As of release
1.4, all of these throwables have been retrofitted to use the standard
exception chaining mechanism, while continuing to implement their "legacy"
chaining mechanisms for compatibility."
Advantages of a unified system for exception chaining
Apparently, Sun decided to unify these facilities, and they list the
following advantages of doing so:
-
Anyone who wants to record the fact that one exception caused another can
do so, regardless of what the exceptions are.
-
Because a common API makes it easier to record the fact that one exception
caused another, it is more likely that programmers will take the trouble
to do so.
-
Providing a common API for accessing causative exceptions greatly enhances
the likelihood that this information will be made available to those who
need it.
Upgraded printStackTrace method
Sun goes on to point out that with respect to the third advantage, the
printStackTrace
method has been upgraded such that it prints the entire causal chain
as part of the standard stack backtrace. This ensures that preexisting
programs will provide this information with no additional effort on the
part of their authors. You will see an example of this in the sample
program later in this lesson.
Two new methods support cause facility
In addition to the two new constructors mentioned earlier, Sun added
two new methods to the Throwable class to support the new cause
facility:
You will see examples of the use of both of these methods in the sample
program to be discussed later.
General-purpose exception classes upgraded
In addition to the Throwable class, other general-purpose exception
classes, such as Exception, RunTimeException, and Error,
were upgraded to support the two new overloaded constructors. As
you will see in the sample program later, even exceptions without such
upgraded constructors (such as DOMException, for example)
will be usable as wrapped exceptions via the initCause method.
Programmatic access to the stack trace for
In addition to upgrading the printStackTrace method as described
above, a new getStackTrace method was added, which provides programmatic
access to the same stack trace information that is displayed by the printStackTrace
method. This makes it possible for you to write code that can make
decisions on the basis of information contained in the stack trace.
Now back to the Throwable class
Whenever an error or an exception occurs, an object is thrown.
That object must be instantiated from the class Throwable, or a
subclass of Throwable. A Throwable object contains
a snapshot of the execution stack of its thread at the time it was created.
As mentioned above, there are a couple of methods that provide access to
that snapshot:
-
printStackTrace
-
getStackTrace
Also contains message and cause
The Throwable object can also contain a message string that provides
information about the error or the exception. With the advent of
version 1.4, the Throwable object can also contain a cause.
What is a cause?
A cause is a reference to another Throwable object.
The intent is that this object will be interpreted as the thing that caused
this throwable to get thrown in the first place.
(However, you could encapsulate any Throwable object
in a new Throwable object, whether or not it had anything to do
with the true cause. All that you are required to do is pass a Throwable
object's reference to the constructor for a new Throwable object,
or invoke the initCause method on an existing Throwable object,
passing a reference to another Throwable object as a parameter.
It then becomes a cause.)
Two ways to encapsulate a cause
As suggested above, you can associate a cause with a Throwable
in two different ways. One way is to invoke one of the constructors
that accepts a Throwable as a parameter. This assumes, of
course, that the class from which you are instantiating the new object
has such a constructor. You will see an example of this in the sample
program later in this lesson.
The other way to associate a cause with a Throwable is
to invoke the initCause method on an existing Throwable object's
reference, passing a reference to another Throwable object as a
parameter. This works even when you are instantiating a new object
from a class that doesn't have a constructor that accepts a parameter of
type Throwable.
Support for legacy exception classes
According to Sun:
"... the initCause method ... allows a cause to be associated
with any throwable, even a "legacy throwable" whose implementation predates
the addition of the exception chaining mechanism to Throwable."
I will apply the initCause method to an IndexOutOfBoundsException
object in the sample program later in this lesson. According to the
first edition of Java in a Nutshell, by David Flanagan, this exception
type has been part of Java for at least as far back as release 1.1.
Go to page: 1 2 3 4 Next
|