Archivo mensual: septiembre 2010

Making sure Hibernate does not leak sessions/entity managers

Resource cleanup is a must for all applications, especially long-running ones.

The most precious Hibernate resource requiring cleanup is the Session. If you are using JPA, as I frequently do, a session is wrapped by an EntityManager, but is still there.

In order for your apps to behave, you will need to make sure you are not forgetting to release sessions, or you’ll end up having trouble sooner than later.

To ensure that, you’ll need a way to check whether, after finishing some self contained operation, you have released all sessions you allocated -but not more.

To do that, I created a utility class, called SessionAllocationChecker. How do you use it? The typical usage is as follows:

SessionAllocatorChecker checker = new SessionAllocatorChecker( 
   myHibernateSessionFactory );
try {
  // ... code that opens and closes hibernate sessions/entity managers
}
finally {
  checker.dispose();
}

Here, if there are 5 sessions open when checker is created, and 8 sessions open when dispose is called, you will get an AssertionError, telling you you’ve got 3 extra sessions open you probably forgot to close.

How does it do that?

Now, how does it work? When an instance of SessionAllocationChecker is allocated, it checks the statistics maintained by Hibernate and notes the number of open sessions vs. the number of closed sessions at the moment: the difference will be the number of effectively open sessions at creation time.

If Hibernate statistics are not enabled, it will enable them -and quietly disable them when you call dispose.

Next, when you call getOutstandingSessions or hasOutstandingSessions, it performs the same operation, to check the number of currently open sessions. getOutstandingSessions will return the number of sessions currently open minus the number of session open at creation time, that is, the number of additional sessions you have open and not closed yet.

When you call the dispose method, it ensures Hibernate statistics enabled state is restored to its old status. Besides, it will check whether you have session leakage, by calling getOutstandingSessions , but only if you are executing you app with the JVM -enableassertions option. If that’s the case, an AssertionError exception will be thrown.

If the -enableassertions flag is not set, you will have to check the number of outstanding sessions by calling getOutstandingSessions on you own.

While rough, I found this trick extremely useful: in fact, I use this class to check that I never forget to release sessions/entity managers in my own tests.

Another place where you can put this to good use is in web app filters, to ensure that your requests do not leak sessions.

Here is the full source code:

package com.softwarementors.bzngine.engines.hibernate;

import javax.persistence.EntityManagerFactory;

import org.hibernate.SessionFactory;
import org.hibernate.ejb.HibernateEntityManagerFactory;
import org.hibernate.stat.Statistics;

public class SessionAllocationChecker {

  private boolean statisticsEnabledOnEntry;

  private long outstandingSessionsOnEntry;

  private SessionFactory factory;

  private boolean disposed;

  public SessionAllocationChecker( SessionFactory factory ) {
    assert factory != null;
    
    this.factory = factory;
    Statistics statistics = factory.getStatistics();
    this.statisticsEnabledOnEntry = statistics.isStatisticsEnabled();
    statistics.setStatisticsEnabled(true);
    this.outstandingSessionsOnEntry = 
       statistics.getSessionOpenCount() 
       - statistics.getSessionCloseCount();
  }
  
  public SessionAllocationChecker(EntityManagerFactory factory) {
    this( ((HibernateEntityManagerFactory) factory).getSessionFactory() );
  }
  
/*  
  public SessionAllocationChecker( EntityManager entityManager ) {
    this( entityManager.getEntityManagerFactory());
  }
  
  public SessionAllocationChecker( Session session ) {
    this(session.getSessionFactory());
  }
*/  

  public boolean isDisposed() {
    return this.disposed;
  }
  
  public long getOutstandingSessions() {
    assert !isDisposed();

    Statistics statistics = this.factory.getStatistics();
    assert statistics.isStatisticsEnabled() : 
       "Somebody else disabled the statistics: avoid that";
    long outstandingSessions = statistics.getSessionOpenCount() 
       - statistics.getSessionCloseCount();
    long sessionsNotReleasedAfterCreation = outstandingSessions 
       - this.outstandingSessionsOnEntry;

    assert sessionsNotReleasedAfterCreation >= 0 : 
       "Somebody else played with the statistics " + 
       "(maybe cleared/disabled them?): avoid that";
    return sessionsNotReleasedAfterCreation;
  }

  public boolean hasOutstandingSessions() {
    assert !isDisposed();
    
    return getOutstandingSessions() > 0;
  }

  public void dispose() {
    if (!isDisposed()) {     
      assert !hasOutstandingSessions() : "There are " 
         + getOutstandingSessions() + 
         " additional sessions open and not closed";

      this.disposed = true;
      
      // Reset statistics to the state they were before we started to
      // be interested in open/closed session count
      Statistics statistics = this.factory.getStatistics();
      statistics.setStatisticsEnabled(this.statisticsEnabledOnEntry);
    }
  }
}

If you take a look at the code, you will find I have added lots of sanity checks via assertions: using the Hibernate statistics as I do is not completely foolproof, if you clear them or disable them between the moment you created the checker and you disposed it, you will get incoherent readings that will fool SessionAllocationChecker.

While I have provided lots of checks, they are not completely safe -not could they be, given the way statistics are implemented by Hibernate. Keep an open eye, and all will be well.

You’ll have noticed that I use assertions all around: you can read this article if you are interested in knowing why I’m such a fan of assertions.

By the way, if you don’t have at hand a SessionFactory or a EntityManagerFactory to pass it to the SessionAllocationChecker constructor, all is not lost: you can get them from a Session or an EntityManager. How to do it is part of the code: just look at the commented code.

Believe it or not, the code is commented because I just have no tests for it in the library where SessionAllocationChecker belongs.

No test, no public exposure -at least, as part of a high quality library.

Anuncios

JRebel is great for an agile environment

One of the basic tenets of agility is to be always in a fast feedback loop. You develop in short iterations (sprints, is you use Scrum). If you believe in Test Driven Development (as I do), you execute tests as frequently as possible.

Accordingly, I believe your development environment must facilitate the capability to redeploy your application in zero time (ok, a few seconds might be bearable). If you have to wait two or three minutes to redeploy your app to see the effect of your last code change, you will be destroying the fast feedback loop, with subtle consequences to your concentration and focus. And you will probably avoid it, getting less and less feedback.

I’ve always been able to create almost instant-redeploy environments in which we did not have to pack jars or wars, and little changes to a class did not force us to redeploy the apps.

However, my last project is a distributed app that includes several wars which share several jars, and so on. When I arrived there, it was taking almost 4 minutes to recompile and redeploy the whole app. Of course, changing a class and expecting the change not to force us to redeploy the app was a dream. Yup!

Besides, I wasn’t able to use the setup I had been using for my last projects: I was caught between a rock and a hard place.

Enter JRebel.

JRebel is a tool that allows instant class redeploy to a jar, war or ear: it just sits there checking whether a .class file is modified, and silently loads it when that happens. And it works. And, it works always! No corner cases of ifs so far.

Configuration is so easy you’ll probably have to configure it just once for the whole project lifetime, and will be able to forget it.

If you are not using JRebel, you are throwing away your time, focus and energy. Seriously: get it!