KickJava   Java API By Example, From Geeks To Geeks.

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


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

7
8 package org.jboss.web.tomcat.tc5.session;
9
10 import java.io.IOException JavaDoc;
11 import java.util.*;
12 import javax.management.ObjectName JavaDoc;
13
14 import org.jboss.cache.CacheException;
15 import org.jboss.cache.Fqn;
16 import org.jboss.cache.TreeCache;
17 import org.jboss.cache.TreeCacheListener;
18 import org.jboss.cache.TreeCacheMBean;
19 import org.jboss.cache.lock.TimeoutException;
20 import org.jboss.invocation.MarshalledValue;
21 import org.jboss.logging.Logger;
22 import org.jboss.mx.util.MBeanProxyExt;
23 import org.jboss.web.tomcat.tc5.Tomcat5;
24 import org.jgroups.View;
25
26 /**
27  * A wrapper class to JBossCache. This is currently needed to handle various operations such as
28  * <ul>
29  * <li>Using MarshalledValue to replace Serializable used inside different web app class loader context.</li>
30  * <li>Stripping out any id string after ".". This is to handle the JK failover properly with
31  * Tomcat JvmRoute.</li>
32  * <li>Cache exception retry.</li>
33  * <li>Helper APIS.</li>
34  * </ul>
35  */

