I’m currently working on a project that has given me the lovely opportunity to revisit the holy hell that is Tomcat. It’s fine and all, but it can be a real bitch to you. So I have a fine mix of JPA 2 (via EclipseLink 2.0.0), Spring 3.0.0, and AspectJ 1.6.6 running on Tomcat 6.0.20. I have been trying to get persisted HttpSessions to autowire with AspectJ injecting Spring beans back into the JSF beans (also managed by Spring) when the sessions were deserialized. Things look kind of like this…


We have our load time weaving configured via the Tomcat class loader instrumentation, courtesy of Spring. Since we are using Tomcat, -javaagent: is not required on the command line.

WebContent/META-INF/context.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Context>
	<Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader" />
</Context>

Next, we have our AspectJ configuration to handle our application classes (xxx stands for our package base), skipping what is already instrumented (lest we run into stack brokenness), and injecting things during deserialization.

WebContent/WEB-INF/classes/META-INF/aop.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
	<weaver options="-verbose -showWeaveInfo -XmessageHandlerClass:org.springframework.aop.aspectj.AspectJWeaverMessageHandler">
		<include within="xxx..*" />
		<exclude within="xxx..*CGLIB*" />
		<exclude within="xxx..*javassist*" />
	</weaver>
	<aspects>
		<aspect name="org.springframework.beans.factory.aspectj.AbstractInterfaceDrivenDependencyInjectionAspect" />
	</aspects>
</aspectj>

Spring is configured with the standard bunch of annotation processing and AOP tags.

WebContent/WEB-INF/applicationContext.xml:

	<context:load-time-weaver />
	<context:annotation-config />
	<context:component-scan annotation-config="false" base-package="xxx" />
	<context:spring-configured />
	<tx:annotation-driven mode="aspectj" proxy-target-class="true" />

Finally, we have some service classes, DAOs, etc. annotated thusly… Note: None of these annotated component/configurable beans are configured in applicationContext.xml, although you can use prototypes in the XML.

@Configurable(autowire = Autowire.BY_TYPE, dependencyCheck = true)
@Controller("thingTableModel")
@Scope("session")
public class ThingTableModel
	extends AbstractTableModel<Thing>
{

	private static final long serialVersionUID = 1L;

	@Autowired(required = true)
	private transient ThingController _thingController;

	...
}
This thingTableModel is accessible as a JSF bean thanks to a bit like this:

faces-config.xml:

	<application>
		...
		<el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
	</application>
@Service("thingController")
@Transactional
public class ThingController {

	@PersistenceContext
	private EntityManager _entityManager;

	@Autowired(required = true)
	private ThingDAO _thingDAO;

	@Secured("ROLE_ADMINISTRATOR")
	@Transactional
	public void createThing(Thing thing) {
		_thingDAO.persistRefresh(thing);
	}

	@Secured("ROLE_ADMINISTRATOR")
	@SuppressWarnings("unchecked")
	@Transactional(readOnly = true)
	public List<Thing> getThings() {
		return _entityManager.createQuery("SELECT x FROM Thing x ORDER BY x.shape").getResultList();
	}

	...
}

You can imagine the typical generics DAO hierarchy here with the interfaces and JPA-specific implementation yadda yadda, but the annotation here is on the final concrete class (although there is an @PersistenceContext stuck on an EntityManager field in the abstract DAO implementation.)

@Repository
public class ThingDAOImpl
	extends AbstractDAOImpl<Thing, Integer>
	implements ThingDAO
{
	...
}

Everything was working great, until NPEs showed up after sessions were restored after Tomcat restarts. The controllers were all null in the JSF beans. This was obviously broken injection. After poring over (no, not “pouring over”, numbskull) about 20MiB of logs, I had proof:

+main DEBUG wiring.BeanConfigurerSupport - BeanFactory has not been set on BeanConfigurerSupport: Make sure this configurer runs in a Spring container. Unable to configure bean of type [xxx.view.ThingTableModel]. Proceeding without injection.

Based on the location of this line, which was before the application context started up and right after sessions were being loaded from disk, it all made sense. But, how to fix…

This is all it was! One fucking line (or three if you like it that way):

<?xml version="1.0" encoding="UTF-8"?>
<Context>
	<Loader loaderClass="org.springframework.instrument.classloading.tomcat.TomcatInstrumentableClassLoader" />
	<Manager className="org.apache.catalina.session.StandardManager" algorithm="SHA" maxInactiveInterval="600" />
	<!--
	 - Also a possibility...
	<Manager className="org.apache.catalina.session.PersistentManager" algorithm="SHA" maxInactiveInterval="600">
		<Store className="org.apache.catalina.session.FileStore" />
	</Manager>
	 -->
</Context>

The issue at hand was that the sessions were being loaded by the main Tomcat StandardManager before the Spring application context was even a glimmer in Tomcat’s eye. Now that this web app had its own session manager configured, everything was fixed, just in time for the winter solstice!

Keep in mind that if you use Spring Security and you do anything extra during injection with a setter method, there will be no authorization context at that time and the injection will fail. So, make sure that there are no @Secured methods in your call path.

Also, keep an eye on your jars. org.springframework.instrument.tomcat-.jar goes in ${catalina.home}/lib (no more common/lib in Tomcat 6) and org.springframework.instrument-.jar goes nowhere. You don’t need it in your app’s WEB-INF/lib.

  • Digg
  • Delicious
  • StumbleUpon
  • Technorati Favorites
  • Reddit
  • Yahoo Buzz
  • Twitter
  • DZone
  • Google Bookmarks
  • LinkedIn
  • Amazon Wish List
  • Share/Bookmark