Clover coverage report -
Coverage timestamp: Thu Jul 5 2007 20:02:32 EDT
file stats: LOC: 629   Methods: 22
NCLOC: 474   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
PojoCacheDelegate.java 42.4% 52.5% 63.6% 49.7%
coverage coverage
 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.pojo.impl;
 8   
 9    import org.apache.commons.logging.Log;
 10    import org.apache.commons.logging.LogFactory;
 11    import org.jboss.aop.Advised;
 12    import org.jboss.aop.Advisor;
 13    import org.jboss.aop.InstanceAdvisor;
 14    import org.jboss.aop.advice.Interceptor;
 15    import org.jboss.aop.proxy.ClassProxy;
 16    import org.jboss.cache.Cache;
 17    import org.jboss.cache.CacheException;
 18    import org.jboss.cache.CacheSPI;
 19    import org.jboss.cache.Fqn;
 20    import org.jboss.cache.Node;
 21    import org.jboss.cache.Region;
 22    import org.jboss.cache.pojo.PojoCacheException;
 23    import org.jboss.cache.pojo.collection.CollectionInterceptorUtil;
 24    import org.jboss.cache.pojo.interceptors.dynamic.AbstractCollectionInterceptor;
 25    import org.jboss.cache.pojo.interceptors.dynamic.BaseInterceptor;
 26    import org.jboss.cache.pojo.memory.FieldPersistentReference;
 27    import org.jboss.cache.pojo.util.AopUtil;
 28   
 29    import java.lang.reflect.Field;
 30    import java.util.Collection;
 31    import java.util.HashMap;
 32    import java.util.Iterator;
 33    import java.util.List;
 34    import java.util.Map;
 35    import java.util.Set;
 36   
 37    /**
 38    * Delegate class for PojoCache, the real implementation code happens here.
 39    *
 40    * @author Ben Wang
 41    */
 42    public class PojoCacheDelegate
 43    {
 44    private PojoCacheImpl pojoCache;
 45    private Cache<Object, Object> cache;
 46    private final static Log log = LogFactory.getLog(PojoCacheDelegate.class);
 47    private InternalHelper internal_;
 48    private AdvisedPojoHandler advisedHandler_;
 49    private ObjectGraphHandler graphHandler_;
 50    private CollectionClassHandler collectionHandler_;
 51    private SerializableObjectHandler serializableHandler_;
 52    // Use ThreadLocal to hold a boolean isBulkRemove
 53    private ThreadLocal<Boolean> bulkRemove_ = new ThreadLocal<Boolean>();
 54    private final String DETACH = "DETACH";
 55    private PojoUtil util_ = new PojoUtil();
 56   
 57  499 public PojoCacheDelegate(PojoCacheImpl cache)
 58    {
 59  499 pojoCache = cache;
 60  499 this.cache = pojoCache.getCache();
 61  499 internal_ = new InternalHelper(this.cache);
 62  499 graphHandler_ = new ObjectGraphHandler(pojoCache, internal_);
 63  499 collectionHandler_ = new CollectionClassHandler(pojoCache, internal_);
 64  499 serializableHandler_ = new SerializableObjectHandler(pojoCache, internal_);
 65  499 advisedHandler_ = new AdvisedPojoHandler(pojoCache, internal_, util_);
 66    }
 67   
 68  6316 public void setBulkRemove(boolean bulk)
 69    {
 70  6316 bulkRemove_.set(bulk);
 71    }
 72   
 73  0 private boolean getBulkRemove()
 74    {
 75  0 return bulkRemove_.get();
 76    }
 77   
 78  28025 public Object getObject(Fqn fqn, String field) throws CacheException
 79    {
 80    // TODO Must we really to couple with BR? JBCACHE-669
 81  28025 Object pojo = internal_.getPojo(fqn, field);
 82  28020 if (pojo != null)
 83    {
 84    // we already have an advised instance
 85  16219 if (log.isDebugEnabled())
 86    {
 87  0 log.debug("getObject(): id: " + fqn + " retrieved from existing instance directly. ");
 88    }
 89  16219 return pojo;
 90    }
 91   
 92    // OK. So we are here meaning that this is a failover or passivation since the transient
 93    // pojo instance is not around. Let's also make sure the right classloader is used
 94    // as well.
 95  11801 ClassLoader prevCL = Thread.currentThread().getContextClassLoader();
 96  11801 try
 97    {
 98  11801 Region region = cache.getRegion(fqn, false);
 99  11801 if (region != null)
 100  10555 Thread.currentThread().setContextClassLoader(region.getClassLoader());
 101   
 102  11801 return getObjectInternal(fqn, field);
 103    }
 104    finally
 105    {
 106  11801 Thread.currentThread().setContextClassLoader(prevCL);
 107    }
 108    }
 109   
 110  7639 public Object putObjectI(Fqn fqn, Object obj, String field) throws CacheException
 111    {
 112    // Skip some un-necessary update if obj is the same class as the old one
 113  7639 Object oldValue = internal_.getPojo(fqn, field);
 114  7639 if (oldValue == obj && (obj instanceof Advised || obj instanceof ClassProxy))
 115    {
 116  2 if (log.isDebugEnabled())
 117    {
 118  0 log.debug("putObject(): id: " + fqn + " pojo is already in the cache. Return right away.");
 119    }
 120  2 return obj;
 121    }
 122  7637 return null;
 123    }
 124   
 125    /**
 126    * Note that caller of this method will take care of synchronization within the <code>fqn</code> sub-tree.
 127    */
 128  7637 public Object putObjectII(Fqn fqn, Object obj, String field) throws CacheException
 129    {
 130    // Skip some un-necessary update if obj is the same class as the old one
 131  7637 Object oldValue = internal_.getPojo(fqn, field);
 132  7637 if (oldValue == obj)
 133    {
 134  3 if (log.isDebugEnabled())
 135    {
 136  0 log.debug("putObject(): id: " + fqn + " pojo is already in the cache. Return right away.");
 137    }
 138  3 return obj;
 139    }
 140   
 141    // remove old value before overwriting it. This is necessary to detach any interceptor.
 142    // TODO Or can we simply walk thru that somewhere? Well, there is also implication of Collection though
 143  7634 pojoCache.detach(fqn, field);
 144   
 145  7634 if (obj == null)
 146    {
 147  3 return oldValue;// we are done
 148    }
 149   
 150    // creates the internal node first without going thru the interceptor.
 151    // This way we don't block on __JBossInternal__ node.
 152    //createChildNodeFirstWithoutLocking(internalFqn);
 153   
 154  7631 if ((obj instanceof Advised || obj instanceof ClassProxy) && isMultipleReferencedPut(obj))
 155    {
 156    // we pass in the originating fqn intentionaly
 157  192 graphHandler_.put(fqn, obj, field);
 158    }
 159    else
 160    {
 161  7439 Fqn internalFqn = createInternalFqn(fqn, obj);
 162  7439 if (log.isDebugEnabled())
 163    {
 164  0 log.debug("putObject(): id: " + fqn + " will store the pojo in the internal area: "
 165    + internalFqn);
 166    }
 167   
 168  7439 if (obj instanceof Advised)
 169    {
 170  2269 advisedHandler_.put(internalFqn, fqn, obj);
 171    }
 172  5170 else if (isCollection(obj))
 173    {
 174  1673 collectionHandler_.put(internalFqn, fqn, obj);
 175    //
 176    }
 177    else
 178    {
 179    // must be Serializable, including primitive types
 180  3497 serializableHandler_.put(internalFqn, obj);
 181    }
 182   
 183    // Used by notification sub-system
 184  7438 cache.put(internalFqn, InternalConstant.POJOCACHE_STATUS, "ATTACHED");
 185   
 186  7438 setPojoReference(fqn, obj, field, internalFqn);
 187    }
 188   
 189  7630 return oldValue;
 190    }
 191   
 192  7439 Fqn createInternalFqn(Fqn fqn, Object obj) throws CacheException
 193    {
 194    // Create an internal Fqn name
 195  7439 return AopUtil.createInternalFqn(fqn, cache);
 196    }
 197   
 198  7438 Fqn setPojoReference(Fqn fqn, Object obj, String field, Fqn internalFqn) throws CacheException
 199    {
 200    // Create PojoReference
 201  7438 CachedType type = pojoCache.getCachedType(obj.getClass());
 202  7438 PojoReference pojoReference = new PojoReference();
 203  7438 pojoReference.setPojoClass(type.getType());
 204   
 205    // store PojoReference
 206  7438 pojoReference.setFqn(internalFqn);
 207  7438 internal_.putPojoReference(fqn, pojoReference, field);
 208  7438 if (log.isDebugEnabled())
 209    {
 210  0 log.debug("put(): inserting PojoReference with id: " + fqn);
 211    }
 212    // store obj in the internal fqn
 213  7438 return internalFqn;
 214    }
 215   
 216  0 private void createChildNodeFirstWithoutLocking(Fqn internalFqn)
 217    {
 218  0 int size = internalFqn.size();
 219  0 Fqn f = internalFqn.getSubFqn(0, size - 1);
 220  0 Fqn child = internalFqn.getSubFqn(size - 1, size);
 221   
 222  0 Node base = cache.getRoot().getChild(f);
 223  0 if (base == null)
 224    {
 225  0 log.debug("The node retrieved is null from fqn: " + f);
 226  0 return;
 227    }
 228  0 base.addChild(child);
 229    }
 230   
 231    /**
 232    * Note that caller of this method will take care of synchronization within the <code>fqn</code> sub-tree.
 233    *
 234    * @param fqn
 235    * @return
 236    * @throws CacheException
 237    */
 238  6316 public Object removeObject(Fqn fqn, String field) throws CacheException
 239    {
 240    // the class attribute is implicitly stored as an immutable read-only attribute
 241  6316 PojoReference pojoReference = internal_.getPojoReference(fqn, field);
 242  6316 if (pojoReference == null)
 243    {
 244    // clazz and pojoReference can be not null if this node is the replicated brother node.
 245  0 if (log.isTraceEnabled())
 246    {
 247  0 log.trace("removeObject(): clazz is null. id: " + fqn + " No need to remove.");
 248    }
 249  0 return null;
 250    }
 251   
 252  6316 Class clazz = pojoReference.getPojoClass();
 253  6316 Fqn internalFqn = pojoReference.getFqn();
 254   
 255  6316 if (log.isDebugEnabled())
 256    {
 257  0 log.debug("removeObject(): removing object from id: " + fqn
 258    + " with the corresponding internal id: " + internalFqn);
 259    }
 260   
 261  6316 Object result = pojoCache.getObject(internalFqn);
 262  6316 if (result == null)
 263    {
 264  0 return null;
 265    }
 266   
 267  6316 if (graphHandler_.isMultipleReferenced(internalFqn))
 268    {
 269  38 graphHandler_.remove(fqn, internalFqn, result);
 270    }
 271    else
 272    {
 273  6278 cache.put(internalFqn, InternalConstant.POJOCACHE_STATUS, "DETACHING");
 274  6278 if (Advised.class.isAssignableFrom(clazz))
 275    {
 276  1885 advisedHandler_.remove(internalFqn, result, clazz);
 277  1885 internal_.cleanUp(internalFqn, null);
 278    }
 279  4393 else if (isCollectionGet(clazz))
 280    {
 281    // We need to return the original reference
 282  1332 result = collectionHandler_.remove(internalFqn, result);
 283  1332 internal_.cleanUp(internalFqn, null);
 284    }
 285    else
 286    {// Just Serializable objects. Do a brute force remove is ok.
 287  3061 serializableHandler_.remove();
 288  3061 internal_.cleanUp(internalFqn, null);
 289    }
 290    }
 291   
 292  6316 internal_.cleanUp(fqn, field);
 293    // remove the interceptor as well.
 294  6316 return result;
 295    }
 296   
 297  9 public Map findObjects(Fqn fqn) throws CacheException
 298    {
 299   
 300    // Traverse from fqn to do getObject, if it return a pojo we then stop.
 301  9 Map map = new HashMap();
 302  9 Object pojo = getObject(fqn, null);
 303  9 if (pojo != null)
 304    {
 305  0 map.put(fqn, pojo);// we are done!
 306  0 return map;
 307    }
 308   
 309  9 findChildObjects(fqn, map);
 310  9 if (log.isDebugEnabled())
 311    {
 312  0 log.debug("_findObjects(): id: " + fqn + " size of pojos found: " + map.size());
 313    }
 314  9 return map;
 315    }
 316   
 317  11801 private Object getObjectInternal(Fqn fqn, String field) throws CacheException
 318    {
 319  11801 Fqn internalFqn = fqn;
 320  11801 PojoReference pojoReference = internal_.getPojoReference(fqn, field);
 321  11801 if (pojoReference != null)
 322    {
 323  270 internalFqn = pojoReference.getFqn();
 324    }
 325  11531 else if (field != null)
 326    {
 327  6217 return null;
 328    }
 329   
 330  5584 if (log.isDebugEnabled())
 331  0 log.debug("getObject(): id: " + fqn + " with a corresponding internal id: " + internalFqn);
 332   
 333    /**
 334    * Reconstruct the managed POJO
 335    */
 336  5584 Object obj;
 337   
 338  5584 PojoInstance pojoInstance = internal_.getPojoInstance(internalFqn);
 339   
 340  5584 if (pojoInstance == null)
 341  5263 return null;
 342    //throw new PojoCacheException("PojoCacheDelegate.getObjectInternal(): null PojoInstance for fqn: " + internalFqn);
 343   
 344  321 Class clazz = pojoInstance.getPojoClass();
 345   
 346    // Check for both Advised and Collection classes for object graph.
 347    // Note: no need to worry about multiple referencing here. If there is a graph, we won't come this far.
 348  321 if (Advised.class.isAssignableFrom(clazz))
 349    {
 350  185 obj = advisedHandler_.get(internalFqn, clazz, pojoInstance);
 351    }
 352  136 else if (isCollectionGet(clazz))
 353    {// Must be Collection classes. We will use aop.ClassProxy instance instead.
 354  75 obj = collectionHandler_.get(internalFqn, clazz, pojoInstance);
 355    }
 356    else
 357    {
 358    // Maybe it is just a serialized object.
 359  61 obj = serializableHandler_.get(internalFqn, clazz, pojoInstance);
 360    }
 361   
 362  321 InternalHelper.setPojo(pojoInstance, obj);
 363  321 return obj;
 364    }
 365   
 366  4529 private boolean isCollectionGet(Class clazz)
 367    {
 368  4529 if (Map.class.isAssignableFrom(clazz) || Collection.class.isAssignableFrom(clazz))
 369    {
 370  1407 return true;
 371    }
 372   
 373  3122 return false;
 374    }
 375   
 376   
 377  2461 private boolean isMultipleReferencedPut(Object obj)
 378    {
 379  2461 Interceptor interceptor = null;
 380  2461 if (obj instanceof Advised)
 381    {
 382  2430 InstanceAdvisor advisor = ((Advised) obj)._getInstanceAdvisor();
 383  2430 if (advisor == null)
 384    {
 385  0 throw new PojoCacheException("_putObject(): InstanceAdvisor is null for: " + obj);
 386    }
 387   
 388    // Step Check for cross references
 389  2430 interceptor = AopUtil.findCacheInterceptor(advisor);
 390    }
 391    else
 392    {
 393  31 interceptor = CollectionInterceptorUtil.getInterceptor((ClassProxy) obj);
 394    }
 395  2269 if (interceptor == null) return false;
 396   
 397  192 Fqn originalFqn = null;
 398   
 399    // ah, found something. So this will be multiple referenced.
 400  192 originalFqn = ((BaseInterceptor) interceptor).getFqn();
 401   
 402  192 return originalFqn != null;
 403   
 404    }
 405   
 406  5170 private boolean isCollection(Object obj)
 407    {
 408  5170 return obj instanceof Collection || obj instanceof Map;
 409   
 410    }
 411   
 412    /**
 413    * Based on the pojo to perform a bulk remove recursively if there is no object graph
 414    * relationship for performance optimization.
 415    */
 416  0 private boolean bulkRemove(Fqn fqn, Object obj) throws CacheException
 417    {
 418    // Check for cross-reference. If there is, we can't do bulk remove
 419    // map contains (pojo, cacheinterceptor) pair that needs to rollback the the removal.
 420  0 Map undoMap = new HashMap();
 421  0 if (pojoGraphMultipleReferenced(obj, undoMap))
 422    {
 423  0 undoInterceptorDetach(undoMap);
 424  0 return false;
 425    }
 426    else
 427    {
 428  0 cache.removeNode(fqn);// interceptor has been removed so it is safe to do bulk remove now.
 429    }
 430  0 return true;
 431    }
 432   
 433  0 private void detachInterceptor(InstanceAdvisor advisor, Interceptor interceptor,
 434    boolean detachOnly, Map undoMap)
 435    {
 436  0 if (!detachOnly)
 437    {
 438  0 util_.detachInterceptor(advisor, interceptor);
 439  0 undoMap.put(advisor, interceptor);
 440    }
 441    else
 442    {
 443  0 undoMap.put(DETACH, interceptor);
 444    }
 445    }
 446   
 447  0 private static void undoInterceptorDetach(Map undoMap)
 448    {
 449  0 for (Iterator it = undoMap.keySet().iterator(); it.hasNext();)
 450    {
 451  0 Object obj = it.next();
 452   
 453  0 if (obj instanceof InstanceAdvisor)
 454    {
 455  0 InstanceAdvisor advisor = (InstanceAdvisor) obj;
 456  0 BaseInterceptor interceptor = (BaseInterceptor) undoMap.get(advisor);
 457   
 458  0 if (interceptor == null)
 459    {
 460  0 throw new IllegalStateException("PojoCacheDelegate.undoInterceptorDetach(): null interceptor");
 461    }
 462   
 463  0 advisor.appendInterceptor(interceptor);
 464    }
 465    else
 466    {
 467  0 BaseInterceptor interceptor = (BaseInterceptor) undoMap.get(obj);
 468  0 boolean copyToCache = false;
 469  0 ((AbstractCollectionInterceptor) interceptor).attach(null, copyToCache);
 470    }
 471    }
 472    }
 473   
 474    /**
 475    * Check recursively if the pojo and its graph is multiple referenced. If it is, we can't
 476    * do a bulk remove.
 477    */
 478  0 private boolean pojoGraphMultipleReferenced(Object obj, Map undoMap) throws CacheException
 479    {
 480  0 return true;
 481    }
 482   
 483  0 private boolean XpojoGraphMultipleReferenced(Object obj, Map undoMap) throws CacheException
 484    {
 485    // store object in cache
 486  0 if (obj instanceof Advised)
 487    {
 488  0 CachedType type = pojoCache.getCachedType(obj.getClass());
 489    // add interceptor
 490  0 InstanceAdvisor advisor = ((Advised) obj)._getInstanceAdvisor();
 491  0 if (advisor == null)
 492    {
 493  0 throw new PojoCacheException("pojoGraphMultipleReferenced(): InstanceAdvisor is null for: " + obj);
 494    }
 495   
 496  0 BaseInterceptor interceptor = (BaseInterceptor) AopUtil.findCacheInterceptor(advisor);
 497    // just in case
 498  0 if (interceptor == null)
 499    {
 500  0 return false;
 501    }
 502  0 PojoInstance pojoInstance = interceptor.getAopInstance();
 503    // Check if there is cross referenced.
 504  0 if (pojoInstance.getRefCount() != 0) return true;// I have been referenced
 505  0 if (pojoInstance.getInternalFqn() != null) return true;// I am referencing others
 506   
 507  0 boolean hasFieldAnnotation = hasAnnotation(obj.getClass(), ((Advised) obj)._getAdvisor(), type);
 508    // Check the fields
 509  0 for (Iterator i = type.getFields().iterator(); i.hasNext();)
 510    {
 511  0 Field field = (Field) (((FieldPersistentReference) i.next())).get();
 512  0 Object value = null;
 513  0 try
 514    {
 515  0 value = field.get(obj);
 516    }
 517    catch (IllegalAccessException e)
 518    {
 519  0 throw new CacheException("field access failed", e);
 520    }
 521   
 522  0 CachedType fieldType = pojoCache.getCachedType(field.getType());
 523   
 524    // we simply treat field that has @Serializable as a primitive type.
 525  0 if (fieldType.isImmediate() ||
 526    (hasFieldAnnotation &&
 527    CachedType.hasSerializableAnnotation(field, ((Advised) obj)._getAdvisor())))
 528    {
 529  0 continue;
 530    }
 531   
 532    // check for non-replicatable types
 533  0 if (CachedType.isPrimitiveNonReplicatable(field))
 534    {
 535  0 continue;
 536    }
 537   
 538  0 if (!hasFieldAnnotation)
 539    {
 540  0 if (CachedType.hasTransientAnnotation(field, ((Advised) obj)._getAdvisor()))
 541    {
 542  0 continue;
 543    }
 544    }
 545   
 546    // Need to do a getObject just in case this is a failover removeObject.
 547  0 if (value == null)
 548    {
 549  0 value = getObject(new Fqn(interceptor.getFqn(), field.getName()), null);
 550    }
 551   
 552  0 if (value == null) continue;// this is no brainer.
 553   
 554  0 if (pojoGraphMultipleReferenced(value, undoMap)) return true;
 555    }
 556  0 boolean detachOnly = false;
 557  0 detachInterceptor(advisor, interceptor, detachOnly, undoMap);
 558    }
 559  0 else if (obj instanceof Map || obj instanceof List || obj instanceof Set)
 560    {
 561    // TODO Is this really necessary?
 562  0 if (!(obj instanceof ClassProxy)) return false;
 563   
 564  0 InstanceAdvisor advisor = ((ClassProxy) obj)._getInstanceAdvisor();
 565  0 BaseInterceptor interceptor = (BaseInterceptor) AopUtil.findCollectionInterceptor(advisor);
 566  0 PojoInstance pojoInstance = interceptor.getAopInstance();
 567  0 if (pojoInstance == null) return false;// safeguard
 568    // Check if there is cross referenced.
 569  0 if (pojoInstance.getRefCount() != 0) return true;// I have been referenced
 570  0 if (pojoInstance.getInternalFqn() != null) return true;// I am referencing others
 571    // iterate thru the keys
 572  0 if (obj instanceof Map)
 573    {
 574  0 for (Iterator it = ((Map) obj).keySet().iterator(); it.hasNext();)
 575    {
 576  0 Object subObj = ((Map) obj).get(it.next());
 577  0 if (pojoGraphMultipleReferenced(subObj, undoMap)) return true;
 578    }
 579    }
 580  0 else if (obj instanceof List || obj instanceof Set)
 581    {
 582  0 for (Iterator it = ((Collection) obj).iterator(); it.hasNext();)
 583    {
 584  0 Object subObj = it.next();
 585  0 if (pojoGraphMultipleReferenced(subObj, undoMap)) return true;
 586    }
 587    }
 588    // Don't remove now.
 589  0 boolean removeFromCache = false;
 590  0 ((AbstractCollectionInterceptor) interceptor).detach(removeFromCache);// detach the interceptor. This will trigger a copy and remove.
 591  0 boolean detachOnly = true;
 592  0 detachInterceptor(advisor, interceptor, detachOnly, undoMap);
 593    }
 594   
 595  0 return false;
 596    }
 597   
 598  0 private static boolean hasAnnotation(Class clazz, Advisor advisor, CachedType type)
 599    {
 600  0 return CachedType.hasAnnotation(clazz, advisor, type);
 601    }
 602   
 603  26 private void findChildObjects(Fqn fqn, Map map) throws CacheException
 604    {
 605    // We need to traverse then
 606  26 Node root = cache.getRoot();
 607  26 Node current = root.getChild(fqn);
 608   
 609  0 if (current == null) return;
 610   
 611  26 Collection<Node> col = current.getChildren();
 612  0 if (col == null) return;
 613  26 for (Node n : col)
 614    {
 615  36 Fqn newFqn = n.getFqn();
 616  5 if (InternalHelper.isInternalNode(newFqn)) continue;// skip
 617   
 618  31 Object pojo = getObject(newFqn, null);
 619  31 if (pojo != null)
 620    {
 621  14 map.put(newFqn, pojo);
 622    }
 623    else
 624    {
 625  17 findChildObjects(newFqn, map);
 626    }
 627    }
 628    }
 629    }