Tuesday, October 26, 2010

Wednesday, February 17, 2010

[Spring] CustomDateEditor and RedirectOnPause

Now, I'm having a similar custom property editor issue as in my previous post.
I've registered the following CustomDateEditor to my property editor registrar.

<bean class="org.springframework.beans.propertyeditors.CustomDateEditor" id="customDateEditor">
  <constructor-arg>
    <bean class="java.text.SimpleDateFormat"><constructor-arg value="dd.MM.yyyy HH:mm"/></bean>
  </constructor-arg>
  <constructor-arg value="true"/>
</bean>

Entering date's using the configured format works but when the form gets reloaded the date's toString() method is called instead of the getAsText method of the property editor.

This is due to redirectOnPause, which requires 'setupForm' in each view as a render-action.

[Spring] Custom PropertyEditors' and <form:select>

I wanted to create a select box for a property of my formbacking object using a custom property editor like in this example. Everything seemed to work fine...
If I selected user and submitted the form, the user object was correctly set in the formbacking object. But whenever the page was reloaded (e.g. due to a validation error in a different field of that form), the select box was not correctly set to the previously chosen user.
After a lot of debuggung and googling I found a post in the SpringSource forum explaining that Spring uses the equals()-method to set the select box to the correct value.
Unfortunately, I hadn't overridden the equals()-method and I was not using the same instances of the user class to fill the select box (via its items attribute) and to set the property of my formbacking object in my custom property editor (via super.setValue(...)).

So, If you ever have the problem of a select box not being set to the correct value, do
  1. override the equals()-method in the class of the property you want to set using or
  2. assure that you are using the same instances of the property's class to fill your as you use to set the value in your custom property editor

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();
}