[jboss-cvs] JBossCache/old/src/org/jboss/cache/aop ...
Ben Wang
bwang at jboss.com
Tue Oct 31 03:01:13 EST 2006
User: bwang
Date: 06/10/31 03:01:13
Added: old/src/org/jboss/cache/aop
AopMarker.java WriteReplaceable.java
PojoCacheIfc.java TreeCacheAopIfc.java
ModificationEntry.java PojoCache.java
MarshalledTreeCache.java
SerializableObjectHandler.java
CollectionClassHandler.java TreeCacheAopView.java
CacheInterceptor.java
PojoTxSynchronizationHandler.java
ObjectGraphHandler.java InstanceOfAopMarker.java
WriteReplacer.java CachedType.java
TreeCacheAop.java InternalDelegate.java
TreeCacheAopDelegate.java CachedAttribute.java
AOPInstance.java BaseInterceptor.java
Log:
Deprecated files moved to old dir.
Revision Changes Path
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/AopMarker.java
Index: AopMarker.java
===================================================================
package org.jboss.cache.aop;
/**
* Marker interface for use in aop annotation declaration. For example, can use this in pojo
* annotation instead of declaring jboss-aop.xml.
* @author Ben Wang
*/
public interface AopMarker {
}
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/WriteReplaceable.java
Index: WriteReplaceable.java
===================================================================
/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.aop;
import java.io.ObjectStreamException;
/**
* @author <a href="mailto:harald at gliebe.de">Harald Gliebe</a>
* @deprecated 1.0
*/
public interface WriteReplaceable
{
Object writeReplace() throws ObjectStreamException;
}
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/PojoCacheIfc.java
Index: PojoCacheIfc.java
===================================================================
/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.aop;
import org.jboss.cache.CacheException;
import org.jboss.cache.Fqn;
import java.util.Map;
/**
* Interface for PojoCache. User should use this interface directly to access the public APIs.
* <p>PojoCache is an in-memory, transactional, fine-grained, and object-oriented cache system. It
* differs from the typical generic cache library by operating on the pojo level directly without requiring
* that object to be serializable (although it does require "aspectizing" the POJO). Further, it can
* preserve object graph during replication or persistency. It also track the replication via fine-grained
* maner, i.e., only changed fields are replicated.</p>
*
* @author Ben Wang
* @since 1.4
*/
public interface PojoCacheIfc
{
/**
* Retrieve pojo from the cache. Return null if object does not exist in the cache.
* Note that this operation is fast if there is already an POJO instance attached to the cache.
*
* @param fqn Instance that associates with this node.
* @return Current content value. Null if does not exist.
* @throws CacheException Throws if there is an error related to the cache operation, e.g.,
* {@link org.jboss.cache.lock.TimeoutException}.
*/
Object getObject(String fqn) throws CacheException;
/**
* Retrieve pojo object from the cache. Return null if object does not exist in the cache.
* Note that this operation is fast if there is already an POJO instance attached to the cache.
*
* @param fqn Instance that associates with this node.
* @return Current content value. Null if does not exist.
* @throws CacheException Throws if there is an error related to the cache operation, e.g.,
* {@link org.jboss.cache.lock.TimeoutException}.
*/
Object getObject(Fqn fqn) throws CacheException;
/**
* Insert a pojo into the cache.
* It will also recursively put the any sub-object that is
* declared as aop-capable (i.e., in <code>jboss-aop.xml</code>).
* Note that <code>List</code>, <code>Map</code>, <code>Set</code>
* attributes are aop-enabled, by default, as well.
*
* @param fqn The fqn instance to associate with the object in the cache.
* @param obj aop-enabled object to be inerted into the cache. If null,
* it will nullify the fqn node.
* @param obj Return the previous content under fqn.
* @return Existing POJO or null if there is not.
* @throws CacheException Throws if there is an error related to the cache operation, e.g.,
* {@link org.jboss.cache.lock.TimeoutException}.
*/
Object putObject(String fqn, Object obj) throws CacheException;
/**
* Insert a pojo into the cache.
* It will also recursively put the any sub-object that is
* declared as aop-capable (i.e., in <code>jboss-aop.xml</code>).
* Note that <code>List</code>, <code>Map</code>, <code>Set</code>
* attributes are aop-enabled, by default, as well.
*
* @param fqn The fqn instance to associate with the object in the cache.
* @param obj aop-enabled object to be inerted into the cache. If null,
* it will nullify the fqn node.
* @param obj Return the previous content under fqn.
* @return Existing POJO or null if there is not.
* @throws CacheException Throws if there is an error related to the cache operation, e.g.,
* {@link org.jboss.cache.lock.TimeoutException}.
*/
Object putObject(Fqn fqn, Object obj) throws CacheException;
/**
* Remove pojo object from the cache.
*
* @param fqn Instance that associates with this node.
* @return Original value object from this node.
* @throws CacheException Throws if there is an error related to the cache operation, e.g.,
* {@link org.jboss.cache.lock.TimeoutException}.
*/
Object removeObject(String fqn) throws CacheException;
/**
* Remove pojo object from the cache.
*
* @param fqn Instance that associates with this node.
* @return Original value object from this node.
* @throws CacheException Throws if there is an error related to the cache operation, e.g.,
* {@link org.jboss.cache.lock.TimeoutException}.
*/
Object removeObject(Fqn fqn) throws CacheException;
/**
* Query all managed pojo objects under the fqn recursively. Note that this will not return the sub-object pojos,
* e.g., if Person has a sub-object of Address, it won't return Address pojo. Note also that this operation is
* not thread-safe now. In addition, it assumes that once a pojo is found with a fqn, no more pojo is stored
* under the children of the fqn. That is, we don't mixed the fqn with different pojos.
* @param fqn The starting place to find all pojos.
* @return Map of all pojos found with (fqn, pojo) pair. Return size of 0, if not found.
* @throws CacheException Throws if there is an error related to the cache operation, e.g.,
* {@link org.jboss.cache.lock.TimeoutException}.
*/
Map findObjects(String fqn) throws CacheException;
/**
* Query all managed pojo objects under the fqn recursively. Note that this will not return the sub-object pojos,
* e.g., if Person has a sub-object of Address, it won't return Address pojo. Note also that this operation is
* not thread-safe now. In addition, it assumes that once a pojo is found with a fqn, no more pojo is stored
* under the children of the fqn. That is, we don't mixed the fqn with different pojos.
* @param fqn The starting place to find all pojos.
* @return Map of all pojos found with (fqn, pojo) pair. Return size of 0, if not found.
* @throws CacheException
*/
Map findObjects(Fqn fqn) throws CacheException;
/**
* If the flag is set, then POJO which is not instrumented under AOP and which is not implementing the
* Serializable interface will still be marshalled and make serializable.
*
*/
void setMarshallNonSerializable(boolean marshall);
/**
* Indicate whether the flag is set or not.
*/
boolean isMarshallNonSerializable();
/**
* Obtain a cache aop type for user to traverse the defined "primitive" types in aop.
*
* @param clazz The original pojo class
* @return CachedType
*/
public CachedType getCachedType(Class clazz);
}
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/TreeCacheAopIfc.java
Index: TreeCacheAopIfc.java
===================================================================
/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.aop;
import org.jboss.cache.CacheException;
import org.jboss.cache.Fqn;
/**
*
* @author Harald Gliebe
* @author Ben Wang
* @deprecated Since 1.4, replaced by {@link PojoCacheIfc}
*/
public interface TreeCacheAopIfc extends PojoCacheIfc
{
}
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/ModificationEntry.java
Index: ModificationEntry.java
===================================================================
/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.aop;
import org.jboss.cache.Fqn;
import org.jboss.aop.InstanceAdvisor;
import java.io.Serializable;
import java.util.List;
import java.util.ArrayList;
import java.lang.reflect.Field;
/**
* Modification entry for undo compensation. This is kind of ad hoc now.
* We need to come up with an more formal undo operations to handle other cases
* like object graph.
* @author Ben Wang
*/
public class ModificationEntry
{
public static final int INTERCEPTOR_ADD = 0;
public static final int INTERCEPTOR_REMOVE = 1;
public static final int COLLECTION_REPLACE = 2;
private InstanceAdvisor advisor_;
private BaseInterceptor interceptor_;
private Field field_;
private Object key_;
private Object oldValue_;
int opType_;
public ModificationEntry(InstanceAdvisor advisor, BaseInterceptor interceptor, int op)
{
advisor_ = advisor;
interceptor_ = interceptor;
opType_ = op;
}
public ModificationEntry(Field field, Object key, Object oldValue)
{
field_ = field;
key_ = key;
oldValue_ = oldValue;
opType_ = COLLECTION_REPLACE;
}
public int getOpType() { return opType_; }
public InstanceAdvisor getInstanceAdvisor() { return advisor_; }
public BaseInterceptor getCacheInterceptor() { return interceptor_; }
public Field getField() {
return field_;
}
public Object getKey() {
return key_;
}
public Object getOldValue() {
return oldValue_;
}
}
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/PojoCache.java
Index: PojoCache.java
===================================================================
/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.aop;
import org.jboss.aop.InstanceAdvisor;
import org.jboss.cache.CacheException;
import org.jboss.cache.Fqn;
import org.jboss.cache.NodeImpl;
import org.jboss.cache.Region;
import org.jboss.cache.RegionNotEmptyException;
import org.jboss.cache.TreeCache;
import org.jboss.cache.aop.eviction.AopEvictionPolicy;
import org.jboss.cache.aop.statetransfer.PojoStateTransferManager;
import org.jboss.cache.lock.UpgradeException;
import org.jboss.cache.marshall.ObjectSerializationFactory;
import org.jboss.cache.marshall.RegionNameConflictException;
import org.jboss.cache.transaction.BatchModeTransactionManager;
import org.jboss.cache.xml.XmlHelper;
import org.jgroups.JChannel;
import org.w3c.dom.Element;
import javax.transaction.RollbackException;
import javax.transaction.Status;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
/**
* PojoCache implementation class. User should use the {@link PojoCacheIfc} interface directly to
* access the public APIs.
* <p>PojoCache is an in-memory, transactional, fine-grained, and object-oriented cache system. It
* differs from the typical generic cache library by operating on the pojo level directly without requiring
* that object to be serializable (although it does require "aspectizing" the POJO). Further, it can
* preserve object graph during replication or persistency. It also track the replication via fine-grained
* maner, i.e., only changed fields are replicated.</p>
*
* @author Ben Wang
* @since 1.4
*/
public class PojoCache extends TreeCache// implements PojoCacheMBean //TODO: Need to properly implement a JMX interface
{
// Class -> CachedType
// use WeakHashMap to allow class reloading
protected Map cachedTypes = new WeakHashMap();
// Use batch mode tm to simulate the batch processing.
TransactionManager localTm_ = BatchModeTransactionManager.getInstance();
protected TreeCacheAopDelegate delegate_;
Element config_ = null;
protected final String LOCK = "_lock_";
protected final int RETRY = 5; // retry times for lockPojo just in case there is upgrade exception during concurrent access.
// boolean detachPojoWhenEvicted_ = false;
protected boolean marshallNonSerializable_ = false;
protected ThreadLocal undoListLocal_ = new ThreadLocal();
protected ThreadLocal hasSynchronizationHandler_ = new ThreadLocal();
public PojoCache(String cluster_name,
String props,
long state_fetch_timeout)
throws Exception
{
// TODO: use a CacheFactory
//super(cluster_name, props, state_fetch_timeout);
init();
}
public PojoCache() throws Exception
{
init();
}
public PojoCache(JChannel channel) throws Exception
{
super(channel);
init();
}
protected void init()
{
delegate_ = new TreeCacheAopDelegate(this);
}
public void start() throws Exception
{
// Replace the default state transfer manager
setStateTransferManager(new PojoStateTransferManager(this));
super.start();
parseConfig();
}
public void stop()
{
super.stop();
}
protected void parseConfig()
{
if (config_ == null)
{
log.info("parseConfig(): PojoCacheConfig is empty");
return;
}
marshallNonSerializable_ = XmlHelper.readBooleanContents(config_, "marshallNonSerializable");
log.info("marshallNonSerializable flag is set: " + marshallNonSerializable_);
// detachPojoWhenEvicted_ = XmlHelper.readBooleanContents(config_, "DetachPojoWhenEvicted");
}
/**
* Over-ride to make sure we are using an eviction policy specific to aop.
*/
public void setEvictionPolicyClass(String eviction_policy_class)
{
getConfiguration().setEvictionPolicyClass(eviction_policy_class);
if (eviction_policy_class == null || eviction_policy_class.length() == 0)
return;
try
{
Object obj = loadClass(eviction_policy_class).newInstance();
if (!(obj instanceof AopEvictionPolicy))
throw new RuntimeException("PojoCache.setEvictionPolicyClass(). Eviction policy provider:" +
eviction_policy_class + " is not an instance of AopEvictionPolicy.");
getConfiguration().setEvictionPolicyClass(eviction_policy_class);
}
catch (RuntimeException ex)
{
log.error("setEvictionPolicyClass(): failed creating instance of " + eviction_policy_class, ex);
throw ex;
}
catch (Throwable t)
{
log.error("setEvictionPolicyClass(): failed creating instance of " + eviction_policy_class, t);
}
}
public void addUndoInterceptor(InstanceAdvisor advisor, BaseInterceptor interceptor, int op)
{
List list = (List) undoListLocal_.get();
if (list == null)
{
list = new ArrayList();
undoListLocal_.set(list);
}
ModificationEntry ent = new ModificationEntry(advisor, interceptor, op);
list.add(ent);
}
public void addUndoCollectionProxy(Field field, Object key, Object oldValue)
{
List list = (List) undoListLocal_.get();
if (list == null)
{
list = new ArrayList();
undoListLocal_.set(list);
}
ModificationEntry ent = new ModificationEntry(field, key, oldValue);
list.add(ent);
}
public void resetUndoOp()
{
List list = (List) undoListLocal_.get();
if (list != null)
list.clear();
hasSynchronizationHandler_.set(null);
}
public List getModList()
{
// No need to make it unmodifiable since this is thread local
return (List) undoListLocal_.get();
}
/**
* Override to provide aop specific eviction.
* <p/>
* <p/>
* Called by eviction policy provider. Note that eviction is done only in local mode,
* that is, it doesn't replicate the node removal. This will cause the replication nodes
* not synchronizing, but it is ok since user is supposed to add the node again when get is
* null. After that, the contents will be in sync.
*
* @param fqn Will remove everythign assoicated with this fqn.
* @throws org.jboss.cache.CacheException
*/
public void evict(Fqn fqn) throws CacheException
{
// We will remove all children nodes as well since we assume all children nodes are part
// of this "object" node.
if (delegate_.isAopNode(fqn))
{
if (log.isDebugEnabled())
{
log.debug("evict(): evicting whole aop node " + fqn);
}
// _evictObject(fqn);
recursiveEvict(fqn);
}
else
{
super.evict(fqn);
}
}
void recursiveEvict(Fqn fqn) throws CacheException
{
boolean create_undo_ops = true;
boolean sendNodeEvent = false;
// Let's do it brute force.
_remove(null, fqn, create_undo_ops, sendNodeEvent);
}
/**
* Package level evict for plain cache.
*
* @param fqn
* @throws CacheException
*/
void plainEvict(Fqn fqn) throws CacheException
{
super.evict(fqn);
}
protected void createEvictionPolicy()
{
super.createEvictionPolicy();
this.evictionInterceptorClass = "org.jboss.cache.aop.interceptors.PojoEvictionInterceptor";
}
protected void _evictSubtree(Fqn subtree) throws CacheException
{
if (log.isTraceEnabled())
log.trace("_evictSubtree(" + subtree + ")");
// We will remove all children nodes as well since we assume all children nodes are part
// of this "object" node.
if (delegate_.isAopNode(subtree))
{
if (log.isDebugEnabled())
{
log.debug("evict(): evicting whole aop node " + subtree);
}
// _evictObject(subtree);
recursiveEvict(subtree);
}
else
{
super._evictSubtree(subtree);
}
}
/**
* Overrides the {@link TreeCache#activateRegion(String) superclass method} by
* ensuring that the internal region where information on shared object is stored
* has been activated.
*/
public void activateRegion(String subtreeFqn) throws RegionNotEmptyException, RegionNameConflictException, CacheException
{
if (!getConfiguration().isUseRegionBasedMarshalling())
throw new IllegalStateException("TreeCache.activateRegion(). useRegionBasedMarshalling flag is not set!");
if ("/".equals(subtreeFqn))
{
// Just pass it through, as we'll get the internal area
// with the normal state transfer
super.activateRegion(subtreeFqn);
}
else
{
// If the internal region is not activated yet, activate it first
//MarshRegion region = regionManager_.getRegion(InternalDelegate.JBOSS_INTERNAL);
Region region = getCacheSPI().getRegion(InternalDelegate.JBOSS_INTERNAL, false);
if ((region == null && getConfiguration().isInactiveOnStartup())
|| (region != null && !region.isActive()))
{
super.activateRegion(InternalDelegate.JBOSS_INTERNAL.toString());
}
// If we don't have an internal map node yet, create one.
// Doing this ensures the code that integrates map references for
// the region will have a node to integrate into
if (get(InternalDelegate.JBOSS_INTERNAL_MAP) == null)
createSubtreeRootNode(InternalDelegate.JBOSS_INTERNAL_MAP);
// Now activate the requested region
super.activateRegion(subtreeFqn);
}
}
/**
* 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;
}
}
/**
*/
public Object getObject(String fqn) throws CacheException
{
return getObject(Fqn.fromString(fqn));
}
/**
*/
public Object getObject(Fqn fqn) throws CacheException
{
return _getObject(fqn);
}
/**
*/
public Object putObject(String fqn, Object obj) throws CacheException
{
return putObject(Fqn.fromString(fqn), obj);
}
/**
*/
public Object putObject(Fqn fqn, Object obj) throws CacheException
{
checkFqnValidity(fqn);
if (log.isDebugEnabled())
{
log.debug("putObject(): Fqn:" + fqn);
}
Object owner = null;
if (hasCurrentTransaction()) // We have a transaction context going on now.
{
// Start a new transaction, we need transaction so the operation is batch.
owner = getOwnerForLock(); // lock it for the whole duration of batch mode.
// Lock the parent, create and add the child
if (!lockPojo(owner, fqn))
{
throw new CacheException("PojoCache.putObject(): Can't obtain the pojo lock under fqn: " + fqn);
}
return _putObject(fqn, obj);
}
else
{
// Start a new transaction, we need transaction so the operation is batch.
try
{
// Need this just in case the node does yet exist.
localTm_.begin();
owner = getOwnerForLock(); // lock it for the whole duration of batch mode.
// Lock the parent, create and add the child
if (!lockPojo(owner, fqn))
{
throw new CacheException("PojoCache.putObject(): Can't obtain the pojo lock under fqn: " + fqn);
}
Object objOld = _putObject(fqn, obj);
return objOld;
}
catch (Exception e)
{
log.warn("putObject(): exception occurred: " + e);
try
{
localTm_.setRollbackOnly();
}
catch (Exception exn)
{
exn.printStackTrace();
}
if (!(e instanceof CacheException))
throw new RuntimeException("PojoCache.putObject(): fqn: " + fqn, e);
else
throw(CacheException) e;
}
finally
{
endTransaction(fqn);
// Release no matter what.
// releasePojo(owner, fqn);
}
}
}
/**
*/
public Object removeObject(String fqn) throws CacheException
{
return removeObject(Fqn.fromString(fqn));
}
/**
*/
public Object removeObject(Fqn fqn) throws CacheException
{
checkFqnValidity(fqn);
if (log.isDebugEnabled())
{
log.debug("removeObject(): Fqn:" + fqn);
}
Object owner = null;
if (hasCurrentTransaction()) // We have a transaction context going on now.
{
owner = getOwnerForLock();
if (!lockPojo(owner, fqn))
{
throw new CacheException("PojoCache.removeObject(): Can't obtain the pojo lock under fqn: " + fqn);
}
return _removeObject(fqn, true);
}
else
{
// Start a new transaction, we need transaction so the operation is atomic.
try
{
localTm_.begin();
owner = getOwnerForLock();
if (!lockPojo(owner, fqn))
{
throw new CacheException("PojoCache.removeObject(): Can't obtain the pojo lock under fqn: " + fqn);
}
return _removeObject(fqn, true);
}
catch (Exception e)
{
log.warn("removeObject(): exception occurred: " + e);
try
{
localTm_.setRollbackOnly();
}
catch (Exception exn)
{
exn.printStackTrace();
}
if (!(e instanceof CacheException))
throw new RuntimeException("PojoCache.removeObject(): fqn: " + fqn, e);
else
throw(CacheException) e;
}
finally
{
endTransaction(fqn);
// Release no matter what.
// releasePojo(owner, fqn);
}
}
}
/**
*/
public Map findObjects(String fqn) throws CacheException
{
return findObjects(Fqn.fromString(fqn));
}
/**
*/
public Map findObjects(Fqn fqn) throws CacheException
{
return _findObjects(fqn);
}
public void setMarshallNonSerializable(boolean marshall)
{
if (marshall)
{
if (!ObjectSerializationFactory.useJBossSerialization())
{
throw new IllegalStateException("PojoCache.setMarshallNonSerializable(). " +
"Can't set MarshallNonSerializable to true since useJBossSerialization is false");
}
}
marshallNonSerializable_ = marshall;
}
public boolean isMarshallNonSerializable()
{
return marshallNonSerializable_;
}
/**
* Inject the config element that is specific to PojoCache.
*
* @param config
* @throws CacheException
*/
public void setPojoCacheConfig(Element config) throws CacheException
{
config_ = config;
}
public Element getPojoCacheConfig()
{
return config_;
}
private void checkFqnValidity(Fqn fqn)
{
// throws exception is fqn is JBossInternal
if (fqn.equals(InternalDelegate.JBOSS_INTERNAL))
{
throw new IllegalArgumentException("checkFqnValidity(): fqn is not valid: " + fqn);
}
}
protected boolean lockPojo(Object owner, Fqn fqn) throws CacheException
{
if (log.isDebugEnabled())
{
log.debug("lockPojo(): Fqn:" + fqn + " Owner: " + owner);
}
boolean isNeeded = true;
int retry = 0;
while (isNeeded)
{
try
{
put(fqn, LOCK, "LOCK");
isNeeded = false;
}
catch (UpgradeException upe)
{
log.warn("lockPojo(): can't upgrade the lock during lockPojo. Will re-try. Fqn: " + fqn
+ " retry times: " + retry);
get(fqn).release(owner);
if (retry++ > RETRY)
{
return false;
}
// try to sleep a little as well.
try
{
Thread.sleep(10);
}
catch (InterruptedException e)
{
;
}
continue;
}
}
return true;
}
protected void releasePojo(Object owner, Fqn fqn) throws CacheException
{
NodeImpl node = get(fqn);
if (node == null)
{
if (log.isDebugEnabled())
{
log.debug("releasePojo(): node could have been released already.");
}
return;
}
node.release(owner);
}
protected boolean hasCurrentTransaction()
{
try
{
if (getCurrentTransaction() != null || localTm_.getTransaction() != null)
{
// We have transaction context. Return null to signify don't do anything
return true;
}
}
catch (SystemException e)
{
throw new RuntimeException("PojoCache.hasCurrentTransaction: ", e);
}
return false;
}
protected void endTransaction(Fqn fqn)
{
if (localTm_ == null)
{
log.warn("PojoCache.endTransaction(): tm is null for fqn: " + fqn);
return;
}
try
{
if (localTm_.getTransaction().getStatus() != Status.STATUS_MARKED_ROLLBACK)
{
localTm_.commit();
}
else if (localTm_.getTransaction().getStatus() == Status.STATUS_ROLLEDBACK)
{
log.info("PojoCache.endTransaction(): has been rolled back for fqn: " + fqn);
}
else
{
log.info("PojoCache.endTransaction(): rolling back tx for fqn: " + fqn);
localTm_.rollback();
}
}
catch (RollbackException re)
{
// Do nothing here since cache may rollback automatically.
log.warn("PojoCache.endTransaction(): rolling back transaction with exception: " + re);
}
catch (Exception e)
{
log.warn("PojoCache.endTransaction(): Failed with exception: " + e);
}
}
/**
* Used by internal implementation. Not for general public.
*/
public Object _getObject(Fqn fqn) throws CacheException
{
return delegate_._getObject(fqn);
}
/**
* Used by internal implementation. Not for general public.
*/
public Object _putObject(Fqn fqn, Object obj) throws CacheException
{
registerTxHandler();
return delegate_._putObject(fqn, obj);
}
protected void registerTxHandler() throws CacheException
{
try
{
// Need to have this in case of rollback
Boolean isTrue = (Boolean) hasSynchronizationHandler_.get();
if (isTrue == null || !isTrue.booleanValue())
{
Transaction tx = getLocalTransaction();
if (tx == null) tx = localTm_.getTransaction();
if (tx == null)
{
throw new IllegalStateException("PojoCache.registerTxHanlder(). Can't have null tx handle.");
}
tx.registerSynchronization(
new PojoTxSynchronizationHandler(tx, this));
hasSynchronizationHandler_.set(Boolean.TRUE);
}
}
catch (RollbackException e)
{
throw new CacheException("_putObject(). Exception: " + e);
}
catch (SystemException e)
{
throw new CacheException("_putObject(). Exception: " + e);
}
}
/**
* Used by internal implementation. Not for general public.
*/
public Object _removeObject(Fqn fqn) throws CacheException
{
boolean removeCacheInterceptor = true;
return _removeObject(fqn, removeCacheInterceptor);
}
/**
* Used by internal implementation. Not for general public.
*/
public Object _removeObject(Fqn fqn, boolean removeCacheInterceptor) throws CacheException
{
boolean evict = false;
// Don't trigger bulk remove now since there is still some problem with Collection class
// when it is detached.
delegate_.setBulkRemove(true);
registerTxHandler();
return delegate_._removeObject(fqn, removeCacheInterceptor, evict);
}
/**
* 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, evict);
}
/**
* Used by internal implementation. Not for general public.
*/
public Map _findObjects(Fqn fqn) throws CacheException
{
return delegate_._findObjects(fqn);
}
}
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/MarshalledTreeCache.java
Index: MarshalledTreeCache.java
===================================================================
/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.aop;
import org.jboss.cache.CacheException;
import org.jboss.cache.CacheListener;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.Fqn;
import org.jboss.cache.TreeCache;
import org.jboss.cache.config.Configuration;
import org.jboss.cache.lock.IsolationLevel;
import org.jboss.aop.util.MarshalledValue;
import org.jgroups.JChannel;
import org.jgroups.View;
import org.jgroups.stack.IpAddress;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* <p>Version of TreeCache that added call to handle marshalling values. You will need marshalling when your application
* is running under different class loader scope, for example, under JBoss AS where your application has a scoped
* class loader.</p>
* <p>Note that: Currently, we also have a in-memory cache copy to minimize the call to unmarshalling. And we also
* have an invalidation mechanism in place to synchronize the external updates.</p>
* <p>In the future, it'd be best if JBossCache can provides 1) notification excluding myself, 2) notification granulairty
* with specific modified key, 3) we will also move this to different package.</p>
* <p>Finally, since the use of in-memory copy, the memory usage is almost doubled since we have one in-memory copy and
* the marshalled value in the cache store.</p>
*
* @author Ben Wang
*/
public class MarshalledTreeCache extends TreeCache implements CacheListener
{
// Store the in-memory copy of the treecache (key, value) pair.
// This is used for performance reason so there is no need to un-marshall every single operation.
// In addition, it will support an invalidation mechanism.
protected TreeCache localCopy_;
protected String nodeId_;
// Key to denotes the caller's ID. We will use this to check whether this is from myself or not.
// TODO Will need to document this.
protected static final String NODEID_KEY = "__NODEID_KEY__";
// Context class loader. If it is not null, marshalling/unmarshalling will use this.
protected ClassLoader tcl_ = null;
// If it is on, will use an internal copy to keep the unmarshalling value.
protected boolean useLocalOptimization_ = true;
// Indicate whether we want marshalling or not. If it is false, useLocalOptimization will be false as wel.
protected boolean marshalling_ = true;
public MarshalledTreeCache(String cluster_name,
String props,
long state_fetch_timeout)
throws Exception
{
// TODO: Use factory
//super(cluster_name, props, state_fetch_timeout);
this._init();
}
public MarshalledTreeCache() throws Exception
{
this._init();
}
public MarshalledTreeCache(JChannel channel) throws Exception
{
super(channel);
this._init();
}
private void _init() throws Exception
{
localCopy_ = new TreeCache();
Configuration conf = new Configuration();
conf.setCacheMode("LOCAL");
conf.setIsolationLevel(IsolationLevel.REPEATABLE_READ);
localCopy_.setConfiguration(conf);
marshalling_ = true;
useLocalOptimization_ = true;
tcl_ = null;
}
public void start() throws Exception
{
super.getNotifier().addCacheListener(this);
super.start();
if (localCopy_ == null)
throw new RuntimeException("start(): null localCopy_");
localCopy_.start();
obtainNodeId();
}
public void stop()
{
nodeId_ = null;
localCopy_.stop();
super.stop();
}
/**
* Get a node id based on jgroups properties.
*/
protected void obtainNodeId()
{
IpAddress address = (IpAddress) getLocalAddress();
if (address == null)
{
log.info("obtainNodeId(): has null IpAddress. Assume it is running in local mode.");
nodeId_ = "local";
return;
}
if (address.getAdditionalData() == null)
{
nodeId_ = address.getIpAddress().getHostAddress() + ":" + address.getPort();
}
else
{
nodeId_ = new String(address.getAdditionalData());
}
}
/**
* DataNode id is a communication id that denotes the cluster node.
*/
public String getNodeId()
{
return nodeId_;
}
/**
* Turn marshalling layer on or off. If off, no marshalling. Default is on.
*/
public void setMarshalling(boolean marshalling)
{
marshalling_ = marshalling;
}
/**
* Indicate whether to have a in-memory copy of the unmarshalling object such that
* there is no need to unmarshal. If it is on, invlidation will be handled where another active
* node has update this fqn.
*/
public void setLocalOptimization(boolean optimization)
{
useLocalOptimization_ = optimization;
throw new RuntimeException("MarshalledTreeCache.setLocalOptimization(): operation not supported yet.");
}
/**
* The context class loader to perform marshalling/unmarshalling
*/
public void setClassLoader(ClassLoader tcl)
{
tcl_ = tcl;
}
public void marshalledPut(String fqn, Object key, Object value) throws CacheException
{
marshalledPut(Fqn.fromString(fqn), key, value);
}
/**
* Marshalled put. That is, the value that is put into cache is marshalled first. Note that
* we still require that key to be primitive type.
*/
public void marshalledPut(Fqn fqn, Object key, Object value) throws CacheException
{
if (marshalling_)
{
marshalledPut_(fqn, key, value);
}
else
{
put(fqn, key, value);
}
}
public void marshalledPut_(Fqn fqn, Object key, Object value) throws CacheException
{
MarshalledValue mv = null;
try
{
mv = new MarshalledValue(value);
}
catch (IOException e)
{
e.printStackTrace();
throw new CacheException("marshalledPut() exception: " + e);
}
// Put into local copy first.
localCopy_.put(fqn, key, value);
// Put into cache
Map map = new HashMap();
map.put(key, mv);
map.put(NODEID_KEY, nodeId_);
this.put(fqn, map);
}
public Object marshalledGet(String fqn, Object key) throws CacheException
{
return marshalledGet(Fqn.fromString(fqn), key);
}
/**
* Obtain the value from the marshalled cache. Note that the return value is un-marshalled
* either from the local copy or from the distributed store.
*/
public Object marshalledGet(Fqn fqn, Object key) throws CacheException
{
if (marshalling_)
{
ClassLoader prevTCL = null;
if (tcl_ != null)
{
prevTCL = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(tcl_);
}
try
{
return marshalledGet_(fqn, key);
}
finally
{
if (tcl_ != null && prevTCL != null)
{
Thread.currentThread().setContextClassLoader(prevTCL);
}
}
}
else
{
return get(fqn, key);
}
}
public Object marshalledGet_(Fqn fqn, Object key) throws CacheException
{
// Check if it is in local copy first.
Object value;
try
{
if ((value = localCopy_.get(fqn, key)) != null)
return value;
else
{ // get it from cache store
value = get(fqn, key);
if (value == null) return null;
checkValue(value);
MarshalledValue mv = (MarshalledValue) value;
value = mv.get();
// populate the local copy
localCopy_.put(fqn, key, value);
return value;
}
}
catch (IOException e)
{
e.printStackTrace();
throw new CacheException("marshalledGet(): exception encountered: ", e);
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
throw new CacheException("marshalledGet(): exception encountered: ", e);
}
}
public Object marshalledRemove(String fqn, Object key) throws CacheException
{
return marshalledRemove(Fqn.fromString(fqn), key);
}
/**
* Remove a marshalled node. This is required if you have performed a marshalledPut since
* we will need to do clean up.
*/
public Object marshalledRemove(Fqn fqn, Object key) throws CacheException
{
if (marshalling_)
{
return marshalledRemove_(fqn, key);
}
else
{
return remove(fqn, key);
}
}
public Object marshalledRemove_(Fqn fqn, Object key) throws CacheException
{
if (!exists(fqn, key))
log.warn("marshalledRemove(): fqn: " + fqn + " key: " + key + " not found.");
Object value = localCopy_.get(fqn, key);
localCopy_.remove(fqn);
remove(fqn, NODEID_KEY);
Object obj = remove(fqn, key);
if (value != null) return value;
checkValue(obj);
try
{
return ((MarshalledValue) obj).get();
}
catch (IOException e)
{
e.printStackTrace();
throw new CacheException("marshalledRemove(): exception encountered: ", e);
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
throw new CacheException("marshalledRemove(): exception encountered: ", e);
}
}
public void nodeCreated(Fqn fqn, boolean pre, boolean isLocal)
{
}
public void nodeModified(Fqn fqn, boolean pre, boolean isLocal, Map data)
{
if (!pre) invalidate(fqn);
}
public void nodeRemoved(Fqn fqn, boolean pre, boolean isLocal, Map data)
{
if (!pre) invalidate(fqn);
}
public void nodeVisited(Fqn fqn, boolean pre)
{
}
public void nodeEvicted(Fqn fqn, boolean pre, boolean isLocal)
{
if (!pre) invalidate(fqn);
}
public void nodeLoaded(Fqn fqn, boolean pre, Map data)
{
}
public void nodeMoved(Fqn from, Fqn to, boolean pre)
{
}
public void nodeActivated(Fqn fqn, boolean pre)
{
}
public void nodePassivated(Fqn fqn, boolean pre)
{
}
public void cacheStarted(CacheSPI cache)
{
}
public void cacheStopped(CacheSPI cache)
{
}
public void viewChange(View new_view)
{
}
protected void checkValue(Object value)
{
if (value != null && !(value instanceof MarshalledValue))
throw new RuntimeException("checkValue: return object is not instance of MarshalledValue. object: " + value);
}
/**
* Invalidate the local copy cache. Assumption is invlidation should not happen that often anyway.
* In addition, we will invalidate the whole thing under the fqn.
*
* @param fqn
*/
protected void invalidate(Fqn fqn)
{
if (!marshalling_) return; // No need if there is no marshalling!
if (fqn.isRoot()) return; // No need to handle root.
if (!localCopy_.exists(fqn)) return; // probably not a mv node anyway.
try
{
String eventId = (String) get(fqn, NODEID_KEY);
if (eventId == null)
throw new RuntimeException("invlidate(): fqn to invlidate has null node id. fqn: " + fqn);
if (nodeId_.equals(eventId)) return; // skip since this event is initiated by myself.
localCopy_.remove(fqn);
}
catch (CacheException e)
{
e.printStackTrace();
}
}
}
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/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.aop;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.Fqn;
import org.jboss.cache.CacheException;
import java.io.Serializable;
import java.util.Map;
import java.util.HashMap;
/**
* Handle Serializable object cache management.
*
* @author Ben Wang
* Date: Aug 4, 2005
* @version $Id: SerializableObjectHandler.java,v 1.1 2006/10/31 08:01:13 bwang Exp $
*/
public class SerializableObjectHandler {
protected PojoCache cache_;
protected InternalDelegate internal_;
protected final static Log log=LogFactory.getLog(SerializableObjectHandler.class);
public SerializableObjectHandler(PojoCache cache, InternalDelegate internal)
{
cache_ = cache;
internal_ = internal;
}
Object serializableObjectGet(Fqn fqn)
throws CacheException
{
Object obj = internal_.get(fqn, InternalDelegate.SERIALIZED);
/*
if(cache_.isMarshallNonSerializable() && obj instanceof MarshalledObject )
{
try {
obj = ((MarshalledObject)obj).get();
if (log.isDebugEnabled()) {
log.debug("getObject(): since obj is non-serilaizable, we need to unmarshall it first: "
+ obj.getClass());
}
internal_.localPut(fqn, InternalDelegate.SERIALIZED, obj);
} catch (Exception e) {
e.printStackTrace();
throw new CacheException("Exception occurred during unmarshalling of non-serializable pojo: " +
obj + " using JBoss Serialization", e);
}
}
*/
return obj;
}
protected boolean serializableObjectPut(Fqn fqn, Object obj)
throws CacheException
{
// Note that JBoss Serialization can serialize any type now.
if (obj instanceof Serializable || cache_.isMarshallNonSerializable()) {
if (log.isDebugEnabled()) {
log.debug("putObject(): obj (" + obj.getClass() + ") is non-advisable but is Serializable. ");
}
putIntoCache(fqn, obj);
return true;
}
// If the flag is set, we will marshall it using JBossSerialization
/* if(cache_.isMarshallNonSerializable())
{
if (log.isDebugEnabled()) {
log.debug("serialiableObjectPut(): obj (" + obj.getClass() + ") is non-Serializable." +
" Will marshall it first");
}
putIntoCache(fqn, obj);
return true;
}
*/
return false;
}
protected void putIntoCache(Fqn fqn, Object obj)
throws CacheException{
Map map = new HashMap();
internal_.putAopClazz(fqn, obj.getClass(), map);
// Special optimization here.
AOPInstance aopInstance = new AOPInstance();
aopInstance.set(obj);
map.put(AOPInstance.KEY, aopInstance);
/*
if(cache_.isMarshallNonSerializable())
{
try {
obj = new MarshalledObject(obj);
} catch (IOException e) {
e.printStackTrace();
throw new CacheException("Exception occurred during marshalling of non-serializable pojo: " +
obj + " using JBoss Serialization", e);
}
} */
// Note that we will only have one key in this fqn.
map.put(InternalDelegate.SERIALIZED, obj);
internal_.put(fqn, map);
}
protected boolean serializableObjectRemove(Fqn fqn)
throws CacheException
{
// No need to do anything here since we will do clean up afterwards.
return true;
}
}
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/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.aop;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.Fqn;
import org.jboss.cache.CacheException;
import org.jboss.cache.aop.collection.CollectionInterceptorUtil;
import org.jboss.cache.aop.collection.AbstractCollectionInterceptor;
import org.jboss.aop.proxy.ClassProxy;
import org.jboss.aop.advice.Interceptor;
import org.jboss.aop.InstanceAdvisor;
import java.lang.reflect.Field;
import java.util.*;
/**
* Handling the Collection class management.
*
* @author Ben Wang
* Date: Aug 4, 2005
* @version $Id: CollectionClassHandler.java,v 1.1 2006/10/31 08:01:13 bwang Exp $
*/
public class CollectionClassHandler {
protected final static Log log=LogFactory.getLog(CollectionClassHandler.class);
protected PojoCache cache_;
protected InternalDelegate internal_;
protected ObjectGraphHandler graphHandler_;
public CollectionClassHandler(PojoCache cache, InternalDelegate internal, ObjectGraphHandler graphHandler)
{
cache_ = cache;
internal_ = internal;
graphHandler_ = graphHandler;
}
Object collectionObjectGet(Fqn fqn, Class clazz)
throws CacheException
{
Object obj = null;
try {
if(Map.class.isAssignableFrom(clazz)) {
Object map = clazz.newInstance();
obj=CollectionInterceptorUtil.createMapProxy(cache_, fqn, clazz, (Map)map);
} else if(List.class.isAssignableFrom(clazz)) {
Object list = clazz.newInstance();
obj=CollectionInterceptorUtil.createListProxy(cache_, fqn, clazz, (List)list);
} else if(Set.class.isAssignableFrom(clazz)) {
Object set = clazz.newInstance();
obj=CollectionInterceptorUtil.createSetProxy(cache_, fqn, clazz, (Set)set);
}
} catch (Exception e) {
throw new CacheException("failure creating proxy", e);
}
return obj;
}
boolean collectionObjectPut(Fqn fqn, Object obj) throws CacheException {
boolean isCollection = false;
CachedType type = null;
AbstractCollectionInterceptor interceptor = null;
if(obj instanceof ClassProxy)
{
Class originalClaz = obj.getClass().getSuperclass();
interceptor = CollectionInterceptorUtil.getInterceptor((ClassProxy)obj);
type = cache_.getCachedType(originalClaz);
} else
{
type = cache_.getCachedType(obj.getClass());
}
if(obj instanceof ClassProxy)
{
// A proxy here. We may have multiple references.
if(interceptor == null)
{
if(log.isDebugEnabled())
{
log.debug("collectionObjectPut(): null interceptor. Could be removed previously. "+fqn);
}
} else
{
if( interceptor.isAttached() ) // If it is not attached, it is not active.
{
// Let's check for object graph, e.g., multiple and circular references first
if (graphHandler_.objectGraphPut(fqn, interceptor, type, obj)) { // found cross references
return true;
}
} else
{
// Re-attach the interceptor to this fqn.
boolean copyToCache = true;
interceptor.attach(fqn, copyToCache);
internal_.putAopClazz(fqn, type.getType());
internal_.setPojo(fqn, obj);
return true; // we are done
}
}
}
if (obj instanceof Map) {
if (log.isDebugEnabled()) {
log.debug("collectionPutObject(): aspectized obj is a Map type of size: " + ((Map) obj).size());
}
internal_.putAopClazz(fqn, type.getType());
// 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(cache_, fqn, clazz, (Map)obj);
} catch (Exception e) {
throw new CacheException("failure creating proxy", e);
}
}
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;
internal_.putAopClazz(fqn, type.getType());
// Let's replace it with a proxy if necessary
if( !(obj instanceof ClassProxy)) {
Class clazz = obj.getClass();
try {
obj=CollectionInterceptorUtil.createListProxy(cache_, fqn, clazz, (List)obj);
} catch (Exception e) {
throw new CacheException("failure creating proxy", e);
}
}
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;
internal_.putAopClazz(fqn, type.getType());
// Let's replace it with a proxy if necessary
if( !(obj instanceof ClassProxy)) {
Class clazz = obj.getClass();
try {
obj=CollectionInterceptorUtil.createSetProxy(cache_, fqn, clazz, (Set)obj);
} catch (Exception e) {
throw new CacheException("failure creating proxy", e);
}
}
isCollection = true;
// populate via the proxied collection
for (Iterator i = set.iterator(); i.hasNext();) {
((Set)obj).add(i.next());
}
}
if(isCollection)
{
// Always initialize the ref count so that we can mark this as an AopNode.
AOPInstance aopInstance = internal_.initializeAopInstance(fqn);
cache_.put(fqn, AOPInstance.KEY, aopInstance);
// Attach aopInstance to that interceptor
BaseInterceptor baseInterceptor = (BaseInterceptor)CollectionInterceptorUtil.getInterceptor((ClassProxy)obj);
baseInterceptor.setAopInstance(aopInstance);
internal_.setPojo(aopInstance, obj);
}
return isCollection;
}
boolean collectionObjectRemove(Fqn fqn, boolean removeCacheInterceptor,
boolean evict) throws CacheException
{
Class clazz = internal_.peekAopClazz(fqn);
if (!Map.class.isAssignableFrom(clazz) && !Collection.class.isAssignableFrom(clazz))
{
return false;
}
Object obj = cache_.getObject(fqn);
if( !(obj instanceof ClassProxy))
{
throw new RuntimeException("CollectionClassHandler.collectionRemoveObject(): object is not a proxy :" +obj);
}
Interceptor interceptor = CollectionInterceptorUtil.getInterceptor((ClassProxy)obj);
boolean removeFromCache = true;
((AbstractCollectionInterceptor)interceptor).detach(removeFromCache); // detach the interceptor. This will trigger a copy and remove.
return true;
}
void collectionReplaceWithProxy(Object obj, Object value, Field field, Fqn tmpFqn)
throws CacheException
{
// If value (field member) is of Collection type, e.g., composite class
// that contains Collection member, we will swap out the old reference
// with the proxy one.
// This can probably be optimized with check for instanceof proxy
if( value instanceof Map || value instanceof List || value instanceof Set ) {
Object newValue = cache_.getObject(tmpFqn);
try {
field.set(obj, newValue);
cache_.addUndoCollectionProxy(field, obj, value);
} catch (IllegalAccessException e) {
log.error("collectionReplaceWithProxy(): Can't swap out the Collection class of field " +field.getName() +
"Exception " +e);
throw new CacheException("CollectionClassHandler.collectionReplaceWithProxy(): Can't swap out the Collection class of field \" +field.getName(),"
+e);
}
}
}
}
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/TreeCacheAopView.java
Index: TreeCacheAopView.java
===================================================================
/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
* Created on March 25 2003
*/
package org.jboss.cache.aop;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.CacheListener;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.Fqn;
import org.jboss.cache.TreeCacheView;
import org.jboss.cache.factories.XmlConfigurationParser;
import org.jgroups.View;
import javax.swing.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Vector;
//import java.util.List;
/**
* Graphical view of a ReplicatedTree (using the MVC paradigm). An instance of this class needs to be given a
* reference to the underlying model (ReplicatedTree) and needs to registers as a ReplicatedTreeListener. Changes
* to the tree structure are propagated from the model to the view (via ReplicatedTreeListener), changes from the
* GUI (e.g. by a user) are executed on the tree model (which will broadcast the changes to all replicas).<p>
* The view itself caches only the nodes, but doesn't cache any of the data (HashMap) associated with it. When
* data needs to be displayed, the underlying tree will be accessed directly.
*
* @author Ben Wang
* @version $Revision: 1.1 $
* @deprecated Not used now. Use TreeCacheView2 directly.
*/
public class TreeCacheAopView
{
TreeCacheAopGui gui_ = null;
PojoCache cache_ = null;
static Log log = LogFactory.getLog(TreeCacheAopView.class.getName());
public TreeCacheAopView(PojoCache cache) throws Exception
{
this.cache_ = cache;
}
public void start() throws Exception
{
if (gui_ == null)
{
log.info("start(): creating the GUI");
gui_ = new TreeCacheAopGui(cache_);
}
}
public void stop()
{
if (gui_ != null)
{
log.info("stop(): disposing the GUI");
gui_.dispose();
gui_ = null;
}
}
void populateTree(String dir) throws Exception
{
File file = new File(dir);
if (!file.exists()) return;
put(dir, null);
if (file.isDirectory())
{
String[] children = file.list();
if (children != null && children.length > 0)
{
for (int i = 0; i < children.length; i++)
populateTree(dir + "/" + children[i]);
}
}
}
void put(String fqn, Map m)
{
try
{
cache_.put(fqn, m);
}
catch (Throwable t)
{
log.error("TreeCacheAopView.put(): " + t);
}
}
public static void main(String args[])
{
PojoCache tree = null;
TreeCacheAopView demo;
String start_directory = null;
String resource = "META-INF/replSync-service.xml";
for (int i = 0; i < args.length; i++)
{
if (args[i].equals("-config"))
{
resource = args[++i];
continue;
}
help();
return;
}
try
{
tree = new PojoCache();
tree.setConfiguration(new XmlConfigurationParser().parseFile(resource));
tree.getNotifier().addCacheListener(new TreeCacheView.MyListener());
tree.create();
tree.start();
Runtime.getRuntime().addShutdownHook(new ShutdownThread(tree));
demo = new TreeCacheAopView(tree);
demo.start();
if (start_directory != null && start_directory.length() > 0)
{
demo.populateTree(start_directory);
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
static class ShutdownThread extends Thread
{
PojoCache tree = null;
ShutdownThread(PojoCache tree)
{
this.tree = tree;
}
public void run()
{
tree.stop();
}
}
static void help()
{
System.out.println("TreeCacheAopView [-help] [-config <configuration file (XML)]" +
"[-mbean_name <name of TreeCache MBean>] " +
"[-start_directory <dirname>] [-props <props>] " +
"[-use_queue <true/false>] [-queue_interval <ms>] " +
"[-queue_max_elements <num>]");
}
}
class TreeCacheAopGui extends JFrame implements WindowListener, CacheListener,
TreeSelectionListener, TableModelListener
{
private static final long serialVersionUID = -7044654602510185865L;
PojoCache cache_;
DefaultTreeModel tree_model = null;
Log log = LogFactory.getLog(getClass());
JTree jtree = null;
DefaultTableModel table_model = new DefaultTableModel();
JTable table = new JTable(table_model);
MyNode root = new MyNode(SEP, Fqn.fromString(SEP));
String props = null;
String selected_node = null;
JPanel tablePanel = null;
JMenu operationsMenu = null;
JPopupMenu operationsPopup = null;
JMenuBar menubar = null;
boolean use_system_exit = false;
static String SEP = Fqn.SEPARATOR;
private static final int KEY_COL_WIDTH = 20;
private static final int VAL_COL_WIDTH = 300;
final String STRING = String.class.getName();
final String MAP = Map.class.getName();
final String OBJECT = Object.class.getName();
String currentNodeSelected = null;
public TreeCacheAopGui(PojoCache cache) throws Exception
{
this.cache_ = cache;
cache_.getNotifier().addCacheListener(this);
addNotify();
setTitle("TreeCacheAopGui: mbr=" + getLocalAddress());
tree_model = new DefaultTreeModel(root);
jtree = new JTree(tree_model);
jtree.setDoubleBuffered(true);
jtree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
JScrollPane scroll_pane = new JScrollPane(jtree);
panel.add(scroll_pane, BorderLayout.CENTER);
populateTree();
getContentPane().add(panel, BorderLayout.CENTER);
addWindowListener(this);
table_model.setColumnIdentifiers(new String[]{"Name", "Value"});
table_model.addTableModelListener(this);
setTableColumnWidths();
tablePanel = new JPanel();
tablePanel.setLayout(new BorderLayout());
tablePanel.add(table.getTableHeader(), BorderLayout.NORTH);
tablePanel.add(table, BorderLayout.CENTER);
getContentPane().add(tablePanel, BorderLayout.SOUTH);
jtree.addTreeSelectionListener(this);//REVISIT
MouseListener ml = new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
int selRow = jtree.getRowForLocation(e.getX(), e.getY());
TreePath selPath = jtree.getPathForLocation(e.getX(), e.getY());
if (selRow != -1)
{
selected_node = makeFQN(selPath.getPath());
jtree.setSelectionPath(selPath);
if (e.getModifiers() == java.awt.event.InputEvent.BUTTON3_MASK)
{
operationsPopup.show(e.getComponent(),
e.getX(), e.getY());
}
}
}
};
jtree.addMouseListener(ml);
createMenus();
setLocation(50, 50);
setSize(getInsets().left + getInsets().right + 485,
getInsets().top + getInsets().bottom + 367);
init();
setVisible(true);
}
void setSystemExit(boolean flag)
{
use_system_exit = flag;
}
public void windowClosed(WindowEvent event)
{
}
public void windowDeiconified(WindowEvent event)
{
}
public void windowIconified(WindowEvent event)
{
}
public void windowActivated(WindowEvent event)
{
}
public void windowDeactivated(WindowEvent event)
{
}
public void windowOpened(WindowEvent event)
{
}
public void windowClosing(WindowEvent event)
{
dispose();
}
public void tableChanged(TableModelEvent evt)
{
int row, col;
String key, val;
if (evt.getType() == TableModelEvent.UPDATE)
{
row = evt.getFirstRow();
col = evt.getColumn();
if (col == 0)
{ // set()
key = (String) table_model.getValueAt(row, col);
val = (String) table_model.getValueAt(row, col + 1);
if (key != null && val != null)
{
// tree.put(selected_node, key, val);
try
{
cache_.put(selected_node, key, val);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
else
{ // add()
key = (String) table_model.getValueAt(row, col - 1);
val = (String) table.getValueAt(row, col);
if (key != null && val != null)
{
put(selected_node, key, val);
}
}
}
}
public void valueChanged(TreeSelectionEvent evt)
{
TreePath path = evt.getPath();
String fqn = SEP;
String component_name;
Map data = null;
for (int i = 0; i < path.getPathCount(); i++)
{
component_name = ((MyNode) path.getPathComponent(i)).name;
if (component_name.equals(SEP))
continue;
if (fqn.equals(SEP))
fqn += component_name;
else
fqn = fqn + SEP + component_name;
}
data = getData(fqn);
System.out.println("valueChanged(): fqn: " + fqn + " data: " + data);
if (data != null)
{
getContentPane().add(tablePanel, BorderLayout.SOUTH);
populateTable(data);
validate();
}
else
{
clearTable();
getContentPane().remove(tablePanel);
validate();
}
}
/* ------------------ ReplicatedTree.ReplicatedTreeListener interface ------------ */
public void nodeCreated(Fqn fqn, boolean pre, boolean isLocal)
{
if (!pre)
{
MyNode n, p;
n = root.add(fqn);
if (n != null)
{
p = (MyNode) n.getParent();
tree_model.reload(p);
jtree.scrollPathToVisible(new TreePath(n.getPath()));
}
}
}
public void nodeRemoved(Fqn fqn, boolean pre, boolean isLocal, Map data)
{
if (!pre)
{
MyNode n;
TreeNode par;
n = root.findNode(fqn.toString());
if (n != null)
{
n.removeAllChildren();
par = n.getParent();
n.removeFromParent();
tree_model.reload(par);
}
}
}
public void nodeLoaded(Fqn fqn, boolean pre, Map data)
{
nodeCreated(fqn, pre, true);
}
public void nodeEvicted(Fqn fqn, boolean pre, boolean isLocal)
{
nodeRemoved(fqn, pre, isLocal, null);
}
public void nodeMoved(Fqn from, Fqn to, boolean pre)
{
}
public void nodeActivated(Fqn fqn, boolean pre)
{
}
public void nodePassivated(Fqn fqn, boolean pre)
{
}
public void nodeModified(final Fqn fqn, boolean pre, boolean isLocal, Map d)
{
if (!pre)
{
// needs to be in a separate thread because Swing thread is different from callback thread,
// resulting in a lock until lock timeout kicks in
// new Thread() {
// public void run() {
Map data;
if (currentNodeSelected != null && !currentNodeSelected.equals(fqn.toString()))
return; // DataNode modified is not visible. Continue...
data = getData(fqn.toString());
populateTable(data); // REVISIT
// }
//}.start();
/*
poulateTable is the current table being shown is the info of the node. that is modified.
*/
}
}
public void nodeVisited(Fqn fqn, boolean pre)
{
}
public void cacheStarted(CacheSPI cache)
{
}
public void cacheStopped(CacheSPI cache)
{
}
public void viewChange(final View new_view)
{
new Thread()
{
public void run()
{
Vector mbrship;
if (new_view != null && (mbrship = new_view.getMembers()) != null)
{
_put(SEP, "members", mbrship);
_put(SEP, "coordinator", mbrship.firstElement());
}
}
}.start();
}
/* ---------------- End of ReplicatedTree.ReplicatedTreeListener interface -------- */
/*----------------- Runnable implementation to make View change calles in AWT Thread ---*/
public void run()
{
}
/* ----------------------------- Private Methods ---------------------------------- */
/**
* Fetches all data from underlying tree model and display it graphically
*/
void init()
{
Vector mbrship = null;
addGuiNode(SEP);
mbrship = getMembers() != null ? (Vector) getMembers().clone() : null;
if (mbrship != null && mbrship.size() > 0)
{
_put(SEP, "members", mbrship);
_put(SEP, "coordinator", mbrship.firstElement());
}
}
/**
* Fetches all data from underlying tree model and display it graphically
*/
private void populateTree()
{
addGuiNode(SEP);
}
/**
* Recursively adds GUI nodes starting from fqn
*/
void addGuiNode(String fqn)
{
Set children;
String child_name;
if (fqn == null) return;
// 1 . Add myself
root.add(Fqn.fromString(fqn));
// 2. Then add my children
children = getChildrenNames(fqn);
if (children != null)
{
for (Iterator it = children.iterator(); it.hasNext();)
{
child_name = (String) it.next();
addGuiNode(fqn + SEP + child_name);
}
}
}
String makeFQN(Object[] path)
{
StringBuffer sb = new StringBuffer("");
String tmp_name;
if (path == null) return null;
for (int i = 0; i < path.length; i++)
{
tmp_name = ((MyNode) path[i]).name;
if (tmp_name.equals(SEP))
{
}
else
sb.append(SEP + tmp_name);
}
tmp_name = sb.toString();
if (tmp_name.length() == 0)
return SEP;
else
return tmp_name;
}
void clearTable()
{
int num_rows = table.getRowCount();
if (num_rows > 0)
{
for (int i = 0; i < num_rows; i++)
table_model.removeRow(0);
table_model.fireTableRowsDeleted(0, num_rows - 1);
repaint();
}
}
void populateTable(Map data)
{
String key, strval = "<null>";
Object val;
int num_rows = 0;
Map.Entry entry;
if (data == null) return;
num_rows = data.size();
clearTable();
if (num_rows > 0)
{
for (Iterator it = data.entrySet().iterator(); it.hasNext();)
{
entry = (Map.Entry) it.next();
key = (String) entry.getKey();
val = entry.getValue();
if (val != null) strval = val.toString();
table_model.addRow(new Object[]{key, strval});
}
table_model.fireTableRowsInserted(0, num_rows - 1);
validate();
}
}
private void setTableColumnWidths()
{
table.sizeColumnsToFit(JTable.AUTO_RESIZE_NEXT_COLUMN);
TableColumn column = null;
column = table.getColumnModel().getColumn(0);
column.setMinWidth(KEY_COL_WIDTH);
column.setPreferredWidth(KEY_COL_WIDTH);
column = table.getColumnModel().getColumn(1);
column.setPreferredWidth(VAL_COL_WIDTH);
}
private void createMenus()
{
menubar = new JMenuBar();
operationsMenu = new JMenu("Operations");
AddNodeAction addNode = new AddNodeAction();
addNode.putValue(AbstractAction.NAME, "Add to this node");
RemoveNodeAction removeNode = new RemoveNodeAction();
removeNode.putValue(AbstractAction.NAME, "Remove this node");
AddModifyDataForNodeAction addModAction = new AddModifyDataForNodeAction();
addModAction.putValue(AbstractAction.NAME, "Add/Modify data");
PrintLockInfoAction print_locks = new PrintLockInfoAction();
print_locks.putValue(AbstractAction.NAME, "Print lock information (stdout)");
ReleaseAllLocksAction release_locks = new ReleaseAllLocksAction();
release_locks.putValue(AbstractAction.NAME, "Release all locks");
ExitAction exitAction = new ExitAction();
exitAction.putValue(AbstractAction.NAME, "Exit");
operationsMenu.add(addNode);
operationsMenu.add(removeNode);
operationsMenu.add(addModAction);
operationsMenu.add(print_locks);
operationsMenu.add(release_locks);
operationsMenu.add(exitAction);
menubar.add(operationsMenu);
setJMenuBar(menubar);
operationsPopup = new JPopupMenu();
operationsPopup.add(addNode);
operationsPopup.add(removeNode);
operationsPopup.add(addModAction);
}
Object getLocalAddress()
{
try
{
return cache_.getLocalAddress();
}
catch (Throwable t)
{
log.error("TreeCacheAopGui.getLocalAddress(): " + t);
return null;
}
}
Map getData(String fqn)
{
Map data;
Set keys;
String key;
Object value;
if (fqn == null) return null;
// Let's track the node displayed.
currentNodeSelected = fqn;
// System.out.println("findNode(): fqnStr: " +fqn);
MyNode node = root.findNode(fqn);
if (node == null) return null;
// System.out.println("findNode(): fqnStr: " + fqn + " node fqn: " + node.getFqn().toString());
keys = getKeys(node.getFqn());
if (keys == null) return null;
data = new HashMap();
for (Iterator it = keys.iterator(); it.hasNext();)
{
key = it.next().toString();
value = get(node.getFqn(), key);
if (value != null)
data.put(key, value);
}
return data;
}
void put(String fqn, Map m)
{
try
{
cache_.put(fqn, m);
}
catch (Throwable t)
{
log.error("TreeCacheAopGui.put(): " + t);
}
}
private void put(String fqn, String key, Object value)
{
try
{
cache_.put(fqn, key, value);
}
catch (Throwable t)
{
log.error("TreeCacheAopGui.put(): " + t);
}
}
void _put(String fqn, String key, Object value)
{
try
{
cache_._put(null, fqn, key, value, false);
}
catch (Throwable t)
{
log.error("TreeCacheAopGui._put(): " + t);
}
}
Set getKeys(Fqn fqn)
{
try
{
return cache_.getKeys(fqn);
}
catch (Throwable t)
{
t.printStackTrace();
log.error("TreeCacheAopGui.getKeys(): " + t);
return null;
}
}
Object get(Fqn fqn, String key)
{
try
{
return cache_.get(fqn, key);
}
catch (Throwable t)
{
log.error("TreeCacheAopGui.get(): " + t);
return null;
}
}
Set getChildrenNames(String fqn)
{
try
{
return cache_.getChildrenNames(fqn);
}
catch (Throwable t)
{
log.error("TreeCacheAopGui.getChildrenNames(): " + t);
return null;
}
}
Vector getMembers()
{
try
{
return cache_.getMembers();
}
catch (Throwable t)
{
log.error("TreeCacheAopGui.getMembers(): " + t);
return null;
}
}
/* -------------------------- End of Private Methods ------------------------------ */
/*----------------------- Actions ---------------------------*/
class ExitAction extends AbstractAction
{
private static final long serialVersionUID = 3309349735197545040L;
public void actionPerformed(ActionEvent e)
{
dispose();
System.exit(0);
}
}
class AddNodeAction extends AbstractAction
{
private static final long serialVersionUID = -1846726449742865362L;
public void actionPerformed(ActionEvent e)
{
JTextField fqnTextField = new JTextField();
if (selected_node != null)
fqnTextField.setText(selected_node);
Object[] information = {"Enter fully qualified name",
fqnTextField};
final String btnString1 = "OK";
final String btnString2 = "Cancel";
Object[] options = {btnString1, btnString2};
int userChoice = JOptionPane.showOptionDialog(null,
information,
"Add DataNode",
JOptionPane.YES_NO_OPTION,
JOptionPane.PLAIN_MESSAGE,
null,
options,
options[0]);
if (userChoice == 0)
{
String userInput = fqnTextField.getText();
put(userInput, null);
}
}
}
class PrintLockInfoAction extends AbstractAction
{
private static final long serialVersionUID = -1547808705937201967L;
public void actionPerformed(ActionEvent e)
{
System.out.println("\n*** lock information ****\n" + cache_.printLockInfo());
}
}
class ReleaseAllLocksAction extends AbstractAction
{
private static final long serialVersionUID = -856964838566617006L;
public void actionPerformed(ActionEvent e)
{
cache_.releaseAllLocks("/");
}
}
class RemoveNodeAction extends AbstractAction
{
private static final long serialVersionUID = 116863545772054467L;
public void actionPerformed(ActionEvent e)
{
try
{
cache_.remove(selected_node);
}
catch (Throwable t)
{
log.error("RemoveNodeAction.actionPerformed(): " + t);
}
}
}
class AddModifyDataForNodeAction extends AbstractAction
{
private static final long serialVersionUID = 8035968024123769723L;
public void actionPerformed(ActionEvent e)
{
Map data = getData(selected_node);
if (data != null)
{
}
else
{
clearTable();
data = new HashMap();
data.put("Add Key", "Add Value");
}
populateTable(data);
getContentPane().add(tablePanel, BorderLayout.SOUTH);
validate();
}
}
class MyNode extends DefaultMutableTreeNode
{
private static final long serialVersionUID = -3391522498362829190L;
String name = "<unnamed>"; // node name
Fqn fqn; // corresponding fqn
MyNode(String name, Fqn fqn)
{
this.fqn = fqn;
this.name = name;
}
public Fqn getFqn()
{
return fqn;
}
/**
* Adds a new node to the view. Intermediary nodes will be created if they don't yet exist.
* Returns the first node that was created or null if node already existed
*/
public MyNode add(Fqn fqn)
{
MyNode curr, n, ret = null;
StringTokenizer tok;
String child_name;
if (fqn == null) return null;
curr = this;
String fqnStr = fqn.toString();
tok = new StringTokenizer(fqnStr, TreeCacheAopGui.SEP);
int i = 0;
while (tok.hasMoreTokens())
{
child_name = tok.nextToken();
n = curr.findChild(child_name);
if (n == null)
{
n = new MyNode(child_name, fqn.getFqnChild(i + 1));
if (ret == null) ret = n;
curr.add(n);
}
curr = n;
i++;
}
return ret;
}
/**
* Removes a node from the view. Child nodes will be removed as well
*/
public void remove(String fqn)
{
removeFromParent();
}
MyNode findNode(String fqn)
{
MyNode curr, n;
StringTokenizer tok;
String child_name;
if (fqn == null) return null;
curr = this;
tok = new StringTokenizer(fqn, TreeCacheAopGui.SEP);
while (tok.hasMoreTokens())
{
child_name = tok.nextToken();
n = curr.findChild(child_name);
if (n == null)
return null;
curr = n;
}
return curr;
}
MyNode findChild(String relative_name)
{
MyNode child;
if (relative_name == null || getChildCount() == 0)
return null;
for (int i = 0; i < getChildCount(); i++)
{
child = (MyNode) getChildAt(i);
if (child.name == null)
{
continue;
}
if (child.name.equals(relative_name))
return child;
}
return null;
}
String print(int indent)
{
StringBuffer sb = new StringBuffer();
for (int i = 0; i < indent; i++)
sb.append(" ");
if (!isRoot())
{
if (name == null)
sb.append("/<unnamed>");
else
{
sb.append(TreeCacheAopGui.SEP + name);
}
}
sb.append("\n");
if (getChildCount() > 0)
{
if (isRoot())
indent = 0;
else
indent += 4;
for (int i = 0; i < getChildCount(); i++)
sb.append(((MyNode) getChildAt(i)).print(indent));
}
return sb.toString();
}
public String toString()
{
return name;
}
}
}
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/CacheInterceptor.java
Index: CacheInterceptor.java
===================================================================
/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.aop;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.aop.joinpoint.FieldReadInvocation;
import org.jboss.aop.joinpoint.FieldWriteInvocation;
import org.jboss.aop.joinpoint.Invocation;
import org.jboss.aop.joinpoint.MethodInvocation;
import org.jboss.aop.joinpoint.FieldInvocation;
import org.jboss.aop.Advised;
import org.jboss.aop.InstanceAdvisor;
import org.jboss.aop.Advisor;
import org.jboss.cache.Fqn;
import org.jboss.cache.aop.util.AopUtil;
import org.jboss.cache.aop.references.FieldPersistentReference;
import java.io.Externalizable;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Iterator;
/**
* Created: Sat Apr 26 10:35:01 2003
*
* @author Harald Gliebe
* @author Ben Wang
*/
public class CacheInterceptor implements BaseInterceptor
{
protected static final Log log_ = LogFactory.getLog(CacheInterceptor.class);
protected PojoCache cache;
protected CachedType type;
protected Fqn fqn;
boolean checkSerialization;
protected String name;
protected AOPInstance aopInstance;
static Method writeExternal, readExternal;
static
{
try {
writeExternal =
Externalizable.class.getMethod("writeExternal",
new Class[]{ObjectOutput.class});
readExternal =
Externalizable.class.getMethod("readExternal",
new Class[]{ObjectInput.class});
} catch (Exception e) {
e.printStackTrace();
}
}
public CacheInterceptor(PojoCache cache, Fqn fqn, CachedType type)
{
this.cache = cache;
this.fqn = fqn;
this.type = type;
checkSerialization =
!WriteReplaceable.class.isAssignableFrom(type.getType());
}
public AOPInstance getAopInstance() {
return aopInstance;
}
public void setAopInstance(AOPInstance aopInstance) {
this.aopInstance = aopInstance;
}
public String getName()
{
if(name == null)
{
this.name = "CacheInterceptor on [" + fqn + "]";
}
return name;
}
public Object invoke(Invocation invocation) throws Throwable
{
// Check if CLASS_INTERNAL exists. If not, that means we are done. We need to remove ourself.
// Note that if speed is important, we will need to perform the detach step pro-actively,
// that is, use a listener to listen for the removeObject event.
if(isPojoDetached(invocation))
{
return invocation.invokeNext(); // invoke the in-memory pojo directly
}
if (invocation instanceof FieldWriteInvocation) {
FieldInvocation fieldInvocation =
(FieldInvocation) invocation;
Advisor advisor = fieldInvocation.getAdvisor();
Field field = fieldInvocation.getField();
// Only if this field is replicatable. static, transient and final are not.
CachedType fieldType = cache.getCachedType(field.getType());
CachedType parentType = cache.getCachedType(field.getDeclaringClass());
if(!isNonReplicatable(field, advisor, parentType)) {
Object value = ((FieldWriteInvocation)fieldInvocation).getValue();
if (fieldType.isImmediate() || hasSerializableAnnotation(field, advisor, parentType)) {
cache.put(fqn, field.getName(), value);
} else {
//cache.putObject(((Fqn)fqn.clone()).add(field.getName()), value);
cache.putObject(new Fqn(fqn, field.getName()), value);
}
}
} else if (invocation instanceof FieldReadInvocation) {
FieldInvocation fieldInvocation =
(FieldInvocation) invocation;
Field field = fieldInvocation.getField();
Advisor advisor = fieldInvocation.getAdvisor();
// Only if this field is replicatable
CachedType fieldType = cache.getCachedType(field.getType());
CachedType parentType = cache.getCachedType(field.getDeclaringClass());
if( !isNonReplicatable(field, advisor, parentType)) {
Object result;
if (fieldType.isImmediate()|| hasSerializableAnnotation(field, advisor, parentType)) {
result = cache.get(fqn, field.getName());
} else {
//result = cache.getObject(((Fqn)fqn.clone()).add(field.getName()));
result = cache.getObject(new Fqn(fqn, field.getName()));
}
// if result is null, we need to make sure the in-memory reference is null
// as well. If it is not, then we know this one is null because it has
// been evicted. Will need to reconstruct it
if(result != null)
return result;
else {
// TODO There is a chance of recursive loop here if caller tries to print out obj that will trigger the fieldRead interception.
Object value = invocation.getTargetObject();
if(value == null || field.get(value) == null) // if both are null, we know this is null as well.
return null;
else {
if(log_.isTraceEnabled()) {
log_.trace("invoke(): DataNode on fqn: " +fqn + " has obviously been evicted. Will need to reconstruct it");
}
cache.putObject(fqn, value);
}
}
}
} else if (checkSerialization) { // Have no use now.
MethodInvocation methodInvocation = (MethodInvocation) invocation;
Method method = methodInvocation.getMethod();
if (method != null
&& method.getName().equals("writeReplace")
&& method.getReturnType().equals(Object.class)
&& method.getParameterTypes().length == 0) {
beforeSerialization(invocation.getTargetObject());
} else if (method == writeExternal) {
Object target = methodInvocation.getTargetObject();
beforeSerialization(target);
}
}
return invocation.invokeNext();
}
/**
* See if this field is non-replicatable such as @Transient or transient modifier.
*/
private boolean isNonReplicatable(Field field, Advisor advisor, CachedType type)
{
if(CachedType.hasAnnotation(field.getDeclaringClass(), advisor, type))
{
if(CachedType.hasTransientAnnotation(field, advisor)) return true;
}
if(CachedType.isPrimitiveNonReplicatable(field)) return true;
return false;
}
private boolean hasSerializableAnnotation(Field field, Advisor advisor, CachedType type)
{
if(CachedType.hasAnnotation(field.getDeclaringClass(), advisor, type))
{
if(CachedType.hasSerializableAnnotation(field, advisor)) return true;
}
return false;
}
/**
* Check if the pojo is detached already. If it is and we still have the cache interceptor on
* this pojo, we will go ahead and remove it since it should not be there in the first place.
* @param invocation
* @return
* @throws Exception
*/
protected boolean isPojoDetached(Invocation invocation) throws Exception
{
boolean detached = false;
if( !cache.exists(fqn, InternalDelegate.CLASS_INTERNAL) )
{
detached = true;
Object obj = invocation.getTargetObject();
if(! (obj instanceof Advised) )
throw new RuntimeException("Interception on non-advised pojo " +obj.toString());
InstanceAdvisor advisor = ((Advised)obj)._getInstanceAdvisor();
CacheInterceptor interceptor = (CacheInterceptor) AopUtil.findCacheInterceptor(advisor);
if (interceptor != null)
{
if (log_.isDebugEnabled()) {
log_.debug("isPojoDetached(): removed cache interceptor fqn: " + fqn + " interceptor: "+interceptor);
}
advisor.removeInterceptor(interceptor.getName());
}
}
return detached;
}
protected void checkCacheConsistency() throws Exception
{
if (this != cache.peek(fqn, AOPInstance.KEY)) {
throw new RuntimeException("Cache inconsistency: Outdated AOPInstance");
}
}
public void beforeSerialization(Object target) throws Exception
{
// fill objects
for (Iterator i = type.getFields().iterator(); i.hasNext();) {
Field field = (Field)(((FieldPersistentReference) i.next())).get();
CachedType fieldType = cache.getCachedType(field.getType());
Object value = null;
if (fieldType.isImmediate()) {
value = cache.get(fqn, field.getName());
} else {
// value = removeObject(fqn+TreeCache.SEPARATOR+field.getName());
//value = cache.getObject(((Fqn)fqn.clone()).add(field.getName()));
value = cache.getObject(new Fqn(fqn, field.getName()));
}
// System.out.println("Setting field " + field.getName() + "[" + field.getDeclaringClass() + "] of "+ target.getClass() + " to " + value);
field.set(target, value);
}
}
boolean isChildOf(Fqn parentFqn)
{
return fqn.isChildOf(parentFqn);
}
// void setFqn(Fqn fqn)
// {
// this.fqn = fqn;
// }
public Fqn getFqn() {
return fqn;
}
public void setFqn(Fqn fqn)
{
this.fqn = fqn;
}
}
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/PojoTxSynchronizationHandler.java
Index: PojoTxSynchronizationHandler.java
===================================================================
/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.aop;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.aop.InstanceAdvisor;
import javax.transaction.Status;
import javax.transaction.Synchronization;
import javax.transaction.Transaction;
import java.util.List;
import java.lang.reflect.Field;
/**
* Handling the rollback operation for PojoCache level, specifically interceptor add/remove, etc.
*
* @author Ben Wang
* @version $Id: PojoTxSynchronizationHandler.java,v 1.1 2006/10/31 08:01:13 bwang Exp $
*/
public class PojoTxSynchronizationHandler implements Synchronization {
static Log log = LogFactory.getLog(PojoTxSynchronizationHandler.class.getName());
private Transaction tx_;
private PojoCache cache_;
PojoTxSynchronizationHandler(Transaction tx, PojoCache cache)
{
tx_ = tx;
cache_ = cache;
}
public void beforeCompletion() {
// Not interested
}
public void afterCompletion(int status) {
try {
switch (status) {
case Status.STATUS_COMMITTED:
break;
case Status.STATUS_MARKED_ROLLBACK:
case Status.STATUS_ROLLEDBACK:
log.debug("Running rollback phase");
runRollbackPhase();
log.debug("Finished rollback phase");
break;
default:
throw new IllegalStateException("illegal status: " + status);
}
}
finally {
}
}
private void runRollbackPhase()
{
// Rollback the pojo interceptor add/remove
List list = cache_.getModList();
for(int i=(list.size()-1); i >= 0; i--)
{
ModificationEntry ent = (ModificationEntry)list.get(i);
InstanceAdvisor advisor = ent.getInstanceAdvisor();
BaseInterceptor interceptor = ent.getCacheInterceptor();
switch (ent.getOpType())
{
case ModificationEntry.INTERCEPTOR_ADD:
advisor.removeInterceptor(interceptor.getName());
break;
case ModificationEntry.INTERCEPTOR_REMOVE:
advisor.appendInterceptor(interceptor);
break;
case ModificationEntry.COLLECTION_REPLACE:
Field field = ent.getField();
Object key = ent.getKey();
Object value = ent.getOldValue();
try {
field.set(key, value);
} catch (IllegalAccessException e) {
throw new RuntimeException("PojoTxSynchronizationHandler.runRollbackPhase(): Exception: " +e);
}
break;
default:
throw new IllegalArgumentException("PojoTxSynchronizationHandler.runRollbackPhase: getOptType: "
+ent.getOpType());
}
}
cache_.resetUndoOp();
}
}
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/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.aop;
import org.jboss.cache.Fqn;
import org.jboss.cache.CacheException;
import org.jboss.cache.aop.util.AopUtil;
import org.jboss.cache.aop.collection.AbstractCollectionInterceptor;
import org.jboss.aop.advice.Interceptor;
import org.jboss.aop.InstanceAdvisor;
import org.jboss.aop.Advised;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Handle the object graph management.
*
* @author Ben Wang
* Date: Aug 4, 2005
* @version $Id: ObjectGraphHandler.java,v 1.1 2006/10/31 08:01:13 bwang Exp $
*/
public class ObjectGraphHandler {
protected PojoCache cache_;
protected InternalDelegate internal_;
protected final static Log log=LogFactory.getLog(ObjectGraphHandler.class);
protected TreeCacheAopDelegate delegate_;
public ObjectGraphHandler(PojoCache cache, InternalDelegate internal, TreeCacheAopDelegate delegate)
{
cache_ = cache;
internal_ = internal;
delegate_ = delegate;
}
Object objectGraphGet(Fqn fqn) throws CacheException
{
// Note this is actually the aliasFqn, not the real fqn!
String refFqn = internal_.getRefFqn(fqn);
Object obj;
if (refFqn != null) {
// this is recursive. Need to obtain the object from parent fqn
// No need to add CacheInterceptor as a result. Everything is re-directed.
// In addition, this op will not be recursive.
if (log.isDebugEnabled()) {
log.debug("getObject(): obtain value from reference fqn: " + refFqn);
}
obj = cache_.getObject(refFqn);
if(obj == null)
throw new RuntimeException("ObjectGraphHandler.objectGraphGet(): null object from internal ref node." +
" Original fqn: " +fqn + " Internal ref node: " +refFqn);
return obj; // No need to set the instance under fqn. It is located in refFqn anyway.
}
return null;
}
boolean objectGraphPut(Fqn fqn, Interceptor interceptor, CachedType type, Object obj) throws CacheException
{
Fqn originalFqn = null;
if (interceptor == null) {
return false; // No interceptor no object graph possibility.
}
if(interceptor instanceof AbstractCollectionInterceptor)
{
// Special case for Collection class. If it is detached, we don't care.
if( !((AbstractCollectionInterceptor)interceptor).isAttached())
{
return false;
}
}
// ah, found something. So this will be multiple referenced.
originalFqn = ((BaseInterceptor) interceptor).getFqn();
if(originalFqn == null) return false;
if (log.isDebugEnabled()) {
log.debug("handleObjectGraph(): fqn: " + fqn + " and " + originalFqn + " share the object.");
}
// This will increment the ref count, reset, and add ref fqn in the current fqn node.
setupRefCounting(fqn, originalFqn);
internal_.putAopClazz(fqn, type.getType());
return true;
}
boolean objectGraphRemove(Fqn fqn, boolean removeCacheInterceptor, Object pojo, boolean evict)
throws CacheException
{
boolean isTrue = false;
// Note this is actually the aliasFqn, not the real fqn!
AOPInstance aopInstance = internal_.getAopInstance(fqn);
String refFqn = internal_.getRefFqn(aopInstance, fqn);
// check if this is a refernce
if (refFqn != null) {
if (log.isDebugEnabled()) {
log.debug("objectGraphRemove(): removing object fqn: " + fqn + " but is actually from ref fqn: " + refFqn
+ " Will just de-reference it.");
}
removeFromReference(fqn, refFqn, removeCacheInterceptor, evict);
internal_.cleanUp(fqn, evict);
isTrue = true;
} else {
if(internal_.isReferenced(aopInstance, fqn))
{
// This node is currently referenced by others. We will relocate it to the next in line,
// and update the indirectFqnMap
// First decrement counter.
decrementRefCount(fqn, null);
// Determine where to move first.
Fqn newFqn = internal_.getNextFqnInLine(fqn);
// Is newFqn is child of fqn?
if(newFqn.isChildOf(fqn))
{
// Take out the child fqn reference to me.
internal_.removeRefFqn(newFqn);
if (log.isDebugEnabled()) {
log.debug("objectGraphRemove(): this node "+ fqn + " is currently referenced by a cyclic reference: "
+ newFqn + "Will only decrement reference count.");
}
} else
{
// Relocate all the contents from old to the new fqn
internal_.relocate(fqn, newFqn);
// Reset the fqn in the cache interceptor
InstanceAdvisor advisor = ((Advised) pojo)._getInstanceAdvisor();
CacheInterceptor interceptor = (CacheInterceptor) AopUtil.findCacheInterceptor(advisor);
if(interceptor == null)
throw new IllegalStateException("ObjectGraphHandler.objectGraphRemove(): null interceptor");
interceptor.setFqn(newFqn);
// reset the fqn in the indirect fqn map
internal_.setIndirectFqn(fqn.toString(), newFqn.toString());
isTrue = true;
if (log.isDebugEnabled()) {
log.debug("objectGraphRemove(): this node "+ fqn + " is currently referenced by " +
+internal_.getRefCount(newFqn) +
" other pojos after relocating to " +newFqn.toString());
}
}
}
}
return isTrue;
}
/**
* Remove the object from the the reference fqn, meaning just decrement the ref counter.
* @param fqn
* @param refFqn
* @param removeCacheInterceptor
* @param evict
* @throws CacheException
*/
private void removeFromReference(Fqn fqn, String refFqn, boolean removeCacheInterceptor,
boolean evict) throws CacheException {
synchronized (refFqn) { // we lock the internal fqn here so no one else has access.
// Decrement ref counting on the internal node
if (decrementRefCount(Fqn.fromString(refFqn), fqn) == AOPInstance.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.
delegate_._removeObject(Fqn.fromString(refFqn), removeCacheInterceptor, evict);
}
}
// Remove ref fqn from this fqn
internal_.removeRefFqn(fqn);
}
/**
* 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
*/
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
String aliasFqn = null;
if( incrementRefCount(refFqn, fqn) == 1 )
{
// We have the first multiple reference
aliasFqn = internal_.createIndirectFqn(refFqn.toString());
} else
{
aliasFqn = internal_.getIndirectFqn(refFqn.toString());
}
// 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);
}
internal_.setRefFqn(fqn, aliasFqn);
}
}
int incrementRefCount(Fqn originalFqn, Fqn referencingFqn) throws CacheException
{
return internal_.incrementRefCount(originalFqn, referencingFqn);
}
int decrementRefCount(Fqn originalFqn, Fqn referencingFqn) throws CacheException
{
int count = 0;
if( (count = internal_.decrementRefCount(originalFqn, referencingFqn)) == (AOPInstance.INITIAL_COUNTER_VALUE +1) )
{
internal_.removeIndirectFqn(originalFqn.toString());
}
return count;
}
}
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/InstanceOfAopMarker.java
Index: InstanceOfAopMarker.java
===================================================================
package org.jboss.cache.aop;
/**
* Marker interface for use in aop annotation declaration. For example, can use this in pojo
* annotation instead of declaring jboss-aop.xml. This is used specifically for instanceof
* declaration.
* @author Ben Wang
*/
public interface InstanceOfAopMarker {
}
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/WriteReplacer.java
Index: WriteReplacer.java
===================================================================
/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.aop;
//import java.util.Iterator;
//import java.util.List;
import org.jboss.aop.Advised;
import org.jboss.aop.InstanceAdvisor;
import java.io.NotSerializableException;
import java.io.ObjectStreamException;
/**
* @author <a href="mailto:harald at gliebe.de">Harald Gliebe</a>
* @deprecated 1.0
*/
public class WriteReplacer implements WriteReplaceable /*, Interceptor*/
{
// interceptor
/*
public WriteReplacer() {
}
public String getName() {
return "WriteReplacer";
}
public InvocationResponse invoke(Invocation invocation) throws Throwable {
Method method = MethodInvocation.getMethod(invocation);
if (method != null
&& method.getName().equals("writeReplace")
&& method.getReturnType().equals(Object.class)
&& method.getParameterTypes().length == 0) {
//hack
this.obj = MethodInvocation.getTargetObject(invocation);
this.writeReplace(); // fills Fieldvalues
}
return invocation.invokeNext();
}
*/
// mixin
Object obj;
public WriteReplacer(Object obj)
{
this.obj = obj;
}
public Object writeReplace() throws ObjectStreamException
{
if (obj instanceof Advised) {
InstanceAdvisor advisor = ((Advised) obj)._getInstanceAdvisor();
org.jboss.aop.advice.Interceptor[] interceptors = advisor.getInterceptors();
CacheInterceptor cacheInterceptor = null;
for (int i = 0; i < interceptors.length; i++) {
if (interceptors[i] instanceof CacheInterceptor) {
cacheInterceptor = (CacheInterceptor) interceptors[i];
break;
}
}
if (cacheInterceptor != null) {
try {
cacheInterceptor.beforeSerialization(obj);
} catch (Exception e) {
e.printStackTrace();
throw new NotSerializableException(e.getMessage());
}
}
}
return obj;
}
}
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/CachedType.java
Index: CachedType.java
===================================================================
package org.jboss.cache.aop;
import org.jboss.aop.joinpoint.FieldInvocation;
import org.jboss.aop.Advisor;
import org.jboss.cache.aop.references.FieldPersistentReference;
import org.jboss.cache.aop.references.PersistentReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.ref.WeakReference;
import java.util.Set;
import java.util.HashSet;
import java.util.Arrays;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.List;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Iterator;
/** Represent a cached object type, e.g., whether it is <b>primitive</b> or not.
* Note: need to pay special attention not to leak classloader.
* @author <a href="mailto:harald at gliebe.de">Harald Gliebe</a>
* @author Ben Wang
*/
public class CachedType
{
// Types that are considered "primitive".
protected static final Set immediates =
new HashSet(Arrays.asList(new Object[]{
String.class,
Boolean.class,
Double.class,
Float.class,
Integer.class,
Long.class,
Short.class,
Character.class,
Boolean.TYPE,
Double.TYPE,
Float.TYPE,
Integer.TYPE,
Long.TYPE,
Short.TYPE,
Character.TYPE,
Class.class}));
protected WeakReference type;
protected boolean immutable;
protected boolean immediate;
// This map caches the class that contains no annotation.
protected static Map CachedClassWithNoAnnotation_ = new WeakHashMap();
protected static Map CachedClassWithAnnotation_ = new WeakHashMap();
// Java fields . Will use special FieldPersistentReference to prevent classloader leakage.
protected List fields = new ArrayList();
protected Map fieldMap = new HashMap(); // Name -> CachedAttribute
public CachedType()
{
}
public CachedType(Class type)
{
this.type = new WeakReference(type);
analyze();
}
public Class getType()
{
return (Class)type.get();
}
// determines if the object should be stored in the Nodes map or as a subnode
public boolean isImmediate()
{
return immediate;
}
public static boolean isImmediate(Class clazz)
{
return immediates.contains(clazz);
}
public boolean isImmutable()
{
return immutable;
}
public List getFields()
{
return fields;
}
public Field getField(String name)
{
FieldPersistentReference ref = (FieldPersistentReference)fieldMap.get(name);
if (ref==null) return null;
return (Field)ref.get();
}
/*
public List getAttributes()
{
return attributes;
}
public CachedAttribute getAttribute(Method m)
{
return (CachedAttribute) attributeMap.get(m);
}
protected void setAttributes(List attributes)
{
this.attributes = attributes;
attributeMap.clear();
// TODO: is a class with no set methods immutable ?
this.immutable = true;
for (Iterator i = attributes.iterator(); i.hasNext();) {
CachedAttribute attribute = (CachedAttribute) i.next();
if (attribute.getGet() != null) {
attributeMap.put(attribute.getGet(), attribute);
}
if (attribute.getSet() != null) {
attributeMap.put(attribute.getSet(), attribute);
immutable = false;
}
}
}
*/
public String toString()
{
StringBuffer sb = new StringBuffer();
sb.append(getType().getName()).append(" {\n");
/*
for (Iterator i = attributes.iterator(); i.hasNext();) {
CachedAttribute attr = (CachedAttribute) i.next();
sb
.append("\t")
.append(attr.getType().getName())
.append(" ")
.append(attr.getName())
.append(" [")
.append(attr.getGet() == null
? "<no get>"
: attr.getGet().getName())
.append(", ")
.append(attr.getSet() == null
? "<no set>"
: attr.getSet().getName())
.append("]\n");
}
*/
sb.append("}, immutable =").append(immutable);
return sb.toString();
}
/* ---------------------------------------- */
private void analyze()
{
/*
// We intercept all fields now (instead of setter methods) so there is no need to
// track the individual fields.
HashMap attributes = new HashMap();
Method[] methods = type.getMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if (isGet(method)) {
CachedAttribute attribute =
getAttribute(method, attributes, true);
attribute.setGet(method);
attribute.setType(method.getReturnType());
} else if (isSet(method)) {
CachedAttribute attribute =
getAttribute(method, attributes, true);
attribute.setSet(method);
attribute.setType(method.getParameterTypes()[0]);
}
}
this.setAttributes(new ArrayList(attributes.values()));
*/
analyzeFields(getType());
immediate = isImmediate(getType());
}
void analyzeFields(Class clazz)
{
if (clazz == null)
return;
analyzeFields(clazz.getSuperclass());
Field[] classFields = clazz.getDeclaredFields();
for (int i = 0; i < classFields.length; i++) {
Field f = classFields[i];
if(isPrimitiveNonReplicatable(f)) continue;
f.setAccessible(true);
FieldPersistentReference persistentRef = new FieldPersistentReference(f, PersistentReference.REFERENCE_SOFT);
fields.add(persistentRef);
fieldMap.put(f.getName(), persistentRef);
}
}
/**
* We check whether this class has any field annotation declaration. We assume that there is only
* one such declaring class per vm and it is static.
*/
protected static boolean hasAnnotation(Class clazz, Advisor advisor, CachedType type)
{
// It is ok that we don't synchronize it here.
if(CachedClassWithNoAnnotation_.get(clazz) != null)
{
return false;
} else if (CachedClassWithAnnotation_.get(clazz) != null)
{
return true;
}
for (Iterator i = type.getFields().iterator(); i.hasNext();) {
Field field = (Field)(((FieldPersistentReference) i.next())).get();
// check for non-replicatable types
if(CachedType.hasFieldAnnotation(field, advisor))
{
synchronized(CachedClassWithAnnotation_)
{
CachedClassWithAnnotation_.put(clazz, clazz.getName());
}
return true;
}
}
// This obj class doesn't have field annotation. It is ok that multiple threads
// put it repeatedly actually.
synchronized(CachedClassWithNoAnnotation_)
{
CachedClassWithNoAnnotation_.put(clazz, clazz.getName());
}
return false;
}
protected static boolean isPrimitiveNonReplicatable(Field f) {
int mods = f.getModifiers();
/**
* The following modifiers are ignored in the cache, i.e., they will not be stored in the cache.
* Whenever, user trying to access these fields, it will be accessed from the in-memory version.
*/
if (Modifier.isStatic(mods)
|| Modifier.isTransient(mods)
|| Modifier.isFinal(mods)) {
return true;
}
return false;
}
/**
* Check if we have @Transient annotation.
* @param invocation
* @return true if @Transient is present.
*/
protected static boolean hasTransientAnnotation(FieldInvocation invocation)
{
Object obj = invocation.resolveAnnotation(org.jboss.cache.aop.annotation.Transient.class);
if(obj != null)
{
return true;
}
return false;
}
protected static boolean hasFieldAnnotation(Field field, Advisor advisor)
{
return hasTransientAnnotation(field, advisor) || hasSerializableAnnotation(field, advisor);
}
protected static boolean hasTransientAnnotation(Field field, Advisor advisor)
{
Object obj = advisor.resolveAnnotation(field, org.jboss.cache.aop.annotation.Transient.class);
if(obj != null)
{
return true;
}
return false;
}
public static boolean hasSerializableAnnotation(Field field, Advisor advisor)
{
Object obj = advisor.resolveAnnotation(field, org.jboss.cache.aop.annotation.Serializable.class);
if(obj != null)
{
return true;
}
return false;
}
public static boolean hasSerializableAnnotation(FieldInvocation invocation)
{
Object obj = invocation.resolveAnnotation(org.jboss.cache.aop.annotation.Serializable.class);
if(obj != null)
{
return true;
}
return false;
}
public static boolean isNonReplicatable(FieldInvocation fieldInvocation)
{
return hasTransientAnnotation(fieldInvocation) || isPrimitiveNonReplicatable(fieldInvocation.getField());
}
/*
* converts a get/set method to an attribute name
*/
protected String attributeName(String methodName)
{
return methodName.substring(3, 4).toLowerCase()
+ methodName.substring(4);
}
protected CachedAttribute getAttribute(Method method,
Map map,
boolean create)
{
String name = attributeName(method.getName());
CachedAttribute attribute = (CachedAttribute) map.get(name);
if (create && attribute == null) {
attribute = new CachedAttribute(name);
map.put(name, attribute);
}
return attribute;
}
protected boolean isGet(Method method)
{
return method.getName().startsWith("get")
&& method.getParameterTypes().length == 0
&& method.getReturnType() != Void.TYPE;
}
protected boolean isSet(Method method)
{
return method.getName().startsWith("set")
&& method.getParameterTypes().length == 1
&& method.getReturnType() == Void.TYPE;
}
} // CachedType
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/TreeCacheAop.java
Index: TreeCacheAop.java
===================================================================
/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.aop;
import org.jgroups.JChannel;
/**
* @author Harald Gliebe
* @author Ben Wang
* @deprecated Since 1.4, replaced by {@link PojoCache}
*/
public class TreeCacheAop extends PojoCache //implements TreeCacheAopMBean // TODO; Need to come up with proper JMX interfaces
{
public TreeCacheAop(String cluster_name,
String props,
long state_fetch_timeout)
throws Exception {
super(cluster_name, props, state_fetch_timeout);
}
public TreeCacheAop() throws Exception
{
}
public TreeCacheAop(JChannel channel) throws Exception
{
super(channel);
}
}
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/InternalDelegate.java
Index: InternalDelegate.java
===================================================================
/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.aop;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.CacheException;
import org.jboss.cache.DataNode;
import org.jboss.cache.Fqn;
import org.jboss.cache.aop.util.ObjectUtil;
import org.jboss.cache.config.Option;
import java.util.Iterator;
import java.util.Map;
/**
* PojoCache delegation to handle internal cache sotre, that is, the portion that is not part of user's data.
*
* @author Ben Wang
*/
public class InternalDelegate
{
static Log log = LogFactory.getLog(InternalDelegate.class.getName());
public static final String CLASS_INTERNAL = "__jboss:internal:class__";
public static final String SERIALIZED = "__SERIALIZED__";
public static final Fqn JBOSS_INTERNAL = new Fqn("__JBossInternal__");
public static final Fqn JBOSS_INTERNAL_MAP = new Fqn(JBOSS_INTERNAL, "__RefMap__");
// 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_;
protected PojoCache cache_;
InternalDelegate(PojoCache 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_;
}
protected AOPInstance 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 (AOPInstance) get(fqn, AOPInstance.KEY, false);
}
protected AOPInstance 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 (AOPInstance) get(fqn, AOPInstance.KEY, true);
}
AOPInstance initializeAopInstance(Fqn fqn) throws CacheException
{
AOPInstance aopInstance = new AOPInstance();
aopInstance.incrementRefCount(null);
return aopInstance;
}
/**
* Increment reference count for the pojo. Note that this is not thread safe or atomic.
*/
int incrementRefCount(Fqn originalFqn, Fqn referencingFqn) throws CacheException
{
AOPInstance aopInstance = getAopInstance(originalFqn);
if (aopInstance == null)
throw new RuntimeException("InternalDelegate.incrementRefCount(): null aopInstance for fqn: " + originalFqn);
int count = aopInstance.incrementRefCount(referencingFqn);
// need to update it.
put(originalFqn, AOPInstance.KEY, aopInstance);
return count;
}
/**
* Has a delegate method so we can use the switch.
*/
protected Object get(Fqn fqn, Object key) throws CacheException
{
return get(fqn, key, false);
}
protected 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);
return cache_.get(fqn, key);
}
else if (cache_.getCacheLoader() != null)
{
// We have cache loader, we can't get it directly from the local get.
cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(cacheOperationSkipLocking);
return cache_.get(fqn, key);
}
else
{
return cache_._get(fqn, key, false);
}
}
protected 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(cacheOperationSkipLocking);
cache_.put(fqn, key, value);
// cache_.put(fqn, key, value);
}
protected void put(Fqn fqn, Map map) throws CacheException
{
// Use option to ski locking since we have parent lock already.
cache_.getInvocationContext().getOptionOverrides().setSuppressLocking(cacheOperationSkipLocking);
cache_.put(fqn, map);
// 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().setCacheModeLocal(true);
cache_.put(fqn, key, value);
}
/**
* decrement reference count for the pojo. Note that this is not thread safe or atomic.
*/
int decrementRefCount(Fqn originalFqn, Fqn referencingFqn) throws CacheException
{
AOPInstance aopInstance = getAopInstance(originalFqn);
if (aopInstance == null)
throw new RuntimeException("InternalDelegate.decrementRefCount(): null aopInstance.");
int count = aopInstance.decrementRefCount(referencingFqn);
if (count < -1) // can't dip below -1
throw new RuntimeException("InternalDelegate.decrementRefCount(): null aopInstance.");
// need to update it.
put(originalFqn, AOPInstance.KEY, aopInstance);
return count;
}
boolean isReferenced(AOPInstance aopInstance, Fqn fqn) throws CacheException
{
// If ref counter is greater than 0, we fqn is being referenced.
return (aopInstance.getRefCount() > 0);
}
int getRefCount(Fqn fqn) throws CacheException
{
return getAopInstance(fqn).getRefCount();
}
String getRefFqn(Fqn fqn) throws CacheException
{
AOPInstance aopInstance = getAopInstance(fqn);
return getRefFqn(aopInstance, fqn);
}
String getRefFqn(AOPInstance aopInstance, Fqn fqn) throws CacheException
{
if (aopInstance == null)
return null;
String aliasFqn = aopInstance.getRefFqn();
if (aliasFqn == null || aliasFqn.length() == 0) return null;
return getRefFqnFromAlias(aliasFqn);
}
void setRefFqn(Fqn fqn, String internalFqn) throws CacheException
{
AOPInstance aopInstance = getAopInstance(fqn);
if (aopInstance == null)
aopInstance = new AOPInstance();
aopInstance.setRefFqn(internalFqn);
put(fqn, AOPInstance.KEY, aopInstance);
}
void removeRefFqn(Fqn fqn) throws CacheException
{
AOPInstance aopInstance = getAopInstance(fqn);
if (aopInstance == null)
throw new RuntimeException("InternalDelegate.getRefFqn(): null aopInstance.");
aopInstance.removeRefFqn();
put(fqn, AOPInstance.KEY, aopInstance);
}
Object getPojo(Fqn fqn) throws CacheException
{
AOPInstance aopInstance = getAopInstance(fqn);
if (aopInstance == null)
return null;
return aopInstance.get();
}
Object getPojoWithGravitation(Fqn fqn) throws CacheException
{
// This is for buddy replication
AOPInstance aopInstance = getAopInstanceWithGravitation(fqn);
if (aopInstance == null)
return null;
return aopInstance.get();
}
void setPojo(Fqn fqn, Object pojo) throws CacheException
{
AOPInstance aopInstance = getAopInstance(fqn);
if (aopInstance == null)
{
aopInstance = new AOPInstance();
put(fqn, AOPInstance.KEY, aopInstance);
}
aopInstance.set(pojo);
// No need to do a cache put since pojo is transient anyway.
}
void setPojo(AOPInstance aopInstance, Object pojo) throws CacheException
{
// No need to do a cache put since pojo is transient anyway.
aopInstance.set(pojo);
}
void setPojo(Fqn fqn, Object pojo, AOPInstance aopInstance) throws CacheException
{
if (aopInstance == null)
{
aopInstance = new AOPInstance();
put(fqn, AOPInstance.KEY, aopInstance);
}
aopInstance.set(pojo);
// No need to do a cache put since pojo is transient anyway.
}
/**
* We store the class name in string.
*/
void putAopClazz(Fqn fqn, Class clazz) throws CacheException
{
put(fqn, CLASS_INTERNAL, clazz);
}
/**
* We store the class name in string and put it in map instead of directly putting
* it into cache for optimization.
*/
void putAopClazz(Fqn fqn, Class clazz, Map map) throws CacheException
{
map.put(CLASS_INTERNAL, clazz);
}
Class peekAopClazz(Fqn fqn) throws CacheException
{
return (Class) get(fqn, CLASS_INTERNAL);
}
boolean isAopNode(Fqn fqn) throws CacheException
{
// Use this API so it doesn't go thru the interceptor.
DataNode node = cache_.peek(fqn);
if (node == null) return false;
if (node.get(AOPInstance.KEY) != null)
return true;
else
return false;
}
void removeInternalAttributes(Fqn fqn) throws CacheException
{
cache_.remove(fqn, AOPInstance.KEY);
cache_.remove(fqn, CLASS_INTERNAL);
}
void cleanUp(Fqn fqn, boolean evict) throws CacheException
{
// We can't do a brute force remove anymore?
if (!evict)
{
if (!cache_._get(fqn).hasChildren())
{
// remove everything
cache_.remove(fqn);
}
else
{
// Assume everything here is all PojoCache data for optimization
cache_.removeData(fqn);
if (log.isTraceEnabled())
{
log.trace("cleanup(): fqn: " + fqn + " is not empty. That means it has sub-pojos. Will not remove node");
}
}
}
else
{
// This has to use plainEvict method otherwise it is recursively calling aop version of evict.
cache_.plainEvict(fqn);
}
}
String createIndirectFqn(String fqn) throws CacheException
{
String indirectFqn = getIndirectFqn(fqn);
Fqn internalFqn = getInternalFqn(fqn);
put(internalFqn, indirectFqn, fqn);
return indirectFqn;
}
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(JBOSS_INTERNAL_MAP, indirectFqn);
// return JBOSS_INTERNAL_MAP;
}
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);
if (cache_.exists(tmpFqn, indirectFqn)) // No need to update if it doesn't exist.
{
put(tmpFqn, indirectFqn, newFqn);
}
}
void updateIndirectFqn(Fqn originalFqn, Fqn newFqn) throws CacheException
{
put(getInternalFqn(originalFqn.toString()), getIndirectFqn(originalFqn.toString()), newFqn.toString());
}
String getRefFqnFromAlias(String aliasFqn) throws CacheException
{
return (String) get(getInternalFqn(aliasFqn), aliasFqn, true);
}
Fqn getNextFqnInLine(Fqn currentFqn) throws CacheException
{
AOPInstance ai = getAopInstance(currentFqn);
return ai.getAndRemoveFirstFqnInList();
}
void relocate(Fqn thisFqn, Fqn newFqn) throws CacheException
{
/**
DataNode node = cache_.get(thisFqn);
DataNode newParent = (DataNode)cache_.get(newFqn).getParent();
node.relocate(newParent, newFqn); // relocation
updateIndirectFqn(thisFqn, newFqn);
*/
// Let's do cache-wide copy then. It won't be fast and atomic but
// at least it preserves the pojo structure and also do replication
// TODO Can TreeCache provide a method to do this??
// First do a recursive copy using the new base fqn
DataNode node = cache_.get(thisFqn);
Map value = node.getData();
cache_.put(newFqn, value);
Map children = node.getChildren();
if (children == null || children.size() == 0)
{
cache_.remove(thisFqn);
return; // we are done
}
Iterator it = children.keySet().iterator();
while (it.hasNext())
{
Object key = it.next();
Fqn thisChildFqn = new Fqn(thisFqn, key);
Fqn newChildFqn = new Fqn(newFqn, key);
relocate(thisChildFqn, newChildFqn);
}
// Finally do a remove
cache_.remove(thisFqn);
}
/**
* 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(InternalDelegate.JBOSS_INTERNAL)) return true;
return false;
}
}
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/TreeCacheAopDelegate.java
Index: TreeCacheAopDelegate.java
===================================================================
/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.aop;
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.ClassInstanceAdvisor;
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.Fqn;
import org.jboss.cache.GlobalTransaction;
import org.jboss.cache.TreeNode;
import org.jboss.cache.aop.collection.AbstractCollectionInterceptor;
import org.jboss.cache.aop.references.FieldPersistentReference;
import org.jboss.cache.aop.util.AopUtil;
import org.jboss.cache.marshall.MethodCall;
import org.jboss.cache.marshall.MethodCallFactory;
import org.jboss.cache.marshall.MethodDeclarations;
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.
*
* @author Ben Wang
*/
public class TreeCacheAopDelegate
{
protected PojoCache cache_;
protected final static Log log = LogFactory.getLog(TreeCacheAopDelegate.class);
protected InternalDelegate internal_;
protected ObjectGraphHandler graphHandler_;
protected CollectionClassHandler collectionHandler_;
protected SerializableObjectHandler serializableHandler_;
// Use ThreadLocal to hold a boolean isBulkRemove
protected ThreadLocal bulkRemove_ = new ThreadLocal();
protected final String DETACH = "DETACH";
public TreeCacheAopDelegate(PojoCache cache)
{
cache_ = cache;
internal_ = new InternalDelegate(cache);
graphHandler_ = new ObjectGraphHandler(cache_, internal_, this);
collectionHandler_ = new CollectionClassHandler(cache_, internal_, graphHandler_);
serializableHandler_ = new SerializableObjectHandler(cache_, internal_);
}
public void setBulkRemove(boolean bulk)
{
bulkRemove_.set(Boolean.valueOf(bulk));
}
public boolean getBulkRemove()
{
return ((Boolean) bulkRemove_.get()).booleanValue();
}
protected Object _getObject(Fqn fqn) throws CacheException
{
// TODO Must we really to couple with BR? JBCACHE-669
Object pojo = internal_.getPojoWithGravitation(fqn);
if (pojo != null)
{
// we already have an advised instance
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);
}
}
protected Object _getObjectInternal(Fqn fqn) throws CacheException
{
// the class attribute is implicitly stored as an immutable read-only attribute
Class clazz = internal_.peekAopClazz(fqn);
// clazz and aopInstance can be not null if this node is the replicated brother node.
if (clazz == null)
return null;
/**
* Reconstruct the managed POJO
*/
CachedType type = cache_.getCachedType(clazz);
Object obj;
// Check for both Advised and Collection classes for object graph.
if ((obj = graphHandler_.objectGraphGet(fqn)) != null)
return obj; // retrieved from internal ref node. We are done.
AOPInstance aopInstance = internal_.getAopInstance(fqn);
if (aopInstance == null)
{
throw new RuntimeException("TreeCacheAopDelegate._getObject(): null AOPInstance.");
}
if (Advised.class.isAssignableFrom(clazz))
{
try
{
obj = clazz.newInstance();
// TODO Need to populate the object from the cache as well.
}
catch (Exception e)
{
throw new CacheException("failed creating instance of " + clazz.getName(), e);
}
// Insert interceptor at runtime
InstanceAdvisor advisor = ((Advised) obj)._getInstanceAdvisor();
CacheInterceptor interceptor = new CacheInterceptor(cache_, fqn, type);
interceptor.setAopInstance(aopInstance);
advisor.appendInterceptor(interceptor);
cache_.addUndoInterceptor(advisor, interceptor, ModificationEntry.INTERCEPTOR_ADD);
}
else
{ // Must be Collection classes. We will use aop.ClassProxy instance instead.
try
{
if ((obj = collectionHandler_.collectionObjectGet(fqn, clazz)) != null)
{
}
else
{
// Maybe it is just a serialized object.
obj = serializableHandler_.serializableObjectGet(fqn);
}
}
catch (Exception e)
{
throw new CacheException("failure creating proxy", e);
}
}
internal_.setPojo(aopInstance, obj);
return obj;
}
/**
* Note that caller of this method will take care of synchronization within the <code>fqn</code> sub-tree.
*
* @param fqn
* @param obj
* @return
* @throws CacheException
*/
protected Object _putObject(Fqn fqn, Object obj) throws CacheException
{
if (!cache_.isMarshallNonSerializable())
AopUtil.checkObjectType(obj);
if (obj == null)
{
return cache_._removeObject(fqn, true);
}
// Skip some un-necessary update if obj is the same class as the old one
Object oldValue = internal_.getPojo(fqn);
if (oldValue == obj) return obj; // value already in cache. return right away.
if (oldValue != null)
{
// Trigger bulk remove here for performance
setBulkRemove(true);
cache_._removeObject(fqn, true); // remove old value before overwriting it.
}
// Remember not to print obj here since it will trigger the CacheInterceptor.
if (log.isDebugEnabled())
{
log.debug("putObject(): fqn: " + fqn);
}
// store object in cache
if (obj instanceof Advised)
{
CachedType type = cache_.getCachedType(obj.getClass());
// add interceptor
InstanceAdvisor advisor = ((Advised) obj)._getInstanceAdvisor();
if (advisor == null)
throw new RuntimeException("_putObject(): InstanceAdvisor is null for: " + obj);
// Step Check for cross references
Interceptor interceptor = AopUtil.findCacheInterceptor(advisor);
// Let's check for object graph, e.g., multiple and circular references first
if (graphHandler_.objectGraphPut(fqn, interceptor, type, obj))
{ // found cross references
return oldValue;
}
// We have a clean slate then.
_regularPutObject(fqn, obj, advisor, type);
/**
* Handling collection classes here.
* First check if obj has been aspectized? That is, if it is a ClassProxy or not.
* If not, we will need to create a proxy first for the Collection classes
*/
}
else if (collectionHandler_.collectionObjectPut(fqn, obj))
{
//
}
else if (serializableHandler_.serializableObjectPut(fqn, obj))
{
// must be Serializable, including primitive types
}
else
{
// I really don't know what this is.
throw new RuntimeException("putObject(): obj: " + obj + " type is not recognizable.");
}
return oldValue;
}
/**
* Based on the pojo to perform a bulk remove recursively if there is no object graph
* relationship for performance optimization.
*/
protected 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 undo the the removal.
// return false;
Map undoMap = new HashMap();
if (pojoGraphMultipleReferenced(obj, undoMap))
{
undoInterceptorDetach(undoMap);
return false;
}
else
{
cache_.remove(fqn); // interceptor has been removed so it is safe to do bulk remove now.
}
return true;
}
protected void detachInterceptor(InstanceAdvisor advisor, Interceptor interceptor,
boolean detachOnly, Map undoMap)
{
if (!detachOnly)
{
advisor.removeInterceptor(interceptor.getName());
undoMap.put(advisor, interceptor);
}
else
{
undoMap.put(DETACH, interceptor);
}
}
protected 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("TreeCacheAopDelegate.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.
*/
protected boolean pojoGraphMultipleReferenced(Object obj, Map undoMap) throws CacheException
{
// store object in cache
if (obj instanceof Advised)
{
CachedType type = cache_.getCachedType(obj.getClass());
// add interceptor
InstanceAdvisor advisor = ((Advised) obj)._getInstanceAdvisor();
if (advisor == null)
throw new RuntimeException("pojoGraphMultipleReferenced(): InstanceAdvisor is null for: " + obj);
BaseInterceptor interceptor = (BaseInterceptor) AopUtil.findCacheInterceptor(advisor);
// just in case
if (interceptor == null)
{
return false;
}
AOPInstance aopInstance = interceptor.getAopInstance();
// Check if there is cross referenced.
if (aopInstance.getRefCount() != 0) return true; // I have been referenced
if (aopInstance.getRefFqn() != 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 = cache_.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);
AOPInstance aopInstance = interceptor.getAopInstance();
if (aopInstance == null) return false; // safeguard
// Check if there is cross referenced.
if (aopInstance.getRefCount() != 0) return true; // I have been referenced
if (aopInstance.getRefFqn() != 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;
}
protected void _regularPutObject(Fqn fqn, Object obj, InstanceAdvisor advisor, CachedType type) throws CacheException
{
// 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.
AOPInstance aopInstance = internal_.initializeAopInstance(fqn);
// Insert interceptor at runtime
CacheInterceptor interceptor = new CacheInterceptor(cache_, fqn, type);
interceptor.setAopInstance(aopInstance);
advisor.appendInterceptor(interceptor);
cache_.addUndoInterceptor(advisor, interceptor, ModificationEntry.INTERCEPTOR_ADD);
map.put(AOPInstance.KEY, aopInstance);
// This is put into map first.
internal_.putAopClazz(fqn, type.getType(), map);
// 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 = cache_.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_.put(fqn, map, internal_.getLockOption());
cache_.put(fqn, map);
// This is in-memory operation only
internal_.setPojo(aopInstance, obj);
Iterator it = subPojoMap.keySet().iterator();
while (it.hasNext())
{
Field field = (Field) it.next();
Object value = subPojoMap.get(field);
Fqn tmpFqn = new Fqn(fqn, field.getName());
_putObject(tmpFqn, value);
// If it is Collection classes, we replace it with dynamic proxy.
// But we will have to ignore it if value is null
collectionHandler_.collectionReplaceWithProxy(obj, value, field, tmpFqn);
}
// Need to make sure this is behind put such that obj.toString is done correctly.
if (log.isDebugEnabled())
{
log.debug("_regularPutObject(): inserting with fqn: " + fqn);
}
}
private boolean hasAnnotation(Class clazz, Advisor advisor, CachedType type)
{
return CachedType.hasAnnotation(clazz, advisor, type);
}
private void createNode(Fqn fqn, GlobalTransaction tx)
{
TreeNode n, child_node;
Object child_name;
Fqn tmp_fqn = Fqn.ROOT;
if (fqn == null) return;
int treeNodeSize = fqn.size();
n = cache_.getRoot();
for (int i = 0; i < treeNodeSize; i++)
{
child_name = fqn.get(i);
tmp_fqn = new Fqn(tmp_fqn, child_name);
child_node = n.getChild(child_name);
if (child_node == null)
{
child_node = n.createChild(child_name, tmp_fqn, n);
if (tx != null)
{
MethodCall undo_op = MethodCallFactory.create(MethodDeclarations.removeNodeMethodLocal, tx, tmp_fqn, false);
cache_.addUndoOperation(tx, undo_op);
}
}
n = child_node;
}
}
/**
* Note that caller of this method will take care of synchronization within the <code>fqn</code> sub-tree.
*
* @param fqn
* @param removeCacheInterceptor
* @param evict
* @return
* @throws CacheException
*/
public Object _removeObject(Fqn fqn, boolean removeCacheInterceptor, boolean evict)
throws CacheException
{
Class clazz = internal_.peekAopClazz(fqn);
if (clazz == null)
{
if (log.isTraceEnabled())
{
log.trace("_removeObject(): clasz is null. fqn: " + fqn + " No need to remove.");
}
return null;
}
if (log.isDebugEnabled())
{
log.debug("_removeObject(): removing object from fqn: " + fqn);
}
Object result = cache_.getObject(fqn);
if (result == null)
{
// This is not a *Pojo*. Must be regular cache stuffs
if (cache_.exists(fqn))
{
// TODO What do we do here. It can still have children pojo though.
if (!evict)
{
cache_.remove(fqn);
}
else
{
cache_._evict(fqn);
}
}
return null;
}
// can check if we need to do any bulk remove. E.g., if there is no object graph.
if (getBulkRemove())
{
if (bulkRemove(fqn, result))
{
// Remember not to print obj here since it will trigger the CacheInterceptor.
if (log.isDebugEnabled())
{
log.debug("_removeObject(): fqn: " + fqn + "removing exisiting object in bulk.");
}
return result;
}
setBulkRemove(false);
}
if (graphHandler_.objectGraphRemove(fqn, removeCacheInterceptor, result, evict))
{
return result;
}
// Not multi-referenced
if (Advised.class.isAssignableFrom(clazz))
{
_regularRemoveObject(fqn, removeCacheInterceptor, result, clazz, evict);
}
else if (collectionHandler_.collectionObjectRemove(fqn, removeCacheInterceptor, evict))
{
}
else
{ // Just Serializable objects. Do a brute force remove is ok.
serializableHandler_.serializableObjectRemove(fqn);
}
internal_.cleanUp(fqn, evict);
// remove the interceptor as well.
return result;
}
protected void _regularRemoveObject(Fqn fqn, boolean removeCacheInterceptor, Object result, Class clazz,
boolean evict) throws CacheException
{
InstanceAdvisor advisor = ((Advised) result)._getInstanceAdvisor();
CachedType type = cache_.getCachedType(clazz);
for (Iterator i = type.getFields().iterator(); i.hasNext();)
{
Field field = (Field) (((FieldPersistentReference) i.next())).get();
CachedType fieldType = cache_.getCachedType(field.getType());
if (!fieldType.isImmediate())
{
_removeObject(new Fqn(fqn, field.getName()), removeCacheInterceptor, evict);
}
}
// batch remove
cache_.removeData(fqn);
// Determine if we want to keep the interceptor for later use.
if (removeCacheInterceptor)
{
CacheInterceptor interceptor = (CacheInterceptor) 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);
}
advisor.removeInterceptor(interceptor.getName());
cache_.addUndoInterceptor(advisor, interceptor, ModificationEntry.INTERCEPTOR_REMOVE);
}
}
}
boolean isAopNode(Fqn fqn)
{
try
{
return (internal_.isAopNode(fqn));
}
catch (Exception e)
{
log.warn("isAopNode(): cache get operation generated exception " + e);
return false;
}
}
protected 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(): Fqn: " + fqn + " size of pojos found: " + map.size());
}
return map;
}
protected void findChildObjects(Fqn fqn, Map map) throws CacheException
{
// We need to traverse then
Set set = cache_.getChildrenNames(fqn);
if (set == null) return; // We stop here.
Iterator it = set.iterator();
while (it.hasNext())
{
String obj = (String) it.next();
Fqn newFqn = new Fqn(fqn, obj);
Object pojo = _getObject(newFqn);
if (pojo != null)
{
map.put(newFqn, pojo);
}
else
{
findChildObjects(newFqn, map);
}
}
}
}
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/CachedAttribute.java
Index: CachedAttribute.java
===================================================================
package org.jboss.cache.aop;
import java.lang.reflect.Method;
/**
* Class represent a class attribute. Currently not in use.
* @author <a href="mailto:harald at gliebe.de">Harald Gliebe</a>
* @author Ben Wang
*/
public class CachedAttribute
{
protected String name;
protected Class type;
protected Method get, set;
public CachedAttribute()
{
}
public CachedAttribute(String name)
{
this.name = name;
}
public String getName()
{
return this.name;
}
public Class getType()
{
return this.type;
}
public void setType(Class type)
{
if (this.type != null && this.type != type) {
// TODO: provide better info
throw new IllegalArgumentException("get/set types differ");
}
this.type = type;
}
public Method getGet()
{
return this.get;
}
public void setGet(Method get)
{
this.get = get;
}
public Method getSet()
{
return this.set;
}
public void setSet(Method set)
{
this.set = set;
}
} // CachedAttribute
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/AOPInstance.java
Index: AOPInstance.java
===================================================================
/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.aop;
import java.io.Serializable;
import java.util.List;
import java.util.ArrayList;
import org.jboss.cache.Fqn;
/**
* Wrapper type for cached AOP instances.
* When an object is looked up or put in TreeCacheAOP, this object will be advised with a CacheInterceptor.
* The tree cache stores a reference to this object (for example to update the instance variables, etc.).
* Since this reference need to be transactional but never replicated (the reference is only valid within the VM)
* this reference is wrapped into an AOPInstance.
* In addition, this instance also serves as a metadata for PojoCache. E.g., it has a reference count for
* multiple references and reference FQN.
*
* @author Harald Gliebe
* @author Ben Wang
*/
public class AOPInstance implements Serializable // Use Serializable since Externalizable is not smaller
{
// protected static Log log=LogFactory.getLog(AOPInstance.class.getName());
public static final Object KEY = "AOPInstance";
public static final int INITIAL_COUNTER_VALUE = -1;
static final long serialVersionUID = 6492134565825613209L;
// The instance is transient to avoid reflection outside the VM
protected transient Object instance_;
// If not null, it signifies that this is a reference that points to this fqn.
// Note that this will get replicated.
protected String refFqn_ = null;
// Reference counting. THis will get replicated as well. This keep track of number of other instances
// that referenced this fqn.
protected int refCount_ = INITIAL_COUNTER_VALUE;
// List of fqns that reference this fqn. Assume list size is not big.
protected List referencingFqnList_ = null;
public AOPInstance()
{
}
public AOPInstance(Object instance)
{
set(instance);
}
Object get()
{
return instance_;
}
void set(Object instance)
{
instance_ = instance;
}
String getRefFqn()
{
return refFqn_;
}
void setRefFqn(String refFqn)
{
refFqn_ = refFqn;
}
void removeRefFqn()
{
refFqn_ = null;
}
synchronized int incrementRefCount(Fqn referencingFqn)
{
if(referencingFqn != null)
{
if( referencingFqnList_ == null)
{
referencingFqnList_ = new ArrayList();
}
if(referencingFqnList_.contains(referencingFqn))
throw new IllegalStateException("AOPInstance.incrementRefCount(): source fqn: " +
referencingFqn + " is already present.");
referencingFqnList_.add(referencingFqn);
}
refCount_ += 1;
//logger_.info("incrementRefCount(): current ref count " +refCount_);
return refCount_;
}
synchronized int decrementRefCount(Fqn sourceFqn)
{
if(sourceFqn != null)
{
if(!referencingFqnList_.contains(sourceFqn))
throw new IllegalStateException("AOPInstance.decrementRefCount(): source fqn: " +
sourceFqn + " is not present.");
referencingFqnList_.remove(sourceFqn);
}
refCount_ -= 1;
//logger_.info("decrementRefCount(): current ref count " +refCount_);
return refCount_;
}
synchronized int getRefCount()
{
return refCount_;
}
synchronized Fqn getAndRemoveFirstFqnInList()
{
return (Fqn)referencingFqnList_.remove(0);
}
synchronized void addFqnIntoList(Fqn fqn)
{
referencingFqnList_.add(0, fqn);
}
}
1.1 date: 2006/10/31 08:01:13; author: bwang; state: Exp;JBossCache/old/src/org/jboss/cache/aop/BaseInterceptor.java
Index: BaseInterceptor.java
===================================================================
/*
* JBoss, the OpenSource J2EE webOS
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.aop;
import org.jboss.cache.Fqn;
/**
* Base cache interceptor
* @author Ben Wang
*/
public interface BaseInterceptor
extends org.jboss.aop.advice.Interceptor
{
/**
* Get the original fqn that is associated with this interceptor (or advisor).
*
*/
Fqn getFqn();
void setFqn(Fqn fqn);
AOPInstance getAopInstance();
void setAopInstance(AOPInstance aopInstance);
}
More information about the jboss-cvs-commits
mailing list