
The JMX notification API adds a good management aspect to an application. Bypassing the philosophical question of what is management and how does it differ from the rest of my application code here. Once we’ve decided to add management to our application there is a set of concerns that comes with that decision. Minimal impact on application performance/runtime being, minimal impact on “business” code. A natural direction to go with here is AOP since it introduces minimal changes to the code. I want to show how you can combine Spring AOP with JMX notifications to perform lightweight remote monitoring a business object in your application.
First we define our business object, a search service in this case, accepting a set of arguments defining the criteria for the search and a maximum result count limiter, these are the values that I wish to monitor and log elsewhere.
public class MyService {
public void executeSerch(int maxResults, SearchCriteria criteria) {
System.out.println("executing search...");
// execute search logic
}
}
Now we create an interceptor to our service service and send a notification with JMX for each executeSearch with the arguments of the search. First we define the publishing MBean interface:
public interface MethodInvocationPublisherMBean {
void publish(Method method, Object[] args);
}
Next the service interceptor is defined, since this is a “raw” Method Invocation interceptor it can be used with any method on any bean you wish to monitor :
public class NotificationInterceptor implements MethodInterceptor {
private final MethodInvocationPublisherMBean methodInvocationPublisher;
public NotificationInterceptor(final MethodInvocationPublisherMBean methodInvocationPublisher) {
this.methodInvocationPublisher = methodInvocationPublisher;
}
@Override
public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
methodInvocationPublisher.publish(methodInvocation.getMethod(), methodInvocation
.getArguments());
return methodInvocation.proceed();
}
}
Finally we need to implement the MBean emitting the information, to do this we extend NotificationBroadcasterSupport, a utility class for implementing notification broadcasting.
@ManagedResource(objectName = "org.echotech:listener=MethodInvocationEmitter", description = "Method Invocation Emitter")
public class MethodInvocationEmitter extends NotificationBroadcasterSupport implements
MethodInvocationPublisherMBean {
private boolean active = false;
@Override
public void publish(Method method, Object[] args) {
if (active) {
final Notification notification = new Notification(
method.getDeclaringClass().getName(), method.getName(), System.nanoTime(),
System.currentTimeMillis());
notification.setUserData(args);
sendNotification(notification);
}
}
@ManagedOperation()
public void activate() {
active = true;
}
@ManagedOperation()
public void deactivate() {
active = false;
}
@ManagedAttribute()
public boolean isActive() {
return active;
}
}
Now we wrap everything up with spring applying the interceptor to the service
<bean id="myService" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="target"> <bean class="org.echotech.MyService" /> </property> <property name="interceptorNames"> <list> <value>notificationInterceptor</value> </list> </property> </bean> <bean id="notificationInterceptor" class="org.echotech.jmx.NotificationInterceptor"> <constructor-arg ref="methodInvocationEmitter" /> </bean> <bean id="methodInvocationEmitter" class="org.echotech.jmx.MethodInvocationEmitter" />
And publish our MBean in JMX
<bean class="org.springframework.jmx.export.MBeanExporter" lazy-init="false"> <property name="beans"> <map> <entry key="org.echotech:listener=MethodInvocationEmitter" value-ref="methodInvocationEmitter" /> </map> </property> </bean>
Notice the lazy-init attribute on the exporter.
Start your application (e.g., tomcat) with the Spring beans above included in the context. Next fire up jconsole and look for the MBean
jconsole service:jmx:rmi:///jndi/rmi://localhost:9999/jmxrmi
The next step involves listening to invocation of my service. First lets implement a NotificationListener:
public class LoggingNotificationListener implements NotificationListener {
protected static final Logger LOG = Logger.getLogger(LoggingNotificationListener.class);
@Override
public void handleNotification(Notification notification, Object handback) {
if (LOG.isDebugEnabled()) {
LOG.debug(formatNotification(notification));
}
}
private String formatNotification(Notification notification) {
StringBuilder sb = new StringBuilder();
sb.append("recieved notification class: ").append(notification.getType());
sb.append(", method: ").append(notification.getSource());
sb.append(", sequence: ").append(notification.getSequenceNumber());
sb.append(", timestamp: ").append(notification.getTimeStamp());
sb.append(", data: ").append(
StringUtils.arrayToCommaDelimitedString((Object[]) notification.getUserData()));
return sb.toString();
}
}
And define a server connection and a listener in spring
<bean id="notificationListener" class="org.echotech.jmx.LoggingNotificationListener" /> <bean id="beanServerConnection" class="org.springframework.jmx.support.MBeanServerConnectionFactoryBean"> <property name="serviceUrl" value="service:jmx:rmi:///jndi/rmi://localhost:7000/jmxrmi" /> </bean>
And now wrap it up with a simple listener
public class Listener {
public static void main(String[] args) throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
"/spring/listener-context.xml");
MBeanServerConnection beanServerConnection = (MBeanServerConnection) context
.getBean("beanServerConnection");
NotificationListener notificationListener = (NotificationListener) context
.getBean("notificationListener");
ObjectName objectName = ObjectName
.getInstance("org.echotech:listener=MethodInvocationEmitter");
beanServerConnection.invoke(objectName, "activate", null, null);
beanServerConnection.addNotificationListener(objectName, notificationListener, null, null);
System.in.read();
beanServerConnection.removeNotificationListener(objectName, notificationListener);
beanServerConnection.invoke(objectName, "deactivate", null, null);
}
}
This way we get on-demand remote listener to our service. This can be used for debugging of live applications with little impact on the application performance.

Seriously like the fresh look. I really liked the content. Thank you for the helpful blog post.
| January 6, 2012 @ 6:55 PM