KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > cache > aop > TreeCacheAop


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

7 package org.jboss.cache.aop;
8
9 import org.jboss.aop.Advised;
10 import org.jboss.aop.ClassInstanceAdvisor;
11 import org.jboss.aop.InstanceAdvisor;
12 import org.jboss.aop.advice.Interceptor;
13 import org.jboss.aop.proxy.ClassProxy;
14 import org.jboss.cache.*;
15 import org.jboss.cache.transaction.BatchModeTransactionManager;
16 import org.jboss.cache.eviction.AopEvictionPolicy;
17 import org.jboss.util.NestedRuntimeException;
18 import org.jgroups.JChannel;
19
20 import javax.transaction.*;
21 import java.io.Serializable JavaDoc;
22 import java.lang.reflect.Field JavaDoc;
23 import java.util.*;
24
25 /**
26  * Implementation of the cache using dyanmic aop interceptors. The basic idea is that the caller only
27  * uses the {@link #putObject(String,Object)}, {@link #getObject(String)} and
28  * {@link #removeObject(String)} methods.<br>
29  * When putting an object into the cache, the cache essentially takes care of how the object
30  * will be replicated. It 'breaks' the object apart to map it onto the tree, and adds an
31  * interceptor recursively for each object reachable from the root object. Those interceptor
32  * 'know' to which part of the tree they are mapped. Whenever there is state change
33  * ("set*" interceptors), the tree is modified using the superclass. All "get*" operations
34  * to the object are intercepted and redirected to the tree.<br>
35  * Bottom line is that a user simply puts <em>any</em> object into the cache, and the object
36  * will be replicated to all caches in the cluster. The only requirement on cachable objects
37  * is that they access all state through getters and setters.
38  *
39  * @author Harald Gliebe
40  * @author Ben Wang
41  * @jmx.mbean extends="org.jboss.cache.TreeCacheMBean"
42  */

