Exception handling in your services
I know my approach to exception handling within services are not original to me, but when I was recently looking for a concise, consolidated written explanation of it to share with a coworker, I couldn’t find one.
In a nutshell, it is:
- Throw only unchecked exceptions in your business logic; exceptions should not be used for control flow, so why force other modules in business logic to deal with hiccups?
- Use a transactional management strategy that rolls back transactions on RuntimeException
- Hide the implementation details of exceptions behind the service layer just as you would the implementation details of the code
- Define one or more checked exceptions specific to your service layer; when an error propagates up to the top service class, log it and its stacktrace and then throw a generic checked exception
An example:
public interface TestService {
public void runTest() throws TestServiceException;
}
public class TestServiceException extends Exception {
public TestServiceException(String message) {
super(message);
}
}
public class TestServiceImpl implements TestService {
private static final Logger logger
= Logger.getLogger(TestServiceImpl.class);
public void runTest() throws TestServiceException {
try {
//integration with business logic here
} catch(RuntimeException e) {
logger.error(e, e);
throw new TestServiceException(
e.getClass().getName()
+ " encountered in TestService");
}
}
}
This type of standardization is especially important if you have opted to generate web services or other remoting code directly from your services. In the same project I mentioned previously where the web service was required to accept a wide variety of abbreviations and spellings of state names, a remote web service would occasionally throw Oracle database statement errors back to the web service client. This is very dangerous; if combined with poor security practices and other poor coding techniques, it is likely to provide a method for remote SQL injection attacks.