Wednesday, February 10, 2010

[Hibernate] Thread Local Session pattern in web applications

When using the Thread Local Session pattern in a web application, I got the following exception:
ERROR : [...HibernateSessionFactory  currentSession]  - Error Creating sessionFactory org.hibernate.HibernateException: Could not parse configuration: hibernate.cfg.xml
            at org.hibernate.cfg.Configuration.doConfigure(Configuration.java:1376)
            at org.hibernate.cfg.Configuration.configure(Configuration.java:1310)
            at ...HibernateSessionFactory.currentSession(HibernateSessionFactory.java:60)
[...]
Caused by: org.dom4j.DocumentException: FWK005 parse may not be called while parsing. Nested exception: FWK005 parse may not be called while parsing.
            at org.dom4j.io.SAXReader.read(SAXReader.java:484)
            at org.hibernate.cfg.Configuration.doConfigure(Configuration.java:1366)
            ... 71 more
It occured if two concurrent requests were sent to the server right after server startup (when the session factory was not initialized yet). Here is the snippet of my HibernateSessionFactory
[...]
private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";

/** Holds a single instance of Session */
private static final ThreadLocal threadLocal = new ThreadLocal();

/** The single instance of hibernate configuration */
private static final Configuration cfg = new Configuration();

/** The single instance of hibernate SessionFactory */
private staticSessionFactory sessionFactory;
/**
 * Returns the ThreadLocal Session instance. Lazy initialize the SessionFactory if needed.
 *
 * @return Session
 * @throws HibernateException
 */
public static Session currentSession() throws HibernateException {
   Session s = (Session) session.get();
   if (s == null) {
     SessionFactory sf = getSessionFactory();
     s = sf.openSession();
     session.set(s);
   }
   return s;
}

/**
 * Creating a session factory , if not already loaded
 */
private static SessionFactory getSessionFactory() throws HibernateException {
   try {
     if (sessionFactory == null){
       cfg.configure(CONFIG_FILE_LOCATION);
       sessionFactory = cfg .buildSessionFactory();
     }
     return sessionFactory;
   } catch (Exception e) {
     throw new HibernateException("Error getting Factory");
   }
}
[...]

The bold line was the culprit, which apparently cannot be called concurrently. The solution was to make the creation of the session factory synchronized.
/**
 * Creating a session factory , if not already loaded
 */
private static SessionFactory getSessionFactory() throws HibernateException {
   try {
     if (sessionFactory == null){
      createSessionFactory();
     }
     return sessionFactory;
   } catch (Exception e) {
     throw new HibernateException("Error getting Factory");
   }
}

private static synchronized void createSessionFactory() {
  cfg.configure(CONFIG_FILE_LOCATION);
  sessionFactory = cfg .buildSessionFactory();
}

No comments:

Post a Comment