KickJava   Java API By Example, From Geeks To Geeks.

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


1 /*
2 * JBoss, Home of Professional Open Source
3 * Copyright 2005, JBoss Inc., and individual contributors as indicated
4 * by the @authors tag. See the copyright.txt in the distribution for a
5 * full listing of individual contributors.
6 *
7 * This is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU Lesser General Public License as
9 * published by the Free Software Foundation; either version 2.1 of
10 * the License, or (at your option) any later version.
11 *
12 * This software is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this software; if not, write to the Free
19 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
21 */

22 package org.jboss.web.tomcat.tc6.session;
23
24 import java.io.ByteArrayOutputStream JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.ObjectInputStream JavaDoc;
27 import java.io.ObjectOutputStream JavaDoc;
28 import java.lang.reflect.Field JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.Collection JavaDoc;
31 import java.util.HashMap JavaDoc;
32 import java.util.HashSet JavaDoc;
33 import java.util.Iterator JavaDoc;
34 import java.util.List JavaDoc;
35 import java.util.Map JavaDoc;
36 import java.util.Set JavaDoc;
37 import java.util.StringTokenizer JavaDoc;
38 import java.util.WeakHashMap JavaDoc;
39
40 import javax.management.ObjectName JavaDoc;
41 import javax.transaction.TransactionManager JavaDoc;
42
43 import org.apache.catalina.Context;
44 import org.jboss.aspects.patterns.observable.Observer;
45 import org.jboss.aspects.patterns.observable.Subject;
46 import org.jboss.cache.Cache;
47 import org.jboss.cache.CacheException;
48 import org.jboss.cache.Fqn;
49 import org.jboss.cache.Node;
50 import org.jboss.cache.Region;
51 import org.jboss.cache.pojo.PojoCache;
52 import org.jboss.cache.pojo.jmx.PojoCacheJmxWrapperMBean;
53 import org.jboss.cache.buddyreplication.BuddyManager;
54 import org.jboss.cache.config.CacheLoaderConfig;
55 import org.jboss.cache.transaction.BatchModeTransactionManager;
56 import org.jboss.invocation.MarshalledValue;
57 import org.jboss.logging.Logger;
58 import org.jboss.mx.util.MBeanProxyExt;
59 import org.jboss.serial.io.MarshalledObject;
60
61 /**
62  * A wrapper class to JBossCache. This is currently needed to handle various operations such as
63  * <ul>
64  * <li>Using MarshalledValue to replace Serializable used inside different web app class loader context.</li>
65  * <li>Stripping out any id string after ".". This is to handle the JK failover properly with
66  * Tomcat JvmRoute.</li>
67  * <li>Cache exception retry.</li>
68  * <li>Helper APIS.</li>
69  * </ul>
70  */

