KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > web > tomcat > tc5 > session > AttributeBasedClusteredSession


1 /*
2  * JBoss, the OpenSource WebOS
3  *
4  * Distributable under LGPL license.
5  * See terms of license at gnu.org.
6  */

7 package org.jboss.web.tomcat.tc5.session;
8
9 import org.jboss.metadata.WebMetaData;
10
11 import java.beans.PropertyChangeSupport JavaDoc;
12 import java.io.Serializable JavaDoc;
13 import java.security.Principal JavaDoc;
14 import java.util.*;
15
16 /**
17  * Implementation of a clustered session for the JBossCacheManager. The replication granularity
18  * level is attribute based; that is, we replicate only the dirty attributes.
19  * We use JBossCache for our internal, deplicated data store.
20  * The internal structure is like in JBossCache:
21  * <pre>
22  * /JSESSION
23  * /web_app_path (path + session id is unique)
24  * /id Map(id, session)
25  * (VERSION_KEY, version) // Used for version tracking. version is an Integer.
26  * /ATTRIBUTE Map(attr_key, value)
27  * </pre>
28  * <p/>
29  * Note that the isolation level of the cache dictates the
30  * concurrency behavior. Also note that session and its associated attribtues are stored in different nodes.
31  * This will be ok since cache will take care of concurrency. When replicating, we will need to replicate both
32  * session and its attributes.</p>
33  *
34  * @author Ben Wang
35  * @version $Revision: 1.3.2.5 $
36  */

