Exception Chaining In Java | Exception Handling

What do you understand by chained exceptions in Java?

Mouad Oumous
The Fresh Writes
9 min readFeb 22, 2023

--

Exception chaining, or exception wrapping, is an object-oriented programming technique of handling exceptions. Exception chaining occurs when one exception causes another exception. a chained exception is an exception that is caused by another exception. Chained exceptions are associated such that the previous exception causes each exception in the chain.

· Throwable Class
· Using Chained Exceptions
· Why chained exceptions?
· Difficulties with chained exceptions
· Program

Throwable Class

Constructors Of Throwable class Which support chained exceptions in java :

  1. Throwable(Throwable cause) :- Where cause is the exception that causes the current exception.
  2. Throwable(String msg, Throwable cause) :- Where msg is the exception message and cause is the exception that causes the current exception.

Methods Of Throwable class Which support chained exceptions in java :

  1. getCause() method :- This method returns actual cause of an exception.
  2. initCause(Throwable cause) method :- This method sets the cause for the calling exception.

Using Chained Exceptions

Often you want to catch one exception and throw another, but still keep the information about the originating exception — this is called exception chaining. The original exception is the cause of the second exception. So Chained Exceptions allows to relate one exception with another exception, i.e one exception describes cause of another exception.

Prior to JDK 1.4, programmers had to write their own code to preserve the original exception information, but now all Throwable subclasses have the option to take a cause object in their constructor. The cause is intended to be the originating exception, and by passing it in you maintain the stack trace back to its origin, even though you’re creating and throwing a new exception.

Exception chaining is also called exception wrapping because we do this by re-throwing a caught exception after wrapping it inside a new exception. The original exception is saved as a property (such as cause) of the new exception.

Practice


try {
// creating an exception
NullPointerException e = new NullPointerException("Actual cause");
// wrapping the original exception in a new exception
ArithmeticException exc = new ArithmeticException("Apearent cause",e);
// throwing the exception
throw exc;
} catch(ArithmeticException e) {
// display top level exception (aprearent cause)
System.out.println("Caught: " + e);
// Getting the actual cause of the exception
System.out.println("Original cause: " + e.getCause());
}
}

Output

Caught: java.lang.ArithmeticException: Apearent cause
Original cause: java.lang.NullPointerException: Actual cause

So chained Exception helps to identify a situation in which one exception causes another Exception in an application, it can also help debug and track down the root cause of an error.

For instance, consider a method which throws an ArithmeticException because of an attempt to divide by zero but the actual cause of exception was an I/O error which caused the divisor to be zero.The method will throw the ArithmeticException to the caller. The caller would not know about the actual cause of an Exception. Chained Exception is used in such situations.

It’s interesting to note that the only Throwable subclasses that provide the cause argument in the constructor are the three fundamental exception classes Error (used by the JVM to report system errors), Exception, and RuntimeException. If you want to chain any other exception types, you do it through the initCause( ) method rather than the constructor.

Example

Here is an example that illustrates the mechanics of handling chained exceptions:

// Demonstrate exception chaining.
class ChainExcDemo {
static void demoproc() {
// create an exception
NullPointerException e = new NullPointerException("top layer");
// add a cause
e.initCause(new ArithmeticException("cause"));
throw e;
}
public static void main(String args[]) {
try {
demoproc();
} catch (NullPointerException e) {
// display top level exception
System.out.println("Caught: " + e);
// display cause exception
System.out.println("Original cause: " + e.getCause());
}
}
}

Output:

Caught: java.lang.NullPointerException: top layer
Original cause: java.lang.ArithmeticException: cause

In this example, the top-level exception is NullPointerException. To it is added a cause exception, ArithmeticException. When the exception is thrown out of demoproc( ) method, it is caught by main( ). There, the top-level exception is displayed, followed by the underlying exception, which is obtained by calling getCause( ).

Chained exceptions can be carried on to whatever depth is necessary. Thus, the cause exception can, itself, have a cause. Be aware that overly long chains of exceptions may indicate poor design.

Chained exceptions are not something that every program will need. However, in cases in which knowledge of an underlying cause is useful, they offer an elegant solution.

Why chained exceptions?

Exception chaining allows you to map one exception type to another, so that a method can throw exceptions defined at the same abstraction level as the method itself, without discarding important debugging information.

