March 2, 2021
Hot Topics:

Processing Stack Trace Data in Java

  • By Richard G. Baldwin
  • Send Email »
  • More Articles »

Java Programming Notes # 1784


The recently released JavaTM 2 SDK, Standard Edition Version 1.4 contains a number of new features.  This article explains how to use some of those new features to get, interpret, and process stack trace data.

Prior to release 1.4, stack trace information was available only in the printed format produced by the printStackTrace method.  With the advent of the StackTraceElement class, and the getStackTrace method of the Throwable class, stack trace information is now available through programmatic access.

As a result, you can write logic into your programs to analyze and make decisions on the basis of stack trace information.  In addition, you can encapsulate that information into objects.  This makes it possible for you to serialize and write stack trace data to a disk file, send it out over a network, or perform any other action that is appropriate for information encapsulated in an object.

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 www.DickBaldwin.com.

Discussion and Sample Code

I'm going to jump right into a sample program that illustrates the programmatic handling of stack trace data without a lot of preliminary discussion.  Along the way, I will also illustrate Java's new cause or exception chaining facility.  (An earlier article entitled Chained Exceptions in Java discusses this facility in detail.)

The program named StackTr01

A complete listing of the program named StackTr01 is provided in Listing 21 near the end of the lesson.  I will discuss the program in fragments.  However, before getting into the fragments, I will provide an overview of the operation of the program.

Five classes

The program consists of the following five classes:

  • StackTr01
  • Class01
  • NewEx01
  • NewEx02
  • NewEx03
The controlling class is named StackTr01.  Most of the work gets done in the class named Class01.

There are three new exception classes named NewEx01, NewEx02, and NewEx03.  These classes are used to instantiate exception objects, which are thrown by the methods in Class01.  Each of these three classes extends Exception.

The main method of the controlling class

The controlling class consists solely of the main method.  Code in the main method instantiates an object of type Class01 and invokes a method named meth01 on that object's reference.

Methods in Class01

The class named Class01 defines the following methods:

  • meth01
  • meth02
  • meth03
  • meth04
  • encapsulate - encapsulate stack trace data in a Vector object
  • writeSer - write a serialized Vector object to a disk file
  • readSer - read a serialized Vector object from a disk file
  • display - display the stack trace data in a Vector object
The first four methods in the list are used to create a chain of exceptions.  The remaining four methods in the list provide the behavior indicated in the text.

Method invocation sequence

As mentioned above, the method named meth01 is invoked by the main method in the controlling class.

The method named meth01 invokes meth02.  The method named meth02 invokes meth03, and the method named meth03 invokes meth04.  Each of these method invocations is contained in a try block, followed by a catch block of type Exception.

Throw an exception

The method named meth04 at the end of the invocation chain throws an exception of type NewEx03.  This exception is caught by the catch block in meth03.

The code in the catch block in meth03 encapsulates the incoming object's reference as the cause in a new exception object of type NewEx02, and throws that new exception object.

Similarly, the exception thrown by meth03 is caught by meth02.  The code in the catch block of meth02 encapsulates its incoming exception as the cause in a new exception object of type NewEx01 and throws it.  As you can see, this code creates a chain of exceptions as control moves back up the call stack.

Here is where things get interesting

Finally, the exception thrown by meth02 is caught by meth01.  Most of the interesting code is contained in the catch block for meth01, which is designed to illustrate the programmatic handling of stack trace data.

Print the stack trace

The catch block in meth01 receives an incoming exception parameter of type NewEx01.  The printStackTrace method is invoked on that object's reference to give us a baseline for comparison.

Encapsulate stack trace data in a Vector object

Then the stack trace data is extracted from the incoming exception object and encapsulated in an object of type Vector.  The Vector object is serialized and written to a disk file to illustrate one form of programmatic handling of stack trace data.  This Vector object contains the entire chain of exceptions, and the stack trace data associated with each exception in the chain.

Read and display the serialized stack tract data

Then the serialized data is read from the disk file, and a Vector object containing the original stack trace data is reconstructed.  The reconstructed object is passed to a custom print method, which extracts the information from the Vector object and displays it in a format similar to the standard format produced by the printStackTrace method.

Encapsulating stack trace data in an object

These operations illustrate that you now have the ability to extract stack trace data and encapsulate it in an object.  Then, you can do just about anything with the stack trace data that you can do with any other kind of data encapsulated in an object.  As a result, stack trace data is now available for programmatic access and handling.

The custom exception classes

Listing 1 shows the three custom exception classes.

class NewEx01 extends Exception{
  public NewEx01(String message,
                  Throwable throwable){
    super(message, throwable);}
}//end NewEx01
class NewEx02 extends Exception{
  public NewEx02(String message,
                  Throwable throwable){
    super(message, throwable);}
}//end NewEx02
class NewEx03 extends Exception{
  public NewEx03(String message){
}//end NewEx03

Listing 1

Overloaded constructors

Some comments are in order regarding these exception class definitions.  Subsequent to release 1.4, whenever you define a new exception class, you should provide four overloaded constructors having argument lists that match the following four constructors of the Throwable class:

