Introduction
In java it is very easy to handle exception , by adding try-catch-finally block. For simple application or testing purpose it is ok. But while you are thinking of working on an enterprise application then you could understand that you need some well structure code to habndle this in a better and efficient way. In this article I am trying to explain how to handle exception in a better and managed way.
Background
In traditional approach there are so many issues you could face while working on enterprise application.
Initilay it is very hard to identify the problem. But while the volume of the application is increase, you could understand that.
Below are the key factor :
1. Manitainability : Often you can see that , same type handler code has been written for different type or same type of exception. It is very hard to keep track if any changes required. So maintaining the code itself is a big challenge for a modular base application.
2. Reusability: If purpose is the same then why shouldn't we use the same piece of code for different exception.As discussed in point 1, it is also decrease the chance of reusability for same purpose.
3. Plugable & Flexible: Today hadnler codes only job to log the exception. In future, may be need to sent mail or need to post that in some jms queue or need to do some rollback task or may be required to do all the task or may be the combination of the task. In normal approach , it is almost imposible to alter the code on demand because handling codes are tightly couple with the source of exception.
4. Readability: Often see, catch blocks itself conatins some complicated code which not only increase the lenght of the method and also decrease the code readability.
5. Easy to Use: In legacy application, sometimes ,it is very tough to track down the exception handling. In trdationl approach you have to spend enough time to understand the flow.
Above issues are very common and everybody have faced those issue once.
The key strategy behind this framework is to separate the handler logic from the source of exception to make the relation as lossely couple and hook this hadnler when needed. Separating the handler logic is not so big deal . we can create a handler class and keep the logic inside it. But the main concern is that how to hook it so that it wil increase the reusability, maintainbility, flexibilty and simple too. Here we go.
Lets take the following simple example :
public void checkLogin() {
try {
} catch (UserNotExistException uex) {
} catch (InvalidCredentialException fex) {
} catch (DBException dex) {
} catch (Exception ex) {
}
}
Above example is very simple one to validate the user. We can see that this method may throw 4 types of exceptions (in real world it may be different). There are also some catch blocks to catch those exceptions. We are very familiar with such type of scenario.
As per the requirements, if there are any exception occoured,application may take the following handler actions (single action or combination of actions)to handle each type exception : (For example purpose I have taken 4 handler actions)
- want to log exception
- want to send error details to some jms queue
- want to send mail
- want to do some default work
Now rewrite the catch block by adding the above handlers
public boolean checkLogin() {
try {
} catch (UserNotExistException uex) {
} catch (InvalidCredentialException fex) {
} catch (DBException dex) {
} catch (Exception ex) {
}
return <some_flag>;
}
When you working in a module base application, you could understand the pain to maintain the codebase. Now take the following example which is doing the same thing as the above example but in an effective way.
@HandleExceptions({
@HandleException(exceptionType = UserNotExistException.class, handlers = {
"logExceptionHandler","defaultExceptionHandler"
}) ,
@HandleException(exceptionType = InvalidCredentialException.class, handlers = {
"logExceptionHandler", "sendEmailExceptionHandler", "defaultExceptionHandler"
}) ,
@HandleException(exceptionType = DBException.class, handlers = {
"logExceptionHandler", "JMSExceptionHandler", "sendEmailExceptionHandler", "defaultExceptionHandler"
}) ,
@HandleException(exceptionType = Exception.class, handlers = {
"logExceptionHandler"
})
})
public void checkLogin() {
}
If we ignore the anotation part(coming later), one can easily understand that it is very simple and more clean one than the above example. You can see that there are no catch blocks inside the method body. Now the method is more clear and readable and it is only focused on what it is intended to do.
Now start digging the codebase to understand the framework. I have created this framework on top of Spring framework.
The key idea behind this framework is that intercept an exception after it’s thrown, but before it’s caught and do the needfull action as required. To acheive this , I have used Spring AOP @AfterThrowing
advice in a Spring application. (If you are not familiar with AOP(Asepect Oriented Programing), then go to http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html ).
Into the depths
Now it is time to look into the code base. There are three user defined annotation inside the com.sm.exceptionhandler.annotation pacakge.
Handlers.java
HandleException.java
HandleExceptions.java
There are two way to bound handler with exception in this framework. One is :
Handlers.java
: Used to annoted a custom exception class to register with default handler(s).
@Handlers({"fileExceptionHandler", "defaultExceptionHandler", "logExceptionHandler", "JMSExceptionHandler"})
public class UserNotExistException extends BaseException {
and the second onr is :
HandleException.java
: Class which may throw exception(s) must be annoted with this annotation.
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface HandleException {
static final String DEFAULT_HANDLER = "defaultExceptionHandler";
public Class<?> exceptionType() default Exception.class;
public String[] handlers() default {DEFAULT_HANDLER};
}
It has two properties. To respond on a specific exception ,user need to mention the type of exception as exceptionType
and the list of handlers to react on this exception. Here handlers are Spring bean id. One can mention multiple handlers for a specific exception.
If there is a possibility to get multiple type of exception and you want to handle them individually then exceptionType
param will help you to solve this problem.
You could use the following code to handle multiple type exception
@HandleExceptions({
@HandleException(exceptionType = FileNotExistException.class, handlers = {
"defaultExceptionHandler", "logExceptionHandler",
"JMSExceptionHandler"}),
@HandleException(exceptionType = UserNotExistException.class, handlers = {
"logExceptionHandler", "JMSExceptionHandler"})
})
public Integer testExceptionOne(int type) {
}
Or you can write the most simple one which will act on each exception.
@HandleException(handlers = {"fileExceptionHandler",
"defaultExceptionHandler", "logExceptionHandler"})
public void testExceptionTwo() {
throw new RuntimeException("Io Exception");
}
HandleException.java
: Contains list of HandleException
.
HandleExceptionAspect.java
is the aspect which basically weaving at runtime with the application. Using @AfterThrowing
, aspect can able to execute the advice only when exceptions of a given type are thrown, and also able to access to the thrown exception in the advice body.
ExceptionHandlerFramework.java
Contains the main logic to call and process the respective handler.
Rest of the java classes are very straight forward and are self-explanatory.
Points of Interest
It is very simple and easy to use
Keep in Mind
Attached code base is a netbeans project and tested in java 1.7 .