What is the advantage of chained exceptions
Why Chain exception?
We need to chain the exceptions to make logs readable.
Take following examples of 1. without chaining and 2. chaining, exceptions to feel the difference
Create following Exceptions
class NoLeaveGrantedException extends Exception {
public NoLeaveGrantedException(String message, Throwable cause) {
super(message, cause);
}
public NoLeaveGrantedException(String message) {
super(message);
}
}
class TeamLeadUpsetException extends Exception {
public TeamLeadUpsetException(String message, Throwable cause) {
super(message, cause);
}
public TeamLeadUpsetException(String message) {
super(message);
}
}
class ManagerUpsetException extends Exception {
public ManagerUpsetException(String message, Throwable cause) {
super(message, cause);
}
public ManagerUpsetException(String message) {
super(message);
}
}
class GirlFriendOfManagerUpsetException extends Exception {
public GirlFriendOfManagerUpsetException(String message, Throwable cause) {
super(message, cause);
}
public GirlFriendOfManagerUpsetException(String message) {
super(message);
}
}
Now use them
1. Without chaining
public class MainClass {
public static void main(String[] args) throws Exception {
getLeave();
}
static void getLeave() throws NoLeaveGrantedException {
try {
howIsTeamLead();
} catch (TeamLeadUpsetException e) {
e.printStackTrace();
throw new NoLeaveGrantedException("Leave not sanctioned.");
}
}
static void howIsTeamLead() throws TeamLeadUpsetException {
try {
howIsManager();
} catch (ManagerUpsetException e) {
e.printStackTrace();
throw new TeamLeadUpsetException(
"Team lead is not in good mood");
}
}
static void howIsManager() throws ManagerUpsetException {
try {
howIsGirlFriendOfManager();
} catch (GirlFriendOfManagerUpsetException e) {
e.printStackTrace();
throw new ManagerUpsetException("Manager is in bad mood");
}
}
static void howIsGirlFriendOfManager()
throws GirlFriendOfManagerUpsetException {
throw new GirlFriendOfManagerUpsetException(
"Girl friend of manager is in bad mood");
}
}
2. Chaining
public class MainClass {
public static void main(String[] args) throws Exception {
getLeave();
}
static void getLeave() throws NoLeaveGrantedException {
try {
howIsTeamLead();
} catch (TeamLeadUpsetException e) {
throw new NoLeaveGrantedException("Leave not sanctioned.", e);
}
}
static void howIsTeamLead() throws TeamLeadUpsetException {
try {
howIsManager();
} catch (ManagerUpsetException e) {
throw new TeamLeadUpsetException(
"Team lead is not in good mood", e);
}
}
static void howIsManager() throws ManagerUpsetException {
try {
howIsGirlFriendOfManager();
} catch (GirlFriendOfManagerUpsetException e) {
throw new ManagerUpsetException("Manager is in bad mood", e);
}
}
static void howIsGirlFriendOfManager()
throws GirlFriendOfManagerUpsetException {
throw new GirlFriendOfManagerUpsetException(
"Girl friend of manager is in bad mood");
}
}
Now compare logs
1. Without chaining
com.bskyb.svod.autoingest.GirlFriendOfManagerUpsetException: Girl friend of manager is in bad mood
at com.bskyb.svod.autoingest.MainClass.howIsGirlFriendOfManager(MainClass.java:61)
at com.bskyb.svod.autoingest.MainClass.howIsManager(MainClass.java:52)
at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:43)
at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:34)
at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)
com.bskyb.svod.autoingest.ManagerUpsetException: Manager is in bad mood
at com.bskyb.svod.autoingest.MainClass.howIsManager(MainClass.java:55)
at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:43)
at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:34)
at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)
com.bskyb.svod.autoingest.TeamLeadUpsetException: Team lead is not in good mood
at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:46)
at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:34)
at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)
Exception in thread "main" com.bskyb.svod.autoingest.NoLeaveGrantedException: Leave not sanctioned.
at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:37)
at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)
2. Chaining
Exception in thread "main" com.bskyb.svod.autoingest.NoLeaveGrantedException: Leave not sanctioned.
at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:36)
at com.bskyb.svod.autoingest.MainClass.main(MainClass.java:29)
Caused by: com.bskyb.svod.autoingest.TeamLeadUpsetException: Team lead is not in good mood
at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:44)
at com.bskyb.svod.autoingest.MainClass.getLeave(MainClass.java:34)
... 1 more
Caused by: com.bskyb.svod.autoingest.ManagerUpsetException: Manager is in bad mood
at com.bskyb.svod.autoingest.MainClass.howIsManager(MainClass.java:52)
at com.bskyb.svod.autoingest.MainClass.howIsTeamLead(MainClass.java:42)
... 2 more
Caused by: com.bskyb.svod.autoingest.GirlFriendOfManagerUpsetException: Girl friend of manager is in bad mood
at com.bskyb.svod.autoingest.MainClass.howIsGirlFriendOfManager(MainClass.java:58)
at com.bskyb.svod.autoingest.MainClass.howIsManager(MainClass.java:50)
... 3 more
Can anyone please provide information on the need for chained exceptions?
The article says it quite well:
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.
Else, the programmer can catch both the exceptions in the same code than having to throw a new Throwable instance?
I don't quite follow your reasoning here. The point is that he should not need to worry about the SQLException
at this level of abstraction.
A caller of the loadResource
should not need to know the exact details of how those resources are loaded, or at the very least not care about the details of why it failed. (keep in mind it might not be you that wrote loadResources
, or it might be someone else that needs to use the loadResources method).
All you should care about when calling loadResource is it might throw a ResourceLoadException. Not that implementation details fails because of a SQLException - this might change with time too, later on someone might decide to load resources from somewhere else that could fail too.
You just need to load some resources, you need to handle if it fails, and not handle potential MainframeHasCrashedException, FileNotFoundException and the dozen other reasons loading those resources might fail.
Now, when something does fail, it's handy to have the original exception that caused the failure, such as an SQLException - so someone scouring through log files or similar can figure out the cause of the error by inspecting the stacktrace
You shouldn't be tempted to just catch Exception here either, if loadResources could also throw other exceptions , e.g. an UnautorizedException, you might not want to deal with that when you're calling loadResources - you might want to propagate that exception up to other callers that can deal with an UnautorizedException(and perhaps prompt the user for some credentials). a catch(Exception e) would swallow that UnautorizedException that you really can't deal with there.