package org.jboss.cache.aop;
import org.jboss.aop.advice.Interceptor;
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.cache.Fqn;
import org.jboss.logging.Logger;
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;
public class CacheInterceptor implements Interceptor
{
protected static Logger log_ = Logger.getLogger(CacheInterceptor.class);
TreeCacheAop cache;
Fqn fqn;
CachedType type;
boolean checkSerialization;
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(TreeCacheAop cache, Fqn fqn, CachedType type)
{
this.cache = cache;
this.fqn = fqn;
this.type = type;
checkSerialization =
!WriteReplaceable.class.isAssignableFrom(type.getType());
}
public String getName()
{
return "CacheInterceptor on [" + fqn + "]";
}
public Object invoke(Invocation invocation) throws Throwable
{
if (invocation instanceof FieldWriteInvocation) {
checkCacheConsistency();
FieldWriteInvocation fieldInvocation =
(FieldWriteInvocation) invocation;
Field field = fieldInvocation.getField();
if( !CachedType.isNonReplicatable(field)) {
CachedType fieldType = cache.getCachedType(field.getType());
Object value = fieldInvocation.getValue();
if (fieldType.isImmediate()) {
cache.put(fqn, field.getName(), value);
} else {
cache.putObject(new Fqn(fqn, field.getName()), value);
}
}
} else if (invocation instanceof FieldReadInvocation) {
checkCacheConsistency();
FieldReadInvocation fieldInvocation =
(FieldReadInvocation) invocation;
Field field = fieldInvocation.getField();
if( !CachedType.isNonReplicatable(field)) {
CachedType fieldType = cache.getCachedType(field.getType());
Object result;
if (fieldType.isImmediate()) {
result = cache.get(fqn, field.getName());
} else {
result = cache.getObject(new Fqn(fqn, field.getName()));
}
if(result != null)
return result;
else {
Object value = invocation.getTargetObject();
if(value == null || field.get(value) == null) return null;
else {
if(log_.isDebugEnabled()) {
log_.debug("invoke(): Node on fqn: " +fqn + " has obviously been evicted. Will need to reconstruct it");
}
cache.putObject(fqn, value);
}
}
}
} else if (checkSerialization) {
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();
}
protected void checkCacheConsistency() throws Exception
{
if (this != cache.peek(fqn, AOPInstance.KEY)) {
new RuntimeException("Cache inconsistency: Outdated AOPInstance");
}
}
public void beforeSerialization(Object target) throws Exception
{
for (Iterator i = type.getFields().iterator(); i.hasNext();) {
Field field = (Field) i.next();
CachedType fieldType = cache.getCachedType(field.getType());
Object value = null;
if (fieldType.isImmediate()) {
value = cache.get(fqn, field.getName());
} else {
value = cache.getObject(new Fqn(fqn, field.getName()));
}
field.set(target, value);
}
}
boolean isChildOf(Fqn parentFqn)
{
return fqn.isChildOf(parentFqn);
}
Fqn getFqn()
{
return fqn;
}
void setFqn(Fqn fqn)
{
this.fqn = fqn;
}
}