001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */
004: package com.tcspring;
005:
006: import org.apache.commons.logging.Log;
007: import org.apache.commons.logging.LogFactory;
008:
009: import com.tc.aspectwerkz.joinpoint.StaticJoinPoint;
010:
011: import javax.servlet.http.HttpSession;
012: import javax.servlet.http.HttpSessionBindingEvent;
013: import javax.servlet.http.HttpSessionBindingListener;
014:
015: /**
016: * Clustering aspects related to http session.
017: *
018: * @author Eugene Kuleshov
019: */
020: public class SessionProtocol {
021:
022: private final transient Log logger = LogFactory.getLog(getClass());
023:
024: private static final String TC_SESSION_SCOPE_CONVERSATION_ID = "_TC_SESSION_SCOPE_CONVERSATION_ID";
025:
026: /**
027: * Uses clustered http session to store session id, to protect from the case when session id is
028: * different between nodes.
029: *
030: * cflow(execution(* org.springframework.web.context.request.SessionScope.getConversationId()))
031: * AND withincode(* org.springframework.web.context.request.ServletRequestAttributes.getSessionId())
032: * AND call(* javax.servlet.http.HttpSession+.getId())
033: * AND target(session)
034: *
035: * @see org.springframework.web.context.request.SessionScope#getConversationId()
036: * @see org.springframework.web.context.request.ServletRequestAttributes#getSessionId()
037: */
038: public Object clusterSessionId(StaticJoinPoint jp,
039: HttpSession session) throws Throwable {
040: Object conversationId = session
041: .getAttribute(TC_SESSION_SCOPE_CONVERSATION_ID);
042: if (conversationId == null) {
043: conversationId = jp.proceed();
044: session.setAttribute(TC_SESSION_SCOPE_CONVERSATION_ID,
045: conversationId);
046: }
047:
048: return conversationId;
049: }
050:
051: private ThreadLocal cflowCallback = new ThreadLocal();
052:
053: /**
054: * @see org.springframework.web.context.request.ServletRequestAttributes.registerSessionDestructionCallback(String name, Runnable callback)
055: */
056: public Object captureDestructionCallback(StaticJoinPoint jp,
057: String name, Runnable callback) throws Throwable {
058: if (callback instanceof ScopedBeanDestructionCallBack) {
059: try {
060: cflowCallback.set(callback);
061: return jp.proceed();
062: } finally {
063: cflowCallback.set(null);
064: }
065: }
066: return jp.proceed();
067: }
068:
069: /**
070: * @see org.springframework.web.context.request.ServletRequestAttributes.registerSessionDestructionCallback(String name, Runnable callback)
071: * @see javax.servlet.http.HttpSession#setAttribute(String name, Object value)
072: */
073: public Object virtualizeSessionDestructionListener(
074: StaticJoinPoint jp, String name, HttpSession session)
075: throws Throwable {
076: Object oldAttribute = session.getAttribute(name);
077: ScopedBeanDestructionCallBack callback = (ScopedBeanDestructionCallBack) cflowCallback
078: .get();
079: if (callback != null) {
080: if (oldAttribute == null) {
081: DestructionCallbackBindingListener listener = new DestructionCallbackBindingListener(
082: callback, ""
083: + System.identityHashCode(callback));
084: logger.info("registering destruction callback for "
085: + name + " callback:" + callback
086: + " listener:" + listener);
087: session.setAttribute(name, listener);
088: return null;
089: }
090:
091: if (oldAttribute instanceof DestructionCallbackBindingListener) {
092: logger.info("reinitializing destruction callback for "
093: + name + " callback:" + callback
094: + " oldattr:"
095: + oldAttribute.getClass().getName());
096: ((DestructionCallbackBindingListener) oldAttribute)
097: .setScopedBeanDestructionCallBack(callback);
098: return null;
099: }
100: }
101:
102: return jp.proceed();
103: }
104:
105: /**
106: * Adapter that implements the Servlet 2.3 HttpSessionBindingListener
107: * interface, wrapping a request destruction callback.
108: */
109: private static class DestructionCallbackBindingListener implements
110: HttpSessionBindingListener {
111:
112: private transient ScopedBeanDestructionCallBack destructionCallback;
113: private String name;
114:
115: public DestructionCallbackBindingListener(
116: ScopedBeanDestructionCallBack destructionCallback,
117: String name) {
118: this .destructionCallback = destructionCallback;
119: this .name = name;
120: }
121:
122: public void setScopedBeanDestructionCallBack(
123: ScopedBeanDestructionCallBack destructionCallback) {
124: LogFactory.getLog(getClass()).info(
125: "destructionCallback: " + destructionCallback);
126: this .destructionCallback = destructionCallback;
127: }
128:
129: public ScopedBeanDestructionCallBack getScopedBeanDestructionCallBack() {
130: return destructionCallback;
131: }
132:
133: public void valueBound(HttpSessionBindingEvent event) {
134: }
135:
136: public void valueUnbound(HttpSessionBindingEvent event) {
137: if (this .destructionCallback == null) {
138: // TODO destructionCallback can be nulled out by the memory manager. we need to find way to recreate it
139: LogFactory.getLog(getClass()).info(
140: "destructionCallback is NULL " + this );
141: } else {
142: this .destructionCallback.run();
143: }
144: }
145:
146: public String toString() {
147: return name;
148: }
149: }
150:
151: }
|