Exception is disruption to a normal execution flow of a Java (in general any) program. This disruption can be because of a runtime problem e.g. data problem, initialization problem, etc. or because of a failing business condition. Java is capable of handling all such undesired scenarios elegantly. But when it comes to a developer to use these java generated exceptions to provide enough information to different stakeholders, then few mistakes occur. In this article, I am not going to discuss how Java’s Exception handling works, instead the common mistakes are listed with example. I am assuming that the author already knows about exception hierarchy, checked exceptions, unchecked exceptions and runtime exceptions. Here we discuss the common mistakes to be avoided along with rational behind.
Stakeholders of Exception:
Whenever any execution flow breaks this information needs to go back first to the executor of program. This execution might have started from a screen, from a batch job, or from any other mode. All these triggers expect any disruption to the flow to be communicated back in certain format. Screen based trigger may expect a soft message saying the actual business reason, or saying that there is some technical problem and advising to contact help desk. Batch processing or standalone java program may expect the result be flowing in some sort of logging. But business people should receive the exception in business language only. Banging NullPointerException on a business user’s head is really annoying and scary. Sometimes these exception messages are intended but sometimes these are bugs in application and require some fix.
Second important stakeholder is the person who is going to fix a problem arising due to exception. This person may not be watching actual program execution when an exception occurred. This person is going to rely on two things – first is the information provided by user who encountered problem, and second is some kind of log generated post exception. Another thing, person who analyses a problem may not be same as the person who actually wrote program, and hence a developer of program needs to provide enough information to the analyzer. Only mode of communication between a developer and analyzer is through logs. Also sometimes because of security reason, logs cannot contain actual business data.
Let us go through common exception handling mistakes one by one. Some of them sound stupid but there were people who may be do not have time to pay attention to such things until there is a problem to analyze.
Empty Exception Block:
This is one of the most undesired mistakes in exception handling. In this case, the exception is just ignored. The catch block contains nothing but maximum a commented line to leave the exception.
try{
}catch(SQLException sqe){
// do nothing
}
In few rare scenarios, it is desired to do nothing with an exception, e.g. in finally block, we try to close database connection, and some exception occurs. In this case, exception can be ignored.
try{
}catch(SQLException sqe){
...
...
}finally{
try{
conn.close();
}catch(Exception e){
//leave it.
}
}
Improper Generalization of Exception Message:
This point is subjective in nature. After catching an exception, you may want to report it to end user in the form of a friendly message. While doing this it is possible that the details of original message are lost while converting the message in a general message. Here the message should provide proper information to end user so that when support team is contacted, the information provided is enough to go to appropriate log.
try{
File file = new File("");
file.getCanonicalPath();
}catch(IOException ioe){
throw new Exception("File Processing Failed",ioe);
}
Unnecessary Exception Transformation:
In a layered application, many times each layer catches exception and throws new type of exception. Sometimes it is absolutely unnecessary to transform an exception in certain type.
- Data Access Layer -> throws DataAccessException
- Business Implementation Layer -> throws BusinessException
- Application Service Layer -> throws ApplicationServiceException
Everything looks logical, isn’t it? Here we might have to choose between ApplicationServiceException and BusinessException, as both may represent same information. One of these can be a redundant exception transformation.
Wiping Out Stacktrace:
While transforming exception into any application specific exception type, it is necessary to carry entire exception stacktrace until it is logged or presented to end user properly. If the stacktrace is removed, it becomes nightmare to trace the actual cause of exception. In code below you can see a new exception getting thrown without any information from original exception.
try{
File file = new File("");
file.getCanonicalPath();
}catch(IOException ioe){
throw new MyException("Problem in data reading.");
}
Overriding Original Cause:
Each exception message carries a cause of exception. This cause gets carried if the original exception is propagated through layers. But if all together a new exception is thrown, then the original cause gets removed. This is similar to above example where the stacktrace gets removed because of throwing of new exception altogether.
Not Catching Specific Exception:
Sometimes developers like to play safe in exception handling. Sometimes it is easy to implement. But catching java.lang.Exception instead of a specific exception is not acceptable.
try{
File file = new File("");
file.getCanonicalPath();
}catch(Exception exe){
throw new MyException("Problem in data reading.");
}
Unnecessary Catch and Throws:
Catch an exception if you want to transform the exception in some other type or you want to add some information to the message/cause that will help end user or in analysis. Otherwise just add throws clause of all possible exceptions to the signature of the method and don’t catch any exception.
Catching RuntimeException:
I would recommend avoiding catching RuntimeException. Instead of this it is better to catch specific exceptions and treat them properly.
Catching Unchecked Exceptions:
It is a controversial topic whether to catch unchecked exceptions or not. NullPointerException and ArrayIndexOutOfBound can be the best examples of unchecked exceptions. Instead of catching these exceptions, you can change the code to handle these scenarios. E.g. to avoid NullPointerException, ensure initialization of all variables, to avoid ArrayIndexOutOfBound exception, define array with correct length, etc.
Problems Related to Logging Exception:
Logging is going to help the second stakeholder i.e. the person analyzing problem. Sometimes we find log statement in each catch bock. It unnecessarily floods the log file. Log an exception at in particular layer or where it first time occurs. Also an exception should be logged using logger severity level Error instead of other levels like Debug, Info etc. Take a call for warnings whether you want to terminate the flow or continue, but ensure the exception is logged with warning severity.
Exception to Control Flow:
Exception is disruption of flow, this should be treated to terminate the flow and to inform the end user. If we add a method call, which is an alternate flow, in exception catch block, it is not a good practice, and it will lead to huge maintenance problems. Also using exception to check condition, and decide flow based on it, is another pattern which should be avoided.
try{
myObject.getValue();
}catch(NullPointerException npe){
alternateMethod();
}
Throwing java.lang.Exception:
It is not good to throw any exception using java.lang.Exception. Instead make it specific using application specific exception or closely applicable java exception.
Hi,
This article on java exceptions is really very nice.
Thank you,
regards,
Srini
Hi Deepak,
Very Nice and simple explanation about Exceptions
Thanks & Regards
Raja.
Hi,
Very nice explanations about exceptions;
Thanks a lot.
Clément.