Thursday, May 20, 2010

Seam Application Scoped Repositories

Sometimes bugs are so obvious that they hit your blind spot. This happened to me last day, while working on a Seam project at one of our customers. I was writing JMeter tests as part of the QA team to stress test the application, and I got some weird exceptions...

As soon as I went beyond the point of a single user hitting the application, I would at random get "Connection closed" or "Session already closed" exceptions from Hibernate.

As our (Hibernate) sessions are managed by Seam, my guess was that we were getting back stale connections from our connection pool. After removing that, and disabling all the cacheing, the exceptions seemed to occur less, but they were not gone.

So now I needed to get my hands dirty and take a deep dive in our code to see what was really the issue here. I started out by looking at our components.xml file, which contains the Seam managed components. In it we have configured the Hibernate SessionFactory and (managed) Hibernate Session.



And the Repository code, which deals with Hibernate looks like the following


@Name("petRepository")
@Scope(ScopeType.APPLICATION)
@AutoCreate
public class HibernatePetRepository implements PetRepository {
@In
private Session hibernateSession;

public List<Pet> loadSomePets() {
return hibernateSession.createQuery("from Pet").list();
}

... omitted methods ...
}


This looks ok, right.... Ok, now wait a minute, go back a few lines... We've Application scoped our Repository component, and we've injected it with a hibernate Session. That can't be right. A Repository should be stateless, and a Session is state. Once you start hitting the application with a few users, you will get random threading issues. Instead we should have created the repository as such:


@Name("petRepository")
@Scope(ScopeType.APPLICATION)
@AutoCreate
public class HibernatePetRepository implements PetRepository {
@In
private SessionFactory sessionFactory;

public List<Pet> loadSomePets() {
Session session = null;
try {
session = sessionFactory.openSession();
return session.createQuery("from Pet").list();
} finally {
if (session != null) {
session.close();
}
}
}

... omitted methods ...
}


Now our repository is threadsafe again, and can thus safely be used in the application!

1 comment:

  1. [...] yesterdays blog I wrote about repositories in a Seam application that have application scope. In this blog I also [...]

    ReplyDelete