Friday, August 31

Reusable Advice - Part II: Unobtrusive Notification

Besides encapsulating behaviors that affect multiple classes into reusable modules, AOP can also be used for unobtrusive notification tasks.
Instead of browsing application logs looking for exceptions, for example, I like to have the application itself email me the exception stack-trace and the arguments that caused the error when something goes wrong.

Here's an example advice that sends an email after an exception is thrown. The date of occurrence and the exception stack-trace are sent in the body of the email.

The Java code for the advice:



...
public class ThrowableAdvice {
private MailSender mailSender;
private SimpleMailMessage mailMessage;
public static final String NEW_LINE = System.getProperty("line.separator");

// Setters and getters omitted for clarity
...

/**
* Send email notification with exception stacktrace
*
* @param throwable
* @return boolean frue if email message is sent, false otherwise
*/
public boolean notifyByEmail(Throwable throwable) {
SimpleMailMessage msg = new SimpleMailMessage(this.mailMessage);
StringBuffer txt = new StringBuffer(msg.getText());
txt.append(NEW_LINE).append(new Date());
txt.append(NEW_LINE).append(ExceptionUtils.stackTraceToString(throwable));
msg.setText(txt.toString());
try {
this.mailSender.send(msg);
return true;
} catch (MailException me) {
logger.error("Error sending email notification ", me);
}
return false;
}
}
...


A useful method to "print" a stack-trace onto a String:



...
public static String stackTraceToString(Throwable throwable) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
throwable.printStackTrace(pw);
return sw.getBuffer().toString();
}
...


And a snippet of the Spring XML configuration:



...
<aop:config>
<aop:aspect id="appThrowableInterceptor" ref="throwableAdvice">
<aop:after-throwing
method="notifyByEmail"
throwing="throwable"
pointcut="execution(* some.package.*.*(..))" />
</aop:aspect>
</aop:config>

<bean id="throwableAdvice" class="ThrowableAdvice">
<property name="mailSender" ref="mailSender"/>
<property name="mailMessage" ref="throwableMessage"/>
</bean>

<bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl">
<property name="host" value="localhost"/>
</bean>

<bean id="throwableMessage" class="org.springframework.mail.SimpleMailMessage">
<property name="subject" value="ALERT! Exception thrown @ some app module"/>
<property name="text" value="ALERT! Exception thrown from some module"/>
<property name="from" value="the.notifier@application.org"/>
<property name="to">
<list>
<value>developers@application.org</value>
</list>
</property>
</bean>
...


This is as flexible and unobtrusive as it gets -the throwableAdvice advice above can be:
  • easily reused by specifying a different pointcut (above in bold);
  • removed altogether without needing to touch the advised (target) code;



(Vaguely) Related Posts:
Better Living Through (Unit) Testing and Advice
Reusable Advice - Part I


References:
AOP@Work: Dependency injection with AspectJ and Spring
AOP@Work: Check out library aspects with AspectJ 5
AOP@Work: AOP myths and realities
Improve modularity with aspect-oriented programming
Spring AOP Reference documentation

No comments: