Monday, June 23, 2008

Migration to Spring

We've decided to migrate to Spring. We are already using Hibernate, but want to improve our server architecture to use Dependency Injection. Why am I blogging about this when so much has been written about it? Believe it or not, a lot of Java shops still haven't migrated their apps to Spring, but are thinking about it, and the more recipes there are out there, the easier it is for people to do.

Why Spring and not EJB3? The differences are minimal for the features we care about. For example, we are fine with using XML configuration for dependency injection instead of EJB3-style annotations. We do not use stateful session beans, which seems to be one of the few EJB features that Spring does not support.

The benefits we believe we will get are:

  • Loosely-coupled objects

  • Simpler transaction code

  • Less dependency on JBoss

Loosely-coupled objects

Before we embarked on this migration, we already had a pretty good architecture based on the DAO/DTO pattern, and a facade that insulates our presentation layer from the server's implementation. The most compelling reason to use Dependency Injection, other than it incrementally improves our architecture and our flexibility, is that it makes our code much easier to test. You can now test one component in isolation from all the others on which it doesn't depend. Before this change, when you ran a single unit test, the test would pull in the whole code base. In fact, before this change, you had to deploy the entire code base to the app server to test a single method. Now you can run (and debug!) a single method in a unit test in the IDE.

An example of incremental improvement is that all of our DAO objects are created by factory classes. Spring allows us to get rid of all the factory code altogether. While it's not a lot of code, every line of code has a maintainence cost. Until the advent of technologies like Spring, it was quite a bit of work to create a truly loosely-coupled architecture. At some point I would inevitably find myself creating an implementation in code, thereby creating coupling. Spring solves the problem once and for all, allowing you to properly program to interfaces rather than implementations. Loose coupling is one of those best practices that has more benefits down the line than initially meets the eye.

Simpler transaction code

One of Spring's advantages is the reduction of the amount of code you have to write and maintain. In the case of transactions, you can get rid of all the code that obtains a Hibernate session, starts a transaction, and rolls it back for each type of error that's encountered. The code is instead replaced by (at a minimum) one annotation per DAO class. Another significant benefit is that Spring automatically rolls back transactions in the event of an exception, unless you tell it otherwise. It also takes care of closing the session. Both of these features reduce two common causes of bugs.

Less dependency on JBoss

One of Spring's advantages is that you need not depend on an application server to provide EJB services. In fact, by using Spring and Hibernate, you can dispense altogether with the traditional EJB container. Also, we have certain partners that prefer we use a different app server than JBoss. While JBoss and other app servers have implemented Spring and Hibernate's pioneering ideas in the form of EJB3, we would rather take the initial step of switching to Spring before going all the way to EJB3.

How we did it

Slow start

The first question was how to inject the Spring-instantiated singleton beans into our existing code without requiring major surgery. We wanted to ease into Spring, and not have to take a year to rewrite our entire code base. In my past experience, I added a context loader listener to the web application, then accessing the web application context from a servlet. In our case, the server code is packaged in an EAR file, and the presentation layer is in separate application, packaged in a separate WAR file. We wanted to keep any knowledge that Spring is even being used out of the presentation layer code. So we ended up creating a bean lookup class called SpringContextLoader, which has static getters for all the beans. This was a good way to get started, because any call like:

FooFactory.getFooInstance()

could be search-and-replaced with

SpringContextLoader.getFooInstance()

But over time we changed as many of these as possible to use dependency injection. The nice thing was that it didn't all have to be done at once.

SpringContextLoader itself gets a handle to the Spring application context as follows:

package com.my.server.spring;

public class SpringContextLoader
{
private static ApplicationContext ctx;
public static ApplicationContext getApplicationContext()
{
if (ctx == null)
{
ctx = new ClassPathXmlApplicationContext(
"com/my/server/spring/applicationContext.xml");
}
return ctx;
}
...

All that is required is to colocate the Spring configuration file, applicationContext.xml, next to SpringContextLoader.java. The webapp didn't need to even be told that Spring was being used. In our case, all our services are started by a JMX bean, so we changed the first line of the "start" method of this bean to call:

 SpringContextLoader.getApplicationContext()

This takes care of instantiating the Spring beans before they are used.

The GUI code also uses the SpringContextLoader to get at the beans that it needs. At some point I would like to replace this with EJB3 annotation-based injection.

Transactions

I won't go over the contents of applicationContext.xml that relate to dependency injection, since it's covered so well in many other places. But I will do so when it comes to transactions.

To move from coding Hibernate sessions directly to using Spring's Hibernate support and Spring transactions, this is the pattern we started with:

public class FooDAOImpl implements FooDAO {
public void saveFoo(Foo foo) throws MyFooException {
try {
Context ctx = new InitialContext();
SessionFactory sessionFactory =
(SessionFactory) ctx.lookup("java:/hibernate/SessionFactory");
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
fooDTO = new FooDTO(foo);
session.save(fooDTO);
tx.commit();
} catch (Exception e) {
tx.rollback();
throw MyFooException(e);
} finally {
if (session != null && session.isOpen()) {
session.close();
}
}
}

This is the pattern we ended with:

public class FooDAOImpl extends HibernateDaoSupport implements FooDAO {
@Transactional(rollbackFor={MyFooException.class})
public void saveFoo(Foo foo) throws MyFooException {
try {
FooDTO fooDTO = new FooDTO(foo);
getHibernateTemplate().save(fooDTO);
} catch (Exception e) {
throw MyFooException(e);
}
}
}

The application context

As you can see, there's a lot less code. To achieve it, here's what needed to go in applicationContext.xml. We obtain the session factory from JNDI, but in your case you may do it differently. The session factory also takes care of resolving all the hibernate mapping files (not shown):

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">

<!-- Spring/Hibernate Session Factory (obtained from JDNI) -->
<bean id="sessionFactory"
class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:/hibernate/SessionFactory</value>
</property>
</bean>
<!-- Spring/Hibernate Transaction Support (in our case, JTA provided by JBoss) -->
<bean id="txManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="userTransactionName">
<value>UserTransaction</value>
</property>
<property name="transactionManagerName" value="java:/TransactionManager"></property>
</bean>
<!-- Spring/Hibernate Template -->
<bean id="hibernateTemplate"
class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory">
<ref bean="sessionFactory" />
</property>
</bean>
<!-- This is a bean post processor.
It gets a chance to look at every bean in this file as it is created.
If the bean contains the @Transactional annotation,
a transactional proxy is automatically created to wrap it.
-->
<tx:annotation-driven transaction-manager="txManager" />

<!-- Bean definitions start here -->

Problems

Session was already closed

We ran into the dreaded "org.hibernate.SessionException: Session was already closed" quite a bit. The problem appears to happen when static singletons that were not instantiated by Spring attempted to access Spring beans during the initialization process.


The solution to most of these was to delay all such accesses till after initialization occurred, typically by lazily accessing the beans the first time they are needed instead of in the singleton's constructors.


But this did not solve all of the cases. The source of the rest turned out to be that we were mixing transaction managers. We were using the Hibernate transaction manager in Spring, while the rest of the code was using the JTA transaction manager that was configured in the JBoss setup. We changed the following:


<bean id="txManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>

To:

<bean id="txManager"
class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="userTransactionName">
<value>UserTransaction</value>
</property>
<property name="transactionManagerName" value="java:/TransactionManager"></property>
</bean>

Proxy problems

Another thing that took a while to understand was "org.springframework.beans.TypeMismatchException: Failed to convert property value of type [$ProxyNN] to required type Foo". This happens because we are using AOP autoproxying: Spring creates a subclass at runtime called a proxy that contains the additional transactional code. It turns out there are two ways in which proxies can be created: JDK dynamic proxies, which works with interfaces, the other one utilizes CGLib and is based on concrete classes (you can read more on this here). Even though we include cglib.jar in our deployment, for some reason there are cases where Spring could not instantiate a proxied subclass of a concrete class like Foo. The solution was to create an interface IFoo, and change the code to operate on IFoo instead of Foo. This is better practice anyway and it solved the problem.

Could not unbind factory from JNDI

Another type of exception we got was related to resolving a JNDI name when creating the Hibernate session factory:

[SessionFactoryObjectFactory] Could not unbind factory from JNDI
javax.naming.NoInitialContextException:
Need to specify class name in environment or system property,
or as an applet parameter, or in an application resource file:
java.naming.factory.initial
at javax.naming.spi.NamingManager.getInitialContext()

It turned out to be an unnecessary attribute in our hibernate.cfg.xml file:

<session-factory name="TestSessionFactory">

Needed to be changed to:

<session-factory> 

There is no need to specify a session factory in hibernate.cfg.xml since it's already supplied by Spring.

5 comments:

Eyal said...

Hi,
Thanks for an interesting article.
I am not a Spring / Hibernate developer.
I'm a more user of these frameworks in our application.
This was very helpful as I am going to get interviewed for a position to build from scratch the back end of an NMS app.
This article really helped me prepare myself :)

Rod Johnson said...

You can have a lot less code in your Spring DAO--only one line per method, in fact.

- Consider whether you want to have your own DAO exceptions, or use Spring's automatic exception translation to Spring's DAO exception hierarchy. Application-specific exceptions are usually unnecessary
- That will also eliminate the need for the try/catch block.
- You also don't need to use HibernateTemplate any more--check out recent Spring examples

Btw you should not catch (Exception) in general, and you should be careful to preserve the stack trace, not just the message, when wrapping an exception.

Rgds
Rod Johnson

Rod Johnson said...

"For example, we are fine with using XML configuration for dependency injection instead of EJB3-style annotations."
Spring 2.5 supports EJB style DI annotations. It also offers its own @Autowired annotation which is much more powerful.

jsinai said...

Rod, thanks for the feedback.

You are right about swallowing the stack trace--in our production code we don't do that. I oversimplified it for the sake of the example. I've corrected it.

Regarding using Spring's DAO exceptions instead of ours--we specifically didn't because we wanted to change as little of the client code as possible, but as time goes by we can eliminate our own exceptions in favour of Spring's.

Regarding your other comment about Spring 2.5--one thing I neglected to mention is that we are currently restricted to using Spring version 2.0.1.

Julian Sinai said...

Eyal, glad I could help!