36 public class JBossCacheService implements TreeCacheListener
37 {
38    private TreeCacheMBean proxy_;
39    private ObjectName JavaDoc cacheServiceName_;
40    protected static Logger log_ = Logger.getLogger(JBossCacheService.class);
41    public static final String JavaDoc SESSION = "JSESSION";
42    public static final String JavaDoc ATTRIBUTE = "ATTRIBUTE";
43    public static final String JavaDoc KEY = "ATRR_KEY";
44    private static final int RETRY = 3;
45    // Needed for cache invalidation
46
private static final String JavaDoc VERSION_KEY = "VERSION";
47    // Needed to reconstruct the original session id, if JvmRoute is used.
48
private String JavaDoc jvmRoute_;
49    // web app path (JBAS-1367). Idea is web_app_path + session id is a unique pair.
50
private String JavaDoc webAppPath_;
51    // List of new session ids that is being replicated but not yet on the in-memory space. Note that it is not synchronzied
52
private List newSessionIDList_;
53
54    // Class loader for this web app.
55
private ClassLoader JavaDoc tcl_;
56    private JBossCacheManager manager_;
57
58    public JBossCacheService() throws ClusteringNotSupportedException
59    {
60       // Find JBossCacheService
61
try
62       {
63          cacheServiceName_ = new ObjectName JavaDoc(Tomcat5.DEFAULT_CACHE_NAME);
64          // Create Proxy-Object for this service
65
// TODO We can also consider using getInstance to get directly the reference handle.
66
proxy_ = (TreeCacheMBean) MBeanProxyExt.create(TreeCacheMBean.class, cacheServiceName_);
67          if (proxy_ == null)
68          {
69             throw new RuntimeException JavaDoc("JBossCacheService: locate null TomcatCacheMbean");
70          }
71
72          newSessionIDList_ = new ArrayList();
73       }
74       catch (Throwable JavaDoc e)
75       {
76          String JavaDoc str = cacheServiceName_ + " service to Tomcat clustering not found";
77          log_.error(str);
78          throw new ClusteringNotSupportedException(str);
79       }
80    }
81
82    public void start(ClassLoader JavaDoc tcl, JBossCacheManager manager)
83    {
84       tcl_ = tcl;
85       manager_ = manager;
86       jvmRoute_ = null;
87       proxy_.addTreeCacheListener(this);
88       String JavaDoc path = manager_.getContainer().getName();
89       if( path.length() == 0 || path.equals("/")) {
90          // If this is root.
91
webAppPath_ = "ROOT";
92       } if ( path.startsWith("/") ) {
93          webAppPath_ = path.substring(1);
94       } else {
95          webAppPath_ = path;
96       }
97       log_.debug("Old and new web app path are: " +path + ", " +webAppPath_);
98    }
99
100    public void stop()
101    {
102       proxy_.removeTreeCacheListener(this);
103    }
104
105    /**
106     * Find session ids for the whole Manager instance. Note that this also depends on the web app
107     * path setting.
108     *
109     * @return Empty list if not found.
110     */

111    public List findSessionIDs()
112    {
113       List ids = new ArrayList();
114       try {
115          // Construct the fqn
116
Object JavaDoc[] objs = new Object JavaDoc[]{SESSION, webAppPath_};
117          Fqn path = new Fqn( objs );
118          // locate children under each web app path
119
Set names = proxy_.getChildrenNames(path);
120
121          if( names == null ) return ids;
122          for(Iterator it = names.iterator(); it.hasNext();) {
123             Object JavaDoc id = it.next();
124             if(id==null) continue;
125             ids.add(id);
126             if(log_.isTraceEnabled()) {
127                log_.trace("Retrieving through web app path with fqn: " +path + " and session id: " +id);
128             }
129          }
130       } catch (CacheException e) {
131          e.printStackTrace();
132          throw new RuntimeException JavaDoc("JBossCacheService: exception occurred in cache getChildrenNames ... ", e);
133       }
134       return ids;
135    }
136
137    public Object JavaDoc getSession(String JavaDoc id)
138    {
139       String JavaDoc realId = stripJvmRoute(id);
140       Fqn fqn = getSessionFqn(realId);
141       return getUnMarshalledValue(_get(fqn, realId));
142    }
143
144    public void putSession(String JavaDoc id, Object JavaDoc session)
145    {
146       String JavaDoc realId = stripJvmRoute(id);
147       Fqn fqn = getSessionFqn(realId);
148 // TODO Need to investigate why this causes deadlock??
149
// Map map = new HashMap();
150
// map.put(realId, getMarshalledValue(session));
151
// Put in (VERSION_KEY, version) after the real put for cache invalidation
152
// map.put(VERSION_KEY, new Integer(((ClusteredSession)session).getVersion()));
153
// _put(fqn, map);
154
// This is not very efficient now since it generates two nodeAdded event.
155
_put(fqn, realId, getMarshalledValue(session));
156       // Put in (VERSION_KEY, version) after the real put for cache invalidation
157
_put(fqn, VERSION_KEY, new Integer JavaDoc(((ClusteredSession)session).getVersion()));
158    }
159
160    public Object JavaDoc removeSession(String JavaDoc id)
161    {
162       String JavaDoc realId = stripJvmRoute(id);
163       Fqn fqn = getSessionFqn(realId);
164       if (log_.isDebugEnabled())
165       {
166          log_.debug("Remove session from distributed store. Fqn: " + fqn);
167       }
168       Object JavaDoc obj = getUnMarshalledValue(_remove(fqn, realId));
169       // This needs to go after object removal to support correct cache invalidation.
170
// _remove(fqn, VERSION_KEY);
171
// Will just remove the whole tree now (include the fqn).
172
_remove(fqn);
173       return obj;
174    }
175
176    public void removeSessionLocal(String JavaDoc id)
177    {
178       String JavaDoc realId = stripJvmRoute(id);
179       Fqn fqn = getSessionFqn(realId);
180       if (log_.isDebugEnabled())
181       {
182          log_.debug("Remove session from my own distributed store only. Fqn: " + fqn);
183       }
184       _evict(fqn);
185    }
186
187    public boolean exists(String JavaDoc id)
188    {
189       String JavaDoc realId = stripJvmRoute(id);
190       Fqn fqn = getSessionFqn(realId);
191       return proxy_.exists(fqn);
192    }
193
194    public Object JavaDoc getAttribute(String JavaDoc id, String JavaDoc key)
195    {
196       String JavaDoc realId = stripJvmRoute(id);
197       Fqn fqn = getAttributeFqn(realId);
198       return getUnMarshalledValue(_get(fqn, key));
199    }
200
201    public Object JavaDoc putAttribute(String JavaDoc id, String JavaDoc key, Object JavaDoc value)
202    {
203       String JavaDoc realId = stripJvmRoute(id);
204       Fqn fqn = getAttributeFqn(realId);
205       return _put(fqn, key, getMarshalledValue(value));
206    }
207
208    public void putAttribute(String JavaDoc id, Map map)
209    {
210       String JavaDoc realId = stripJvmRoute(id);
211       Fqn fqn = getAttributeFqn(realId);
212       Set set = map.keySet();
213       Iterator it = set.iterator();
214       while (it.hasNext())
215       {
216          String JavaDoc key = (String JavaDoc) it.next();
217          _put(fqn, key, getMarshalledValue(map.get(key)));
218       }
219    }
220
221    public void removeAttributes(String JavaDoc id)
222    {
223       String JavaDoc realId = stripJvmRoute(id);
224       Fqn fqn = getAttributeFqn(realId);
225       _remove(fqn);
226    }
227
228    public Object JavaDoc removeAttribute(String JavaDoc id, String JavaDoc key)
229    {
230       String JavaDoc realId = stripJvmRoute(id);
231       Fqn fqn = getAttributeFqn(realId);
232       if (log_.isDebugEnabled())
233       {
234          log_.debug("Remove attribute from distributed store. Fqn: " + fqn + " key: " + key);
235       }
236       return getUnMarshalledValue(_remove(fqn, key));
237    }
238
239    public void removeAttributeLocal(String JavaDoc id)
240    {
241       String JavaDoc realId = stripJvmRoute(id);
242       Fqn fqn = getAttributeFqn(realId);
243       if (log_.isDebugEnabled())
244       {
245          log_.debug("Remove attributes from my own distributed store only. Fqn: " + fqn);
246       }
247       _evict(fqn);
248    }
249
250    /**
251     * Obtain the keys associated with this fqn. Note that it is not the fqn children.
252     *
253     * @return
254     */

255    public Set getAttributeKeys(String JavaDoc id)
256    {
257       if (id == null || id.length() == 0)
258          throw new IllegalArgumentException JavaDoc("JBossCacheService: id is either null or empty");
259
260       String JavaDoc realId = stripJvmRoute(id);
261       Fqn fqn = getAttributeFqn(realId);
262       try
263       {
264          return proxy_.getKeys(fqn);
265       }
266       catch (CacheException e)
267       {
268          e.printStackTrace();
269       }
270       return null;
271    }
272
273    /**
274     * Return all attributes associated with this session id. Return empty map if not found.
275     *
276     * @param id
277     * @return
278     */

279    public Map getAttributes(String JavaDoc id)
280    {
281       if (id == null || id.length() == 0) return new HashMap();
282       Set set = getAttributeKeys(id);
283       String JavaDoc realId = stripJvmRoute(id);
284       Fqn fqn = getAttributeFqn(realId);
285       Map map = new HashMap();
286       if(set == null) return map;
287       for (Iterator it = set.iterator(); it.hasNext();)
288       {
289          String JavaDoc key = (String JavaDoc) it.next();
290          Object JavaDoc value = getAttribute(id, key);
291          map.put(key, value);
292       }
293       return map;
294    }
295
296    /**
297     * Retrieve the new sessions in the underlying cache. Return size 0 if not found.
298     *
299     */

300    public List getNewSessionsInStore()
301    {
302       List list = new ArrayList();
303       synchronized(newSessionIDList_)
304       {
305          if(newSessionIDList_.size() != 0)
306          {
307             list.addAll(newSessionIDList_);
308             newSessionIDList_.clear();
309          }
310       }
311       return list;
312    }
313
314    /**
315     * Wrapper to embed retyr logic.
316     *
317     * @param fqn
318     * @param id
319     * @return
320     */

321    protected Object JavaDoc _get(Fqn fqn, String JavaDoc id)
322    {
323       Exception JavaDoc ex = null;
324       for (int i = 0; i < RETRY; i++)
325       {
326          try
327          {
328             return proxy_.get(fqn, id);
329          }
330          catch (TimeoutException e)
331          {
332             e.printStackTrace();
333             ex = e;
334          }
335          catch (Exception JavaDoc e)
336          {
337             e.printStackTrace();
338             throw new RuntimeException JavaDoc("JBossCacheService: exception occurred in cache get ... ", e);
339          }
340       }
341       throw new RuntimeException JavaDoc("JBossCacheService: exception occurred in cache get after retry ... ", ex);
342    }
343
344    /**
345     * Wrapper to embed retry logic.
346     *
347     * @param fqn
348     * @param id
349     * @param value
350     * @return
351     */

352    protected Object JavaDoc _put(Fqn fqn, String JavaDoc id, Object JavaDoc value)
353    {
354       Exception JavaDoc ex = null;
355       for (int i = 0; i < RETRY; i++)
356       {
357          try
358          {
359             return proxy_.put(fqn, id, value);
360          }
361          catch (TimeoutException e)
362          {
363             e.printStackTrace();
364             ex = e;
365          }
366          catch (Exception JavaDoc e)
367          {
368             e.printStackTrace();
369             throw new RuntimeException JavaDoc("JBossCacheService: exception occurred in cache put ... ", e);
370          }
371       }
372       throw new RuntimeException JavaDoc("JBossCacheService: exception occurred in cache put after retry ... ", ex);
373    }
374
375
376    /**
377     * Wrapper to embed retry logic.
378     *
379     * @param fqn
380     * @param map
381     */

382    protected void _put(Fqn fqn, Map map)
383    {
384       Exception JavaDoc ex = null;
385       for (int i = 0; i < RETRY; i++)
386       {
387          try
388          {
389             proxy_.put(fqn, map);
390             return;
391          }
392          catch (TimeoutException e)
393          {
394             e.printStackTrace();
395             ex = e;
396          }
397          catch (Exception JavaDoc e)
398          {
399             e.printStackTrace();
400             throw new RuntimeException JavaDoc("JBossCacheService: exception occurred in cache put ... ", e);
401          }
402       }
403       throw new RuntimeException JavaDoc("JBossCacheService: exception occurred in cache put after retry ... ", ex);
404    }
405
406    /**
407     * Wrapper to embed retyr logic.
408     *
409     * @param fqn
410     * @param id
411     * @return
412     */

413    protected Object JavaDoc _remove(Fqn fqn, String JavaDoc id)
414    {
415       Exception JavaDoc ex = null;
416       for (int i = 0; i < RETRY; i++)
417       {
418          try
419          {
420             return proxy_.remove(fqn, id);
421          }
422          catch (TimeoutException e)
423          {
424             e.printStackTrace();
425             ex = e;
426          }
427          catch (Exception JavaDoc e)
428          {
429             e.printStackTrace();
430             throw new RuntimeException JavaDoc("JBossCacheService: exception occurred in cache remove ... ", e);
431          }
432       }
433       throw new RuntimeException JavaDoc("JBossCacheService: exception occurred in cache remove after retry ... ", ex);
434    }
435
436    /**
437     * Wrapper to embed retry logic.
438     *
439     * @param fqn
440     */

441    protected void _remove(Fqn fqn)
442    {
443       Exception JavaDoc ex = null;
444       for (int i = 0; i < RETRY; i++)
445       {
446          try
447          {
448             proxy_.remove(fqn);
449             return;
450          }
451          catch (TimeoutException e)
452          {
453             e.printStackTrace();
454             ex = e;
455          }
456          catch (Exception JavaDoc e)
457          {
458             e.printStackTrace();
459             throw new RuntimeException JavaDoc("JBossCacheService: exception occurred in cache remove ... ", e);
460          }
461       }
462       throw new RuntimeException JavaDoc("JBossCacheService: exception occurred in cache remove after retry ... ", ex);
463    }
464
465    /**
466     * Wrapper to embed retyr logic.
467     *
468     * @param fqn
469     */

470    protected void _evict(Fqn fqn)
471    {
472       Exception JavaDoc ex = null;
473       for (int i = 0; i < RETRY; i++)
474       {
475          try
476          {
477             proxy_.evict(fqn);
478             return;
479          }
480          catch (TimeoutException e)
481          {
482             e.printStackTrace();
483             ex = e;
484          }
485          catch (Exception JavaDoc e)
486          {
487             e.printStackTrace();
488             throw new RuntimeException JavaDoc("JBossCacheService: exception occurred in cache evict ... ", e);
489          }
490       }
491       throw new RuntimeException JavaDoc("JBossCacheService: exception occurred in cache evict after retry ... ", ex);
492    }
493
494
495    /**
496     * Since we store the base id (i.e., without the JvmRoute) internally while the real session id
497     * has the postfix in there if mod_jk is used, we will need to strip it to get the real key. Now this
498     * is an aspect.
499     *
500     * @param id
501     * @return
502     */

503    private String JavaDoc stripJvmRoute(String JavaDoc id)
504    {
505       // TODO Need optimization later since not every request needs to do this.
506
int index = id.indexOf(".");
507       if (index > 0)
508       {
509          if(jvmRoute_ == null)
510             jvmRoute_ = id.substring(index);
511
512          return id.substring(0, index);
513       }
514       else
515       {
516          return id;
517       }
518    }
519
520    private Fqn getSessionFqn(String JavaDoc id)
521    {
522       // /SESSION/webAppPath/id
523
Object JavaDoc[] objs = new Object JavaDoc[]{SESSION, webAppPath_, id};
524       return new Fqn(objs);
525    }
526
527    private Fqn getAttributeFqn(String JavaDoc id)
528    {
529       // /SESSION/id/ATTR
530
Object JavaDoc[] objs = new Object JavaDoc[]{SESSION, webAppPath_, id, ATTRIBUTE};
531       return new Fqn(objs);
532    }
533
534    private Object JavaDoc getMarshalledValue(Object JavaDoc value)
535    {
536       try
537       {
538          return new MarshalledValue(value);
539       }
540       catch (IOException JavaDoc e)
541       {
542          e.printStackTrace();
543          return null;
544       }
545    }
546
547    private Object JavaDoc getUnMarshalledValue(Object JavaDoc mv)
548    {
549       if (mv == null) return null;
550       // Swap in/out the tcl for this web app. Needed only for un marshalling.
551
ClassLoader JavaDoc prevTCL = Thread.currentThread().getContextClassLoader();
552       Thread.currentThread().setContextClassLoader(tcl_);
553       try
554       {
555          return ((MarshalledValue) mv).get();
556       }
557       catch (IOException JavaDoc e)
558       {
559          e.printStackTrace();
560          return null;
561       }
562       catch (ClassNotFoundException JavaDoc e)
563       {
564          e.printStackTrace();
565          return null;
566       }
567       finally
568       {
569          Thread.currentThread().setContextClassLoader(prevTCL);
570       }
571    }
572
573    // --------------- TreeCacheListener methods ------------------------------------
574

575    public void nodeCreated(Fqn fqn)
576    {
577       // No-op
578
}
579
580    public void nodeRemoved(Fqn fqn)
581    {
582       nodeDirty(fqn);
583    }
584
585    /**
586     * Called when a node is loaded into memory via the CacheLoader. This is not the same
587     * as {@link #nodeCreated(Fqn)}.
588     */

589    public void nodeLoaded(Fqn fqn)
590    {
591    }
592
593    public void nodeModified(Fqn fqn)
594    {
595       nodeDirty(fqn);
596    }
597
598    protected void nodeDirty(Fqn fqn)
599    {
600       // Query if we have version value. If we do, we compare the version. Invalidate if necessary.
601
Integer JavaDoc version = (Integer JavaDoc)_get(fqn, VERSION_KEY);
602       if(version != null)
603       {
604          String JavaDoc realId = getIdFromFqn(fqn);
605
606          /*
607          String id;
608          // Need to reconstruct the session id because it could contain jvm route.
609          if(jvmRoute_ == null)
610          {
611             id = realId;
612          } else
613          {
614             id = realId + "." +jvmRoute_;
615          }
616          */

617
618          ClusteredSession session = (ClusteredSession)manager_.findLocalSession(realId);
619          // if session is null, that means the data is fresh. No need to do anything then.
620
if( session != null )
621          {
622             if( session.isNewData(version.intValue()))
623             {
624                // Need to invalidate
625
session.setIsOutdated(true);
626                if(log_.isDebugEnabled())
627                {
628                   log_.debug("nodeDirty(): session in-memory data is invalidated with id: " +realId
629                   + " and verion id: " +version.intValue());
630                }
631             }
632          } else
633          {
634             // fresh data. We will need to populate it to the local in-memory copy.
635
// if(log_.isDebugEnabled())
636
// {
637
// log_.debug("nodeDirty(): session id recovered -- " +realId);
638
// }
639

640             synchronized(newSessionIDList_)
641             {
642                if(!newSessionIDList_.contains(realId))
643                   newSessionIDList_.add(realId);
644             }
645          }
646       }
647    }
648
649    protected String JavaDoc getIdFromFqn(Fqn fqn)
650    {
651       return (String JavaDoc)fqn.get(fqn.size()-1);
652    }
653
654    public void nodeVisited(Fqn fqn)
655    {
656       // no-op
657
}
658
659    public void cacheStarted(TreeCache cache)
660    {
661       // TODO will need to synchronize this with local sessions
662
}
663
664    public void cacheStopped(TreeCache cache)
665    {
666       // TODO will need to synchronize this with local sessions
667
}
668
669    public void viewChange(View new_view)
670    {
671       // We don't care for this event.
672
}
673
674    public void nodeEvicted(Fqn fqn)
675    {
676       // We don't care for this event.
677
}
678
679 }
680
Popular Tags