To fix this, a file called WEB-INF/zk.xml must be created with the following contents:
<zk><</zk>listener><
description>ThreadLocal Variables Synchronizer</description><
listener-class>org.zkoss.zkplus.util.ThreadLocalListener</listener-class></listener><preference><
name>ThreadLocal</name><
value>org.springframework.transaction.support.TransactionSynchronizationManager=resources,synchronizations,currentTransactionName,currentTransactionReadOnly,actualTransactionActive;
org.springframework.orm.hibernate3.SessionFactoryUtils=deferredCloseHolder;
org.springframework.transaction.interceptor.TransactionAspectSupport=transactionInfoHolder;</value></preference>
The problem is that the Spring OpenSessionInViewFilter opens a session, and binds it to the request's thread. However, ZK uses a multi-threaded event driven approach, so the actual processing happens in a new thread that gets kicked off by the ZK servlet - this breaks the connection to the open session. Adding the above listener copies the ThreadLocal variables with the session etc from the request thread's context into the event thread. As you can see, more prefs can be added to copy other objects from the ThreadLocal context.
I had to inspect a lot of code to fix this, turns out it was documented in great detail all along (but sort of all over the place):
- The 'old' way of fixing the issue, and only sufficient if you use Hibernate without Spring transactions: http://zkoss.org/javadoc/2.4.1/zkplus/org/zkoss/zkplus/hibernate/HibernateSessionContextListener.html
- The 'new' way of fixing it, but the example only shows how to emulate the above, so again not sufficient for Spring transactions: http://www.zkoss.org/smalltalks/zk2.4.1/zk2.4.1.dsp
- The javadoc with the 'new' way, surprisingly enough with exactly the example prefs I was looking for: http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ThreadLocal.html
Another possible solution is to make the ZK servlet perform all processing within its own thread, this was a new feature in ZK 2.4. I haven't tried this though and it looks like this may be dangerous, since the thread will block the entire servlet if it blocks for any reason. To enable this, the following is required in the zk.xml:
<system-config>
<disable-event-thread/></system-config>