37 class AttributeBasedClusteredSession
38    extends ClusteredSession implements Serializable JavaDoc
39 {
40    static final long serialVersionUID = -5625209785550936713L;
41    /**
42     * Descriptive information describing this Session implementation.
43     */

44    protected static final String JavaDoc info = "AttributeBasedClusteredSession/1.0";
45
46    private transient boolean isSessionModifiedSinceLastSave_;
47    private transient JBossCacheService proxy_;
48    // Transient map to store attr changes for replication.
49
private transient Map attrModifiedMap_;
50    // Note that the removed attr is intentionally stored in a map instead of set so it is faster to lookup and remove.
51
private transient Map attrRemovedMap_;
52    private static int REMOVE = 0; // Used to track attribute changes
53
private static int MODIFY = 1;
54    private transient Map attributes_;
55
56    public AttributeBasedClusteredSession(AbstractJBossManager manager)
57    {
58       super(manager);
59       initAfterLoad(manager);
60    }
61
62    /**
63     * Initialize fields marked as transient after loading this session
64     * from the distributed store
65     *
66     * @param manager the manager for this session
67     */

68    public void initAfterLoad(AbstractJBossManager manager)
69    {
70       // Use proxy to determine if this is first time session retrieval.
71
if (this.proxy_ == null)
72       {
73          setManager(manager);
74          listeners = new ArrayList();
75          notes = new HashMap();
76          support = new PropertyChangeSupport JavaDoc(this);
77          expiring = false;
78          attributes_ = Collections.synchronizedMap(new HashMap());
79          attrModifiedMap_ = new HashMap();
80          attrRemovedMap_ = new HashMap();
81
82          // cache invalidate purpose
83
isOutdated = false;
84
85          proxy_ = ((JBossCacheManager) manager).getCacheService();
86
87          // still null???
88
if (proxy_ == null)
89          {
90             throw new RuntimeException JavaDoc("SessionBasedClusteredSession: Cache service is null.");
91          }
92
93          // Notify all attributes of type HttpSessionActivationListener (SRV 7.7.2)
94
this.activate();
95       }
96       // Since attribute maps are transient, we will need to populate it from the underlying store.
97
populateAttributes();
98    }
99
100    /**
101     * Populate the attributes stored in the distributed store to local transient ones.
102     */

103    protected void populateAttributes()
104    {
105       Map map = proxy_.getAttributes(id);
106       if (map.size() != 0) attributes_ = map;
107    }
108
109    // ----------------------------------------------------- Session Properties
110
/**
111     * Set the creation time for this session. This method is called by the
112     * Manager when an existing Session instance is reused.
113     *
114     * @param time The new creation time
115     */

116    public void setCreationTime(long time)
117    {
118       super.setCreationTime(time);
119       sessionIsDirty();
120    }
121
122
123    /**
124     * Set the authenticated Principal that is associated with this Session.
125     * This provides an <code>Authenticator</code> with a means to cache a
126     * previously authenticated Principal, and avoid potentially expensive
127     * <code>Realm.authenticate()</code> calls on every request.
128     *
129     * @param principal The new Principal, or <code>null</code> if none
130     */

131    public void setPrincipal(Principal JavaDoc principal)
132    {
133       Principal JavaDoc oldPrincipal = this.principal;
134       this.principal = principal;
135       support.firePropertyChange("principal", oldPrincipal, this.principal);
136
137       if ((oldPrincipal != null && !oldPrincipal.equals(principal)) ||
138          (oldPrincipal == null && principal != null))
139          sessionIsDirty();
140
141    }
142
143    // ------------------------------------------------- Session Public Methods
144
/**
145     * Return a string representation of this object.
146     */

147    public String JavaDoc toString()
148    {
149
150       StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
151       sb.append("AttributeBasedClusteredSession[");
152       sb.append(id);
153       sb.append("]");
154       return (sb.toString());
155
156    }
157
158    /**
159     * Start to process my local attribute changes to the replication layer.
160     */

161    public synchronized void processSessionRepl()
162    {
163       if (!isSessionDirty())
164       {
165          if (log.isDebugEnabled())
166          {
167             log.debug("processSessionRepl(): session is not dirty. No need to replicate.");
168          }
169          return;
170       }
171       // Replicate this first. Note this will be lightweight since many of the attributes are transient.
172
// And also without attributes
173
if (log.isDebugEnabled())
174       {
175          log.debug("processSessionRepl(): session is dirty. Will increment version from: " +
176                  getVersion() + " and replicate.");
177       }
178       this.incrementVersion();
179       proxy_.putSession(id, this);
180
181       // Go thru the attribute change list
182
// Go thru the remove attr list first
183
{
184          Set set = attrModifiedMap_.keySet();
185          Iterator it = set.iterator();
186          while (it.hasNext())
187          {
188             Object JavaDoc key = it.next();
189             proxy_.putAttribute(id, (String JavaDoc) key, attrModifiedMap_.get(key));
190          }
191       }
192
193       // Go thru the remove attr list
194
{
195          Set set = attrRemovedMap_.keySet();
196          Iterator it = set.iterator();
197          while (it.hasNext())
198          {
199             Object JavaDoc key = it.next();
200             proxy_.removeAttribute(id, (String JavaDoc) key);
201          }
202       }
203
204       clearAttrChangedMap();
205       isSessionModifiedSinceLastSave_ = false;
206    }
207
208    public void removeMyself()
209    {
210       // This is a shortcut to remove session and it's child attributes.
211
proxy_.removeSession(id);
212       if (attributes_ != null)
213          attributes_.clear();
214    }
215
216    public void removeMyselfLocal()
217    {
218       // Need to evict attribute first before session to clean up everything.
219
proxy_.removeAttributeLocal(id);
220       proxy_.removeSessionLocal(id);
221       if (attributes_ != null)
222          attributes_.clear();
223    }
224
225    // ----------------------------------------------HttpSession Public Methods
226

227    public void access()
228    {
229       super.access();
230       // If we do not use the local cache the session is dirty
231
// after every access.
232
if (invalidationPolicy == WebMetaData.SESSION_INVALIDATE_ACCESS)
233       {
234          this.sessionIsDirty();
235       }
236    }
237
238    // ------------------------------------------------ JBoss internal abstract method
239
protected Object JavaDoc getJBossInternalAttribute(String JavaDoc name)
240    {
241       // Check the accumulate change maps first.
242
Object JavaDoc result = null;
243       // TODO Need to check if underlying store is dirty. This will be done with listener in the future.
244
result = attributes_.get(name);
245
246       if (result != null)
247       {
248          int invalidationPolicy = ((AbstractJBossManager) this.manager).getInvalidateSessionPolicy();
249
250          if (invalidationPolicy == WebMetaData.SESSION_INVALIDATE_SET_AND_GET)
251          {
252             attributeChanged(name, result, MODIFY);
253          }
254          else if (invalidationPolicy == WebMetaData.SESSION_INVALIDATE_SET_AND_NON_PRIMITIVE_GET)
255          {
256             if (!(result instanceof String JavaDoc ||
257                result instanceof Integer JavaDoc ||
258                result instanceof Long JavaDoc ||
259                result instanceof Byte JavaDoc ||
260                result instanceof Short JavaDoc ||
261                result instanceof Float JavaDoc ||
262                result instanceof Double JavaDoc ||
263                result instanceof Character JavaDoc ||
264                result instanceof Boolean JavaDoc)
265             )
266             {
267                attributeChanged(name, result, MODIFY);
268             }
269          }
270       }
271       return result;
272    }
273
274    protected Object JavaDoc removeJBossInternalAttribute(String JavaDoc name)
275    {
276       Object JavaDoc result = attributes_.remove(name);
277       attributeChanged(name, result, REMOVE);
278       return result;
279    }
280
281    protected Map getJBossInternalAttributes()
282    {
283       return attributes_;
284    }
285
286    protected Set getJBossInternalKeys()
287    {
288       return attributes_.keySet();
289    }
290
291    /**
292     * Method inherited from Tomcat. Return zero-length based string if not found.
293     */

294    protected String JavaDoc[] keys()
295    {
296       return ((String JavaDoc[]) getJBossInternalKeys().toArray(EMPTY_ARRAY));
297    }
298
299    protected Object JavaDoc setJBossInternalAttribute(String JavaDoc key, Object JavaDoc value)
300    {
301       attributes_.put(key, value);
302       attributeChanged(key, value, MODIFY);
303       return value;
304    }
305
306    protected void sessionIsDirty()
307    {
308       // Session is dirty
309
isSessionModifiedSinceLastSave_ = true;
310    }
311
312    public boolean isSessionDirty()
313    {
314       // Need to check if the attr change map is empty as well??
315
return isSessionModifiedSinceLastSave_;
316    }
317
318    protected synchronized void attributeChanged(Object JavaDoc key, Object JavaDoc value, int op)
319    {
320       if (op == MODIFY)
321       {
322          if (attrRemovedMap_.containsKey(key))
323          {
324             attrRemovedMap_.remove(key);
325          }
326          attrModifiedMap_.put(key, value);
327       }
328       else if (op == REMOVE)
329       {
330          if (attrModifiedMap_.containsKey(key))
331          {
332             attrModifiedMap_.remove(key);
333          }
334          attrRemovedMap_.put(key, value);
335       }
336       sessionIsDirty();
337    }
338
339    protected synchronized void clearAttrChangedMap()
340    {
341       attrRemovedMap_.clear();
342       attrModifiedMap_.clear();
343    }
344 }
345
Popular Tags