Archive for July, 2009

Grafting Spring Transactions on Legacy Transaction Models

In another part of the continuing series on getting Spring to work properly with our legacy components, I recently had to revisit our the way were were handling transactions. In the previous post on this topic, I demonstrated how you can use a HandlerInterceptor to control your transactions. In this post, I demonstrate how you could use spring transactions, even if you can’t use the Spring way of configuring your Hibernate SessionFactory and need to support legacy code. The end result is much like putting a Ferrari body on a Pontiac Fiero, but it will accomplish the job.

The earlier solution has a significant drawback. The transaction will commit after the controller finished processing the request. Since Hibernate will not flush it’s session until a commit, all database errors will occur in a place where you can’t respond to them easily. Pushing the transaction boundary to the level below the controller (where it belongs anyway) will allow for correct error handling at the UI level. This can be accomplished with Spring Transactions.

Spring needs two things for @Transactional annotated code to do what you expect: the session factory and the transaction manager. Our legacy code used a statically initialized class to create a session factory. My first step was to manually create the HibernateTransactionManager with the correct session factory.

	sessionHolder = new HibernateUtilSessionHolder();
	SessionFactory sessionFactory = configuration.buildSessionFactory();
	sessionHolder.setSessionFactory(sessionFactory);
 
	HibernateTransactionManager htm = new HibernateTransactionManager(sessionFactory);
	sessionHolder.setTxManager(htm);

The sessionHolder is just a class I use to hold the current SessionFactory and TransactionManager. The sessionHolder is also the interface between the legacy code TransactionManager. Here is an example method from HibernateUtilSessionHolder:

    public Session getSession() {
        SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
        if (sessionHolder == null) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("opening session");
            }
            Session s = sessionFactory.openSession();
            sessionHolder = new SessionHolder(s);
            TransactionSynchronizationManager.bindResource(getSessionFactory(), sessionHolder);
        }
        if (LOG.isDebugEnabled()) {
            LOG.debug("GETTING session");
        }
        return sessionHolder.getSession();
    }

Since I hide all the functionality of the TransactionManager behind the previous API, none of the legacy code is aware the underlying transaction model has changed and manual commits will work as they have done before.

The next trick is to get annotations working within the context of new code. Exposing the SessionFactory and TransactionManager to Spring is pretty easy. First, create two static methods to access what was created earlier:

    public PlatformTransactionManager getTransactionManagerInstance() {
        return sessionHolder.getTxManager();
    }
 
    public SessionFactory getSessionFactoryInstance() {
        return sessionHolder.getSessionFactory();
    }

Now expose them to the context by using Spring’s factory bean creation mechanism:

<bean id="hibernateUtil" class="net.vijedi.hibernate.util.HibernateUtil" />
 
<bean id="transactionManager" factory-bean="hibernateUtil" factory-method="getTransactionManagerInstance"/>
<bean id="sessionFactory" factory-bean="hibernateUtil" factory-method="getSessionFactoryInstance"/>

HIbernateUtil is the static class that created the SessionFactory and TransactionManager. The other two lines show how to use it as a factory.

Finally, all that’s left to do is to add

<tx :annotation-driven />

to the spring.xml file and mark up a bunch of code with @Transactional. It might be a Fiero underneath, but it still feels like a Ferrari.

  • Share/Bookmark

Ruby-Enterprise Edition, Fusion Passenger, and Nginx on Ubuntu Jaunty

I’ve got a 512MB Slicehost instance that I needed to launch a new Rails app (details coming soon). Since 512MB isn’t a whole lot of memory anymore, I wanted to optimize the services on this machine as much as possible.

The most efficient setup appeared to be running nginx, Passenger-Nginx, and Ruby Enterprise Edition. It was actually pretty easy to get all this running and would have been even easier had I followed the correct order of operations.

Nginx doesn’t support dynamically compiled runtime modules. Everything has to be built-in at compile time. Because of this, running:

$ sudo aptitude install nginx

