[jboss-cvs] JBossCache/src/org/jboss/cache/pojo/impl ...
Ben Wang
bwang at jboss.com
Sat Jan 13 10:55:06 EST 2007
User: bwang
Date: 07/01/13 10:55:06
Added: src/org/jboss/cache/pojo/impl
SerializableObjectHandler.java
PojoCacheDelegate.java PojoCacheImpl.java
ObjectGraphHandler.java CollectionClassHandler.java
InternalHelper.java AdvisedPojoHandler.java
Log:
JBCACHE-922 Merged src-50 and tests-50 into src and tests, respectively.
Revision Changes Path
1.1 date: 2007/01/13 15:55:06; author: bwang; state: Exp;JBossCache/src/org/jboss/cache/pojo/impl/SerializableObjectHandler.java
Index: SerializableObjectHandler.java
===================================================================
/*
* 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.cache.CacheException;
import org.jboss.cache.Fqn;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.pojo.PojoTreeCache;
import org.jboss.cache.pojo.PojoInstance;
import org.jboss.cache.pojo.InternalConstant;
import java.util.HashMap;
import java.util.Map;
/**
* Handle Serializable object cache management.
*
* @author Ben Wang
* @version $Id: SerializableObjectHandler.java,v 1.1 2007/01/13 15:55:06 bwang Exp $
*/
class SerializableObjectHandler
{
private CacheSPI cache_;
private PojoCacheImpl pCache_;
private InternalHelper internal_;
private final Log log_ = LogFactory.getLog(SerializableObjectHandler.class);
public SerializableObjectHandler(PojoCacheImpl cache, InternalHelper internal)
{
pCache_ = cache;
cache_ = (CacheSPI)pCache_.getCache();
internal_ = internal;
}
Object get(Fqn fqn, Class clazz, PojoInstance pojoInstance)
throws CacheException
{
Object obj = internal_.get(fqn, InternalConstant.SERIALIZED);
return obj;
}
boolean put(Fqn fqn, Object obj)
throws CacheException
{
// Note that JBoss Serialization can serialize any type now.
if (log_.isDebugEnabled())
{
log_.debug("put(): obj (" + obj.getClass() + ") is non-advisable but serialize it anyway. "
+ "Note that if it is non-serializable we require to use JBoss Serialization.");
}
putIntoCache(fqn, obj);
return true;
}
private void putIntoCache(Fqn fqn, Object obj)
throws CacheException
{
Map map = new HashMap();
// Special optimization here.
PojoInstance pojoInstance = new PojoInstance();
pojoInstance.set(obj);
pojoInstance.setPojoClass(obj.getClass());
map.put(PojoInstance.KEY, pojoInstance);
// Note that we will only have one key in this fqn.
map.put(InternalConstant.SERIALIZED, obj);
internal_.put(fqn, map);
}
@SuppressWarnings({"CanBeStatic"})
void remove()
{
// No need to do anything here since we will do clean up afterwards.
}
}
1.1 date: 2007/01/13 15:55:06; author: bwang; state: Exp;JBossCache/src/org/jboss/cache/pojo/impl/PojoCacheDelegate.java
Index: PojoCacheDelegate.java
===================================================================
/*
* JBoss, the OpenSource J2EE webOS
*
* 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.Advisor;
import org.jboss.aop.InstanceAdvisor;
import org.jboss.aop.advice.Interceptor;
import org.jboss.aop.proxy.ClassProxy;
import org.jboss.cache.CacheException;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.Fqn;
import org.jboss.cache.Node;
import org.jboss.cache.NodeSPI;
import org.jboss.cache.pojo.CachedType;
import org.jboss.cache.pojo.PojoCacheException;
import org.jboss.cache.pojo.PojoInstance;
import org.jboss.cache.pojo.PojoReference;
import org.jboss.cache.pojo.PojoUtil;
import org.jboss.cache.pojo.collection.CollectionInterceptorUtil;
import org.jboss.cache.pojo.interceptors.dynamic.AbstractCollectionInterceptor;
import org.jboss.cache.pojo.interceptors.dynamic.BaseInterceptor;
import org.jboss.cache.pojo.memory.FieldPersistentReference;
import org.jboss.cache.pojo.observable.Observer;
import org.jboss.cache.pojo.util.AopUtil;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Delegate class for PojoCache, the real implementation code happens here.
*
* @author Ben Wang
*/
public class PojoCacheDelegate
{
private PojoCacheImpl pCache_;
private CacheSPI cache_;
private final static Log log = LogFactory.getLog(PojoCacheDelegate.class);
private InternalHelper internal_;
private AdvisedPojoHandler advisedHandler_;
private ObjectGraphHandler graphHandler_;
private CollectionClassHandler collectionHandler_;
private SerializableObjectHandler serializableHandler_;
// Use ThreadLocal to hold a boolean isBulkRemove
private ThreadLocal<Boolean> bulkRemove_ = new ThreadLocal<Boolean>();
private final String DETACH = "DETACH";
private PojoUtil util_ = new PojoUtil();
// Observer for field event notification
private Observer observer_;
public PojoCacheDelegate(PojoCacheImpl cache, Observer observer)
{
pCache_ = cache;
cache_ = (CacheSPI) pCache_.getCache();
internal_ = new InternalHelper(cache_);
graphHandler_ = new ObjectGraphHandler(pCache_, internal_);
collectionHandler_ = new CollectionClassHandler(pCache_, internal_);
serializableHandler_ = new SerializableObjectHandler(pCache_, internal_);
observer_ = observer;
advisedHandler_ = new AdvisedPojoHandler(pCache_, internal_, util_, observer_);
}
public void setBulkRemove(boolean bulk)
{
bulkRemove_.set(bulk);
}
private boolean getBulkRemove()
{
return bulkRemove_.get();
}
public Object getObject(Fqn fqn) throws CacheException
{
// TODO Must we really to couple with BR? JBCACHE-669
Object pojo = internal_.getPojo(fqn);
if (pojo != null)
{
// we already have an advised instance
if (log.isDebugEnabled())
{
log.debug("getObject(): id: " + fqn + " retrieved from existing instance directly. ");
}
return pojo;
}
// OK. So we are here meaning that this is a failover or passivation since the transient
// pojo instance is not around. Let's also make sure the right classloader is used
// as well.
ClassLoader prevCL = Thread.currentThread().getContextClassLoader();
try
{
if (cache_.getRegionManager() != null)
{
cache_.getRegionManager().setContextClassLoaderAsCurrent(fqn);
}
return getObjectInternal(fqn);
}
finally
{
Thread.currentThread().setContextClassLoader(prevCL);
}
}
public Object putObjectI(Fqn fqn, Object obj) throws CacheException
{
// Skip some un-necessary update if obj is the same class as the old one
Object oldValue = internal_.getPojo(fqn);
if (oldValue == obj)
{
if (log.isDebugEnabled())
{
log.debug("putObject(): id: " + fqn + " pojo is already in the cache. Return right away.");
}
return obj;
}
return null;
}
/**
* Note that caller of this method will take care of synchronization within the <code>fqn</code> sub-tree.
*/
public Object putObjectII(Fqn fqn, Object obj) throws CacheException
{
// Skip some un-necessary update if obj is the same class as the old one
Object oldValue = internal_.getPojo(fqn);
if (oldValue == obj)
{
if (log.isDebugEnabled())
{
log.debug("putObject(): id: " + fqn + " pojo is already in the cache. Return right away.");
}
return obj;
}
// remove old value before overwriting it. This is necessary to detach any interceptor.
// TODO Or can we simply walk thru that somewhere? Well, there is also implication of Collection though
pCache_.detach(fqn);
if (obj == null)
{
return oldValue;// we are done
}
// This create a PojoReference to store a pointer to the real fqn.
Fqn internalFqn = createInternalFqn(fqn, obj);
setPojoReference(fqn, obj, internalFqn);
// creates the internal node first without going thru the interceptor.
// This way we don't block on __JBossInternal__ node.
createChildNodeFirstWithoutLocking(internalFqn);
if (log.isDebugEnabled())
{
log.debug("putObject(): id: " + fqn + " will store the pojo in the internal area: "
+ internalFqn);
}
if ((obj instanceof Advised || obj instanceof ClassProxy) && isMultipleReferencedPut(obj))
{
// we pass in the originating fqn intentionaly
graphHandler_.put(fqn, obj);
}
else if (obj instanceof Advised)
{
advisedHandler_.put(internalFqn, fqn, obj);
}
else if (isCollection(obj))
{
collectionHandler_.put(internalFqn, fqn, obj);
//
}
else
{
// must be Serializable, including primitive types
serializableHandler_.put(internalFqn, obj);
}
return oldValue;
}
Fqn createInternalFqn(Fqn fqn, Object obj) throws CacheException
{
// Create an internal Fqn name
return AopUtil.createInternalFqn(fqn, cache_);
}
Fqn setPojoReference(Fqn fqn, Object obj, Fqn internalFqn) throws CacheException
{
// Create PojoReference
CachedType type = pCache_.getCachedType(obj.getClass());
PojoReference pojoReference = new PojoReference();
pojoReference.setPojoClass(type.getType());
// store PojoReference
pojoReference.setFqn(internalFqn);
internal_.putPojoReference(fqn, pojoReference);
if (log.isDebugEnabled())
{
log.debug("put(): inserting PojoReference with id: " + fqn);
}
// store obj in the internal fqn
return internalFqn;
}
private void createChildNodeFirstWithoutLocking(Fqn internalFqn)
{
int size = internalFqn.size();
Fqn f = internalFqn.getFqnChild(0, size - 1);
Fqn child = internalFqn.getFqnChild(size - 1, size);
NodeSPI base = cache_.getRoot().getChildDirect(f);
if (base == null)
{
log.debug("The node retrieved is null from fqn: " + f);
return;
}
base.addChildDirect(child);
}
/**
* Note that caller of this method will take care of synchronization within the <code>fqn</code> sub-tree.
*
* @param fqn
* @return
* @throws CacheException
*/
public Object removeObject(Fqn fqn) throws CacheException
{
// the class attribute is implicitly stored as an immutable read-only attribute
PojoReference pojoReference = internal_.getPojoReference(fqn);
if (pojoReference == null)
{
// clazz and pojoReference can be not null if this node is the replicated brother node.
if (log.isTraceEnabled())
{
log.trace("removeObject(): clazz is null. id: " + fqn + " No need to remove.");
}
return null;
}
Class clazz = pojoReference.getPojoClass();
Fqn internalFqn = pojoReference.getFqn();
if (log.isDebugEnabled())
{
log.debug("removeObject(): removing object from id: " + fqn
+ " with the corresponding internal id: " + internalFqn);
}
Object result = pCache_.getObject(internalFqn);
if (result == null)
{
return null;
}
if (graphHandler_.isMultipleReferenced(internalFqn))
{
graphHandler_.remove(fqn, internalFqn, result);
}
else if (Advised.class.isAssignableFrom(clazz))
{
advisedHandler_.remove(internalFqn, result, clazz);
internal_.cleanUp(internalFqn);
}
else if (isCollectionGet(clazz))
{
// We need to return the original reference
result = collectionHandler_.remove(internalFqn, result);
internal_.cleanUp(internalFqn);
}
else
{// Just Serializable objects. Do a brute force remove is ok.
serializableHandler_.remove();
internal_.cleanUp(internalFqn);
}
internal_.cleanUp(fqn);
// remove the interceptor as well.
return result;
}
public Map findObjects(Fqn fqn) throws CacheException
{
// Traverse from fqn to do getObject, if it return a pojo we then stop.
Map map = new HashMap();
Object pojo = getObject(fqn);
if (pojo != null)
{
map.put(fqn, pojo);// we are done!
return map;
}
findChildObjects(fqn, map);
if (log.isDebugEnabled())
{
log.debug("_findObjects(): id: " + fqn + " size of pojos found: " + map.size());
}
return map;
}
private Object getObjectInternal(Fqn fqn) throws CacheException
{
// the class attribute is implicitly stored as an immutable read-only attribute
PojoReference pojoReference = internal_.getPojoReference(fqn);
if (pojoReference == null)
{
// clazz and pojoReference can be not null if this node is the replicated brother node.
return null;
}
// This is the real location for the pojo.
Fqn internalFqn = pojoReference.getFqn();
Class clazz = pojoReference.getPojoClass();
if (log.isDebugEnabled())
{
log.debug("getObject(): id: " + fqn + " with a corresponding internal id: "
+ internalFqn);
}
/**
* Reconstruct the managed POJO
*/
Object obj;
PojoInstance pojoInstance = internal_.getAopInstance(internalFqn);
if (pojoInstance == null)
{
throw new PojoCacheException("PojoCacheDelegate.getObjectInternal(): null PojoInstance for fqn: " +
internalFqn);
}
// Check for both Advised and Collection classes for object graph.
// Note: no need to worry about multiple referencing here. If there is a graph, we won't come this far.
if (Advised.class.isAssignableFrom(clazz))
{
obj = advisedHandler_.get(internalFqn, clazz, pojoInstance);
}
else if (isCollectionGet(clazz))
{// Must be Collection classes. We will use aop.ClassProxy instance instead.
obj = collectionHandler_.get(internalFqn, clazz, pojoInstance);
}
else
{
// Maybe it is just a serialized object.
obj = serializableHandler_.get(internalFqn, clazz, pojoInstance);
}
InternalHelper.setPojo(pojoInstance, obj);
return obj;
}
private boolean isCollectionGet(Class clazz)
{
if (Map.class.isAssignableFrom(clazz) || Collection.class.isAssignableFrom(clazz))
{
return true;
}
return false;
}
private boolean isMultipleReferencedPut(Object obj)
{
Interceptor interceptor = null;
if (obj instanceof Advised)
{
InstanceAdvisor advisor = ((Advised) obj)._getInstanceAdvisor();
if (advisor == null)
{
throw new PojoCacheException("_putObject(): InstanceAdvisor is null for: " + obj);
}
// Step Check for cross references
interceptor = AopUtil.findCacheInterceptor(advisor);
}
else
{
interceptor = CollectionInterceptorUtil.getInterceptor((ClassProxy) obj);
}
if (interceptor == null) return false;
Fqn originalFqn = null;
// ah, found something. So this will be multiple referenced.
originalFqn = ((BaseInterceptor) interceptor).getFqn();
return originalFqn != null;
}
private boolean isCollection(Object obj)
{
return obj instanceof Collection || obj instanceof Map;
}
/**
* Based on the pojo to perform a bulk remove recursively if there is no object graph
* relationship for performance optimization.
*/
private boolean bulkRemove(Fqn fqn, Object obj) throws CacheException
{
// Check for cross-reference. If there is, we can't do bulk remove
// map contains (pojo, cacheinterceptor) pair that needs to rollback the the removal.
Map undoMap = new HashMap();
if (pojoGraphMultipleReferenced(obj, undoMap))
{
undoInterceptorDetach(undoMap);
return false;
}
else
{
cache_.removeNode(fqn);// interceptor has been removed so it is safe to do bulk remove now.
}
return true;
}
private void detachInterceptor(InstanceAdvisor advisor, Interceptor interceptor,
boolean detachOnly, Map undoMap)
{
if (!detachOnly)
{
util_.detachInterceptor(advisor, interceptor, observer_);
undoMap.put(advisor, interceptor);
}
else
{
undoMap.put(DETACH, interceptor);
}
}
private static void undoInterceptorDetach(Map undoMap)
{
for (Iterator it = undoMap.keySet().iterator(); it.hasNext();)
{
Object obj = it.next();
if (obj instanceof InstanceAdvisor)
{
InstanceAdvisor advisor = (InstanceAdvisor) obj;
BaseInterceptor interceptor = (BaseInterceptor) undoMap.get(advisor);
if (interceptor == null)
{
throw new IllegalStateException("PojoCacheDelegate.undoInterceptorDetach(): null interceptor");
}
advisor.appendInterceptor(interceptor);
}
else
{
BaseInterceptor interceptor = (BaseInterceptor) undoMap.get(obj);
boolean copyToCache = false;
((AbstractCollectionInterceptor) interceptor).attach(null, copyToCache);
}
}
}
/**
* Check recursively if the pojo and its graph is multiple referenced. If it is, we can't
* do a bulk remove.
*/
private boolean pojoGraphMultipleReferenced(Object obj, Map undoMap) throws CacheException
{
return true;
}
private boolean XpojoGraphMultipleReferenced(Object obj, Map undoMap) throws CacheException
{
// store object in cache
if (obj instanceof Advised)
{
CachedType type = pCache_.getCachedType(obj.getClass());
// add interceptor
InstanceAdvisor advisor = ((Advised) obj)._getInstanceAdvisor();
if (advisor == null)
{
throw new PojoCacheException("pojoGraphMultipleReferenced(): InstanceAdvisor is null for: " + obj);
}
BaseInterceptor interceptor = (BaseInterceptor) AopUtil.findCacheInterceptor(advisor);
// just in case
if (interceptor == null)
{
return false;
}
PojoInstance pojoInstance = interceptor.getAopInstance();
// Check if there is cross referenced.
if (pojoInstance.getRefCount() != 0) return true;// I have been referenced
if (pojoInstance.getInternalFqn() != null) return true;// I am referencing others
boolean hasFieldAnnotation = hasAnnotation(obj.getClass(), ((Advised) obj)._getAdvisor(), type);
// Check the fields
for (Iterator i = type.getFields().iterator(); i.hasNext();)
{
Field field = (Field) (((FieldPersistentReference) i.next())).get();
Object value = null;
try
{
value = field.get(obj);
}
catch (IllegalAccessException e)
{
throw new CacheException("field access failed", e);
}
CachedType fieldType = pCache_.getCachedType(field.getType());
// we simply treat field that has @Serializable as a primitive type.
if (fieldType.isImmediate() ||
(hasFieldAnnotation &&
CachedType.hasSerializableAnnotation(field, ((Advised) obj)._getAdvisor())))
{
continue;
}
// check for non-replicatable types
if (CachedType.isPrimitiveNonReplicatable(field))
{
continue;
}
if (!hasFieldAnnotation)
{
if (CachedType.hasTransientAnnotation(field, ((Advised) obj)._getAdvisor()))
{
continue;
}
}
// Need to do a getObject just in case this is a failover removeObject.
if (value == null)
{
value = getObject(new Fqn(interceptor.getFqn(), field.getName()));
}
if (value == null) continue;// this is no brainer.
if (pojoGraphMultipleReferenced(value, undoMap)) return true;
}
boolean detachOnly = false;
detachInterceptor(advisor, interceptor, detachOnly, undoMap);
}
else if (obj instanceof Map || obj instanceof List || obj instanceof Set)
{
// TODO Is this really necessary?
if (!(obj instanceof ClassProxy)) return false;
InstanceAdvisor advisor = ((ClassProxy) obj)._getInstanceAdvisor();
BaseInterceptor interceptor = (BaseInterceptor) AopUtil.findCollectionInterceptor(advisor);
PojoInstance pojoInstance = interceptor.getAopInstance();
if (pojoInstance == null) return false;// safeguard
// Check if there is cross referenced.
if (pojoInstance.getRefCount() != 0) return true;// I have been referenced
if (pojoInstance.getInternalFqn() != null) return true;// I am referencing others
// iterate thru the keys
if (obj instanceof Map)
{
for (Iterator it = ((Map) obj).keySet().iterator(); it.hasNext();)
{
Object subObj = ((Map) obj).get(it.next());
if (pojoGraphMultipleReferenced(subObj, undoMap)) return true;
}
}
else if (obj instanceof List || obj instanceof Set)
{
for (Iterator it = ((Collection) obj).iterator(); it.hasNext();)
{
Object subObj = it.next();
if (pojoGraphMultipleReferenced(subObj, undoMap)) return true;
}
}
// Don't remove now.
boolean removeFromCache = false;
((AbstractCollectionInterceptor) interceptor).detach(removeFromCache);// detach the interceptor. This will trigger a copy and remove.
boolean detachOnly = true;
detachInterceptor(advisor, interceptor, detachOnly, undoMap);
}
return false;
}
private static boolean hasAnnotation(Class clazz, Advisor advisor, CachedType type)
{
return CachedType.hasAnnotation(clazz, advisor, type);
}
private void findChildObjects(Fqn fqn, Map map) throws CacheException
{
// We need to traverse then
Node root = cache_.getRoot();
Node current = root.getChild(fqn);
if (current == null) return;
Collection<Node> col = current.getChildren();
if (col == null) return;
for (Node n : col)
{
Fqn newFqn = n.getFqn();
if (InternalHelper.isInternalNode(newFqn)) continue;// skip
Object pojo = getObject(newFqn);
if (pojo != null)
{
map.put(newFqn, pojo);
}
else
{
findChildObjects(newFqn, map);
}
}
}
}
1.1 date: 2007/01/13 15:55:06; author: bwang; state: Exp;JBossCache/src/org/jboss/cache/pojo/impl/PojoCacheImpl.java
Index: PojoCacheImpl.java
===================================================================
/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.pojo.impl;
import EDU.oswego.cs.dl.util.concurrent.CopyOnWriteArraySet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.aop.Advised;
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.DefaultCacheFactory;
import org.jboss.cache.Fqn;
import org.jboss.cache.Version;
import org.jboss.cache.config.Configuration;
import org.jboss.cache.factories.XmlConfigurationParser;
import org.jboss.cache.pojo.CachedType;
import org.jboss.cache.pojo.PojoCache;
import org.jboss.cache.pojo.PojoCacheException;
import org.jboss.cache.pojo.PojoCacheListener;
import org.jboss.cache.pojo.annotation.Attach;
import org.jboss.cache.pojo.annotation.Detach;
import org.jboss.cache.pojo.annotation.Find;
import org.jboss.cache.pojo.observable.Observer;
import org.jboss.cache.pojo.observable.Subject;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
/**
* Implementation class for PojoCache interface
*
* @author Ben Wang
* @version $Id: PojoCacheImpl.java,v 1.1 2007/01/13 15:55:06 bwang Exp $
*/
public class PojoCacheImpl implements PojoCache, Observer
{
private CacheSPI cache_ = null;
protected final Log log_ = LogFactory.getLog(PojoCacheImpl.this.getClass());
private PojoCacheDelegate delegate_;
// Class -> CachedType
// use WeakHashMap to allow class reloading
private Map cachedTypes_ = new WeakHashMap();
private boolean hasCreate_ = false;
/**
* Set of TreeCacheListener.
*
* @see #addListener
*/
private final Set listeners = new CopyOnWriteArraySet();
/**
* True if listeners are initialized.
*/
protected boolean hasListeners = false;
public PojoCacheImpl(String configStr, boolean toStart)
{
try
{
// cache_ = new PojoTreeCache();
// cache_.setConfiguration(new XmlConfigurationParser().parseFile(configStr));
XmlConfigurationParser parser = new XmlConfigurationParser();
Configuration expected = parser.parseFile(configStr);
init(expected, toStart);
}
catch (Exception e)
{
e.printStackTrace();
throw new PojoCacheException("PojoTreeCache: " + e);
}
}
public PojoCacheImpl(Configuration config, boolean toStart)
{
init(config, toStart);
}
private void init(Configuration config, boolean toStart)
{
try
{
cache_ = (CacheSPI) DefaultCacheFactory.getInstance().createCache(config, toStart);
}
catch (Exception e)
{
e.printStackTrace();
throw new PojoCacheException("PojoTreeCache: " + e);
}
delegate_ = new PojoCacheDelegate(this, this);
}
public CacheSPI getUnderlyingCache()
{
return cache_;
}
public Object attach(String id, Object pojo) throws PojoCacheException
{
return attach(Fqn.fromString(id), pojo);
}
@Attach
public Object attach(Fqn id, Object pojo) throws PojoCacheException
{
try
{
Object obj = putObject(id, pojo);
return obj;
}
catch (CacheException e)
{
e.printStackTrace();
throw new PojoCacheException("PojoTreeCache: " + e);
}
}
/**
* This public API is called from internal package only.
*/
public Object putObject(Fqn id, Object pojo)
throws CacheException
{
Object obj = null;
// Maybe this is the same instance already.
obj = delegate_.putObjectI(id, pojo);
if (obj != null) return obj;
notifyAttach(pojo, true);
obj = delegate_.putObjectII(id, pojo);
notifyAttach(pojo, false);
return obj;
}
public Object detach(String id) throws PojoCacheException
{
return detach(Fqn.fromString(id));
}
@Detach
public Object detach(Fqn id) throws PojoCacheException
{
try
{
Object pojo = find(id);// TODO need optimization here since it will be redundant here
if (pojo == null) return pojo;
notifyDetach(pojo, true);
Object obj = removeObject(id);
notifyDetach(pojo, false);
return obj;
}
catch (CacheException e)
{
e.printStackTrace();// TODO
throw new PojoCacheException("PojoTreeCache: " + e);
}
}
public Object removeObject(Fqn id) throws CacheException
{
delegate_.setBulkRemove(false);
return delegate_.removeObject(id);
}
public String getPojoID(Object pojo)
{
throw new PojoCacheException("getPojoID not yet implemented");
}
public Object find(String id) throws PojoCacheException
{
return find(Fqn.fromString(id));
}
@Find
public Object find(Fqn id) throws PojoCacheException
{
try
{
return getObject(id);
}
catch (CacheException e)
{
e.printStackTrace();// TODO
throw new PojoCacheException("PojoTreeCache: " + e);
}
}
public Object getObject(Fqn id)
throws CacheException
{
return delegate_.getObject(id);
}
public Map findAll(String id) throws PojoCacheException
{
return findAll(Fqn.fromString(id));
}
@Find
public Map findAll(Fqn id) throws PojoCacheException
{
// Should produce "/"
if (id == null) id = Fqn.fromString(null);
try
{
return delegate_.findObjects(id);
}
catch (CacheException e)
{
e.printStackTrace();// TODO
throw new PojoCacheException("PojoTreeCache: " + e);
}
}
public String getVersion()
{
return Version.printVersion();
}
public void create() throws PojoCacheException
{
log_.info("PojoCache version: " + getVersion());
try
{
cache_.create();
}
catch (Exception e)
{
throw new PojoCacheException("PojoCache create exception", e);
}
hasCreate_ = true;
}
public void start() throws PojoCacheException
{
if (!hasCreate_)
{
create();
}
try
{
log_.info("PojoCache version: " + getVersion());
cache_.start();
}
catch (Exception e)
{
e.printStackTrace();
throw new PojoCacheException("PojoCacheImpl: " + e);
}
}
public void stop() throws PojoCacheException
{
cache_.stop();
}
public void destroy() throws PojoCacheException
{
cache_.destroy();
}
public void addListener(PojoCacheListener listener)
{
// synchronize on listenrs just to
// ensure hasListeners is set correctly
// based on possibility of concurrent adds/removes
//
synchronized (listeners)
{
listeners.add(listener);
hasListeners = true;
}
}
public Collection getListeners()
{
return Collections.unmodifiableCollection(listeners);
}
public void removeListener(PojoCacheListener listener)
{
// synchronize on listenrs just to
// ensure hasListeners is set correctly
// based on possibility of concurrent adds/removes
//
synchronized (listeners)
{
listeners.remove(listener);
hasListeners = !listeners.isEmpty();
}
}
public Cache getCache()
{
// TODO Need to return the real Cache interface later on.
return (Cache) cache_;
}
/**
* Used by internal implementation. Not for general public.
*/
public Object _evictObject(Fqn fqn) throws CacheException
{
boolean evict = true;
boolean removeCacheInterceptor = false;
// Configurable option to see if we want to remove the cache interceptor when the pojo is
// evicted.
// if(detachPojoWhenEvicted_) removeCacheInterceptor = true;
delegate_.setBulkRemove(false);
// return delegate_._removeObject(fqn, removeCacheInterceptor);
return null;
}
/**
* Obtain a cache aop type for user to traverse the defined "primitive" types in aop.
* Note that this is not a synchronized call now for speed optimization.
*
* @param clazz The original pojo class
* @return CachedType
*/
public synchronized CachedType getCachedType(Class clazz)
{
CachedType type = (CachedType) cachedTypes_.get(clazz);
if (type == null)
{
type = new CachedType(clazz);
cachedTypes_.put(clazz, type);
return type;
}
else
{
return type;
}
}
/********************************************************************************
* Internal API for event notification
********************************************************************************/
/**
* Callback for Observable to notify of field modification
*
* @param subject
*/
public void fireChange(Subject subject, Field modifiedField, boolean pre)
{
boolean isLocal = true;// TODO Not yet supported. Always true now.
if (hasListeners)
{
for (Iterator it = listeners.iterator(); it.hasNext();)
{
((PojoCacheListener) it.next()).modify((Object) subject, modifiedField, pre, isLocal);
}
}
}
private void notifyAttach(Object pojo, boolean pre)
{
if (pojo == null) return;
if (!shouldNotify(pojo)) return;
boolean isLocal = true;// TODO Not yet supported. Always true now.
if (hasListeners)
{
for (Iterator it = listeners.iterator(); it.hasNext();)
{
((PojoCacheListener) it.next()).attach(pojo, pre, isLocal);
}
}
}
private void notifyDetach(Object pojo, boolean pre)
{
if (pojo == null) return;
if (!shouldNotify(pojo)) return;
boolean isLocal = true;// TODO Not yet supported. Always true now.
if (hasListeners)
{
for (Iterator it = listeners.iterator(); it.hasNext();)
{
((PojoCacheListener) it.next()).detach(pojo, pre, isLocal);
}
}
}
/**
* Decide whether we want to emit notification event or not.
*
* @param pojo
*/
private boolean shouldNotify(Object pojo)
{
// Not Advised or not Collection
if (!(pojo instanceof Advised) &&
!(pojo instanceof ClassProxy || pojo instanceof Collection ||
pojo instanceof Map))
{
return false;
}
return true;
}
}
1.1 date: 2007/01/13 15:55:06; author: bwang; state: Exp;JBossCache/src/org/jboss/cache/pojo/impl/ObjectGraphHandler.java
Index: ObjectGraphHandler.java
===================================================================
/*
* 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.proxy.ClassProxy;
import org.jboss.aop.advice.Interceptor;
import org.jboss.cache.CacheException;
import org.jboss.cache.Fqn;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.pojo.util.AopUtil;
import org.jboss.cache.pojo.interceptors.dynamic.BaseInterceptor;
import org.jboss.cache.pojo.interceptors.dynamic.CacheFieldInterceptor;
import org.jboss.cache.pojo.PojoTreeCache;
import org.jboss.cache.pojo.CachedType;
import org.jboss.cache.pojo.PojoInstance;
import org.jboss.cache.pojo.PojoReference;
import org.jboss.cache.pojo.PojoCacheException;
import org.jboss.cache.pojo.collection.CollectionInterceptorUtil;
/**
* Handle the object graph management.
*
* @author Ben Wang
* Date: Aug 4, 2005
* @version $Id: ObjectGraphHandler.java,v 1.1 2007/01/13 15:55:06 bwang Exp $
*/
class ObjectGraphHandler
{
private CacheSPI cache_;
private PojoCacheImpl pCache_;
private InternalHelper internal_;
private final static Log log = LogFactory.getLog(ObjectGraphHandler.class);
public ObjectGraphHandler(PojoCacheImpl cache, InternalHelper internal)
{
pCache_ = cache;
cache_ = (CacheSPI)pCache_.getCache();
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 = pCache_.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) throws CacheException
{
CachedType type = pCache_.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);
}
boolean isMultipleReferenced(Fqn internalFqn)
{
// Note this is actually the aliasFqn, not the real fqn!
PojoInstance pojoInstance = null;
try
{
pojoInstance = internal_.getAopInstance(internalFqn);
} catch (CacheException e)
{
e.printStackTrace();
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.
pCache_.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;
}
}
1.1 date: 2007/01/13 15:55:06; author: bwang; state: Exp;JBossCache/src/org/jboss/cache/pojo/impl/CollectionClassHandler.java
Index: CollectionClassHandler.java
===================================================================
/*
* 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.advice.Interceptor;
import org.jboss.aop.proxy.ClassProxy;
import org.jboss.cache.CacheException;
import org.jboss.cache.Fqn;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.pojo.interceptors.dynamic.AbstractCollectionInterceptor;
import org.jboss.cache.pojo.collection.CollectionInterceptorUtil;
import org.jboss.cache.pojo.interceptors.dynamic.BaseInterceptor;
import org.jboss.cache.pojo.PojoTreeCache;
import org.jboss.cache.pojo.CachedType;
import org.jboss.cache.pojo.PojoInstance;
import org.jboss.cache.pojo.PojoCacheException;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Handling the Collection class management. Has no consideration of object graph here.
*
* @author Ben Wang
* Date: Aug 4, 2005
* @version $Id: CollectionClassHandler.java,v 1.1 2007/01/13 15:55:06 bwang Exp $
*/
class CollectionClassHandler
{
private final Log log = LogFactory.getLog(CollectionClassHandler.class);
private CacheSPI cache_;
private PojoCacheImpl pCache_;
private InternalHelper internal_;
public CollectionClassHandler(PojoCacheImpl pCache, InternalHelper internal)
{
pCache_ = pCache;
cache_ = (CacheSPI)pCache_.getCache();
internal_ = internal;
}
Object get(Fqn fqn, Class clazz, PojoInstance pojoInstance)
throws CacheException
{
Object obj = null;
try
{
if (Map.class.isAssignableFrom(clazz))
{
Object map = clazz.newInstance();
obj = CollectionInterceptorUtil.createMapProxy(pCache_, fqn, clazz, (Map) map);
} else if (List.class.isAssignableFrom(clazz))
{
Object list = clazz.newInstance();
obj = CollectionInterceptorUtil.createListProxy(pCache_, fqn, clazz, (List) list);
} else if (Set.class.isAssignableFrom(clazz))
{
Object set = clazz.newInstance();
obj = CollectionInterceptorUtil.createSetProxy(pCache_, fqn, clazz, (Set) set);
}
} catch (Exception e)
{
throw new CacheException("failure creating proxy", e);
}
return obj;
}
void put(Fqn fqn, Fqn referencingFqn, Object obj) throws CacheException
{
boolean isCollection = false;
CachedType type = null;
if (obj instanceof ClassProxy)
{
throw new IllegalStateException("CollectionClassHandler.put(): obj is an ClassProxy instance "+ obj);
}
type = pCache_.getCachedType(obj.getClass());
//JBCACHE-760: for collection - put initialized aopInstance in fqn
if (!(obj instanceof Map || obj instanceof List || obj instanceof Set)) {
return;
}
// Always initialize the ref count so that we can mark this as an AopNode.
PojoInstance pojoInstance = InternalHelper.initializeAopInstance(referencingFqn);
pojoInstance.set(obj);
pojoInstance.setPojoClass(type.getType());
cache_.put(fqn, PojoInstance.KEY, pojoInstance);
if (obj instanceof Map)
{
if (log.isDebugEnabled())
{
log.debug("collectionPutObject(): aspectized obj is a Map type of size: " + ((Map) obj).size());
}
// Let's replace it with a proxy if necessary
Map map = (Map) obj;
if (!(obj instanceof ClassProxy))
{
Class clazz = obj.getClass();
try
{
obj = CollectionInterceptorUtil.createMapProxy(pCache_, fqn, clazz, (Map) obj);
} catch (Exception e)
{
throw new CacheException("failure creating proxy", e);
}
checkMapRecursion(map, obj);
}
isCollection = true;
// populate via the proxied collection
for (Iterator i = map.entrySet().iterator(); i.hasNext();)
{
Map.Entry entry = (Map.Entry) i.next();
((Map) obj).put(entry.getKey(), entry.getValue());
}
} else if (obj instanceof List)
{
if (log.isDebugEnabled())
{
log.debug("collectionPutObject(): aspectized obj is a List type of size: "
+ ((List) obj).size());
}
List list = (List) obj;
// Let's replace it with a proxy if necessary
if (!(obj instanceof ClassProxy))
{
Class clazz = obj.getClass();
try
{
obj = CollectionInterceptorUtil.createListProxy(pCache_, fqn, clazz, (List) obj);
} catch (Exception e)
{
throw new CacheException("failure creating proxy", e);
}
checkListRecursion(list, obj);
}
isCollection = true;
// populate via the proxied collection
for (Iterator i = list.iterator(); i.hasNext();)
{
((List) obj).add(i.next());
}
} else if (obj instanceof Set)
{
if (log.isDebugEnabled())
{
log.debug("collectionPutObject(): aspectized obj is a Set type of size: "
+ ((Set) obj).size());
}
Set set = (Set) obj;
// Let's replace it with a proxy if necessary
if (!(obj instanceof ClassProxy))
{
Class clazz = obj.getClass();
try
{
obj = CollectionInterceptorUtil.createSetProxy(pCache_, fqn, clazz, (Set) obj);
} catch (Exception e)
{
throw new CacheException("failure creating proxy", e);
}
checkSetRecursion(set, obj);
}
isCollection = true;
// populate via the proxied collection
for (Iterator i = set.iterator(); i.hasNext();)
{
((Set) obj).add(i.next());
}
}
if (isCollection)
{
// Need to reset it here in case this is a new proxy instance
pojoInstance.set(obj);
// Attach pojoReference to that interceptor
BaseInterceptor baseInterceptor = (BaseInterceptor) CollectionInterceptorUtil.getInterceptor(
(ClassProxy) obj);
baseInterceptor.setAopInstance(pojoInstance);
}
}
private void checkListRecursion(List list, Object obj)
{
while(true)
{
int i = list.indexOf(list); // check for recursion
if(i == -1) break;
list.remove(list);
list.add(i, obj);
}
}
private void checkSetRecursion(Set set, Object obj)
{
if(set.remove(set))
{
set.add(obj); // replace with proxy
throw new PojoCacheException("CollectionClassHandler.checkSetRecursion(): " +
"detect a recursive set (e.g., set inside the same set). This will fail to " +
"replicate even outside of PojoCache with HashSet. " +set);
}
}
private void checkMapRecursion(Map map, Object obj)
{
Map m = java.util.Collections.unmodifiableMap(map);
for( Object k : m.keySet() )
{
if( m == k)
{
throw new PojoCacheException("CollectionClassHandler.checkMapRecursion(): " +
" Can't handle the recursion map where it is nested in a constant key " +map);
}
Object v = m.get(k);
if( v == map )
{
throw new PojoCacheException("CollectionClassHandler.checkMapRecursion(): " +
"detect a recursive map (e.g., map inside the same map). This will fail to " +
"replicate even outside of PojoCache with HashMap because of hashCode. " +map);
// recursion here, replace it with proxy
// map.put(k, obj);
}
}
}
Object remove(Fqn fqn, Object obj) throws CacheException
{
if (!(obj instanceof ClassProxy))
{
throw new PojoCacheException("CollectionClassHandler.collectionRemoveObject(): object is not a proxy :" + obj);
}
Interceptor interceptor = CollectionInterceptorUtil.getInterceptor((ClassProxy) obj);
boolean removeFromCache = true;
// detach the interceptor. This will trigger a copy and remove.
((AbstractCollectionInterceptor) interceptor).detach(removeFromCache);
return ((AbstractCollectionInterceptor) interceptor).getCurrentCopy();
}
}
1.1 date: 2007/01/13 15:55:06; author: bwang; state: Exp;JBossCache/src/org/jboss/cache/pojo/impl/InternalHelper.java
Index: InternalHelper.java
===================================================================
/*
* JBoss, the OpenSource J2EE webOS
*
* 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.cache.CacheException;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.Fqn;
import org.jboss.cache.config.Option;
import org.jboss.cache.pojo.InternalConstant;
import org.jboss.cache.pojo.PojoInstance;
import org.jboss.cache.pojo.PojoReference;
import org.jboss.cache.pojo.PojoCacheException;
import org.jboss.cache.pojo.util.ObjectUtil;
import java.util.Map;
/**
* Internal helper class to handle internal cache sotre, that is, the portion that is not part of
* user's data.
*
* @author Ben Wang
*/
public class InternalHelper
{
private Log log = LogFactory.getLog(InternalHelper.class.getName());
// This is an optimization flag to skip put lock when we are sure that it has been locked from
// putObject. However, if later on there are transactional field updates, then we we will need
// this off to protected the write lock.
private boolean cacheOperationSkipLocking_ = true;
private Option skipLockOption_;
private Option gravitateOption_;
private Option localModeOption_;
private CacheSPI cache_;
InternalHelper(CacheSPI cache)
{
cache_ = cache;
skipLockOption_ = new Option();
if (cacheOperationSkipLocking_)
{
skipLockOption_.setSuppressLocking(true);
}
else
{
skipLockOption_.setSuppressLocking(false);
}
localModeOption_ = new Option();
localModeOption_.setCacheModeLocal(true);
gravitateOption_ = new Option();
gravitateOption_.setForceDataGravitation(true);
}
Option getLockOption()
{
return skipLockOption_;
}
PojoInstance getAopInstance(Fqn fqn) throws CacheException
{
// Not very efficient now since we are peeking every single time.
// Should have cache it without going to local cache.
return (PojoInstance) get(fqn, PojoInstance.KEY, false);
}
PojoReference getPojoReference(Fqn fqn) throws CacheException
{
// Not very efficient now since we are peeking every single time.
// Should have cache it without going to local cache.
return (PojoReference) get(fqn, PojoReference.KEY, false);
}
PojoReference getPojoReferenceWithGravitation(Fqn fqn) throws CacheException
{
// Not very efficient now since we are peeking every single time.
// Should have cache it without going to local cache.
return (PojoReference) get(fqn, PojoReference.KEY, true);
}
private PojoInstance getAopInstanceWithGravitation(Fqn fqn) throws CacheException
{
// Not very efficient now since we are peeking every single time.
// Should have cache it without going to local cache.
return (PojoInstance) get(fqn, PojoInstance.KEY, true);
}
static PojoInstance initializeAopInstance(Fqn sourceFqn)
{
PojoInstance pojoInstance = new PojoInstance();
pojoInstance.incrementRefCount(sourceFqn);
return pojoInstance;
}
/**
* Increment reference count for the pojo. Note that this is not thread safe or atomic.
*/
int incrementRefCount(Fqn originalFqn, Fqn referencingFqn) throws CacheException
{
PojoInstance pojoInstance = getAopInstance(originalFqn);
if (pojoInstance == null)
throw new PojoCacheException("InternalDelegate.incrementRefCount(): null pojoReference for fqn: " + originalFqn);
int count = pojoInstance.incrementRefCount(referencingFqn);
// need to update it.
put(originalFqn, PojoInstance.KEY, pojoInstance);
return count;
}
/**
* Has a delegate method so we can use the switch.
*/
Object get(Fqn fqn, Object key) throws CacheException
{
return get(fqn, key, false);
}
private Object get(Fqn fqn, Object key, boolean gravitate) throws CacheException
{
// TODO let's find a better way to decouple this.
if (gravitate && cache_.getBuddyManager() != null)
{
cache_.getInvocationContext().getOptionOverrides().setForceDataGravitation(true);
Object obj = cache_.get(fqn, key);
cache_.getInvocationContext().getOptionOverrides().setForceDataGravitation(false);
return obj;
}
else if (cache_.getCacheLoaderManager() != null)
{
// We have cache loader, we can't get it directly from the local get.
// cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(true);
Object obj = cache_.get(fqn, key);
// cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(false);
return obj;
}
else
{
// return cache_._get(fqn, key, false);
return cache_.get(fqn, key);
}
}
private void put(Fqn fqn, Object key, Object value) throws CacheException
{
// Use option to ski locking since we have parent lock already.
// cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(true);
cache_.put(fqn, key, value);
// cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(false);
// cache_.put(fqn, key, value);
}
void put(Fqn fqn, Map map) throws CacheException
{
// Use option to ski locking since we have parent lock already.
// cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(true);
cache_.put(fqn, map);
// cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(false);
// cache_.put(fqn, key, value);
}
protected void localPut(Fqn fqn, Object key, Object value) throws CacheException
{
// Use option to ski locking since we have parent lock already.
// TODO Need to make sure there is no tx here otherwise it won't work.
// cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(true);
cache_.put(fqn, key, value);
// cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(false);
}
/**
* decrement reference count for the pojo. Note that this is not thread safe or atomic.
*/
int decrementRefCount(Fqn originalFqn, Fqn referencingFqn) throws CacheException
{
PojoInstance pojoInstance = getAopInstance(originalFqn);
if (pojoInstance == null)
throw new PojoCacheException("InternalDelegate.decrementRefCount(): null pojoReference.");
int count = pojoInstance.decrementRefCount(referencingFqn);
if (count < -1) // can't dip below -1
throw new PojoCacheException("InternalDelegate.decrementRefCount(): null pojoReference.");
// need to update it.
put(originalFqn, PojoInstance.KEY, pojoInstance);
return count;
}
static boolean isReferenced(PojoInstance pojoInstance)
{
// If ref counter is greater than 0, we fqn is being referenced.
return (pojoInstance.getRefCount() > 0);
}
int getRefCount(Fqn fqn) throws CacheException
{
return getAopInstance(fqn).getRefCount();
}
String XgetRefFqn(Fqn fqn) throws CacheException
{
PojoInstance pojoInstance = getAopInstance(fqn);
return getRefFqn(pojoInstance);
}
String getRefFqn(PojoInstance pojoInstance) throws CacheException
{
if (pojoInstance == null)
return null;
String aliasFqn = pojoInstance.getInternalFqn();
if (aliasFqn == null || aliasFqn.length() == 0) return null;
return getRefFqnFromAlias(aliasFqn);
}
void setRefFqn(Fqn fqn, String internalFqn) throws CacheException
{
PojoInstance pojoInstance = getAopInstance(fqn);
if (pojoInstance == null)
pojoInstance = new PojoInstance();
pojoInstance.setInternalFqn(internalFqn);
put(fqn, PojoInstance.KEY, pojoInstance);
}
void removeRefFqn(Fqn fqn) throws CacheException
{
PojoInstance pojoInstance = getAopInstance(fqn);
if (pojoInstance == null)
throw new PojoCacheException("InternalDelegate.getInternalFqn(): null pojoReference.");
pojoInstance.removeInternalFqn();
put(fqn, PojoInstance.KEY, pojoInstance);
}
Object getPojo(Fqn fqn) throws CacheException
{
PojoReference pojoReference = getPojoReference(fqn);
Fqn realFqn = null;
if (pojoReference != null)
{
// This is outward facing node
realFqn = pojoReference.getFqn();
}
else
{
// This is the internal node.
realFqn = fqn;
}
PojoInstance pojoInstance = getAopInstance(realFqn);
if (pojoInstance == null)
return null;
return pojoInstance.get();
}
Object getPojoWithGravitation(Fqn fqn) throws CacheException
{
// This is for buddy replication
PojoReference pojoReference = getPojoReferenceWithGravitation(fqn);
// Find the real fqn where it is stored.
Fqn internalFqn = pojoReference.getFqn();
PojoInstance pojoInstance = getAopInstanceWithGravitation(internalFqn);
if (pojoInstance == null)
return null;
return pojoInstance.get();
}
void setPojo(Fqn fqn, Object pojo) throws CacheException
{
PojoInstance pojoInstance = getAopInstance(fqn);
if (pojoInstance == null)
{
pojoInstance = new PojoInstance();
put(fqn, PojoInstance.KEY, pojoInstance);
}
pojoInstance.set(pojo);
// No need to do a cache put since pojo is transient anyway.
}
static boolean isMultipleReferenced(PojoInstance pojoInstance)
{
if (pojoInstance.getRefCount() > (PojoInstance.INITIAL_COUNTER_VALUE + 1)) return true;
return false;
}
static void setPojo(PojoInstance pojoInstance, Object pojo)
{
// No need to do a cache put since pojo is transient anyway.
pojoInstance.set(pojo);
}
void setPojo(Fqn fqn, Object pojo, PojoInstance pojoInstance) throws CacheException
{
if (pojoInstance == null)
{
pojoInstance = new PojoInstance();
put(fqn, PojoInstance.KEY, pojoInstance);
}
pojoInstance.set(pojo);
// No need to do a cache put since pojo is transient anyway.
}
void putPojoReference(Fqn fqn, PojoReference pojoReference) throws CacheException
{
put(fqn, PojoReference.KEY, pojoReference);
}
void putAopClazz(Fqn fqn, Class clazz) throws CacheException
{
put(fqn, InternalConstant.CLASS_INTERNAL, clazz);
}
/**
* We store the class name in string and put it in map instead of directly putting
* it into cache for optimization.
*/
static void putAopClazz(Class clazz, Map map)
{
map.put(InternalConstant.CLASS_INTERNAL, clazz);
}
Class peekAopClazz(Fqn fqn) throws CacheException
{
return (Class) get(fqn, InternalConstant.CLASS_INTERNAL);
}
void removeInternalAttributes(Fqn fqn) throws CacheException
{
cache_.remove(fqn, PojoInstance.KEY);
cache_.remove(fqn, InternalConstant.CLASS_INTERNAL);
}
void cleanUp(Fqn fqn) throws CacheException
{
// We can't do a brute force remove anymore?
if (cache_.getRoot().getChild(fqn).getChildren() != null)
{
// remove everything
cache_.removeNode(fqn);
// cache_.getRoot().getChild(fqn).clearData();
// removeNodeWithoutInterceptor(fqn);
}
else
{
// Assume everything here is all PojoCache data for optimization
cache_.getRoot().getChild(fqn).clearData();
if (log.isTraceEnabled())
{
log.trace("cleanup(): fqn: " + fqn + " is not empty. That means it has sub-pojos. Will not remove node");
}
}
}
private void removeNodeWithoutInterceptor(Fqn fqn)
{
Option option = cache_.getInvocationContext().getOptionOverrides();
// option.setBypassInterceptorChain(false);
option.setBypassInterceptorChain(true);
cache_.removeNode(fqn);
}
String createIndirectFqn(String fqn) throws CacheException
{
String indirectFqn = getIndirectFqn(fqn);
Fqn internalFqn = getInternalFqn(fqn);
put(internalFqn, indirectFqn, fqn);
return indirectFqn;
}
private Fqn getInternalFqn(String fqn)
{
if (fqn == null || fqn.length() == 0)
throw new IllegalStateException("InternalDelegate.getInternalFqn(). fqn is either null or empty!");
String indirectFqn = getIndirectFqn(fqn);
return new Fqn(InternalConstant.JBOSS_INTERNAL_MAP, indirectFqn);
// return JBOSS_INTERNAL_MAP;
}
static String getIndirectFqn(String fqn)
{
// TODO This is not unique. Will need to come up with a better one in the future.
return ObjectUtil.getIndirectFqn(fqn);
}
void removeIndirectFqn(String oldFqn) throws CacheException
{
String indirectFqn = getIndirectFqn(oldFqn);
cache_.remove(getInternalFqn(oldFqn), indirectFqn);
}
void setIndirectFqn(String oldFqn, String newFqn) throws CacheException
{
String indirectFqn = getIndirectFqn(oldFqn);
Fqn tmpFqn = getInternalFqn(oldFqn);
put(tmpFqn, indirectFqn, newFqn);
}
void updateIndirectFqn(Fqn originalFqn, Fqn newFqn) throws CacheException
{
put(getInternalFqn(originalFqn.toString()), getIndirectFqn(originalFqn.toString()), newFqn.toString());
}
private String getRefFqnFromAlias(String aliasFqn) throws CacheException
{
return (String) get(getInternalFqn(aliasFqn), aliasFqn, true);
}
Fqn getNextFqnInLine(Fqn currentFqn) throws CacheException
{
PojoInstance ai = getAopInstance(currentFqn);
return ai.getAndRemoveFirstFqnInList();
}
/**
* Test if this internal node.
*
* @param fqn
*/
public static boolean isInternalNode(Fqn fqn)
{
// we ignore all the node events corresponding to JBOSS_INTERNAL
if (fqn.isChildOrEquals(InternalConstant.JBOSS_INTERNAL)) return true;
return false;
}
}
1.1 date: 2007/01/13 15:55:06; author: bwang; state: Exp;JBossCache/src/org/jboss/cache/pojo/impl/AdvisedPojoHandler.java
Index: AdvisedPojoHandler.java
===================================================================
/*
* 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.cache.pojo.CachedType;
import org.jboss.cache.pojo.PojoInstance;
import org.jboss.cache.pojo.PojoUtil;
import org.jboss.cache.pojo.InternalConstant;
import org.jboss.cache.pojo.observable.Observer;
import org.jboss.cache.pojo.memory.FieldPersistentReference;
import org.jboss.cache.pojo.util.AopUtil;
import org.jboss.cache.pojo.interceptors.dynamic.CacheFieldInterceptor;
import org.jboss.cache.Fqn;
import org.jboss.cache.CacheException;
import org.jboss.cache.CacheSPI;
import org.jboss.aop.InstanceAdvisor;
import org.jboss.aop.Advised;
import org.jboss.aop.ClassInstanceAdvisor;
import org.jboss.aop.Advisor;
import java.util.Map;
import java.util.List;
import java.util.Set;
import java.util.Iterator;
import java.util.HashMap;
import java.lang.reflect.Field;
/**
* Handling the advised pojo operations. No consideration of object graph here.
*
* @author Ben Wang
* Date: Aug 4, 2005
* @version $Id: AdvisedPojoHandler.java,v 1.1 2007/01/13 15:55:06 bwang Exp $
*/
class AdvisedPojoHandler
{
private final Log log = LogFactory.getLog(AdvisedPojoHandler.class);
private CacheSPI cache_;
private PojoCacheImpl pCache_;
private PojoUtil util_;
private Observer observer_;
public AdvisedPojoHandler(PojoCacheImpl pCache, InternalHelper internal,
PojoUtil util, Observer observer)
{
pCache_ = pCache;
cache_ = (CacheSPI)pCache_.getCache();
util_ = util;
observer_ = observer;
}
public Object get(Fqn fqn, Class clazz, PojoInstance pojoInstance)
throws CacheException
{
CachedType type = pCache_.getCachedType(clazz);
Object obj = null;
try
{
obj = clazz.newInstance();
// TODO Need to populate the object from the cache as well.
}
catch (Exception e)
{
// Is this a case of no-default constructor, e.g., Enum? Let's try it.
try
{
obj = loadFromOthers(fqn, clazz);
if(obj == null) throw e;
}
catch (Exception e1)
{
throw new CacheException("failed creating instance of " + clazz.getName(), e);
}
}
// Insert interceptor at runtime
InstanceAdvisor advisor = ((Advised) obj)._getInstanceAdvisor();
CacheFieldInterceptor interceptor = new CacheFieldInterceptor(pCache_, fqn, type);
interceptor.setAopInstance(pojoInstance);
util_.attachInterceptor(obj, advisor, interceptor, observer_);
return obj;
}
private Object loadFromOthers(Fqn fqn, Class clazz) throws Exception
{
// Try Enum first.
if(clazz.isEnum())
{
String name = (String)pCache_.getCache().get(fqn, InternalConstant.ENUM_KEY);
if(name == null)
{
throw new CacheException(
"AdvisedPojoHandler.loadFromOthers(): can't retrieve java.lang.Enum name field."
+ " fqn: " +fqn);
}
Object obj = Enum.valueOf(clazz, name);
return obj;
}
return null;
}
void put(Fqn fqn, Fqn referencingFqn, Object obj) throws CacheException
{
CachedType type = pCache_.getCachedType(obj.getClass());
// We have a clean slate then.
InstanceAdvisor advisor = ((Advised) obj)._getInstanceAdvisor();
// TODO workaround for deserialiased objects
if (advisor == null)
{
advisor = new ClassInstanceAdvisor(obj);
((Advised) obj)._setInstanceAdvisor(advisor);
}
// Let's do batch update via Map instead
Map map = new HashMap();
// Always initialize the ref count so we can mark this as an AopNode.
PojoInstance pojoInstance = InternalHelper.initializeAopInstance(referencingFqn);
// Insert interceptor at runtime
CacheFieldInterceptor interceptor = new CacheFieldInterceptor(pCache_, fqn, type);
interceptor.setAopInstance(pojoInstance);
util_.attachInterceptor(obj, advisor, interceptor, observer_);
map.put(PojoInstance.KEY, pojoInstance);
pojoInstance.setPojoClass(type.getType());
// we will do it recursively.
// Map of sub-objects that are non-primitive
Map subPojoMap = new HashMap();
boolean hasFieldAnnotation = hasAnnotation(obj.getClass(), ((Advised) obj)._getAdvisor(), type);
for (Iterator i = type.getFields().iterator(); i.hasNext();)
{
Field field = (Field) (((FieldPersistentReference) i.next())).get();
Object value = null;
try
{
value = field.get(obj);
}
catch (IllegalAccessException e)
{
throw new CacheException("field access failed", e);
}
CachedType fieldType = pCache_.getCachedType(field.getType());
// check for non-replicatable types
if (CachedType.isPrimitiveNonReplicatable(field))
{
continue;
}
if (hasFieldAnnotation)
{
if (CachedType.hasTransientAnnotation(field, ((Advised) obj)._getAdvisor()))
{
continue;
}
}
// we simply treat field that has @Serializable as a primitive type.
if (fieldType.isImmediate() ||
(hasFieldAnnotation &&
CachedType.hasSerializableAnnotation(field, ((Advised) obj)._getAdvisor())))
{
// switched using batch update
map.put(field.getName(), value);
} else
{
subPojoMap.put(field, value);
}
}
// Use option to skip locking since we have parent lock already.
// cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(true);
cache_.getRoot().addChild(fqn).put(map);
// cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(false);
// This is in-memory operation only
InternalHelper.setPojo(pojoInstance, obj);
for (Object o : subPojoMap.keySet())
{
Field field = (Field) o;
Object value = subPojoMap.get(field);
if(value == null) continue; // really no need to map the POJO.
Fqn tmpFqn = new Fqn(fqn, field.getName());
pCache_.attach(tmpFqn, value);
// If it is Collection classes, we replace it with dynamic proxy.
// But we will have to ignore it if value is null
if (value instanceof Map || value instanceof List || value instanceof Set)
{
Object newValue = pCache_.getObject(tmpFqn);
util_.inMemorySubstitution(obj, field, newValue);
}
}
// Need to make sure this is behind put such that obj.toString is done correctly.
if (log.isDebugEnabled())
{
log.debug("internalPut(): inserting with fqn: " + fqn);
}
}
Object remove(Fqn fqn, Object result, Class clazz
) throws CacheException
{
CachedType type = pCache_.getCachedType(clazz);
InstanceAdvisor advisor = ((Advised) result)._getInstanceAdvisor();
for (Iterator i = type.getFields().iterator(); i.hasNext();)
{
Field field = (Field) (((FieldPersistentReference) i.next())).get();
CachedType fieldType = pCache_.getCachedType(field.getType());
Object value = null;
if (!fieldType.isImmediate())
{
value = pCache_.detach(new Fqn(fqn, field.getName()));
// Check for Collection. If it is, we need to reset the original reference.
if ((value instanceof Map || value instanceof List || value instanceof Set))
{
// If this Collection class, we are returning the original value already
util_.inMemorySubstitution(result, field, value);
}
}
}
// batch remove
cache_.getRoot().getChild(fqn).clearData();
// Determine if we want to keep the interceptor for later use.
CacheFieldInterceptor interceptor = (CacheFieldInterceptor) AopUtil.findCacheInterceptor(advisor);
// Remember to remove the interceptor from in-memory object but make sure it belongs to me first.
if (interceptor != null)
{
if (log.isDebugEnabled()) {
log.debug("regularRemoveObject(): removed cache interceptor fqn: " + fqn + " interceptor: "+interceptor);
}
util_.detachInterceptor(advisor, interceptor, observer_);
}
return null; // Not really null though.
}
private static boolean hasAnnotation(Class clazz, Advisor advisor, CachedType type)
{
return CachedType.hasAnnotation(clazz, advisor, type);
}
}
More information about the jboss-cvs-commits
mailing list