001: /* HibernateSessionContextListener.java
002:
003: {{IS_NOTE
004: Purpose:
005:
006: Description:
007:
008: History:
009: Tue Sep 11 12: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.hibernate;
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.util.ExecutionInit;
026: import org.zkoss.zk.ui.util.ExecutionCleanup;
027: import org.zkoss.zk.ui.event.Event;
028: import org.zkoss.zk.ui.event.EventThreadInit;
029: import org.zkoss.zk.ui.event.EventThreadResume;
030: import org.zkoss.lang.Classes;
031: import org.zkoss.lang.ThreadLocals;
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 hibernat session context;
042: * used with Hibernate's "thread" session context (org.hibernate.context.ThreadLocalSessionContext).
043: * </p>
044: * <p>
045: * This listener is used with Hibernate's (version 3.1+) "thread" session context.
046: * That is, when you specify </p>
047: * <pre><code>
048: * hibernate.current_session_context_class = thread
049: * </code></pre>
050: *
051: * <p>then you have to add following lines in application's WEB-INF/zk.xml:</p>
052: * <pre><code>
053: * <listener>
054: * <description>Hibernate thread session context management</description>
055: * <listener-class>org.zkoss.zkplus.hibernate.HibernateSessionContextListener</listener-class>
056: * </listener>
057: * </code></pre>
058: *
059: * @author henrichen
060: */
061: public class HibernateSessionContextListener implements ExecutionInit,
062: ExecutionCleanup, EventThreadInit, EventThreadResume {
063: private static final Log log = Log
064: .lookup(HibernateSessionContextListener.class);
065: private static final String HIBERNATE_SESSION_MAP = "org.zkoss.zkplus.hibernate.SessionMap";
066: private static final Object SOMETHING = new Object();
067:
068: //-- ExecutionInit --//
069: public void init(Execution exec, Execution parent) {
070: if (parent == null) { //root execution
071: //always prepare a ThreadLocal SessionMap in Execution attribute
072: Map map = getSessionMap();
073: if (map == null) {
074: map = new HashMap();
075: setSessionMap(map); //copy to servlet thread's ThreadLocal
076: }
077: exec.setAttribute(HIBERNATE_SESSION_MAP, map); // store in Execution attribute
078:
079: //20060912, henrichen: tricky. Stuff something into session map to
080: //prevent the map from being removed from context ThreadLocal by the
081: //ThreadLocalSessionContext#unbind() when it is empty.
082: map.put(SOMETHING, null);
083: }
084: }
085:
086: //-- ExecutionCleanup --//
087: public void cleanup(Execution exec, Execution parent, List errs) {
088: if (parent == null) { //root execution
089: Map map = getSessionMap();
090: if (map != null) {
091: //20060912, henrichen: tricky. Remove the previously stuffed
092: //something (when ExecutuionInit#init() is called) from
093: //session map to make the map possible to be removed by the
094: //ThreadLocalSessionContext#unbind() when it is empty.
095: map.remove(SOMETHING);
096: }
097: exec.removeAttribute(HIBERNATE_SESSION_MAP);
098: }
099: }
100:
101: //-- EventThreadInit --//
102: public void prepare(Component comp, Event evt) {
103: //do nothing
104: }
105:
106: public boolean init(Component comp, Event evt) {
107: //Copy SessionMap stored in Execution attribute into event's ThreadLocal
108: Map map = (Map) Executions.getCurrent().getAttribute(
109: HIBERNATE_SESSION_MAP);
110: setSessionMap(map); //copy to event thread's ThreadLocal
111: return true;
112: }
113:
114: //-- EventThreadResume --//
115: public void beforeResume(Component comp, Event evt) {
116: //do nothing
117: }
118:
119: public void afterResume(Component comp, Event evt) {
120: //always keep the prepared SessionMap in event's ThreadLocal
121: Map map = (Map) Executions.getCurrent().getAttribute(
122: HIBERNATE_SESSION_MAP);
123: setSessionMap(map); //copy to event thread's ThreadLocal
124: }
125:
126: public void abortResume(Component comp, Event evt) {
127: //do nothing
128: }
129:
130: //-- utilities --//
131: private void setSessionMap(Map map) {
132: getContextThreadLocal().set(map);
133: }
134:
135: private Map getSessionMap() {
136: return (Map) getContextThreadLocal().get();
137: }
138:
139: private ThreadLocal getContextThreadLocal() {
140: return ThreadLocals.getThreadLocal(
141: "org.hibernate.context.ThreadLocalSessionContext",
142: "context");
143: }
144: }
|