will get you nothing useable besides the init script.

Instead, the best approach is:

  1. Install Ruby Enterprise Edition
  2. Install nginx-passenger
  3. Adjust paths
  4. Modify the /etc/init.d/nginx script
  5. Reinstall all gems for REE
  6. Tweak the nginx config
  7. Add nginxensite and nginxdissite scripts to make it more ubuntu-y

The first two steps are pretty self-explanatory and start with the directions found here. Of course, if you are installing nginx, you want to the following instead of the command for apache:

$ /opt/ruby-enterprise/bin/passenger-install-nginx-module

Next you want to tweak the ubuntu /etc/init.d/nginx script to reflect the new paths.

You need to change the two lines that read:

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/usr/sbin/nginx

to

PATH=/opt/nginx/sbin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
DAEMON=/opt/nginx/sbin/nginx

Next, you need to add the Ruby Enterprise Edition interpreter to your path. Open /etc/login.defs and update the paths settings with the location of Ruby Enterprise.

Installing gems is pretty straight forward. There are a few small tweaks to make to the nginx config file so that it works more like the ubuntu package.

At the top of nginx.conf add/change these settings:

user www-data;
worker_processes  4;
 
error_log  /var/log/nginx/error.log;
pid        /var/run/nginx.pid;

In the http section, add:

    include /etc/nginx/sites-enabled/*;

Finally, it’s time to create the nginxensite and nginxdissite scripts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#!/bin/bash
 
# nginxensite
 
if [ -z $1 ]; then
        echo 
        echo "You must specify a site name"
        exit 0
fi
 
NGINX_CONF=/etc/nginx
CONF_FILE="$1"
AVAILABLE_PATH="$NGINX_CONF/sites-available/$CONF_FILE"
ENABLED_PATH="$NGINX_CONF/sites-enabled/$CONF_FILE"
 
echo 
if [ -e $AVAILABLE_PATH ]; then
        ln -s $AVAILABLE_PATH $ENABLED_PATH
 
        echo "$1 has been enabled"
        echo "run /etc/init.d/nginx reload to apply the changes"        
else
        echo "$AVAILABLE_PATH does not exist"
        exit 1
fi

and

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/bin/bash
 
# nginxdissite
 
if [ -z $1 ]; then
        echo 
        echo "You must specify a site name"
        exit 0
fi
 
NGINX_CONF=/etc/nginx
CONF_FILE="$1"
AVAILABLE_PATH="$NGINX_CONF/sites-available/$CONF_FILE"
ENABLED_PATH="$NGINX_CONF/sites-enabled/$CONF_FILE"
 
echo 
if [ -e $ENABLED_PATH ]; then
        rm $ENABLED_PATH
 
        echo "$1 has been disabled"
        echo "run /etc/init.d/nginx reload to apply the changes"        
else
        echo "$ENABLED_PATH does not exist, ignoring"
fi

There you go, a working nginx install for your rails apps in less than an hour.

  • Share/Bookmark

ViJedi is in the Cloud

A few months ago the power supply on my server totally tanked. The cost to replace it was somewhere around $99 and some elbow grease, but it got beyond the point where I wanted to maintain my own hardware. This would be the third hardware failure in as many years and I was frustrated.

I still wanted my own server with full access, so I got an account with Linode. Since I found it very difficult to upgrade between major versions of OpenSUSE, I installed Arch Linux based on the recommendation of the guys over at WeTheCitizens.

This blog, my private repositories and some other stuff sit on this server now, but the shared instance wasn’t going to work for photos.  I signed up for a Flickr pro account and started migrating my public code to GitHub.

When I started this site nearly 5 years ago, none of this would have been practical.  The hosting that you got for $20/mo was worthless.  Code and photosharing sites all sucked.  Now I’ve got a set of tools that work really well and I don’t have to maintain any of it.

  • Share/Bookmark
Return top

About

This is my blog about programming. For random stuff, checkout my Twitter or Tumblr