[TSE] Slides for Advanced MVC, Scheduling and JMX Presentations

Well, I’m finally back from my conference tour and feeling human again. As promised to all the attendees at TSE, here are the presentations for MVC, scheduling and JMX that I gave at TSE along with the code for the sample application and the demos.

Presentations:
Advanced Spring MVC
Enterprise Management with Spring and JMX
Scheduling and Task Execution

Code:
DVD Central Sample Application
JMX Samples
Scheduling Examples

The sample application was written for WebLogic Server 9 and Oracle 10g, but it should be easy to port most functionality to other app servers/databases. The CommonJ scheduling functionality will only work on WebLogic Server 9 or on WebSphere 6.

All code requires Spring 2.0 M1 or above. This is planned for release on Monday but you can obtain the latest nightly snapshot at springframework.org.

WordPress database error: [Can't open file: 'robh_comments.MYI' (errno: 145)]
SELECT count(comment_author) FROM robh_comments WHERE comment_approved='1'

Recent Posts

Controlling MBean Registration Behavior in Spring

In Spring 1.2.5 I modified the MBeanExporter to provide users with more control over MBean registration. Previously, the MBeanExporter would attempt to register an MBean and fail if that MBean already existed. In many cases this is the desired behavior; however there are two particular cases where this behavior is not desirable:

  1. You have many applications sharing an MBean - perhaps as a mechanism for sharing data
  2. You have a situation whereby an MBean may not be unregistered when redeploying the application

In the first situation, you are deloying multiple applications each of which wants to register and access the same MBean say, for example, spring:type=Shared. Previously in Spring, the first application you deployed would deploy successfully and all others would fail when trying to register the shared MBean. As of Spring 1.2.5, you can get around this by setting the registrationBehaviorName property of the MBeanExporter to REGISTRATION_IGNORE_EXISTING, instructing MBeanExporter to attempt to register the MBean and continue happily along if it finds that MBean is already registered. So in the scenario I highlighted, the first application would go ahead and register the MBean spring:type=Shared and each subsequent application would simply detect that this MBean already existed and continue through the registration process.

<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        ...
	<property name="registrationBehaviorName" value="REGISTRATION_IGNORE_EXISTING"/>
	...
</bean>

In the second scenario, you are deploying the same application again and again, perhaps in a development environment, but application server is not correctly signalling the application shutdown to Spring. As such, the MBeanExporter is unaware that it should be unregistering its MBeans and the MBeans are left registered when you come to redeploy the application. In this case, you can set the registrationBehavior property to REGISTRATION_REPLACE_EXISTING and have the MBeanExporter simply unregister the residual MBean and replace it with the new one.

<bean id="exporter" class="org.springframework.jmx.export.MBeanExporter">
        ...
	<property name="registrationBehaviorName" value="REGISTRATION_REPLACE_EXISTING"/>
	...
</bean>

This is just the first of many new JMX-related features coming to Spring in the coming months. In the 1.3 release we will have support for JMX notifications, enabling you to configure notification listeners using Spring and to access notification publication infrastructure. We are introducing a new MBeanInfoAssembler, which allows you to define MBean interfaces using an XML descriptor, plus we will be improving the MBeanExporter itself to allow you to register your own MBeans at runtime using Spring’s management interface generation capabilities. In the longer term we will be adding JMX management capabilities to Spring itself allowing you to control and monitor aspects of framework. In particular, we will be focusing on adding monitoring capabilties to the JDBC and MVC components of the framework.


Caveats with Class Proxying in Spring

Recently, Jieba Wu posted an interesting bug report on the Spring JIRA detailing a problem he was having when proxying Spring MVC Controllers using CGLIB proxies. Essentially, he would create his target Controller object, pass in some dependency using Spring IoC and then proxy the Controller. At runtime, all invocations were sent directly to the proxy and not to the target. As a result he was seeing NullPointerExceptions because state from the target is not copied to the proxy when creating a class proxy.

After many back and forth exchanges we managed to determine that Jieba was actually extending Spring’s AbstractCommandController which declares the handleRequest() method as final. This causes an issue when CGLIB proxies, because CGLIB cannot override that method (just like any Java code) and it will not be proxied. The result at runtime is that, although we have a proxy object, calls to the final methods are not intercepted and redirected through the advice chain to the target, instead they are sent directly to the proxy instance which is not configured with the correct state.

So what does this mean in real terms? Well, consider the class below:

public class MyBean {
	private String name;

	public final String getName() {
		return name;
	}

	public final void setName(String name) {
		this.name = name;
	}
}

Here you can see a simple bean with a single private field and two public final methods. Now consider this unit test:

public void testProxyClassWithFinalMethods() throws Exception {

	MyBean bean = new MyBean();
	bean.setName("Rob Harrop");

	ProxyFactory proxyFactory = new ProxyFactory();
	proxyFactory.addAdvice(new NopInterceptor());
	proxyFactory.setTarget(bean);
	proxyFactory.setProxyTargetClass(true);

	MyBean proxy = (MyBean)proxyFactory.getProxy();

	// name on target should still be not null
	assertNotNull(bean.getName());

	// getName() is final so cant intercept - method call goes directly to
	// the proxy and will not have access to a valid name field
	assertNull(proxy.getName());
}

What we are going to see here is that we first create the target object which is an instance of MyBean, then we create a proxy for the target adding in a NopInterceptor. When CGLIB comes to create the proxy it will take all non-final methods and give Spring a chance to define how that method should be proxied. Note the disclaimer from that sentence - only non-final methods are proxied. Of course, if you think about it, since CGLIB is creating a dynamic subclass of your target class, then there is no real way around this.

At runtime what will happen when you invoke one of the final methods on the proxy is that the invocation will not be intercepted and, instead, it will be sent directly to the proxy object. This essentially presents two problems: firstly, your advice chain won’t be invoked, potentially causing havoc in your application, and secondly the invocation may die when it try to access state that will not be initialized in the proxy.

The moral of this story is that you need to be extra careful when creating your Spring AOP proxies using CGLIB because things like final methods will cause problems. Interestingly, in the case cited in the bug report, the final method in question was actually defined on the Controller interface and as such this kind of proxy will work perfectly fine when using JDK proxies in Spring.

My recommendation? Use JDK proxies unless you have real issues with performance or you are dealing with legacy code that doesn’t implement the interfaces you need for JDK proxies.


Older Posts

[TSE] Slides for Advanced MVC, Scheduling and JMX Presentations

Controlling MBean Registration Behavior in Spring

Caveats with Class Proxying in Spring