KickJava   Java API By Example, From Geeks To Geeks.

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


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.eviction.AopEvictionPolicy;
16 import org.jboss.cache.transaction.BatchModeTransactionManager;
17 import org.jboss.util.NestedRuntimeException;
18 import org.jgroups.JChannel;
19 import org.apache.commons.logging.Log;
20 import org.apache.commons.logging.LogFactory;
21
22 import javax.transaction.Status JavaDoc;
23 import javax.transaction.SystemException JavaDoc;
24 import javax.transaction.TransactionManager JavaDoc;
25 import java.io.Serializable JavaDoc;
26 import java.lang.reflect.Field JavaDoc;
27 import java.util.*;
28
29 /**
30  * @author Ben Wang
31  */

32 public class TreeCacheAopDelegate
33 {
34    protected TreeCacheAop cache_;
35    protected final static Log log=LogFactory.getLog(TreeCacheAopDelegate.class);
36    protected InternalDelegate internal_;
37
38    public TreeCacheAopDelegate(TreeCacheAop cache)
39    {
40       cache_ = cache;
41       internal_ = new InternalDelegate(cache);
42    }
43
44    protected Object JavaDoc _getObject(Fqn fqn) throws CacheException
45    {
46       Object JavaDoc pojo = internal_.getObjectInstance(fqn);
47       if ( pojo != null) {
48          // we already have an advised instance
49
return pojo;
50       }
51
52       // the class attribute is implicitly stored as an immutable read-only attribute
53
Class JavaDoc clazz = internal_.peekAopClazz(fqn);
54       // clazz and aopInstance can be not null if this node is the replicated brother node.
55
if (clazz == null)
56          return null;
57
58       CachedType type = cache_.getCachedType(clazz);
59       Object JavaDoc obj = null;
60       if (Advised.class.isAssignableFrom(clazz)) {
61          String JavaDoc refFqn = internal_.getRefFqn(fqn);
62          if (refFqn == null) { // Create a new instance and also add in the CacheInterceptor for this fqn.
63
try {
64                obj = clazz.newInstance();
65             }
66             catch(Exception JavaDoc e) {
67                throw new CacheException("failed creating instance of " + clazz.getName(), e);
68             }
69             // Insert interceptor at runtime
70
InstanceAdvisor advisor = ((Advised) obj)._getInstanceAdvisor();
71             advisor.appendInterceptor(new CacheInterceptor(cache_, fqn, type));
72          } else {
73             // this is recursive. Need to obtain the object from parent fqn
74
// No need to add CacheInterceptor as a result. Everything is re-directed.
75
// In addition, this op will not be recursive.
76
if (log.isDebugEnabled()) {
77                log.debug("getObject(): obtain value from reference fqn: " + refFqn);
78             }
79             obj = cache_.getObject(refFqn);
80          }
81
82       } else { // Must be Collection classes. We will use aop.ClassProxy instance instead.
83
try {
84             if(Map.class.isAssignableFrom(clazz)) {
85                obj=CollectionInterceptorUtil.createProxy(clazz, new CachedMapInterceptor(cache_, fqn, clazz));
86             } else if(List.class.isAssignableFrom(clazz)) {
87                obj=CollectionInterceptorUtil.createProxy(clazz, new CachedListInterceptor(cache_, fqn, clazz));
88             } else if(Set.class.isAssignableFrom(clazz)) {
89                obj=CollectionInterceptorUtil.createProxy(clazz, new CachedSetInterceptor(cache_, fqn, clazz));
90             } else {
91                // Maybe it is just a serialized object.
92
obj=cache_.peek(fqn, "value");
93             }
94          }
95          catch(Exception JavaDoc e) {
96             throw new CacheException("failure creating proxy", e);
97          }
98       }
99
100       internal_.setPOJO(fqn, obj);
101       return obj;
102    }
103
104    protected Object JavaDoc retrieveCurrentValue(Fqn fqn, Object JavaDoc obj) throws CacheException {
105       Object JavaDoc oldValue = null;
106       Object JavaDoc pojo = internal_.getObjectInstance(fqn);
107       if (pojo != null) {
108          if (pojo == obj) {
109             // obj is already cached. We are done.
110
return obj;
111          }
112
113          // Optimization that won't remove the underlying object if
114
// they are the same object type.
115
log.debug("putObject(): old class type: " + pojo);
116          if (obj.getClass().isInstance(pojo)) {
117             if (log.isDebugEnabled()) {
118                log.debug("putObject(): obj is same class type as the old one");
119             }
120             oldValue = cache_.getObject(fqn);
121          } else {
122             oldValue = cache_.removeObject(fqn);
123          }
124       } else {
125          // If obj is a primitive type or other un-delcared classes, or the first time around.
126
Class JavaDoc claz1 = internal_.peekAopClazz(fqn);
127          if (claz1 != null &&
128                obj.getClass().getName().equals(claz1.getName())) {
129             if (log.isDebugEnabled()) {
130                log.debug("putObject(): obj is same primitive class type as the old one");
131             }
132          }
133       }
134       return oldValue;
135    }
136
137    /**
138     *
139     * @param fqn
140     * @param obj
141     * @return
142     * @throws CacheException
143     */

144    protected Object JavaDoc _putObject(Fqn fqn, Object JavaDoc obj) throws CacheException
145    {
146       AopUtil.checkObjectType(obj);
147       if (obj == null) {
148          return cache_.removeObject(fqn);
149       }
150       // Skip some un-necessary update if obj is the same class as the old one
151
Object JavaDoc oldValue = retrieveCurrentValue(fqn, obj);
152       if(oldValue == obj) return obj; // value already in cache. return right away.
153

154       // Remember not to print obj here since it will trigger the CacheInterceptor.
155
if(log.isDebugEnabled()) {
156          log.debug("putObject(): fqn: " + fqn);
157       }
158
159       // store object in cache
160
if (obj instanceof Advised) {
161          CachedType type = cache_.getCachedType(obj.getClass());
162          // add interceptor
163
InstanceAdvisor advisor = ((Advised) obj)._getInstanceAdvisor();
164          if(advisor == null)
165             throw new RuntimeException JavaDoc("_putObject(): InstanceAdvisor is null for: " +obj);
166
167          // Let's check for object graph, e.g., multiple and circular references first
168
if(handleCircularReference(fqn, advisor, obj, type))
169          {
170             return oldValue;
171          } else if (handleObjectGraph(fqn, advisor, type, obj)) { // found cross references
172
return oldValue;
173          }
174
175          _regularPutObject(fqn, obj, advisor, type);
176
177          /**
178           * Handling collection classes here.
179           * First check if obj has been aspectized? That is, if it is a ClassProxy or not.
180           * If not, we will need to create a proxy first for the Collection classes
181           */

182       } else if (_collectionPutObject(fqn, obj)) {
183         //
184
} else if (obj instanceof Serializable JavaDoc) { // must be Serializable, including pritimive types
185
if (log.isDebugEnabled()) {
186             log.debug("putObject(): obj (" + obj.getClass() + ") is non-advisable but is Serializable. ");
187          }
188
189          internal_.putAopClazz(fqn, obj.getClass());
190
191          internal_.setPOJO(fqn, obj);
192          // TODO Can also use obj.getName()?
193
cache_.put(fqn, "value", obj);
194       }
195
196       return oldValue;
197    }
198
199    protected void _regularPutObject(Fqn fqn, Object JavaDoc obj, InstanceAdvisor advisor, CachedType type) throws CacheException
200    {
201       // TODO ? workaround for deserialiased objects
202
if (advisor == null) {
203          advisor = new ClassInstanceAdvisor(obj);
204          ((Advised) obj)._setInstanceAdvisor(advisor);
205       }
206
207       // Insert interceptor at runtime
208
advisor.appendInterceptor(new CacheInterceptor(cache_, fqn, type));
209       // Always initialize the ref count so we can mark this as an AopNode.
210
internal_.resetRefCount(fqn);
211       // Let's deflate the objet here. If the field is another "aspectized" object,
212
// we will do it recursively.
213
internal_.putAopClazz(fqn, type.getType());
214
215       for (Iterator i = type.getFields().iterator(); i.hasNext();) {
216          Field JavaDoc field = (Field JavaDoc) i.next();
217          Object JavaDoc value = null;
218          try {
219             value=field.get(obj);
220          }
221          catch(IllegalAccessException JavaDoc e) {
222             throw new CacheException("field access failed", e);
223          }
224          CachedType fieldType = cache_.getCachedType(field.getType());
225          if (fieldType.isImmediate()) {
226             cache_.put(fqn, field.getName(), value);
227          } else {
228             Fqn tmpFqn = new Fqn(fqn, field.getName());
229             _putObject(tmpFqn, value);
230             // If value (field member) is of Collection type, e.g., composite class
231
// that contains Collection member, we will swap out the old reference
232
// with the proxy one.
233
// This can probably be optimized with check for instanceof proxy
234
if( value instanceof Map || value instanceof List || value instanceof Set ) {
235                Object JavaDoc newValue = cache_.getObject(tmpFqn);
236                try {
237                   field.set(obj, newValue);
238                } catch (IllegalAccessException JavaDoc e) {
239                   log.error("_putObject(): Can't swap out the Collection class of field " +field.getName() +
240                         "Exception " +e);
241                   throw new CacheException("_putObject(): Can't swap out the Collection class of field \" +field.getName(),"
242                         +e);
243                }
244             }
245          }
246       }
247
248       // Put AOPInstance. Note pojo is "transient".
249
internal_.setPOJO(fqn, obj);
250       // Need to make sure this is behind put such that obj.toString is done correctly.
251
if (log.isDebugEnabled()) {
252          log.debug("putObject(): inserting with fqn: " + fqn.toString());
253       }
254    }
255
256    protected boolean _collectionPutObject(Fqn fqn, Object JavaDoc obj) throws CacheException {
257       boolean isCollection = false;
258
259       if (obj instanceof Map) {
260          if (log.isDebugEnabled()) {
261             log.debug("putObject(): aspectized obj is a Map type of size: " + ((Map) obj).size());
262          }
263
264          // Need to remove the existing ones first.
265
cache_.removeObject(fqn);
266          internal_.putAopClazz(fqn, obj.getClass());
267
268          // Let's replace it with a proxy if necessary
269
Object JavaDoc oldObj = obj;
270          if( !(obj instanceof ClassProxy)) {
271             Class JavaDoc clazz = obj.getClass();
272             try {
273                obj=CollectionInterceptorUtil.createProxy(clazz, new CachedMapInterceptor(cache_, fqn, clazz));
274             } catch (Exception JavaDoc e) {
275                throw new CacheException("failure creating proxy", e);
276             }
277          }
278
279          Map map = (Map) oldObj;
280
281          for (Iterator i = map.entrySet().iterator(); i.hasNext();) {
282             Map.Entry entry = (Map.Entry) i.next();
283             _putObject(new Fqn(fqn, entry.getKey()), entry.getValue());
284          }
285
286          internal_.setPOJO(fqn, obj);
287          isCollection = true;
288       } else if (obj instanceof List) {
289          if (log.isDebugEnabled()) {
290             log.debug("putObject(): aspectized obj is a List type of size: "
291                   + ((List) obj).size());
292          }
293
294          List list = (List) obj;
295          // Need to remove the existing ones first.
296
cache_.removeObject(fqn);
297          internal_.putAopClazz(fqn, obj.getClass());
298
299          // Let's replace it with a proxy if necessary
300
if( !(obj instanceof ClassProxy)) {
301             Class JavaDoc clazz = obj.getClass();
302             try {
303                obj=CollectionInterceptorUtil.createProxy(clazz, new CachedListInterceptor(cache_, fqn, clazz));
304             } catch (Exception JavaDoc e) {
305                throw new CacheException("failure creating proxy", e);
306             }
307          }
308
309          int idx = 0;
310          for (Iterator i = list.iterator(); i.hasNext();) {
311             _putObject(new Fqn(fqn, new Integer JavaDoc(idx++)), i.next());
312          }
313          // } else if (CachedType.isImmediate(obj.getClass())) {
314

315          internal_.setPOJO(fqn, obj);
316          isCollection = true;
317       } else if (obj instanceof Set) {
318          if (log.isDebugEnabled()) {
319             log.debug("putObject(): aspectized obj is a Set type of size: "
320                   + ((Set) obj).size());
321          }
322
323          Set set = (Set) obj;
324          // Need to remove the existing ones first.
325
cache_.removeObject(fqn);
326          internal_.putAopClazz(fqn, obj.getClass());
327
328          // Let's replace it with a proxy if necessary
329
if( !(obj instanceof ClassProxy)) {
330             Class JavaDoc clazz = obj.getClass();
331             try {
332                obj=CollectionInterceptorUtil.createProxy(clazz, new CachedSetInterceptor(cache_, fqn, clazz));
333             } catch (Exception JavaDoc e) {
334                throw new CacheException("failure creating proxy", e);
335             }
336          }
337
338          int idx = 0;
339          for (Iterator i = set.iterator(); i.hasNext();) {
340             _putObject(new Fqn(fqn, new Integer JavaDoc(idx++)), i.next());
341          }
342          // } else if (CachedType.isImmediate(obj.getClass())) {
343

344          internal_.setPOJO(fqn, obj);
345          isCollection = true;
346       }
347
348       return isCollection;
349    }
350
351    protected Object JavaDoc _removeObject(Fqn fqn, boolean removeCacheInterceptor) throws CacheException
352    {
353       Class JavaDoc clazz = internal_.peekAopClazz(fqn);
354       if (clazz == null)
355       {
356          if (log.isTraceEnabled()) {
357             log.trace("removeObject(): clasz is null. fqn: " + fqn);
358          }
359          return null;
360       }
361
362       if (log.isDebugEnabled()) {
363          log.debug("removeObject(): removing object from fqn: " + fqn);
364       }
365
366       Object JavaDoc result = cache_.getObject(fqn);
367       if(result == null)
368       {
369          if(cache_.exists(fqn))
370             cache_.remove(fqn); // brute force.
371

372          return null;
373       }
374
375 // AOPInstance aopInstance = internal_.peekAopInstance(fqn);
376
if (Advised.class.isAssignableFrom(clazz)) {
377          _regularRemoveObject(fqn, removeCacheInterceptor, result, clazz);
378       } else if (_collectionRemoveObject(fqn, removeCacheInterceptor)) {
379       }
380
381       // TODO kind of brute force now.
382
cache_.remove(fqn);
383       // remove the interceptor as well.
384
return result;
385    }
386
387    protected void _regularRemoveObject(Fqn fqn, boolean removeCacheInterceptor, Object JavaDoc result, Class JavaDoc clazz) throws CacheException
388    {
389       String JavaDoc refFqn = internal_.getRefFqn(fqn);
390       InstanceAdvisor advisor = ((Advised) result)._getInstanceAdvisor();
391       // check if this is a refernce
392
if (refFqn != null) {
393          if (log.isDebugEnabled()) {
394             log.debug("removeObject(): removing object fqn: " + fqn + " but is actually from ref fqn: " + refFqn);
395          }
396          removeRefFqn(fqn, refFqn, removeCacheInterceptor);
397       } else {
398          CachedType type = cache_.getCachedType(clazz);
399          for (Iterator i = type.getFields().iterator(); i.hasNext();) {
400             Field JavaDoc field = (Field JavaDoc) i.next();
401             CachedType fieldType = cache_.getCachedType(field.getType());
402             if (!fieldType.isImmediate()) {
403                Object JavaDoc obj = _removeObject(new Fqn(fqn, field.getName()), removeCacheInterceptor);
404                if (obj == null) continue;
405             }
406          }
407          // Determine if we want to keep the interceptor for later use.
408
if(removeCacheInterceptor) {
409             CacheInterceptor interceptor = (CacheInterceptor) AopUtil.findCacheInterceptor(advisor);
410 // if (log_.isDebugEnabled()) {
411
// log_.debug("removeObject(): removing cache interceptor fqn: " + fqn);
412
// }
413
// Remember to remove the interceptor from in-memory object but make sure it belongs to me first.
414
if (interceptor != null)
415             {
416                if (log.isDebugEnabled()) {
417                   log.debug("removeObject(): removed cache interceptor fqn: " + fqn + " interceptor: "+interceptor);
418                }
419                advisor.removeInterceptor(interceptor.getName());
420             }
421          }
422       }
423
424    }
425
426    protected boolean _collectionRemoveObject(Fqn fqn, boolean removeCacheInterceptor) throws CacheException
427    {
428       boolean isCollection = false;
429       Class JavaDoc clazz = internal_.peekAopClazz(fqn);
430
431       if (Map.class.isAssignableFrom(clazz)) {
432          Map values = cache_.get(fqn).getChildren();
433          if (values != null) {
434             ArrayList list = new ArrayList(values.keySet()); // need to clone it first.
435
for (int i=0; i < list.size(); i++) {
436                Object JavaDoc key = list.get(i);
437                _removeObject(new Fqn(fqn, key), removeCacheInterceptor);
438             }
439          }
440          isCollection = true;
441       } else if (Collection.class.isAssignableFrom(clazz)) {
442          Map values = cache_.get(fqn).getChildren();
443          int size = values == null ? 0 : values.size();
444          for (int i = 0; i < size; i++) {
445             _removeObject(new Fqn(fqn, new Integer JavaDoc(i)), removeCacheInterceptor);
446          }
447          isCollection = true;
448       }
449       return isCollection;
450    }
451
452    /**
453     * Check for circular reference. If there is one, we will remove any object from current fqn (if any),
454     * and then put a reference internal fqn in the AOPInstance in the current fqn.
455     */

456    protected boolean handleCircularReference(Fqn fqn, InstanceAdvisor advisor, Object JavaDoc obj, CachedType type)
457       throws CacheException
458    {
459       Fqn internalFqn;
460       if ((internalFqn = checkCircularReference(fqn, advisor, obj)) != null) {
461          // 1. Remove existing one, if there is any. 2. set the ref fqn to the internal one,
462
// 3. put aopInstane in the cache fqn node such that getObject will be redirected to the internal node.
463
cache_.removeObject(fqn);
464          this.addRefFqn(fqn, internalFqn.toString());
465          internal_.putAopClazz(fqn, type.getType());
466          return true;
467       }
468       else {
469          return false;
470       }
471
472    }
473
474    protected Fqn checkCircularReference(Fqn fqn, InstanceAdvisor advisor, Object JavaDoc obj)
475    {
476 // if (advisor == null) return null; // this can be a case of non-advised POJO.
477

478       Fqn originalFqn = null;
479       // Step Check for cross references
480
Interceptor interceptor = AopUtil.findCacheInterceptor(advisor);
481       if (interceptor == null) {
482          return null;
483       }
484
485       // ah, found something. So this will be multiple referenced.
486
originalFqn = ((CacheInterceptor) interceptor).getFqn();
487
488       // if it is a circular reference,
489
// we will simply return the fqn associated that
490
// We know it is circular reference because it is the fqn child references back to parent.
491
if (fqn.isChildOf(originalFqn)) {
492          if (log.isDebugEnabled()) {
493             log.debug("checkCircularReference(): is child for circular ref fqn " + originalFqn);
494          }
495          return originalFqn;
496       }
497       return null;
498    }
499
500    /**
501     * Handle cases where there is circular, e.g., parent refers to child and child to parent, or multiple references,
502     * e.g., two objects reference the same
503     * sub-object. In this case, we will remove the current sub-object and substitue with a ref. The
504     * reference will point to an Jboss internal node where the sub-object is re-created and stored.
505     * It will also have reference counting to keep track of garbage collection.
506     *
507     * @param fqn Current fqn to store this sub-object
508     * @param advisor The associated Advisor instance
509     * @return The fqn that refers to the sub-object stored in JBoss internal node.
510     */

511    protected boolean handleObjectGraph(Fqn fqn, InstanceAdvisor advisor, CachedType type, Object JavaDoc obj) throws CacheException
512    {
513       Fqn internalFqn = getObjectGraphInternalFqn(fqn, advisor, type, obj);
514       if(internalFqn == null) return false;
515
516       // Will need to remove the current object under fqn first.
517
cache_.removeObject(fqn);
518
519       // This will increment the ref count, reset, and add ref fqn in the current fqn node.
520
this.addRefFqn(fqn, internalFqn.toString());
521       internal_.putAopClazz(fqn, type.getType());
522
523       return true;
524    }
525
526    protected Fqn getObjectGraphInternalFqn(Fqn fqn, InstanceAdvisor advisor, CachedType type, Object JavaDoc obj) throws CacheException {
527       if (advisor == null) return null; // this can be a case of non-advised POJO.
528

529       Fqn originalFqn = null;
530       Fqn internalFqn = null;
531       // Step Check for cross references
532
Interceptor interceptor = AopUtil.findCacheInterceptor(advisor);
533       if (interceptor == null) {
534          if (log.isDebugEnabled()) {
535             log.debug("handleMultipleReference(): No multiple refernce found for fqn: " + fqn);
536          }
537          // Nothing found. Means this is a fresh object. No need to handle.
538
return null;
539       }
540
541       // ah, found something. So this will be multiple referenced.
542
originalFqn = ((CacheInterceptor) interceptor).getFqn();
543        // If we are the same fqn, this is not multiple referenced!
544
// This can be the case when we will reconstruct the node behind the scence.
545
if( fqn.equals(originalFqn) ) return null;
546
547       if (log.isDebugEnabled()) {
548          log.debug("handleObjectGraph(): Found multiple refernce at original fqn: " + originalFqn);
549       }
550
551       // Check if the reference node locates under JBoss internal already.
552
// Or if it is a circular reference.
553
// If it is, we will simply return the fqn associated that
554
if (internal_.isInternalNode(originalFqn)) {
555          if (log.isDebugEnabled()) {
556             log.debug("handleObjectGraph(): is child for fqn " + originalFqn);
557          }
558
559          // Remove existing one if any
560
cache_.removeObject(fqn);
561          return originalFqn;
562       } else {
563          // If not, we will need to create the sub-object under JBoss internal
564
internalFqn = internal_.createInternalNode(originalFqn);
565          // Then we will remove the original sub-object node (leaves only AopInstance)
566
Object JavaDoc oldValue = cache_.removeObject(originalFqn);
567          // Then put it under JBoss internal
568
cache_.putObject(internalFqn, obj);
569
570          // Need to call this after putObject so interceptor will be there.
571
this.addRefFqn(originalFqn, internalFqn.toString());
572          internal_.putAopClazz(originalFqn, type.getType());
573
574          if (log.isDebugEnabled()) {
575             log.debug("handleObjectGraph(): relocate the original fqn: " + originalFqn +
576                   " to JBossInternal: " + internalFqn + " with obj: " + oldValue);
577 // log_.debug("handleObjectGraph(): print cache nodes details: " +printDetails());
578
}
579          // Finally, we return with the refFqn.
580
return internalFqn;
581       }
582    }
583
584    /**
585     * 1. increment reference counter
586     * 2. set the current ref counter (@fqn) to 1. Used for aop marker.
587     * 3. put in refFqn so we can get it.
588     */

589    void addRefFqn(Fqn fqn, String JavaDoc refFqn) throws CacheException {
590       synchronized (this) {
591          // increment the reference counting
592
internal_.incrementRefCount(Fqn.fromString(refFqn));
593          internal_.resetRefCount(fqn); // marker for aop node.
594
// set the internal fqn in fqn so we can reference it.
595
internal_.setRefFqn(fqn, refFqn);
596       }
597    }
598
599    void removeRefFqn(Fqn fqn, String JavaDoc refFqn, boolean removeCacheInterceptor) throws CacheException {
600       synchronized (this) {
601          // take care of reference counting
602
// Idea is to track reference counting only for object stored in JBossInternal.
603
if (internal_.decrementRefCount(Fqn.fromString(refFqn)) == 0) {
604             _removeObject(Fqn.fromString(refFqn), removeCacheInterceptor);
605          } else {
606             internal_.removeRefFqn(fqn);
607          }
608       }
609
610       internal_.removeRefFqn(fqn);
611    }
612
613    boolean isAopNode(Fqn fqn)
614    {
615       try {
616          return (internal_.isAopNode(fqn));
617       } catch (Exception JavaDoc e) {
618          log.warn("isAopNode(): cache get operation generated exception " +e);
619          return false;
620       }
621    }
622 }
623
Popular Tags