001: /* SpringTransactionSynchronizationListener.java
002:
003: {{IS_NOTE
004: Purpose:
005:
006: Description:
007:
008: History:
009: Tue Sep 15 13:55:11 2006, Created by henrichen
010: }}IS_NOTE
011:
012: Copyright (C) 2006 Potix Corporation. All Rights Reserved.
013:
014: {{IS_RIGHT
015: This program is distributed under GPL Version 2.0 in the hope that
016: it will be useful, but WITHOUT ANY WARRANTY.
017: }}IS_RIGHT
018: */
019: package org.zkoss.zkplus.spring;
020:
021: import org.zkoss.zk.ui.Component;
022: import org.zkoss.zk.ui.UiException;
023: import org.zkoss.zk.ui.Execution;
024: import org.zkoss.zk.ui.Executions;
025: import org.zkoss.zk.ui.event.Event;
026: import org.zkoss.zk.ui.event.EventThreadInit;
027: import org.zkoss.zk.ui.event.EventThreadResume;
028: import org.zkoss.zk.ui.event.EventThreadCleanup;
029: import org.zkoss.lang.Classes;
030: import org.zkoss.lang.ThreadLocals;
031: import org.zkoss.lang.SystemException;
032: import org.zkoss.util.logging.Log;
033:
034: import java.util.Map;
035: import java.util.HashMap;
036: import java.lang.reflect.Field;
037:
038: import java.util.List;
039:
040: /**
041: * <p>Listener to make sure each ZK thread got the same ThreadLocal value of the
042: * spring's org.springframework.transaction.support.TransactionSynchronizationManager;
043: * especially those thread bound resources.
044: * </p>
045: * <p>
046: * This listener is used with Spring Framework (version 1.2.8+) "thread" bounded
047: * resources.
048: *
049: * <pre><code>
050: * <listener>
051: * <description>Spring TransactionSynchronizationManager handler</description>
052: * <listener-class>org.zkoss.zkplus.spring.SpringTransactionSynchronizationListener</listener-class>
053: * </listener>
054: * </code></pre>
055: * </p>
056: *
057: * @author henrichen
058: */
059: public class SpringTransactionSynchronizationListener implements
060: EventThreadInit, EventThreadCleanup, EventThreadResume {
061: private static final Log log = Log
062: .lookup(SpringTransactionSynchronizationListener.class);
063:
064: private Object[] _threadLocals = null;
065:
066: //-- EventThreadInit --//
067: public void prepare(Component comp, Event evt) {
068: getThreadLocals(); //get from servlet thread's ThreadLocal
069: }
070:
071: public boolean init(Component comp, Event evt) {
072: setThreadLocals(); //copy to event thread's ThreadLocal
073: return true;
074: }
075:
076: //-- EventThreadCleanup --//
077: public void cleanup(Component comp, Event evt, List errs) {
078: getThreadLocals(); //get from event thread's ThreadLocal
079: //we don't handle the exception since the ZK engine will throw it again!
080: }
081:
082: public void complete(Component comp, Event evt) {
083: setThreadLocals(); //copy to servlet thread's ThreadLocal
084: }
085:
086: //-- EventThreadResume --//
087: public void beforeResume(Component comp, Event evt) {
088: getThreadLocals(); //get from servlet thread's ThreadLocal
089: }
090:
091: public void afterResume(Component comp, Event evt) {
092: setThreadLocals(); //copy to event thread's ThreadLocal
093: }
094:
095: public void abortResume(Component comp, Event evt) {
096: //do nothing
097: }
098:
099: //-- utilities --//
100: private void getThreadLocals() {
101: try {
102: Class cls = Classes
103: .forNameByThread("org.springframework.transaction.support.TransactionSynchronizationManager");
104:
105: _threadLocals = new Object[7];
106: _threadLocals[0] = getThreadLocal(cls, "resources").get();
107: _threadLocals[1] = getThreadLocal(cls, "synchronizations")
108: .get();
109: _threadLocals[2] = getThreadLocal(cls,
110: "currentTransactionName").get();
111: _threadLocals[3] = getThreadLocal(cls,
112: "currentTransactionReadOnly").get();
113: _threadLocals[4] = getThreadLocal(cls,
114: "actualTransactionActive").get();
115:
116: //20070907, Henri Chen: bug 1785457, hibernate3 might not used
117: try {
118: cls = Classes
119: .forNameByThread("org.springframework.orm.hibernate3.SessionFactoryUtils");
120: _threadLocals[5] = getThreadLocal(cls,
121: "deferredCloseHolder").get();
122: } catch (ClassNotFoundException ex) {
123: //ignore if hibernate 3 is not used.
124: }
125:
126: cls = Classes
127: .forNameByThread("org.springframework.transaction.interceptor.TransactionAspectSupport");
128:
129: //Spring 1.2.8 and Spring 2.0.x, the ThreadLocal field name has changed, default use 2.0.x
130: //2.0.x transactionInfoHolder
131: //1.2.8 currentTransactionInfo
132: try {
133: _threadLocals[6] = getThreadLocal(cls,
134: "transactionInfoHolder").get();
135: } catch (SystemException ex) {
136: if (ex.getCause() instanceof NoSuchFieldException) {
137: _threadLocals[6] = getThreadLocal(cls,
138: "currentTransactionInfo").get();
139: } else {
140: throw ex;
141: }
142: }
143: } catch (ClassNotFoundException ex) {
144: throw UiException.Aide.wrap(ex);
145: }
146: }
147:
148: private void setThreadLocals() {
149: if (_threadLocals != null) {
150: try {
151: Class cls = Classes
152: .forNameByThread("org.springframework.transaction.support.TransactionSynchronizationManager");
153:
154: getThreadLocal(cls, "resources").set(_threadLocals[0]);
155: getThreadLocal(cls, "synchronizations").set(
156: _threadLocals[1]);
157: getThreadLocal(cls, "currentTransactionName").set(
158: _threadLocals[2]);
159: getThreadLocal(cls, "currentTransactionReadOnly").set(
160: _threadLocals[3]);
161: getThreadLocal(cls, "actualTransactionActive").set(
162: _threadLocals[4]);
163:
164: //20070907, Henri Chen: bug 1785457, hibernate3 might not used
165: try {
166: cls = Classes
167: .forNameByThread("org.springframework.orm.hibernate3.SessionFactoryUtils");
168: getThreadLocal(cls, "deferredCloseHolder").set(
169: _threadLocals[5]);
170: } catch (ClassNotFoundException ex) {
171: //ignore if hibernate 3 is not used.
172: }
173:
174: cls = Classes
175: .forNameByThread("org.springframework.transaction.interceptor.TransactionAspectSupport");
176: //Spring 1.2.8 and Spring 2.0.x, the ThreadLocal field name has changed, default use 2.0.x
177: //2.0.x transactionInfoHolder
178: //1.2.8 currentTransactionInfo
179: try {
180: getThreadLocal(cls, "transactionInfoHolder").set(
181: _threadLocals[6]);
182: } catch (SystemException ex) {
183: if (ex.getCause() instanceof NoSuchFieldException) {
184: getThreadLocal(cls, "currentTransactionInfo")
185: .set(_threadLocals[6]);
186: } else {
187: throw ex;
188: }
189: }
190:
191: _threadLocals = null;
192: } catch (ClassNotFoundException ex) {
193: throw UiException.Aide.wrap(ex);
194: }
195: }
196: }
197:
198: private ThreadLocal getThreadLocal(Class cls, String fldname) {
199: return ThreadLocals.getThreadLocal(cls, fldname);
200: }
201: }
|