71 public class JBossCacheService
72 {
73    protected static Logger log_ = Logger.getLogger(JBossCacheService.class);
74    public static final String JavaDoc BUDDY_BACKUP = BuddyManager.BUDDY_BACKUP_SUBTREE;
75    public static final Fqn BUDDY_BACKUP_FQN = BuddyManager.BUDDY_BACKUP_SUBTREE_FQN;
76    public static final String JavaDoc SESSION = "JSESSION";
77    public static final String JavaDoc ATTRIBUTE = "ATTRIBUTE";
78    // Needed for cache invalidation
79
static final String JavaDoc VERSION_KEY = "VERSION";
80    static final String JavaDoc FQN_DELIMITER = "/";
81    
82    private PojoCache pojoCache_;
83    private Cache plainCache_;
84    private ObjectName JavaDoc cacheServiceName_;
85    
86    // name of webapp's virtual host(JBAS-2194).
87
// Idea is host_name + web_app_path + session id is a unique combo.
88
private String JavaDoc hostName_;
89    // web app path (JBAS-1367 and JBAS-2194).
90
// Idea is host_name + web_app_path + session id is a unique combo.
91
private String JavaDoc webAppPath_;
92    private TransactionManager JavaDoc tm;
93
94    private JBossCacheManager manager_;
95    private CacheListener cacheListener_;
96    private JBossCacheWrapper cacheWrapper_;
97    
98    // Do we have to marshall attributes ourself or can we let
99
// the TreeCache do it?
100
private boolean useTreeCacheMarshalling_ = false;
101    
102    private WeakHashMap JavaDoc typeMap = new WeakHashMap JavaDoc();
103    
104    public JBossCacheService(String JavaDoc treeCacheObjectName) throws ClusteringNotSupportedException
105    {
106       // Find JBossCacheService
107
try
108       {
109          cacheServiceName_ = new ObjectName JavaDoc(treeCacheObjectName);
110          // Get the pojo cache
111
PojoCacheJmxWrapperMBean mbean = (PojoCacheJmxWrapperMBean) MBeanProxyExt.create(PojoCacheJmxWrapperMBean.class,
112                                                                                           cacheServiceName_);
113          if (mbean != null)
114          {
115             pojoCache_ = mbean.getPojoCache();
116          }
117       }
118       catch (Throwable JavaDoc t)
119       {
120
121          String JavaDoc str = "Could not access TreeCache service " +
122                      (cacheServiceName_ == null ? "<null>" : cacheServiceName_.toString()) +
123                      " for Tomcat clustering";
124          log_.debug(str);
125          throw new ClusteringNotSupportedException(str, t);
126       }
127       
128       if (pojoCache_ == null)
129       {
130          String JavaDoc str = "Could not access TreeCache service " +
131                      (cacheServiceName_ == null ? "<null>" : cacheServiceName_.toString()) +
132                      " for Tomcat clustering";
133          log_.debug(str);
134          throw new ClusteringNotSupportedException(str);
135       }
136       
137       plainCache_ = pojoCache_.getCache();
138       cacheWrapper_ = new JBossCacheWrapper(pojoCache_);
139       
140       useTreeCacheMarshalling_ = plainCache_.getConfiguration().isUseRegionBasedMarshalling();
141    }
142
143    public void start(ClassLoader JavaDoc tcl, JBossCacheManager manager)
144    {
145       manager_ = manager;
146       
147       Context webapp = (Context) manager_.getContainer();
148       String JavaDoc path = webapp.getName();
149       if( path.length() == 0 || path.equals("/")) {
150          // If this is root.
151
webAppPath_ = "ROOT";
152       } else if ( path.startsWith("/") ) {
153          webAppPath_ = path.substring(1);
154       } else {
155          webAppPath_ = path;
156       }
157       log_.debug("Old and new web app path are: " +path + ", " +webAppPath_);
158       
159       String JavaDoc host = webapp.getParent().getName();
160       if( host == null || host.length() == 0) {
161          hostName_ = "localhost";
162       }else {
163          hostName_ = host;
164       }
165       log_.debug("Old and new virtual host name are: " + host + ", " + hostName_);
166       
167
168       // Listen for cache changes
169
cacheListener_ = new CacheListener(cacheWrapper_, manager_, hostName_, webAppPath_);
170       plainCache_.addCacheListener(cacheListener_);
171
172       // register the tcl and bring over the state for the webapp
173
Object JavaDoc[] objs = new Object JavaDoc[]{SESSION, hostName_, webAppPath_};
174       Fqn pathFqn = new Fqn( objs );
175       try {
176          if(useTreeCacheMarshalling_)
177          {
178             log_.debug("UseMarshalling is true. We will register the fqn: " +
179                         pathFqn + " with class loader" +tcl +
180                         " and activate the webapp's Region");
181             Region region = plainCache_.getRegion(pathFqn, true);
182             region.registerContextClassLoader(tcl);
183             region.activate();
184          }
185       } catch (Exception JavaDoc ex)
186       {
187          throw new RuntimeException JavaDoc("Can't register class loader", ex);
188       }
189
190       // We require the cache tm to be BatchModeTransactionManager now.
191
tm = plainCache_.getTransactionManager();
192       if( ! (tm instanceof BatchModeTransactionManager) )
193       {
194          throw new RuntimeException JavaDoc("JBossCacheService.start(): JBossCacheAop transaction manager is not of type BatchModeTransactionManager." +
195                  " Please check the tc6-cluster-service.xml TransactionManagerClassLookup field.");
196       }
197       
198       if(isCachePassivationEnabled())
199       {
200          log_.debug("JBossCache passivation is enabled");
201       }
202       else
203       {
204          log_.debug("JBossCache passivation is disabled");
205       }
206    }
207
208    public void stop()
209    {
210       plainCache_.removeCacheListener(cacheListener_);
211
212       // Construct the fqn
213
Object JavaDoc[] objs = new Object JavaDoc[]{SESSION, hostName_, webAppPath_};
214       Fqn pathFqn = new Fqn( objs );
215
216       if(useTreeCacheMarshalling_)
217       {
218          log_.debug("UseMarshalling is true. We will inactivate the fqn: " +
219                     pathFqn + " and un-register its classloader");
220             
221          try {
222             Region region = plainCache_.getRegion(pathFqn, false);
223             if (region != null)
224             {
225                region.deactivate();
226                region.unregisterContextClassLoader();
227             }
228          }
229          catch (Exception JavaDoc e)
230          {
231             log_.error("Exception during inactivation of webapp region " + pathFqn +
232                        " or un-registration of its class loader", e);
233          }
234       }
235
236       // remove session data
237
cacheWrapper_.removeLocalSubtree(pathFqn);
238    }
239
240    /**
241     * Get specfically the BatchModeTransactionManager.
242     */

243    public TransactionManager JavaDoc getTransactionManager()
244    {
245       return tm;
246    }
247    
248    /**
249     * Gets whether TreeCache-based marshalling is available
250     */

251    public boolean isMarshallingAvailable()
252    {
253       return useTreeCacheMarshalling_;
254    }
255
256    /**
257     * Loads any serialized data in the cache into the given session
258     * using its <code>readExternal</code> method.
259     *
260     * @return the session passed as <code>toLoad</code>, or
261     * <code>null</code> if the cache had no data stored
262     * under the given session id.
263     */

264    public ClusteredSession loadSession(String JavaDoc realId, ClusteredSession toLoad)
265    {
266       Fqn fqn = getSessionFqn(realId);
267       Object JavaDoc sessionData = cacheWrapper_.get(fqn, realId, true);
268       
269       if (sessionData == null) {
270          // Requested session is no longer in the cache; return null
271
return null;
272       }
273       
274       boolean firstLoad = (toLoad.getVersion() == 0);
275       
276 // if (useTreeCacheMarshalling_)
277
// {
278
// toLoad.update((ClusteredSession) sessionData);
279
// }
280
// else
281
// {
282
byte[] sessionBytes = (byte[]) sessionData;
283        
284       // Swap in/out the webapp classloader so we can deserialize
285
// attributes whose classes are only available to the webapp
286
ClassLoader JavaDoc prevTCL = Thread.currentThread().getContextClassLoader();
287       Thread.currentThread().setContextClassLoader(manager_.getWebappClassLoader());
288       try
289       {
290          // JBAS-2921 - replaced MarshalledValue calls with SessionSerializationFactory calls
291
// ByteArrayInputStream bais = new ByteArrayInputStream(sessionBytes);
292
// Use MarshalledValueInputStream instead of superclass ObjectInputStream
293
// or else there are problems finding classes with scoped loaders
294
// MarshalledValueInputStream input = new MarshalledValueInputStream(bais);
295
ObjectInputStream JavaDoc input = SessionSerializationFactory.createObjectInputStream(sessionBytes);
296          toLoad.readExternal(input);
297          input.close();
298       }
299       catch (Exception JavaDoc e)
300       {
301          log_.error("loadSession(): id: " + realId + "exception occurred during serialization: " +e);
302          return null;
303       }
304       finally {
305          Thread.currentThread().setContextClassLoader(prevTCL);
306       }
307 // }
308

309       // The internal version of the serialized session may be less than the
310
// real one due to not replicating metadata. If our listener hasn't
311
// been keeping the outdatedVersion of the session up to date because
312
// the session has never been loaded into the JBCManager cache, we
313
// need to fix the version
314
if (firstLoad)
315       {
316          Integer JavaDoc ver = (Integer JavaDoc) cacheWrapper_.get(fqn, VERSION_KEY);
317          if (ver != null)
318             toLoad.setVersion(ver.intValue());
319       }
320       
321       return toLoad;
322    }
323
324    public void putSession(String JavaDoc realId, ClusteredSession session)
325    {
326       Fqn fqn = getSessionFqn(realId);
327       
328       if (session.getReplicateSessionBody())
329       {
330          Map JavaDoc map = new HashMap JavaDoc();
331 // if (useTreeCacheMarshalling_)
332
// map.put(realId, session);
333
// else
334
map.put(realId, externalizeSession(session));
335          // Put in (VERSION_KEY, version) after the real put for cache invalidation
336
map.put(VERSION_KEY, new Integer JavaDoc(session.getVersion()));
337          cacheWrapper_.put(fqn, map);
338       }
339       else
340       {
341          // Invalidate the remote caches
342
cacheWrapper_.put(fqn, VERSION_KEY, new Integer JavaDoc(session.getVersion()));
343       }
344    }
345
346    public void removeSession(String JavaDoc realId)
347    {
348       Fqn fqn = getSessionFqn(realId);
349       if (log_.isDebugEnabled())
350       {
351          log_.debug("Remove session from distributed store. Fqn: " + fqn);
352       }
353       //Object obj = getUnMarshalledValue(cacheWrapper_.remove(fqn, realId));
354
cacheWrapper_.remove(fqn, realId);
355       // This needs to go after object removal to support correct cache invalidation.
356
// _remove(fqn, VERSION_KEY);
357
// Let just remove the whole thing (including the fqn)
358
cacheWrapper_.remove(fqn);
359       //return obj;
360
}
361
362    public void removeSessionLocal(String JavaDoc realId)
363    {
364       Fqn fqn = getSessionFqn(realId);
365       if (log_.isDebugEnabled())
366       {
367          log_.debug("Remove session from my own distributed store only. Fqn: " + fqn);
368       }
369       cacheWrapper_.removeLocalSubtree(fqn);
370    }
371
372    public void removeSessionLocal(String JavaDoc realId, String JavaDoc dataOwner)
373    {
374       if (dataOwner == null)
375       {
376          removeSessionLocal(realId);
377       }
378       else
379       {
380          Fqn fqn = getSessionFqn(realId, dataOwner);
381          if (log_.isDebugEnabled())
382          {
383             log_.debug("Remove session from my own distributed store only. Fqn: " + fqn);
384          }
385          cacheWrapper_.removeLocalSubtree(fqn);
386       }
387    }
388    
389       
390    public void evictSession(String JavaDoc realId)
391    {
392       Fqn fqn = getSessionFqn(realId);
393       if(log_.isDebugEnabled())
394       {
395          log_.debug("evictSession(): evicting session from my distributed store. Fqn: " + fqn);
396       }
397       cacheWrapper_.evictSubtree(fqn);
398       
399    }
400
401    public boolean exists(String JavaDoc realId)
402    {
403       Fqn fqn = getSessionFqn(realId);
404       return plainCache_.hasChild(fqn);
405    }
406
407    public Object JavaDoc getAttribute(String JavaDoc realId, String JavaDoc key)
408    {
409       Fqn fqn = getAttributeFqn(realId);
410       return getUnMarshalledValue(cacheWrapper_.get(fqn, key));
411    }
412
413    public void putAttribute(String JavaDoc realId, String JavaDoc key, Object JavaDoc value)
414    {
415       Fqn fqn = getAttributeFqn(realId);
416       cacheWrapper_.put(fqn, key, getMarshalledValue(value));
417    }
418
419    public void putAttribute(String JavaDoc realId, Map JavaDoc map)
420    {
421       // Duplicate the map with marshalled values
422
Map JavaDoc marshalled = new HashMap JavaDoc(map.size());
423       Set JavaDoc entries = map.entrySet();
424       for (Iterator JavaDoc it = entries.iterator(); it.hasNext(); )
425       {
426          Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
427          marshalled.put(entry.getKey(), getMarshalledValue(entry.getValue()));
428       }
429       
430       Fqn fqn = getAttributeFqn(realId);
431       cacheWrapper_.put(fqn, marshalled);
432       
433    }
434
435    public void removeAttributes(String JavaDoc realId)
436    {
437       Fqn fqn = getAttributeFqn(realId);
438       cacheWrapper_.remove(fqn);
439    }
440
441    public Object JavaDoc removeAttribute(String JavaDoc realId, String JavaDoc key)
442    {
443       Fqn fqn = getAttributeFqn(realId);
444       if (log_.isTraceEnabled())
445       {
446          log_.trace("Remove attribute from distributed store. Fqn: " + fqn + " key: " + key);
447       }
448       return getUnMarshalledValue(cacheWrapper_.remove(fqn, key));
449    }
450
451    public void removeAttributesLocal(String JavaDoc realId)
452    {
453       Fqn fqn = getAttributeFqn(realId);
454       if (log_.isDebugEnabled())
455       {
456          log_.debug("Remove attributes from my own distributed store only. Fqn: " + fqn);
457       }
458       cacheWrapper_.removeLocal(fqn);
459    }
460
461    /**
462     * Obtain the keys associated with this fqn. Note that it is not the fqn children.
463     *
464     */

465    public Set JavaDoc getAttributeKeys(String JavaDoc realId)
466    {
467       Set JavaDoc keys = null;
468       Fqn fqn = getAttributeFqn(realId);
469       try
470       {
471          Node node = plainCache_.getChild(fqn);
472          if (node != null)
473             keys = node.getKeys();
474       }
475       catch (CacheException e)
476       {
477          log_.error("getAttributeKeys(): Exception getting keys for session " + realId, e);
478       }
479       
480       return keys;
481    }
482
483    /**
484     * Return all attributes associated with this session id.
485     *
486     * TODO use Node.getData() and just copy the map
487     *
488     * @param realId the session id with any jvmRoute removed
489     * @return the attributes, or any empty Map if none are found.
490     */

491    public Map JavaDoc getAttributes(String JavaDoc realId)
492    {
493       if (realId == null || realId.length() == 0) return new HashMap JavaDoc();
494       
495       Map JavaDoc map = new HashMap JavaDoc();
496       Set JavaDoc set = getAttributeKeys(realId);
497       if(set != null)
498       {
499          for (Iterator JavaDoc it = set.iterator(); it.hasNext();)
500          {
501             String JavaDoc key = (String JavaDoc) it.next();
502             Object JavaDoc value = getAttribute(realId, key);
503             map.put(key, value);
504          }
505       }
506       return map;
507    }
508
509    /**
510     * Gets the ids of all sessions in the underlying cache.
511     *
512     * @return Set containing all of the session ids of sessions in the cache
513     * (with any jvmRoute removed) or <code>null</code> if there
514     * are no sessions in the cache.
515     */

516    public Map JavaDoc getSessionIds() throws CacheException
517    {
518       Map JavaDoc result = new HashMap JavaDoc();
519       
520       Node bbRoot = plainCache_.getChild(BUDDY_BACKUP_FQN);
521       if (bbRoot != null)
522       {
523          Set JavaDoc owners = bbRoot.getChildren();
524          if (owners != null)
525          {
526             for (Iterator JavaDoc it = owners.iterator(); it.hasNext();)
527             {
528                Node owner = (Node) it.next();
529                Node webRoot = owner.getChild(getWebappFqn());
530                if (webRoot != null)
531                {
532                   Set JavaDoc ids = webRoot.getChildrenNames();
533                   storeSessionOwners(ids, owner, result);
534                }
535             }
536          }
537       }
538       
539       storeSessionOwners(getChildrenNames(getWebappFqn()), null, result);
540
541       return result;
542    }
543    
544    private Set JavaDoc getChildrenNames(Fqn fqn)
545    {
546       Node node = plainCache_.getChild(fqn);
547       return (node == null ? null : node.getChildrenNames());
548    }
549
550    private void storeSessionOwners(Set JavaDoc ids, Object JavaDoc owner, Map JavaDoc map)
551    {
552       if (ids != null)
553       {
554          for (Iterator JavaDoc it = ids.iterator(); it.hasNext();)
555          {
556             map.put(it.next(), owner);
557          }
558       }
559    }
560
561    /**
562     * store the pojo instance in the cache. Note that this is for the aop cache.
563     * THe pojo needs to be "aspectized".
564     *
565     * @param realId the session id with any jvmRoute removed
566     * @param key the attribute key
567     * @param pojo
568     */

569    public Object JavaDoc setPojo(String JavaDoc realId, String JavaDoc key, Object JavaDoc pojo)
570    {
571       if(log_.isTraceEnabled())
572       {
573          log_.trace("setPojo(): session id: " + realId + " key: " + key +
574                     " object: " + pojo.toString());
575       }
576       // Construct the fqn.
577
Fqn fqn = getFieldFqn(realId, key);
578       try {
579          // Ignore any cache notifications that our own work generates
580
SessionReplicationContext.startCacheActivity();
581          return pojoCache_.attach(fqn.toString(), pojo);
582       } catch (CacheException e) {
583          throw new RuntimeException JavaDoc("JBossCacheService: exception occurred in cache setPojo ... ", e);
584       }
585       finally {
586          SessionReplicationContext.finishCacheActivity();
587       }
588    }
589
590    /**
591     * Remove pojo from the underlying cache store.
592     * @param realId the session id with any jvmRoute removed
593     * @param key the attribute key
594     * @return pojo that just removed. Null if there none.
595     */

596    public Object JavaDoc removePojo(String JavaDoc realId, String JavaDoc key)
597    {
598       if(log_.isTraceEnabled())
599       {
600          log_.trace("removePojo(): session id: " +realId + " key: " +key);
601       }
602       // Construct the fqn.
603
Fqn fqn = getFieldFqn(realId, key);
604       try {
605          // Ignore any cache notifications that our own work generates
606
SessionReplicationContext.startCacheActivity();
607          return pojoCache_.detach(fqn.toString());
608       } catch (CacheException e) {
609          throw new RuntimeException JavaDoc("JBossCacheService: exception occurred in cache removePojo ... ", e);
610       }
611       finally {
612          SessionReplicationContext.finishCacheActivity();
613       }
614    }
615
616    /**
617     * Remove all the pojos from the underlying cache store locally
618     * without replication.
619     *
620     * @param realId the session id with any jvmRoute removed
621     */

622    public void removePojosLocal(String JavaDoc realId)
623    {
624       if(log_.isDebugEnabled())
625       {
626          log_.debug("removePojoLocal(): session id: " +realId);
627       }
628       // Construct the fqn.
629
Fqn fqn = getAttributeFqn(realId);
630       try {
631          // Ignore any cache notifications that our own work generates
632
SessionReplicationContext.startCacheActivity();
633          cacheWrapper_.removeLocalSubtree(fqn);
634       }
635       finally {
636          SessionReplicationContext.finishCacheActivity();
637       }
638    }
639
640    /**
641     * Remove all the pojos from the underlying cache store locally
642     * without replication.
643     *
644     * @param realId the session id with any jvmRoute removed
645     */

646    public void removePojoLocal(String JavaDoc realId, String JavaDoc key)
647    {
648       if(log_.isTraceEnabled())
649       {
650          log_.trace("removePojoLocal(): session id: " + realId + " key: " +key);
651       }
652       // Construct the fqn.
653
Fqn fqn = getFieldFqn(realId, key);
654       try {
655          // Ignore any cache notifications that our own work generates
656
SessionReplicationContext.startCacheActivity();
657          cacheWrapper_.removeLocalSubtree(fqn);
658       }
659       finally {
660          SessionReplicationContext.finishCacheActivity();
661       }
662    }
663    
664    public Set JavaDoc getPojoKeys(String JavaDoc realId)
665    {
666       Set JavaDoc keys = null;
667       Fqn fqn = getAttributeFqn(realId);
668       try
669       {
670          keys = getChildrenNames(fqn);
671       }
672       catch (CacheException e)
673       {
674          log_.error("getPojoKeys(): Exception getting keys for session " + realId, e);
675       }
676       
677       return keys;
678    }
679    
680
681    /**
682     *
683     * @param realId the session id with any jvmRoute removed
684     * @param key the attribute key
685     * @return Pojo that is associated with the attribute
686     */

687    public Object JavaDoc getPojo(String JavaDoc realId, String JavaDoc key)
688    {
689       if(log_.isTraceEnabled())
690       {
691          log_.trace("getPojo(): session id: " +realId + " key: " +key);
692       }
693       // Construct the fqn.
694
Fqn fqn = getFieldFqn(realId, key);
695
696       try
697       {
698          return pojoCache_.find(fqn.toString());
699       }
700       catch (CacheException e)
701       {
702          throw new RuntimeException JavaDoc("JBossCacheService: exception occurred in cache getPojo ... ", e);
703       }
704    }
705
706    /**
707     * Recursively adds session as observer to the pojo graph. Assumes the
708     * whole object graph has Subject "introduction" declared. If a portion
709     * of the graph isn't a Subject, the recursion does not continue below
710     * that part of the graph.
711     *
712     * @param session the session
713     * @param pojo the pojo. Can be <code>null</code>.
714     */

715    public void addObserver(Observer session, Object JavaDoc pojo)
716    {
717       addObserver(session, pojo, new HashSet JavaDoc());
718    }
719    
720    private void addObserver(Observer session, Object JavaDoc pojo, Set JavaDoc processed)
721    {
722       if ( pojo instanceof Collection JavaDoc )
723       {
724          Collection JavaDoc col = (Collection JavaDoc)pojo;
725          for (Iterator JavaDoc i = col.iterator(); i.hasNext();) {
726             // If not a managed pojo, will return anyway
727
addObserver(session, i.next(), processed);
728          }
729       }
730       else if (pojo instanceof Map JavaDoc)
731       {
732          for (Iterator JavaDoc i = ((Map JavaDoc)pojo).entrySet().iterator(); i.hasNext();)
733          {
734             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) i.next();
735
736             // Walk thru key and value
737
addObserver(session, entry.getKey(), processed);
738             addObserver(session, entry.getValue(), processed);
739          }
740       }
741
742       if(! (pojo instanceof Subject) )
743       {
744          return; // No need to add observer since it is primitive.
745
}
746
747       Subject subject = (Subject)pojo;
748       subject.addObserver(session);
749       
750       if(log_.isTraceEnabled())
751       {
752          log_.trace("addObserver(): session: " +session + " pojo name: " +pojo.getClass().getName());
753       }
754       
755       // Examine each field of the type and its superclasses to see if
756
// we need to add the observer to the pojo held by that field
757
// Traverse recursively
758

759       // First identify and cache the names of all the class'
760
// non-immediate fields
761
Class JavaDoc type = pojo.getClass();
762       Set JavaDoc complexFields = (Set JavaDoc) typeMap.get(type);
763       if (complexFields == null)
764       {
765          complexFields = Util.parseComplexFields(type);
766          typeMap.put(type, complexFields);
767       }
768       
769       if (complexFields.size() == 0)
770          return;
771
772       // Store a ref to the pojo to avoid cyclic additions
773
processed.add(pojo);
774       
775       for (Iterator JavaDoc iter = complexFields.iterator(); iter.hasNext();)
776       {
777          String JavaDoc fieldName = (String JavaDoc) iter.next();
778          Class JavaDoc curType = type;
779          while (curType != null)
780          {
781             try
782             {
783                Field JavaDoc field = curType.getDeclaredField(fieldName);
784                boolean accessible = field.isAccessible();
785                Object JavaDoc value = null;
786                try
787                {
788                   field.setAccessible(true);
789                   
790                   value=field.get(pojo);
791                   // Continue recursively unless we've already handled this value
792
if (value != null && !processed.contains(value))
793                      addObserver(session, value, processed);
794                   break;
795                }
796                catch(IllegalAccessException JavaDoc e)
797                {
798                   throw new RuntimeException JavaDoc("field access failed", e);
799                }
800                finally
801                {
802                   field.setAccessible(accessible);
803                }
804             }
805             catch (NoSuchFieldException JavaDoc e)
806             {
807                // Check if the field is declared in a superclass
808
curType = curType.getSuperclass();
809                if (curType == null)
810                   throw new RuntimeException JavaDoc("Field " + fieldName +
811                         " does not exist", e);
812             }
813          }
814       }
815    }
816
817    /**
818     * Recursively removes session as observer to the pojo graph. Assumes the
819     * whole object graph has Subject "introduction" declared. If a portion
820     * of the graph isn't a Subject, the recursion does not continue below
821     * that part of the graph.
822     *
823     * @param session the session
824     * @param pojo the pojo to stop observing. Can be <code>null</code>.
825     */