That is, if you have a method that loads some object from a database, you may rather want some ResourceLoadException (closer related to the methods abstraction level) instead of a low-level SQLException even if that was the original source of the problem. However, if you simply catch the SQLException and throw a ResourceLoadException instead, you may loose important debugging information.

Thus, chaining the exceptions is a good alternative. You throw a “high-level” exception, well suited for the particular method, but chain it with the exception that caused it.

We need to chain the exceptions to make logs readable. Let’s write two examples. First without chaining the exceptions and second, with chained exceptions. Later, we will compare how logs behave in both of the cases.

To start, we will create a series of Exceptions:

class NoLeaveGrantedException extends Exception {

public NoLeaveGrantedException(String message, Throwable cause) {
super(message, cause);
}

public NoLeaveGrantedException(String message) {
super(message);
}
}

class TeamLeadUpsetException extends Exception {
// Both Constructors
}

Without Chaining

Let’s write an example program without chaining our custom exceptions.

public class MainClass {

public void main(String[] args) throws Exception {
getLeave();
}

void getLeave() throws NoLeaveGrantedException {
try {
howIsTeamLead();
} catch (TeamLeadUpsetException e) {
e.printStackTrace();
throw new NoLeaveGrantedException("Leave not sanctioned.");
}
}

void howIsTeamLead() throws TeamLeadUpsetException {
throw new TeamLeadUpsetException("Team Lead Upset");
}
}

In the example above, logs will look like this:

com.baeldung.chainedexception.exceptions.TeamLeadUpsetException: 
Team lead Upset
at com.baeldung.chainedexception.exceptions.MainClass
.howIsTeamLead(MainClass.java:46)
at com.baeldung.chainedexception.exceptions.MainClass
.getLeave(MainClass.java:34)
at com.baeldung.chainedexception.exceptions.MainClass
.main(MainClass.java:29)
Exception in thread "main" com.baeldung.chainedexception.exceptions.
NoLeaveGrantedException: Leave not sanctioned.
at com.baeldung.chainedexception.exceptions.MainClass
.getLeave(MainClass.java:37)
at com.baeldung.chainedexception.exceptions.MainClass
.main(MainClass.java:29)

With Chaining

Next, let’s write an example with chaining our custom exceptions:

public class MainClass {
public void main(String[] args) throws Exception {
getLeave();
}

public getLeave() throws NoLeaveGrantedException {
try {
howIsTeamLead();
} catch (TeamLeadUpsetException e) {
throw new NoLeaveGrantedException("Leave not sanctioned.", e);
}
}

public void howIsTeamLead() throws TeamLeadUpsetException {
throw new TeamLeadUpsetException("Team lead Upset.");
}
}

Finally, let’s look at the logs obtained with chained exceptions:

Exception in thread "main" com.baeldung.chainedexception.exceptions
.NoLeaveGrantedException: Leave not sanctioned.
at com.baeldung.chainedexception.exceptions.MainClass
.getLeave(MainClass.java:36)
at com.baeldung.chainedexception.exceptions.MainClass
.main(MainClass.java:29)
Caused by: com.baeldung.chainedexception.exceptions
.TeamLeadUpsetException: Team lead Upset.
at com.baeldung.chainedexception.exceptions.MainClass
.howIsTeamLead(MainClass.java:44)
at com.baeldung.chainedexception.exceptions.MainClass
.getLeave(MainClass.java:34)
... 1 more

We can easily compare shown logs and conclude that the chained exceptions lead to cleaner logs.

Difficulties with chained exceptions

We can use exception chaining in situations where another exception causes one exception. However, it’s important to note that chaining can make our code more difficult to read and understand. Therefore, we should use exception chaining sparingly and only when necessary.

If we use chained exceptions, it is a good idea to document the chain in our code. It’ll help others understand our code and make it easier to debug if an error occurs.

Program

