/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.pojo.impl;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.aop.Advised;
import org.jboss.aop.InstanceAdvisor;
import org.jboss.aop.advice.Interceptor;
import org.jboss.aop.proxy.ClassProxy;
import org.jboss.cache.Cache;
import org.jboss.cache.CacheException;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.Fqn;
import org.jboss.cache.pojo.PojoCacheException;
import org.jboss.cache.pojo.collection.CollectionInterceptorUtil;
import org.jboss.cache.pojo.interceptors.dynamic.BaseInterceptor;
import org.jboss.cache.pojo.util.AopUtil;
/**
* Handle the object graph management.
*
* @author Ben Wang
* Date: Aug 4, 2005
* @version $Id: ObjectGraphHandler.java 4081 2007-06-28 00:56:07Z jgreene $
*/
class ObjectGraphHandler
{
private PojoCacheImpl cache;
private InternalHelper internal_;
private final static Log log = LogFactory.getLog(ObjectGraphHandler.class);
public ObjectGraphHandler(PojoCacheImpl cache, InternalHelper internal)
{
this.cache = cache;
internal_ = internal;
}
Object get(Fqn fqn, Class clazz, PojoInstance pojoInstance) throws CacheException
{
// Note this is actually the aliasFqn, not the real fqn!
Object obj;
obj = cache.getObject(fqn);
if (obj == null)
throw new PojoCacheException("ObjectGraphHandler.get(): null object from internal ref node." +
" Internal ref node: " + fqn);
return obj; // No need to set the instance under fqn. It is located in refFqn anyway.
}
void put(Fqn fqn, Object obj, String field) throws CacheException
{
CachedType type = cache.getCachedType(obj.getClass());
InstanceAdvisor advisor = null;
Interceptor interceptor = null;
if (obj instanceof Advised)
{
advisor = ((Advised) obj)._getInstanceAdvisor();
if (advisor == null)
throw new PojoCacheException("put(): InstanceAdvisor is null for: " + obj);
// Step Check for cross references
interceptor = AopUtil.findCacheInterceptor(advisor);
}
else
{
advisor = ((ClassProxy) obj)._getInstanceAdvisor();
if (advisor == null)
throw new PojoCacheException("put(): InstanceAdvisor is null for: " + obj);
interceptor = CollectionInterceptorUtil.getInterceptor((ClassProxy) obj);
}
Fqn originalFqn = null;
// ah, found something. So this will be multiple referenced.
originalFqn = ((BaseInterceptor) interceptor).getFqn();
// This will increment the ref count, reset, and add ref fqn in the current fqn node.
setupRefCounting(fqn, originalFqn);
// Store a PojoReference in the external fqn node
PojoReference pojoReference = new PojoReference();
pojoReference.setFqn(originalFqn);
pojoReference.setPojoClass(type.getType());
internal_.putPojoReference(fqn, pojoReference, field);
}
boolean isMultipleReferenced(Fqn internalFqn)
{
// Note this is actually the aliasFqn, not the real fqn!
PojoInstance pojoInstance = null;
try
{
pojoInstance = internal_.getPojoInstance(internalFqn);
}
catch (CacheException e)
{
throw new PojoCacheException("Exception in isMultipleReferenced", e);
}
// check if this is a refernce
return InternalHelper.isMultipleReferenced(pojoInstance);
}
void remove(Fqn referencingFqn, Fqn internalFqn, Object pojo)
throws CacheException
{
if (log.isDebugEnabled())
{
log.debug("remove(): removing object fqn: " + referencingFqn
+ " Will just de-reference it.");
}
removeFromReference(referencingFqn, internalFqn);
}
/**
* Remove the object from the the reference fqn, meaning just decrement the ref counter.
*/
private void removeFromReference(Fqn referencingFqn, Fqn originalFqn) throws CacheException
{
synchronized (referencingFqn)
{ // we lock the internal fqn here so no one else has access.
// Decrement ref counting on the internal node
if (decrementRefCount(referencingFqn, originalFqn) == PojoInstance.INITIAL_COUNTER_VALUE)
{
// No one is referring it so it is safe to remove
// TODO we should make sure the parent nodes are also removed they are empty as well.
cache.detach(referencingFqn);
}
}
}
/**
* 1. increment reference counter
* 2. put in refFqn so we can get it.
*
* @param fqn The original fqn node
* @param refFqn The new internal fqn node
*/
private void setupRefCounting(Fqn fqn, Fqn refFqn) throws CacheException
{
synchronized (refFqn)
{ // we lock the ref fqn here so no one else has access.
// increment the reference counting
incrementRefCount(refFqn, fqn);
// set the internal fqn in fqn so we can reference it.
if (log.isTraceEnabled())
{
log.trace("setupRefCounting(): current fqn: " + fqn + " set to point to: " + refFqn);
}
}
}
private int incrementRefCount(Fqn originalFqn, Fqn referencingFqn) throws CacheException
{
return internal_.incrementRefCount(originalFqn, referencingFqn);
}
private int decrementRefCount(Fqn referencingFqn, Fqn originalFqn) throws CacheException
{
int count = 0;
if ((count = internal_.decrementRefCount(originalFqn, referencingFqn)) == (PojoInstance.INITIAL_COUNTER_VALUE + 1))
{
internal_.removeIndirectFqn(originalFqn.toString());
}
return count;
}
}