A couple of things...
1) I have slightly revisited the contextual exception class that I suggested earlier in this thread. I didn't like my using of a collection to store the context, if all we need is a complementary message that reflects the states of some arbtrary application objects that developers may add. So, here's the revised class:
Code:
package com.yourcompany.common;
/**
* This exception class allows storing contextual information on the exception instances. The contextual data may
* be added to an exception instance as arbitrary application objects and written out as a simple string of toString()
* representations of all stored objects.
*/
public class ContextualException extends RuntimeException {
private final StringBuilder contextMessage = new StringBuilder(1000);
public ContextualException(String msg) {
super(msg);
}
public ContextualException(String msg, Throwable t) {
super(msg, t);
}
/**
* Adds an object to the exception context string.
* @param o application object whose content may be of interest in the context of this exception
*/
public void addToContext(Object o) {
contextMessage.append(o.toString()).append(System.getProperty("line.separator"));
}
/**
* Adds an object and its description to the exception's context string.
* @param o application object whose content may be of interest in the context of this exception
* @param description clarifying description of the object being added to context
*/
public void addToContext(Object o, String description) {
contextMessage.append(description).append(": ").append(o);
}
/**
* Gets the string representation of all objects stored in this exception's context.
* @return concatenated string of all context object toString() representations
*/
public String getContextMessage() {
return contextMessage.toString();
}
}
As you see, you have an option to add a description for each contextual object as well. I chose not to override the getMessage() method to include the context info by default because I don't think it would be appropriate. Let the clients decide when and how to use that information. See an example below...
2) I see a little problem with your code example... How do you know that the method you are intercepting is throwing that specific type of exception. And what happens to all other exceptions that may be thrown inside. What you need to do is to catch any exception, wrap it into your ContextualException, add context data, and re-throw.
Code:
...
catch (Throwable t) {
//get some context information
// ...
ContextualException ce = new ContextualException("Your generic message here: service failure, blah-blah...", t);
// loop through args of the method
for (Object arg : allArgs) {
ce.addToContext(arg);
}
throw ce;
}
3) In your handler - if, for example, you are using Spring MVC in the front-end - you'd have a generic exception resolver for all your system errors that will catch all server-side exceptions that bubble up to it:
Code:
package com.yourcompany.common.web.springmvc;
/**
* This class implements an exception resolver that redirects to the designated view in cases of any exception that
* <i>has not been previously caught and handled</i> by the application or framework (including any Spring Webflow error
* handlers registered with the application). Those are any system and programming errors that should result in a
* graceful termination of the application.
* <p/>
* A bean of this class must be registered with the Spring application context with the order number the highest amongst
* all error resolvers. By default, the prioritization order value for this error handler is set to {@link
* Integer#MAX_VALUE} to ensure that this resolver is evaluated <i>after</i> all other registered resolvers have been
* considered for any exceptions that may require more specific handling. The value may be changed via the {@link
* #setOrder} method, however, in most cases, that should not be done.
*/
public class SystemErrorResolver implements HandlerExceptionResolver, Ordered {
private int order = Integer.MAX_VALUE; // prioritization order number for this Ordered object
private String errorViewName;
private static final Logger logger = Logger.getLogger(SystemErrorResolver.class);
private static final String ERR_MESSAGE =
"Unrecoverable error occured in the application. See the nested exception stack trace for details. Redirecting to the Error View."
;
/**
* Constructs a SystemErrorResolver object with the given name of the view to render in the case of a system error.
* This view name must be properly resolved by the Spring MVC DispatcherServlet's view resolver.
*
* @param viewName name of the View to render, to be resolved by the DispatcherServlet's ViewResolver
*/
public SystemErrorResolver(String viewName) {
errorViewName = viewName;
}
/** {@inheritDoc} */
public ModelAndView resolveException(HttpServletRequest req, HttpServletResponse response, Object handler,
Exception e) {
// return the Error Page view in the case of any unhandled exception
if (e != null) {
logger.error(ERR_MESSAGE, e);
// below is the additional logging of any context message if it is available
if (e instance of ContextualException) {
logger.error(e.getContextInfo());
}
return new ModelAndView(errorViewName);
} else {
return null;
}
}
/** {@inheritDoc} */
public int getOrder() {
return order;
}
/**
* Sets the prioritization order number for this Ordered object. Spring looks at all handler exception resolvers
* (beans implementing the appropriate HandlerExceptionResolver interface) in the application context. Any instances
* that implement the Ordered interface are sorted (lowest order first), and others are added at the end of the
* list. The resolvers are considered in the order they are listed.
* <p/>
* NOTE: By default, the prioritization order value for this error handler is set to {@link Integer#MAX_VALUE} to
* ensure that this resolver is evaluated <i>after</i> all other registered resolvers have been considered for any
* exceptions that may require more specific handling. <b>Do not call this method unless this behavior must
* change.</b>
*
* @param num order number
*/
public void setOrder(int num) {
this.order = num;
}
}
This is a class I wrote a few projects ago, and I have re-used it since on many projects. It's very basic stuff, I am sure you are familiar with it and know how to register an exception resolver with the Spring context. What I added just now, is the optional check for the exception instance being of the ContextualException type. If so, we can also log the context message.
You could write a generic "Around" aspect for each public service method that would do something like that (assuming you have some common marker interface for all your services called IService, for example):
Code:
@Pointcut("execution(* com.yourcompany.common.service.IService.*(..))")
private void anyServiceOperation() {
}
@Around("anyServiceOperation()")
public static void handleServiceFailure(ProceedingJoinPoint point) {
try {
point.proceed();
} catch (Throwable t) {
ContextualException ce = new ContextualException(STD_MSG, t);
for (Object arg : point.getArgs()) {
ce.addToContext(arg);
}
throw ce;
}
}
This is just off the top of my head, and you certainly will need to work with it to make this do whatever you really need. But I hope this gives you the general idea. Let me know if this makes sense.
Good luck,
Constantine