826    public void removeObserver(Observer session, Object JavaDoc pojo)
827    {
828       removeObserver(session, pojo, new HashSet JavaDoc());
829    }
830    
831    private void removeObserver(Observer session, Object JavaDoc pojo, Set JavaDoc stack)
832    {
833       if ( pojo instanceof Collection JavaDoc )
834       {
835          Collection JavaDoc col = (Collection JavaDoc)pojo;
836          for (Iterator JavaDoc i = col.iterator(); i.hasNext();) {
837             Object JavaDoc obj = i.next();
838             // If not a managed pojo, will return anyway
839
removeObserver(session, obj, stack);
840          }
841    
842          return;
843       }
844       else if (pojo instanceof Map JavaDoc)
845       {
846          Map JavaDoc map = (Map JavaDoc)pojo;
847          for (Iterator JavaDoc i = map.keySet().iterator(); i.hasNext();) {
848             Object JavaDoc key = i.next();
849             Object JavaDoc value = map.get(key);
850    
851             // Walk thru key and value
852
removeObserver(session, key, stack);
853             removeObserver(session, value, stack);
854          }
855    
856          return;
857       }
858       // BRIAN 3/14 changed this from checking Advised to checking Subject
859
// since that is what we cast to below
860
if(! (pojo instanceof Subject) )
861       {
862          return; // No need to add observer since it is primitive.
863
}
864
865       Subject subject = (Subject)pojo;
866       subject.removeObserver(session);
867       if(log_.isTraceEnabled())
868       {
869          log_.trace("removeObserver(): session: " +session + " pojo name: " +pojo.getClass().getName());
870       }
871       
872       // Examine each field of the type and its superclasses to see if
873
// we need to remove the observer from the pojo held by that field
874
// Traverse recursively
875

876       // First identify and cache the names of all the class'
877
// non-immediate fields
878
Class JavaDoc type = pojo.getClass();
879       Set JavaDoc complexFields = (Set JavaDoc) typeMap.get(type);
880       if (complexFields == null)
881       {
882          complexFields = Util.parseComplexFields(type);
883          typeMap.put(type, complexFields);
884       }
885
886       if (complexFields.size() == 0)
887          return;
888       
889       // Store a ref to the pojo to avoid cyclic removals
890
stack.add(pojo);
891       
892       for (Iterator JavaDoc iter = complexFields.iterator(); iter.hasNext();)
893       {
894          String JavaDoc fieldName = (String JavaDoc) iter.next();
895          Class JavaDoc curType = type;
896          while (curType != null)
897          {
898             try
899             {
900                Field JavaDoc field = curType.getDeclaredField(fieldName);
901                boolean accessible = field.isAccessible();
902                Object JavaDoc value = null;
903                try
904                {
905                   field.setAccessible(true);
906                   
907                   value=field.get(pojo);
908                   // Continue recursively unless we've already handled this value
909
if (value != null && !stack.contains(value))
910                      removeObserver(session, value, stack);
911                   break;
912                }
913                catch(IllegalAccessException JavaDoc e)
914                {
915                   throw new RuntimeException JavaDoc("field access failed", e);
916                }
917                finally
918                {
919                   field.setAccessible(accessible);
920                }
921             }
922             catch (NoSuchFieldException JavaDoc e)
923             {
924                // Check if the field is declared in a superclass
925
curType = curType.getSuperclass();
926                if (curType == null)
927                   throw new RuntimeException JavaDoc("Field " + fieldName +
928                         " does not exist", e);
929             }
930          }
931       }
932    }
933  
934    public boolean isCachePassivationEnabled()
935    {
936       CacheLoaderConfig clc = plainCache_.getConfiguration().getCacheLoaderConfig();
937       if(clc != null)
938       {
939          return (clc.isPassivation() && !clc.isShared());
940       }
941       else
942       {
943          return false;
944       }
945    }
946    
947    private Fqn getFieldFqn(String JavaDoc id, String JavaDoc key)
948    {
949       // /SESSION/id/ATTR/key
950
// Guard against string with delimiter.
951
List JavaDoc list = new ArrayList JavaDoc(6);
952       list.add(SESSION);
953       list.add(hostName_);
954       list.add(webAppPath_);
955       list.add(id);
956       list.add(ATTRIBUTE);
957       breakKeys(key, list);
958       return new Fqn(list);
959    }
960
961    private void breakKeys(String JavaDoc key, List JavaDoc list)
962    {
963       StringTokenizer JavaDoc token = new StringTokenizer JavaDoc(key, FQN_DELIMITER);
964       while(token.hasMoreTokens())
965       {
966          list.add(token.nextToken());
967       }
968    }
969
970    private Fqn getWebappFqn()
971    {
972       // /SESSION/hostname/webAppPath
973
Object JavaDoc[] objs = new Object JavaDoc[]{SESSION, hostName_, webAppPath_};
974       return new Fqn(objs);
975    }
976
977    private Fqn getWebappFqn(Object JavaDoc dataOwner)
978    {
979       if (dataOwner == null)
980          return getWebappFqn();
981       
982       // /SESSION/hostname/webAppPath
983
Object JavaDoc[] objs = new Object JavaDoc[]{BUDDY_BACKUP, dataOwner, SESSION, hostName_, webAppPath_};
984       return new Fqn(objs);
985    }
986    
987    private Fqn getSessionFqn(String JavaDoc id)
988    {
989       // /SESSION/hostname/webAppPath/id
990
Object JavaDoc[] objs = new Object JavaDoc[]{SESSION, hostName_, webAppPath_, id};
991       return new Fqn(objs);
992    }
993
994    private Fqn getSessionFqn(String JavaDoc id, String JavaDoc dataOwner)
995    {
996       // /_BUDDY_BACKUP_/dataOwner/SESSION/hostname/webAppPath/id
997
Object JavaDoc[] objs = new Object JavaDoc[]{BUDDY_BACKUP, dataOwner, SESSION, hostName_, webAppPath_, id};
998       return new Fqn(objs);
999    }
1000
1001   private Fqn getAttributeFqn(String JavaDoc id)
1002   {
1003      // /SESSION/hostName/webAppPath/id/ATTR
1004
Object JavaDoc[] objs = new Object JavaDoc[]{SESSION, hostName_, webAppPath_, id, ATTRIBUTE};
1005      return new Fqn(objs);
1006   }
1007
1008   private Object JavaDoc getMarshalledValue(Object JavaDoc value)
1009   {
1010      // JBAS-2920. For now, continue using MarshalledValue, as
1011
// it allows lazy deserialization of the attribute on remote nodes
1012
// For Branch_4_0 this is what we have to do anyway for backwards
1013
// compatibility. For HEAD we'll follow suit for now.
1014
// TODO consider only using MV for complex objects (i.e. not primitives)
1015
// and Strings longer than X.
1016

1017// if (useTreeCacheMarshalling_)
1018
// {
1019
// return value;
1020
// }
1021
// else
1022
// {
1023
try
1024         {
1025            // JBAS-2921 - replaced MarshalledValue calls with SessionSerializationFactory calls
1026
// to allow for switching between JBossSerialization and JavaSerialization using
1027
// system property -D=session.serialization.jboss=true / false
1028
// MarshalledValue mv = new MarshalledValue(value);
1029
if (SessionSerializationFactory.useJBossSerialization())
1030            {
1031               MarshalledObject mo = SessionSerializationFactory.createMarshalledObject(value);
1032               if (log_.isTraceEnabled())
1033               {
1034                  log_.trace("JBoss Marshalled Object to size ");
1035               }
1036               return mo;
1037               
1038            }
1039            else
1040            {
1041               MarshalledValue mv = SessionSerializationFactory.createMarshalledValue(value);
1042               if (log_.isTraceEnabled())
1043               {
1044                  log_.trace("marshalled object to size " + mv.size() + " bytes");
1045               }
1046               return mv;
1047            }
1048            
1049         }
1050         catch (IOException JavaDoc e)
1051         {
1052            log_.error("IOException occurred marshalling value ", e);
1053            return null;
1054         }
1055// }
1056
}
1057
1058   private Object JavaDoc getUnMarshalledValue(Object JavaDoc mv)
1059   {
1060      // JBAS-2920. For now, continue using MarshalledValue, as
1061
// it allows lazy deserialization of the attribute on remote nodes
1062
// For Branch_4_0 this is what we have to do anyway for backwards
1063
// compatibility. For HEAD we'll follow suit for now.
1064
// if (useTreeCacheMarshalling_)
1065
// {
1066
// return mv;
1067
// }
1068
// else
1069
// {
1070
if (mv == null) return null;
1071         // Swap in/out the tcl for this web app. Needed only for un marshalling.
1072
ClassLoader JavaDoc prevTCL = Thread.currentThread().getContextClassLoader();
1073         Thread.currentThread().setContextClassLoader(manager_.getWebappClassLoader());
1074         try
1075         {
1076            // JBAS-2921 - replaced MarshalledValue calls with SessionSerializationFactory calls
1077
// to allow for switching between JBossSerialization and JavaSerialization using
1078
// system property -D=session.serialization.jboss=true / false
1079

1080            if (SessionSerializationFactory.useJBossSerialization())
1081            {
1082               return ((MarshalledObject)mv).get();
1083            }
1084            else
1085            {
1086               return ((MarshalledValue) mv).get();
1087            }
1088         }
1089         catch (IOException JavaDoc e)
1090         {
1091            log_.error("IOException occurred unmarshalling value ", e);
1092            return null;
1093         }
1094         catch (ClassNotFoundException JavaDoc e)
1095         {
1096            log_.error("ClassNotFoundException occurred unmarshalling value ", e);
1097            return null;
1098         }
1099         finally
1100         {
1101            Thread.currentThread().setContextClassLoader(prevTCL);
1102         }
1103// }
1104
}
1105
1106   private byte[] externalizeSession(ClusteredSession session)
1107   {
1108      try
1109      {
1110         // Write the contents of session to a byte array and store that
1111
ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
1112         // Use MarshalledValueOutputStream instead of superclass ObjectOutputStream
1113
// or else there are problems finding classes with scoped loaders
1114

1115         // JBAS-2921 - replaced MarshalledValue calls with SessionSerializationFactory calls
1116
// to allow for switching between JBossSerialization and JavaSerialization using
1117
// system property -D=session.serialization.jboss=true / false
1118

1119         // MarshalledValueOutputStream oos = new MarshalledValueOutputStream(baos);
1120

1121         ObjectOutputStream JavaDoc oos = SessionSerializationFactory.createObjectOutputStream(baos);
1122         session.writeExternal(oos);
1123         oos.close(); // flushes bytes to baos
1124

1125         byte[] bytes = baos.toByteArray();
1126         
1127         if (log_.isTraceEnabled())
1128         {
1129            log_.trace("Serialized Object to size " + bytes.length + " bytes");
1130         }
1131
1132         return bytes;
1133      }
1134      catch (Exception JavaDoc e)
1135      {
1136         log_.error("externalizeSession(): exception occurred externalizing session " + session, e);
1137         return null;
1138      }
1139      
1140   }
1141
1142}
1143
Popular Tags