43 public class TreeCacheAop extends TreeCache implements TreeCacheAopMBean
44 {
45    // Class -> CachedType
46
// use WeakHashMap to allow class reloading
47
protected Map cachedTypes = new WeakHashMap();
48    public static final String JavaDoc CLASS_INTERNAL = "jboss:internal:class";
49    public static final Fqn JBOSS_INTERNAL = new Fqn("JBossInternal");
50    public static final String JavaDoc DUMMY = "dummy";
51    // Use batch mode tm to simulate the automicity.
52
TransactionManager localTm_ = BatchModeTransactionManager.getInstance();
53
54
55    public TreeCacheAop(String JavaDoc cluster_name,
56                        String JavaDoc props,
57                        long state_fetch_timeout)
58          throws Exception JavaDoc
59    {
60       super(cluster_name, props, state_fetch_timeout);
61    }
62
63    public TreeCacheAop() throws Exception JavaDoc
64    {
65    }
66
67    public TreeCacheAop(JChannel channel) throws Exception JavaDoc
68    {
69       super(channel);
70    }
71
72    /**
73     * Over-ride to make sure we are using an eviction policy specific to aop.
74     *
75     * @jmx.managed-attribute
76     */

77    public void setEvictionPolicyClass(String JavaDoc eviction_policy_class) {
78       this.eviction_policy_class=eviction_policy_class;
79       if(eviction_policy_class == null || eviction_policy_class.length() ==0)
80          return;
81
82       try {
83          Object JavaDoc obj = getClass().getClassLoader().loadClass(eviction_policy_class).newInstance();
84          if( ! (obj instanceof AopEvictionPolicy))
85             throw new RuntimeException JavaDoc("TreeCacheAop.setEvictionPolicyClass(). Eviction policy provider:" +
86                   eviction_policy_class +" is not an instance of AopEvictionPolicy.");
87          eviction_policy_provider =(TreeCacheListener) obj;
88          this.addTreeCacheListener(eviction_policy_provider );
89       }
90       catch(RuntimeException JavaDoc ex) {
91          log.error("setEvictionPolicyClass(): failed creating instance of " + eviction_policy_class, ex);
92          throw ex;
93       }
94       catch(Throwable JavaDoc t) {
95          log.error("setEvictionPolicyClass(): failed creating instance of " + eviction_policy_class, t);
96       }
97    }
98
99    /**
100     * Insert an aop-enabled object into the cache.
101     * It will also recursively put the any sub-object that is
102     * declared as aop-capable (i.e., in <code>jboss-aop.xml</code>).
103     * Note that <code>List</code>, <code>Map</code>, <code>Set</code>
104     * attributes are aop-enabled, by default, as well.
105     *
106     * @param fqn The fqn string name to associate with the object in the cache.
107     * @param obj aop-enabled object to be inerted into the cache. If null,
108     * it will nullify the fqn node.
109     * @param obj Return the previous content under fqn.
110     * @throws CacheException
111     * @jmx.managed-operation
112     */

113    public Object JavaDoc putObject(String JavaDoc fqn, Object JavaDoc obj) throws CacheException
114    {
115       return putObject(Fqn.fromString(fqn), obj);
116    }
117
118    /**
119     * Insert an aop-enabled object into the cache.
120     * It will also recursively put the any sub-object that is
121     * declared as aop-capable (i.e., in <code>jboss-aop.xml</code>).
122     * Note that <code>List</code>, <code>Map</code>, <code>Set</code>
123     * attributes are aop-enabled, by default, as well.
124     *
125     * @param fqn The fqn instance to associate with the object in the cache.
126     * @param obj aop-enabled object to be inerted into the cache. If null,
127     * it will nullify the fqn node.
128     * @param obj Return the previous content under fqn.
129     * @throws CacheException
130     * @jmx.managed-operation
131     */

132    public Object JavaDoc putObject(Fqn fqn, Object JavaDoc obj) throws CacheException
133    {
134       if( hasCurrentTransaction() ) // We have a transaction context going on now.
135
{
136          return _putObject(fqn, obj);
137       } else
138       {
139          // Start a new transaction, we need transaction so the operation is atomic.
140
try
141          {
142             localTm_.begin();
143             // TODO Need to find a btter way to lock the node.
144
put(fqn, DUMMY, DUMMY);
145             Object JavaDoc objOld = _putObject(fqn,obj);
146             remove(fqn, DUMMY);
147             return objOld;
148          }
149          catch (Exception JavaDoc e)
150          {
151             e.printStackTrace();
152             try
153             {
154                localTm_.setRollbackOnly();
155             }
156             catch (Exception JavaDoc exn)
157             {
158                exn.printStackTrace();
159             }
160             throw new NestedRuntimeException("TreeCacheAop.putObject(): ", e);
161          }
162          finally
163          {
164             endTransaction();
165          }
166       }
167    }
168
169    protected void endTransaction()
170    {
171       try {
172          if(localTm_.getTransaction().getStatus() != Status.STATUS_MARKED_ROLLBACK)
173          {
174             localTm_.commit();
175          } else
176          {
177             localTm_.rollback();
178          }
179       } catch (Exception JavaDoc e) {
180          e.printStackTrace();
181          throw new NestedRuntimeException("TreeCacheAop.endTransaction(): ", e);
182       }
183    }
184
185    /**
186     *
187     * @param fqn
188     * @param obj
189     * @return
190     * @throws CacheException
191     */

192    public Object JavaDoc _putObject(Fqn fqn, Object JavaDoc obj) throws CacheException
193    {
194       checkObjectType(obj);
195       AOPInstance aopInstance = (AOPInstance) peek(fqn, AOPInstance.KEY);
196       // Skip some un-necessary update if obj is the same class as the old one
197
boolean isSameClass = false;
198       Object JavaDoc oldValue = null;
199       // Remember not to print obj here since it will trigger the CacheInterceptor.
200
if(log.isDebugEnabled()) {
201          log.debug("putObject(): fqn: " + fqn);
202       }
203
204       if (aopInstance != null) {
205          if (aopInstance.get() == obj) {
206             // obj is already cached
207
return obj;
208          }
209
210          if (obj == null) {
211             return removeObject(fqn);
212          }
213
214          // Optimization that won't remove the underlying object if
215
// they are the same object type.
216
log.debug("putObject(): old class type: " + aopInstance.get());
217          if (obj.getClass().isInstance(aopInstance.get())) {
218             if (log.isDebugEnabled()) {
219                log.debug("putObject(): obj is same class type as the old one");
220             }
221             oldValue = getObject(fqn);
222             isSameClass = true;
223          } else {
224             oldValue = removeObject(fqn);
225          }
226       } else {
227          // If obj is a primitive type or other un-delcared classes
228
Class JavaDoc claz1 = (Class JavaDoc) peek(fqn, CLASS_INTERNAL);
229          if (claz1 != null &&
230                obj.getClass().getName().equals(claz1.getName())) {
231             if (log.isDebugEnabled()) {
232                log.debug("putObject(): obj is same class type as the old one");
233             }
234             isSameClass = true;
235          }
236       }
237
238
239       if (obj == null) { // means aopInstance is null as well
240
return null;
241       }
242
243       // store object in cache
244
if (obj instanceof Advised) {
245          CachedType type = getCachedType(obj.getClass());
246          // Let's put it right away so the object can be locked by the cache.
247
// put(fqn, CLASS_INTERNAL, type.getType());
248

249          Fqn internalFqn = null;
250          // add interceptor
251
InstanceAdvisor advisor = ((Advised) obj)._getInstanceAdvisor();
252          // Let's check for object graph, e.g., multiple and circular references first
253
if ((internalFqn = checkCircularReference(fqn, advisor, obj)) != null) {
254             // Remove existing one
255
removeObject(fqn);
256             aopInstance = new AOPInstance();
257             aopInstance.setRefFqn(internalFqn.toString());
258             put(fqn, AOPInstance.KEY, aopInstance);
259             put(fqn, CLASS_INTERNAL, type.getType());
260             return oldValue;
261          } else if ((internalFqn = handleObjectGraph(fqn, advisor, type, obj)) != null) { // found cross references
262
// Let's set the reference fqn for getObject later.
263
if (log.isDebugEnabled()) {
264                log.debug("putObject(): detected multiple references. Will use as a reference instead. Current fqn: "
265                      + fqn + " Reference fqn: " + internalFqn);
266             }
267             // Remove existing one if any
268
removeObject(fqn);
269             aopInstance = new AOPInstance();
270             setRefFqn(aopInstance, internalFqn.toString());
271             put(fqn, AOPInstance.KEY, aopInstance);
272             put(fqn, CLASS_INTERNAL, type.getType());
273             return oldValue;
274          }
275
276          // workaround for deserialiased objects
277
if (advisor == null) {
278             advisor = new ClassInstanceAdvisor(obj);
279             ((Advised) obj)._setInstanceAdvisor(advisor);
280          }
281
282          // Insert interceptor at runtime
283
advisor.appendInterceptor(new CacheInterceptor(this, fqn, type));
284
285          // Let's deflate the objet here. If the field is another "aspectized" object,
286
// we will do it recursively.
287
put(fqn, CLASS_INTERNAL, type.getType());
288          for (Iterator i = type.getFields().iterator(); i.hasNext();) {
289             Field JavaDoc field = (Field JavaDoc) i.next();
290             Object JavaDoc value = null;
291             try {
292                value=field.get(obj);
293             }
294             catch(IllegalAccessException JavaDoc e) {
295                throw new CacheException("field access failed", e);
296             }
297             CachedType fieldType = getCachedType(field.getType());
298             if (fieldType.isImmediate()) {
299                put(fqn, field.getName(), value);
300             } else {
301                Fqn tmpFqn = new Fqn(fqn, field.getName());
302                _putObject(tmpFqn, value);
303                // If value (field member) is of Collection type, e.g., composite class
304
// that contains Collection member, we will swap out the old reference
305
// with the proxy one.
306
// This can probably be optimized with check for instanceof proxy
307
if( value instanceof Map || value instanceof List || value instanceof Set ) {
308                   Object JavaDoc newValue = getObject(tmpFqn);
309                   try {
310                      field.set(obj, newValue);
311                   } catch (IllegalAccessException JavaDoc e) {
312                      log.error("_putObject(): Can't swap out the Collection class of field " +field.getName() +
313                            "Exception " +e);
314                      throw new CacheException("_putObject(): Can't swap out the Collection class of field \" +field.getName(),"
315                            +e);
316                   }
317                }
318             }
319          }
320          // Put AOPInstance
321
put(fqn, AOPInstance.KEY, new AOPInstance(obj));
322
323
324          // the empty AOPInstance will be replicated and invalidate outdated references
325
// put(fqn, AOPInstance.KEY, new AOPInstance(obj));
326

327          // Need to make sure this is behind put such that obj.toString is done correctly.
328
if (log.isDebugEnabled()) {
329             log.debug("putObject(): inserting with fqn: " + fqn.toString());
330          }
331
332          /**
333           * Handling collection classes here.
334           * First check if obj has been aspectized? That is, if it is a ClassProxy or not.
335           * If not, we will need to create a proxy first for the Collection classes
336           */

337       } else if (obj instanceof Map) {
338          if (log.isDebugEnabled()) {
339             log.debug("putObject(): aspectized obj is a Map type of size: " + ((Map) obj).size());
340          }
341
342          // Need to remove the existing ones first.
343
removeObject(fqn);
344          put(fqn, CLASS_INTERNAL, obj.getClass());
345
346          // Let's replace it with a proxy if necessary
347
Object JavaDoc oldObj = obj;
348          if( !(obj instanceof ClassProxy)) {
349             Class JavaDoc clazz = obj.getClass();
350             try {
351                obj=CollectionInterceptorUtil.createProxy(clazz, new CachedMapInterceptor(this, fqn, clazz));
352             } catch (Exception JavaDoc e) {
353                throw new CacheException("failure creating proxy", e);
354             }
355          }
356
357          Map map = (Map) oldObj;
358
359          for (Iterator i = map.entrySet().iterator(); i.hasNext();) {
360             Map.Entry entry = (Map.Entry) i.next();
361             _putObject(new Fqn(fqn, entry.getKey()), entry.getValue());
362          }
363
364          put(fqn, AOPInstance.KEY, new AOPInstance(obj));
365       } else if (obj instanceof List) {
366          if (log.isDebugEnabled()) {
367             log.debug("putObject(): aspectized obj is a List type of size: "
368                   + ((List) obj).size());
369          }
370
371          List list = (List) obj;
372          // Need to remove the existing ones first.
373
removeObject(fqn);
374          put(fqn, CLASS_INTERNAL, obj.getClass());
375
376          // Let's replace it with a proxy if necessary
377
if( !(obj instanceof ClassProxy)) {
378             Class JavaDoc clazz = obj.getClass();
379             try {
380                obj=CollectionInterceptorUtil.createProxy(clazz, new CachedListInterceptor(this, fqn, clazz));
381             } catch (Exception JavaDoc e) {
382                throw new CacheException("failure creating proxy", e);
383             }
384          }
385
386          int idx = 0;
387          for (Iterator i = list.iterator(); i.hasNext();) {
388             _putObject(new Fqn(fqn, new Integer JavaDoc(idx++)), i.next());
389          }
390          // } else if (CachedType.isImmediate(obj.getClass())) {
391

392          put(fqn, AOPInstance.KEY, new AOPInstance(obj));
393       } else if (obj instanceof Set) {
394          if (log.isDebugEnabled()) {
395             log.debug("putObject(): aspectized obj is a Set type of size: "
396                   + ((Set) obj).size());
397          }
398
399          Set set = (Set) obj;
400          // Need to remove the existing ones first.
401
removeObject(fqn);
402          put(fqn, CLASS_INTERNAL, obj.getClass());
403
404          // Let's replace it with a proxy if necessary
405
if( !(obj instanceof ClassProxy)) {
406             Class JavaDoc clazz = obj.getClass();
407             try {
408                obj=CollectionInterceptorUtil.createProxy(clazz, new CachedSetInterceptor(this, fqn, clazz));
409             } catch (Exception JavaDoc e) {
410                throw new CacheException("failure creating proxy", e);
411             }
412          }
413
414          int idx = 0;
415          for (Iterator i = set.iterator(); i.hasNext();) {
416             _putObject(new Fqn(fqn, new Integer JavaDoc(idx++)), i.next());
417          }
418          // } else if (CachedType.isImmediate(obj.getClass())) {
419

420          put(fqn, AOPInstance.KEY, new AOPInstance(obj));
421       } else if (obj instanceof Serializable JavaDoc) { // must be Serializable, including pritimive types
422
if (log.isDebugEnabled()) {
423             log.debug("putObject(): obj (" + obj.getClass() + ") is non-advisable but is Serializable. ");
424          }
425
426          if (!isSameClass)
427             put(fqn, CLASS_INTERNAL, obj.getClass());
428
429          put(fqn, AOPInstance.KEY, new AOPInstance(obj));
430          put(fqn, "value", obj);
431       }
432
433       return oldValue;
434    }
435
436    /**
437     * Check whether the object type is valid. An object type is valid if it is either: aspectized,
438     * Serializable, or primitive type. Otherwise a runtime exception is thrown.
439     *
440     * @param obj
441     */

442    protected void checkObjectType(Object JavaDoc obj) {
443       if(obj == null) return;
444       if( ! (obj instanceof Advised) ) {
445           if( !(obj instanceof Serializable JavaDoc ) ) {
446                throw new IllegalArgumentException JavaDoc("TreeCacheAop.putObject(): Object type is neither " +
447                   " aspectized nor Serializable. Object class name is " +obj.getClass().getName());
448           }
449       }
450    }
451
452    protected Fqn checkCircularReference(Fqn fqn, InstanceAdvisor advisor, Object JavaDoc obj)
453    {
454       if (advisor == null) return null; // this can be a case of non-advised POJO.
455

456       Fqn originalFqn = null;
457       // Step Check for cross references
458
org.jboss.aop.advice.Interceptor interceptor = findCacheInterceptor(advisor);
459       if (interceptor == null) {
460          return null;
461       }
462
463       // ah, found something. So this will be multiple referenced.
464
originalFqn = ((CacheInterceptor) interceptor).getFqn();
465
466       // if it is a circular reference,
467
// we will simply return the fqn associated that
468
if (fqn.isChildOf(originalFqn)) {
469          if (log.isDebugEnabled()) {
470             log.debug("checkCircularReference(): is child for circular ref fqn " + originalFqn);
471          }
472          return originalFqn;
473       }
474       return null;
475    }
476
477    /**
478     * Handle cases where there is circular, e.g., parent refers to child and child to parent, or multiple references,
479     * e.g., two objects reference the same
480     * sub-object. In this case, we will remove the current sub-object and substitue with a ref. The
481     * reference will point to an Jboss internal node where the sub-object is re-created and stored.
482     * It will also have reference counting to keep track of garbage collection.
483     *
484     * @param fqn Current fqn to store this sub-object
485     * @param advisor The associated Advisor instance
486     * @return The fqn that refers to the sub-object stored in JBoss internal node.
487     */

488    protected Fqn handleObjectGraph(Fqn fqn, InstanceAdvisor advisor, CachedType type, Object JavaDoc obj) throws CacheException {
489       if (advisor == null) return null; // this can be a case of non-advised POJO.
490

491       Fqn originalFqn = null;
492       Fqn internalFqn = null;
493       // Step Check for cross references
494
org.jboss.aop.advice.Interceptor interceptor = findCacheInterceptor(advisor);
495       if (interceptor == null) {
496          if (log.isDebugEnabled()) {
497             log.debug("handleMultipleReference(): No multiple refernce found for fqn: " + fqn);
498          }
499          // Nothing found. Means this is a fresh object. No need to handle.
500
return null;
501       }
502
503       // ah, found something. So this will be multiple referenced.
504
originalFqn = ((CacheInterceptor) interceptor).getFqn();
505        // If we are the same fqn, this is not multiple referenced!
506
// This can be the case when we will reconstruct the node behind the scence.
507
if( fqn.equals(originalFqn) ) return null;
508
509       if (log.isDebugEnabled()) {
510          log.debug("handleObjectGraph(): Found multiple refernce at original fqn: " + originalFqn);
511       }
512
513       // Step Check if the reference node locates under JBoss internal already.
514
// Or if it is a circular reference.
515
// If it is, we will simply return the fqn associated that
516
if (originalFqn.isChildOf(JBOSS_INTERNAL)) {
517          if (log.isDebugEnabled()) {
518             log.debug("handleObjectGraph(): is child for fqn " + originalFqn);
519          }
520          return originalFqn;
521       } else {
522          // If not, we will need to create the sub-object under JBoss internal
523
internalFqn = createInternalNode(originalFqn);
524          // Then we will remove the original sub-object node (leaves only AopInstance)
525
Object JavaDoc oldValue = removeObject(originalFqn);
526          // Put it under JBoss internal
527
putObject(internalFqn, obj);
528          // Need to call this after putObject so interceptor will be there.
529
AOPInstance instance = new AOPInstance();
530          setRefFqn(instance, internalFqn.toString());
531          put(originalFqn, AOPInstance.KEY, instance); // put aopInstance back to indicate a ref
532
put(originalFqn, CLASS_INTERNAL, type.getType());
533          if (log.isDebugEnabled()) {
534             log.debug("handleObjectGraph(): relocate the original fqn: " + originalFqn +
535                   " to JBossInternal: " + internalFqn + " with obj: " + oldValue);
536 // log.debug("handleObjectGraph(): print cache nodes details: " +printDetails());
537
}
538          // Finally, we return with the refFqn.
539
return internalFqn;
540       }
541    }
542
543    /**
544     * Find cache interceptor with exact fqn.
545     * @param advisor
546     * @param fqn
547     * @return
548     */

549    protected Interceptor findCacheInterceptor(InstanceAdvisor advisor, Fqn fqn)
550    {
551       org.jboss.aop.advice.Interceptor[] interceptors = advisor.getInterceptors();
552       // Step Check for cross references
553
for (int i = 0; i < interceptors.length; i++) {
554          Interceptor interceptor = interceptors[i];
555          if (interceptor instanceof CacheInterceptor) {
556             CacheInterceptor inter = (CacheInterceptor)interceptor;
557             if (inter != null && inter.getFqn().equals(fqn))
558             {
559                return interceptor;
560             }
561          }
562       }
563       return null;
564    }
565
566    /**
567     * Find existing cache interceptor. Since there is supposedly only one cache interceptor per
568     * pojo, this call should suffice. In addition, in cases of cross or circular reference,
569     * fqn can be different anyway.
570     * @param advisor
571     * @return
572     */

573    protected Interceptor findCacheInterceptor(InstanceAdvisor advisor)
574    {
575       org.jboss.aop.advice.Interceptor[] interceptors = advisor.getInterceptors();
576       // Step Check for cross references
577
for (int i = 0; i < interceptors.length; i++) {
578          Interceptor interceptor = interceptors[i];
579          if (interceptor instanceof CacheInterceptor) {
580                return interceptor;
581          }
582       }
583       return null;
584    }
585
586    // TODO Need to mangle the name to obtain uniqueness
587
protected Fqn createInternalNode(Fqn storedFqn)
588    {
589       Fqn fqn = new Fqn(JBOSS_INTERNAL, storedFqn);
590       return fqn;
591    }
592
593    /**
594     * Retrieve the aop-enabled object from the cache.
595     *
596     * @param fqn String name that associates with this node.
597     * @return Current content value. Null if does not exist.
598     * @throws CacheException
599     * @jmx.managed-operation
600     */

601    public Object JavaDoc getObject(String JavaDoc fqn) throws CacheException
602    {
603       return getObject(Fqn.fromString(fqn));
604    }
605
606    protected boolean hasCurrentTransaction()
607    {
608       try {
609          if(getCurrentTransaction() != null || localTm_.getTransaction() != null)
610          {
611             // We have transaction context. Return null to signify don't do anything
612
return true;
613          }
614       } catch (SystemException e) {
615          e.printStackTrace();
616       }
617       return false;
618    }
619
620    /**
621     * Retrieve the aop-enabled object from the cache. Return null if object does not exist in the cache.
622     *
623     * @param fqn Instance that associates with this node.
624     * @return Current content value. Null if does not exist.
625     * @throws CacheException
626     * @jmx.managed-operation
627     */

628    public Object JavaDoc getObject(Fqn fqn) throws CacheException
629    {
630       if( hasCurrentTransaction() ) // We have a transaction context going on now.
631
{
632          Object JavaDoc obj = _getObject(fqn);
633          return obj;
634       } else
635       {
636          // Start a new transaction, we need transaction so the operation is atomic.
637
// getObject may not need it in the future.
638
try
639          {
640             localTm_.begin();
641             // TODO Need to find a btter way to lock the node.
642
put(fqn, DUMMY, DUMMY);
643             Object JavaDoc obj = _getObject(fqn);
644             remove(fqn, DUMMY);
645             return obj;
646          }
647          catch (Exception JavaDoc e)
648          {
649             e.printStackTrace();
650             try
651             {
652                localTm_.setRollbackOnly();
653             }
654             catch (Exception JavaDoc exn)
655             {
656                exn.printStackTrace();
657             }
658             // We will need to alert Tomcat of this exception.
659
throw new NestedRuntimeException("TreeCacheAop.getObject(): ", e);
660          }
661          finally
662          {
663             endTransaction();
664          }
665
666       }
667    }
668
669    private Object JavaDoc _getObject(Fqn fqn) throws CacheException
670    {
671       AOPInstance aopInstance = (AOPInstance) peek(fqn, AOPInstance.KEY);
672
673       if (aopInstance != null && aopInstance.get() != null) {
674          // we already have an advised instance
675
return aopInstance.get();
676       }
677
678       // the class attribute is implicitly stored as an immutable read-only attribute
679
Class JavaDoc clazz = (Class JavaDoc) peek(fqn, CLASS_INTERNAL);
680       // clazz and aopInstance can be not null if this node is the replicated brother node.
681
if (clazz == null || aopInstance == null)
682          return null;
683
684       CachedType type = getCachedType(clazz);
685       Object JavaDoc obj = null;
686       if (Advised.class.isAssignableFrom(clazz)) {
687          String JavaDoc refFqn = aopInstance.getRefFqn();
688          if (refFqn == null) { // Create a new instance and also add in the CacheInterceptor for this fqn.
689
try {
690                obj = clazz.newInstance();
691             }
692             catch(Exception JavaDoc e) {
693                throw new CacheException("failed creating instance of " + clazz.getName(), e);
694             }
695             // Insert interceptor at runtime
696
InstanceAdvisor advisor = ((Advised) obj)._getInstanceAdvisor();
697             advisor.appendInterceptor(new CacheInterceptor(this, fqn, type));
698          } else {
699             // this is recursive. Need to obtain the object from parent fqn
700
// No need to add CacheInterceptor as a result. Everything is re-directed.
701
// In addition, this op will not be recursive.
702
if (log.isDebugEnabled()) {
703                log.debug("getObject(): obtain value from reference fqn: " + refFqn);
704             }
705             obj = getObject(refFqn);
706          }
707
708       } else { // Must be Collection classes. We will use aop.ClassProxy instance instead.
709
try {
710             if(Map.class.isAssignableFrom(clazz)) {
711                obj=CollectionInterceptorUtil.createProxy(clazz, new CachedMapInterceptor(this, fqn, clazz));
712             } else if(List.class.isAssignableFrom(clazz)) {
713                obj=CollectionInterceptorUtil.createProxy(clazz, new CachedListInterceptor(this, fqn, clazz));
714             } else if(Set.class.isAssignableFrom(clazz)) {
715                obj=CollectionInterceptorUtil.createProxy(clazz, new CachedSetInterceptor(this, fqn, clazz));
716             } else {
717                // Maybe it is just a serialized object.
718
obj=peek(fqn, "value");
719             }
720          }
721          catch(Exception JavaDoc e) {
722             throw new CacheException("failure creating proxy", e);
723          }
724       }
725
726       if (aopInstance == null) {
727          // shouldn't happen since we serialize AopInstance now.
728
throw new RuntimeException JavaDoc("getObject(): aopInstance is null");
729       }
730       aopInstance.set(obj);
731       return obj;
732    }
733
734    /**
735     * Remove aop-enabled object from the cache.
736     *
737     * @param fqn String name that associates with this node.
738     * @return Value object from this node.
739     * @throws CacheException
740     * @jmx.managed-operation
741     */

742    public Object JavaDoc removeObject(String JavaDoc fqn) throws CacheException
743    {
744       return removeObject(Fqn.fromString(fqn));
745    }
746
747    /**
748     * Remove aop-enabled object from the cache.
749     *
750     * @param fqn Instance that associates with this node.
751     * @return Original value object from this node.
752     * @throws CacheException
753     * @jmx.managed-operation
754     */

755    public Object JavaDoc removeObject(Fqn fqn) throws CacheException
756    {
757       if( hasCurrentTransaction() ) // We have a transaction context going on now.
758
{
759          return _removeObject(fqn, true);
760       } else
761       {
762          // Start a new transaction, we need transaction so the operation is atomic.
763
try
764          {
765             localTm_.begin();
766             // TODO Need to find a btter way to lock the node.
767
put(fqn, DUMMY, DUMMY);
768             Object JavaDoc obj = _removeObject(fqn, true);
769             remove(fqn, DUMMY);
770             return obj;
771          }
772          catch (Exception JavaDoc e)
773          {
774             e.printStackTrace();
775             try
776             {
777                localTm_.setRollbackOnly();
778             }
779             catch (Exception JavaDoc exn)
780             {
781                exn.printStackTrace();
782             }
783             // We will need to alert Tomcat of this exception.
784
throw new NestedRuntimeException("TreeCacheAop.removeObject(): ", e);
785          }
786          finally
787          {
788             endTransaction();
789          }
790       }
791    }
792
793    protected Object JavaDoc _removeObject(Fqn fqn, boolean removeCacheInterceptor) throws CacheException
794    {
795       if (log.isDebugEnabled()) {
796          log.debug("removeObject(): removing object from fqn: " + fqn);
797       }
798
799       Class JavaDoc clazz = (Class JavaDoc) peek(fqn, CLASS_INTERNAL);
800       // Let's trigger the WL.
801
// Class clazz = (Class) remove(fqn, CLASS_INTERNAL);
802
if (clazz == null)
803       {
804          if (log.isTraceEnabled()) {
805             log.trace("removeObject(): clasz is null. fqn: " + fqn);
806          }
807          return null;
808       }
809
810       Object JavaDoc result = getObject(fqn);
811       AOPInstance aopInstance = (AOPInstance) peek(fqn, AOPInstance.KEY);
812       if (Advised.class.isAssignableFrom(clazz)) {
813          String JavaDoc refFqn = aopInstance.getRefFqn();
814          InstanceAdvisor advisor = ((Advised) result)._getInstanceAdvisor();
815          // check if this is a refernce
816
if (refFqn != null) {
817             if (log.isDebugEnabled()) {
818                log.debug("removeObject(): removing object fqn: " + fqn + " but is actually from ref fqn: " + refFqn);
819             }
820             removeRefFqn(aopInstance, refFqn, removeCacheInterceptor);
821          } else {
822             CachedType type = getCachedType(clazz);
823             for (Iterator i = type.getFields().iterator(); i.hasNext();) {
824                Field JavaDoc field = (Field JavaDoc) i.next();
825                CachedType fieldType = getCachedType(field.getType());
826                if (!fieldType.isImmediate()) {
827                   Object JavaDoc obj = _removeObject(new Fqn(fqn, field.getName()), removeCacheInterceptor);
828                   if (obj == null) continue;
829                }
830             }
831             // Determine if we want to keep the interceptor for later use.
832
if(removeCacheInterceptor) {
833                CacheInterceptor interceptor = (CacheInterceptor) findCacheInterceptor(advisor);
834 // if (log.isDebugEnabled()) {
835
// log.debug("removeObject(): removing cache interceptor fqn: " + fqn);
836
// }
837
// Remember to remove the interceptor from in-memory object but make sure it belongs to me first.
838
if (interceptor != null)
839                {
840                   if (log.isDebugEnabled()) {
841                      log.debug("removeObject(): removed cache interceptor fqn: " + fqn + " interceptor: "+interceptor);
842                   }
843                   advisor.removeInterceptor(interceptor.getName());
844                }
845             }
846          }
847       } else if (Map.class.isAssignableFrom(clazz)) {
848          Map values = get(fqn).getChildren();
849          if (values != null) {
850             ArrayList list = new ArrayList(values.keySet()); // need to clone it first.
851
for (int i=0; i < list.size(); i++) {
852                Object JavaDoc key = list.get(i);
853                _removeObject(new Fqn(fqn, key), removeCacheInterceptor);
854             }
855          }
856       } else if (Collection.class.isAssignableFrom(clazz)) {
857          Map values = get(fqn).getChildren();
858          int size = values == null ? 0 : values.size();
859          for (int i = 0; i < size; i++) {
860             _removeObject(new Fqn(fqn, new Integer JavaDoc(i)), removeCacheInterceptor);
861          }
862       }
863
864       // kind of brute force now.
865
remove(fqn);
866       // remove the interceptor as well.
867
return result;
868    }
869
870
871    /**
872     * Obtain a cache aop type for user to traverse the defined "primitive" types in aop.
873     *
874     * @param clazz The original pojo class
875     * @return CachedType
876     * @jmx.managed-operation
877     */

878    public synchronized CachedType getCachedType(Class JavaDoc clazz)
879    {
880       CachedType type = (CachedType) cachedTypes.get(clazz);
881       if (type == null) {
882          type = new CachedType(clazz);
883          cachedTypes.put(clazz, type);
884       }
885       return type;
886    }
887
888    void setRefFqn(AOPInstance instance, String JavaDoc refFqn) throws CacheException {
889       AOPInstance refInstance = (AOPInstance) peek(Fqn.fromString(refFqn), AOPInstance.KEY);
890       synchronized (this) {
891          refInstance.incrementRefCount();
892          // Will need to propagate the change. Now why can't we advise this as well. :-)
893
put(refFqn, AOPInstance.KEY, refInstance);
894       }
895       instance.setRefFqn(refFqn);
896    }
897
898    void removeRefFqn(AOPInstance instance, String JavaDoc refFqn, boolean removeCacheInterceptor) throws CacheException {
899       AOPInstance refInstance = (AOPInstance) peek(Fqn.fromString(refFqn), AOPInstance.KEY);
900       // take care of reference counting
901
// Idea is to track reference counting only for object stored in JBossInternal.
902
synchronized (this) {
903          if (refInstance.decrementRefCount() == 0) {
904             _removeObject(Fqn.fromString(refFqn), removeCacheInterceptor);
905          } else {
906             // Will need to propagate the change
907
put(refFqn, AOPInstance.KEY, refInstance);
908          }
909       }
910
911       instance.removeRefFqn();
912    }
913
914    /** Override the base class to provide some aop specific behaviors.
915     *
916     */

917    public Object JavaDoc _put(GlobalTransaction tx, Fqn fqn, Object JavaDoc key, Object JavaDoc value,
918                       boolean create_undo_ops) throws CacheException
919    {
920       Object JavaDoc result = super._put(tx, fqn, key, value, create_undo_ops);
921       // Special case
922
if (key == AOPInstance.KEY)
923          return result;
924
925       AOPInstance aopInstance = (AOPInstance) _get(fqn, AOPInstance.KEY, false);
926       Object JavaDoc instance = null;
927       if (aopInstance == null || (instance = aopInstance.get()) == null) {
928          return result;
929       }
930
931       // Besides putting in the cache, also update the in-memory version as well.
932
CachedType type = getCachedType((Class JavaDoc) _get(fqn, CLASS_INTERNAL, false));
933
934       if(type.isImmediate()) return result;
935
936       Field JavaDoc f = type.getField((String JavaDoc) key);
937       if(f == null) return result; // we know this is Serializable then.
938

939       try {
940          f.set(instance, value);
941       } catch (Exception JavaDoc e) {
942          throw new NestedRuntimeException(e);
943       }
944
945       return result;
946    }
947
948    /** Override to provide aop specific eviction.
949     *
950     * <p>
951     * Called by eviction policy provider. Note that eviction is done only in local mode,
952     * that is, it doesn't replicate the node removal. This is will cause the replcation nodes
953     * not synchronizing, but it is ok since user is supposed to add the node again when get is
954     * null. After that, the contents will be in sync.
955     * @param fqn Will remove everythign assoicated with this fqn.
956     * @throws CacheException
957     */

958    public void evict(Fqn fqn) throws CacheException {
959       // We will remove all children nodes as well since we assume all children nodes are part
960
// of this "object" node.
961
if(isAopNode(fqn)) {
962          if(log.isDebugEnabled()) {
963             log.debug("evict(): evicting whole aop node " +fqn);
964          }
965 // _remove(null, fqn, create_undo_ops, false);
966
// TODO Why do we not want to remove the interceptor??? I think we should.
967
// Because if we remove it, the caller has no idea that it has been taken off.
968
// She will still think that caching is in effect. But this returns null value from cache.
969
// What's is a good policy?
970
boolean removeCacheInterceptor = false;
971          _removeObject(fqn, removeCacheInterceptor);
972       } else {
973          super.evict(fqn);
974       }
975    }
976
977    private boolean isAopNode(Fqn fqn)
978    {
979       try {
980          return (peek(fqn, AOPInstance.KEY) != null) ? true: false;
981       } catch (Exception JavaDoc e) {
982          log.warn("isAopNode(): cache get operation generated exception " +e);
983          return false;
984       }
985    }
986 }
987
Popular Tags