Cleanup Stale Hibernate Session in Tomcat

Problem:
When Hibernate is configured to use thread based session management, a thread may get stuck (unable to unbind) with a closed session under certain condition, therefore all subsequent incoming HTTP calls assigned to such a thread would fail until Tomcat is restarted. At problem time, you may see exceptions like “org.hibernate.exception.GenericJDBCException: Cannot release connection.”

Cause:
The typical pseudo code (either standalone or inserted into servlet filter) that can suffer this problem is

Session session = getSessionFactory().getCurrentSession();
try{
  session.getTransaction().commit();
}catch(Throwable t){
  session.getTransaction().rollback();
}finally{
  if(session.isOpen()){
    session.close();
  }
}

When the commit succeeded, Hibernate will unbind the session from the thread and close the session. However if there is an exception occurred during commit, Hibernate may not able to unbind before it closes the session. The related Hibernate pseudo code (see ConnectionManager, JDBCContext, JDBCTransaction, SessionImpl and TreadLocalSessionContext for detail) is

try{
     commitTransaction();
     unbindSessionFromThread()
}finally{
     closeSession()
}

Solution:
Manually unbind the session from the thread is doable but not recommended. In Tomcat, you can use its DBCP or JDBC connection pool configuration to remove stale sessions therefore obtain new sessions on next HTTP call. The full document can be found here, and a sample configure using context.xml is followed below:

<Resource name="jdbc/myDb" auth="Container" type="javax.sql.DataSource"
      maxActive="100" maxIdle="30" maxWait="10000"
      validationQuery="SELECT 1"
      username="xxxxxx" password="xxxxxxx"
      driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"
      url="jdbc:sqlserver://127.0.0.1:1433;DatabaseName=myDb"
      factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
      logAbandoned="true"
      removeAbandoned="true"
      removeAbandonedTimeout="600"
      testOnBorrow="true"
	  />

You may need to copy your jdbc driver jar (i.e. sqljdbc4-4.0.2206.100.jar) into the tomcat lib directory before starting it as the JDBC driver and the DataSourceFactory need to be loaded by the same class loader.

Advertisements
This entry was posted in Database, Java and tagged , , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s