I hate using unnecessary catch
blocks and I like to see my code look beautiful. In most of the cases in my project, which is a web application, I can't declare many exceptions as checked exceptions as there is little that my client (browser) can do to recover from the exception. However, only if there is a way that the client of my code (mostly fellow programmer) can gracefully handle the exceptions, I am declaring such exceptions as checked exceptions (so that there are few throws
clauses as possible).
I am trying to hide the implementation detail specific checked exceptions of my DAO
classes from being known to the Service
layer (by wrapping the exception in a layer specific checked exception).
I have the following approach for logging and displaying error messages to the user in the JSPs. I am logging exceptions in a centralized place. A Servlet Filter
:
public class WebAppUtils {
private static final Logger logger = LoggerFactory.getLogger(WebAppUtils.class);
//Called from the Filter's doFilter() method, which is mapped to /*
public static void log(ServletRequest request, ServletResponse response, FilterChain chain) {
try {
chain.doFilter(request, response);
} catch (Throwable exception) {
String rootCauseMessage = ExceptionUtils.getRootCauseMessage(exception);
request.setAttribute("rootCauseMessage", rootCauseMessage);
if (exception instanceof RuntimeException) {
logger.error("A Runtime Exception has occurred. The cause is " + rootCauseMessage, exception);
throw new RuntimeException();
}
if (exception instanceof Exception) {
logger.error("An Exception has occurred. The cause is " + rootCauseMessage, exception);
} else {
logger.error("An Error has occurred. The cause is " + rootCauseMessage, exception);
}
throw new RuntimeException();
}
}
//Called when context is being destoyed
public static void cleanUp() {
deRegister();
if (logger.isInfoEnabled()) {
logger.info("Jdbc drivers have been " +
"de-registered to prevent memory leak. Shutting down loggers.");
}
LogManager.shutdown();
}
private static void deRegister() {
Enumeration<Driver> drivers = DriverManager.getDrivers();
while (drivers.hasMoreElements()) {
Driver driver = drivers.nextElement();
try {
DriverManager.deregisterDriver(driver);
logger.info(String.format("De-registering jdbc driver: %s", driver));
} catch (SQLException e) {
logger.info(String.format("Error de-registering driver %s", driver), e);
}
}
}
}
My web.xml:
<filter>
<filter-name>logger</filter-name>
<filter-class>com.mycompany.ExceptionLogger</filter-class>
</filter>
<filter-mapping>
<filter-name>logger</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/WEB-INF/error.jsp</location>
</error-page>
Error Page:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<div>
//This shows the caused exception name with the optional message.
//I am not too happy with it as the user may not be interested in my custom exception name.
//How can I improve this?
Oops! Something went wrong. The cause is
<span>
<c:if test="${!empty requestScope.rootCauseMessage}">
<c:out value="${requestScope.rootCauseMessage}"/>
</c:if>
</span>
</div>
How can I improve the shown approach for logging and is there any better way to report error conditions(in a more meaningful way than the one showed)to the user in JSPs?