  • Throwable()
  • Throwable(String message)
  • Throwable(String message, Throwable cause)
  • Throwable(Throwable cause)
By making the argument lists of your constructors match those of the Throwable class, objects instantiated from your new classes will be capable of supporting the new cause or chained exception facility in release 1.4.

Did not provide four overloaded constructors

However, for purposes of this sample program, I knew that I would not need all four constructors in each class.  For brevity, I included only the constructor that I would need in each of the new exception classes.

(Note that the argument list for NewEx03 is different from the argument lists for the other two.  That is because the program does not encapsulate a cause when it instantiates and throws an object of type NewEx03.)
Pass parameters to superclass constructor

The code in each of the constructors is very simple.  In each case, the parameters received by the constructor are passed to the constructor for the superclass.  This causes the parameter information to be saved in instance variables of the Throwable class, and makes it possible later to successfully invoke the methods of the Throwable class on objects instantiated from these classes.

Traversing the call stack

The overview that I provided earlier explains that there is a sequence of method calls, which place several methods on the stack.  In other words, a chain of method calls is created on the runtime stack.

Then a method at the end of the chain named meth04 throws an exception.  That exception is caught by the method named meth03, which was the method that called meth04.  The method named meth03 encapsulates the incoming exception as a cause in another exception of a different type and throws the new exception.

Each time a new exception is thrown, one method is popped off the stack.  This catch and throw process continues until a method near the top of the stack, named meth01, provides code to handle the exception without throwing a new exception.

Begin discussion where the exception is thrown

I'm going to begin the discussion with the method named meth04 that throws the first exception.  Then I will continue back through the call stack discussing each method in sequence.

Listing 2 shows meth04, which throws the first exception.  This triggers the chain of exceptions being thrown as control progresses back through the call stack.

  void meth04() throws NewEx03{
    throw new NewEx03(
                 "Thrown from meth04");
  }//end meth04

Listing 2

The method named meth04 in Listing 2 constructs and throws an exception object of type NewEx03.  Note that this exception object does not encapsulate a cause.

Catch and throw

The exception object of type NewEx03 is caught by the catch block of type Exception in meth03, shown in Listing 3.

  void meth03() throws NewEx02{
    }catch(Exception e){      
      throw new NewEx02(
               "Thrown from meth03",e);
    }//end catch
  }//end meth03

Listing 3

The most important thing to note about Listing 3 is the instantiation and throwing of a new exception object of type NewEx02.  This code uses a constructor for NewEx02 that encapsulates the incoming object reference of type NewEx03 as the cause in the new object.

(Note that the declared type of the catch block in meth03 is Exception.  This catch block can catch objects instantiated from the class Exception or any of its subclasses. NewEx03 is a subclass of Exception.)
Catch and throw again

The exception object thrown by meth03 in Listing 3 is caught by the method named meth02 in Listing 4.

  void meth02() throws NewEx01{
    }catch(Exception e){
      throw new NewEx01(
               "Thrown from meth02",e);
    }//end catch
  }//end meth02

Listing 4

The code in listing 4 is very similar to that in Listing 3.  The code in meth02 instantiates and throws a new object of type NewEx01, with the incoming object of type NewEx02 encapsulated as the cause in that object.

Handling the exception

The exception thrown by meth02 in Listing 4 is caught and processed by the method named meth01, which begins in Listing 5.  The code in meth01 does not instantiate and throw a new exception object.  Rather, it completely handles the incoming exception in its catch block, thus breaking the chain of exceptions.

  void meth01(){
      meth02();//call meth02
    }catch(Exception e){
                   "Print StackTrace");

Listing 5

Print the stack trace

The code in Listing 5 begins by invoking the printStackTrace method on the incoming parameter to cause the stack trace to be printed in the standard format on the standard error stream.  The output is shown in Figure 1 (we will need this later for comparison purposes).

Print StackTrace
NewEx01: Thrown from meth02
 at Class01.meth02(StackTr01.java:92)
 at Class01.meth01(StackTr01.java:60)
 at StackTr01.main(StackTr01.java:52)
Caused by: NewEx02: Thrown from meth03
 at Class01.meth03(StackTr01.java:102)
 at Class01.meth02(StackTr01.java:85)
 ... 2 more
Caused by: NewEx03: Thrown from meth04
 at Class01.meth04(StackTr01.java:112)
 at Class01.meth03(StackTr01.java:100)
 ... 3 more

Figure 1

The print format

A word about the format of the data in Figure 1 may be in order.  Those lines that read ... 2 more and ... 3 more indicate that the specified number of lines were omitted for brevity.  As far as I know, this is the standard output format of the printStackTrace method over which we have no control.  (We will see later that these lines were not omitted from my custom print format for the same stack tract data.)

Encapsulate stack trace data in a Vector object

Continuing with the catch block in meth01, the statement in Listing 6 invokes the encapsulate method to cause the stack trace data to be extracted from the incoming exception object and encapsulated in a Vector object.

      Vector vecOuter = encapsulate(e);

Listing 6

The encapsulate method

At this point, I'm going to set the method named meth01 aside for the moment and discuss the method named encapsulate, (which is a method of my own design).  The beginning of the encapsulate method is shown in Listing 7.  This is a rather long method, so I will discuss it in fragments.  Before getting into the details of the method, however, I will provide some background information.

  Vector encapsulate(Throwable e){
    Vector vecOuter = new Vector();
    Throwable cause = e;

Listing 7

Contents of the incoming Throwable object

The encapsulate method receives a reference to a Throwable object as an incoming parameter.  In this case, it is the Throwable object of type NewEx01 caught by the method named meth01.

Page 1 of 3

This article was originally published on September 17, 2002

Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Thanks for your registration, follow us on our social networks to keep up-to-date