//: exceptions/DynamicFields.java
// A Class that dynamically adds fields to itself.
// Demonstrates exception chaining.
import static net.mindview.util.Print.*;
class DynamicFieldsException extends Exception {}
public class DynamicFields {
private Object[][] fields;
public DynamicFields(int initialSize) {
fields = new Object[initialSize][2];
for(int i = 0; i < initialSize; i++)
fields[i] = new Object[] { null, null };
}

public String toString() {
StringBuilder result = new StringBuilder();
for(Object[] obj : fields) {
result.append(obj[0]);
result.append(": ");
result.append(obj[1]);
result.append("\n");
}
return result.toString();
}

//...................hasField(String id)........................//
private int hasField(String id) {
for(int i = 0; i < fields.length; i++)
if(id.equals(fields[i][0]))
return i;
return -1;
}

//......................getFieldNumber(String id)....................//
private int getFieldNumber(String id) throws NoSuchFieldException {
int fieldNum = hasField(id);
if(fieldNum == -1)
throw new NoSuchFieldException();
return fieldNum;
}

//........................makeField(String id).....................//
private int makeField(String id) {
for(int i = 0; i < fields.length; i++)
if(fields[i][0] == null) {
fields[i][0] = id;
return i;
}
// No empty fields. Add one:
Object[][] tmp = new Object[fields.length + 1][2];
for(int i = 0; i < fields.length; i++)
tmp[i] = fields[i];
for(int i = fields.length; i < tmp.length; i++)
tmp[i] = new Object[] { null, null };
fields = tmp;
// Recursive call with expanded fields:
return makeField(id);
}

//.............................getField(String id).......................//
public Object getField(String id) throws NoSuchFieldException {
return fields[getFieldNumber(id)][1];
}

//.........................setField(String id, Object value)..........................
public Object setField(String id, Object value) throws DynamicFieldsException {
if(value == null) {
// Most exceptions don’t have a "cause" constructor.
// In these cases you must use initCause(),
// available in all Throwable subclasses.
DynamicFieldsException dfe = new DynamicFieldsException();
dfe.initCause(new NullPointerException());
throw dfe;
}
int fieldNumber = hasField(id);
if(fieldNumber == -1)
fieldNumber = makeField(id);
Object result = null;
try {
result = getField(id); // Get old value
} catch(NoSuchFieldException e) {
// Use constructor that takes "cause":
throw new RuntimeException(e);
}
fields[fieldNumber][1] = value;
return result;
}

// ......................main...................//
public static void main(String[] args) {
DynamicFields df = new DynamicFields(3);
print(df);
try {
df.setField("d", "A value for d");
df.setField("number", 47);
df.setField("number2", 48);
print(df);
df.setField("d", "A new value for d");
df.setField("number3", 11);
print("df: " + df);
print("df.getField(\"d\") : " + df.getField("d"));
Object field = df.setField("d", null); // Exception
} catch(NoSuchFieldException e) {
e.printStackTrace(System.out);
} catch(DynamicFieldsException e) {
e.printStackTrace(System.out);
}
}
}

output

null: null 
null: null
null: null

d: A value for d
number: 47
number2: 48

df: d: A new value for d
number: 47
number2: 48
number3: 11

df.getField("d") : A new value for d
DynamicFieldsException
at DynamicFields.setField(DynamicFields.java:64)
at DynamicFields.main(DynamicFields.java:94)
Caused by: java.lang.NullPointerException
at DynamicFields.setField(DynamicFields.java:66)
... 1 more
*///:~

Each DynamicFields object contains an array of Object-Object pairs. The first object is the field identifier (a String), and the second is the field value, which can be any type except an unwrapped primitive. When you create the object, you make an educated guess about how many fields you need. When you call setField( ), it either finds the existing field by that name or creates a new one, and puts in your value. If it runs out of space, it adds new space by creating an array of length one longer and copying the old elements in. If you try to put in a null value, then it throws a DynamicFieldsException by creating one and using initCause( ) to insert a NullPointerException as the cause.

As a return value, setField( ) also fetches out the old value at that field location using getField( ), which could throw a NoSuchFieldException. If the client programmer calls getField( ), then they are responsible for handling NoSuchFieldException, but if this exception is thrown inside setField( ), it’s a programming error, so the NoSuchFieldException is converted to a RuntimeException using the constructor that takes a cause argument.

You’ll notice that toString( ) uses a StringBuilder to create its result. You’ll learn more about StringBuilder in the Strings chapter, but in general you’ll want to use it whenever you’re writing a toString( ) that involves looping, as is the case here.

Happy learning 😃

Join Mouad Oumous Java WhatsApp Group JOIN

Join Mouad Oumous Telegram Channel JOIN

Do support our publication by following it

--

--

Mouad Oumous
The Fresh Writes

I'm a blogger on medium, l'm a java and kotlin developer, also an Android Developer I mastered various topics such as Networking, UI , UX, Animation ...