package org.jboss.cache.interceptors;
import EDU.oswego.cs.dl.util.concurrent.ReentrantLock;
import org.jboss.cache.*;
import org.jgroups.blocks.MethodCall;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class CreateIfNotExistsInterceptor extends Interceptor {
private final ReentrantLock put_lock=new ReentrantLock();
private final ReentrantLock remove_lock=new ReentrantLock();
static final List putMethods=new ArrayList(3);
private final ArrayList put_list=new ArrayList();
private final ArrayList remove_list=new ArrayList();
static {
putMethods.add(TreeCache.putDataEraseMethodLocal);
putMethods.add(TreeCache.putDataMethodLocal);
putMethods.add(TreeCache.putKeyValMethodLocal);
putMethods.add(TreeCache.putFailFastKeyValueMethodLocal);
}
public void setCache(TreeCache cache) {
super.setCache(cache);
}
public Object invoke(MethodCall m) throws Throwable {
Method meth=m.getMethod();
Fqn fqn;
boolean isPut=putMethods.contains(meth),
isRemove=TreeCache.removeNodeMethodLocal.equals(meth),
isEvict=TreeCache.evictNodeMethodLocal.equals(meth);
if(isPut || isRemove || isEvict) { Object[] args=m.getArgs();
fqn=(Fqn)(args != null? (isEvict? args[0] : args[1]) : null);
if(fqn == null)
throw new CacheException("failed extracting FQN from method " + m);
if(isPut) { try {
addFqnToPutList(fqn, put_lock);
findAndBlockOnRemove(fqn, remove_lock);
if(!cache.exists(fqn)) {
GlobalTransaction gtx=cache.getCurrentTransaction();
if(log.isTraceEnabled())
log.trace("creating node " + fqn);
createNode(fqn, gtx);
}
return super.invoke(m);
}
finally {
removeFqnFromPutList(fqn, put_lock);
}
}
else { try {
findAndBlockOnPut(fqn, put_lock); addFqnToRemoveList(fqn, remove_lock);
put_lock.release();
return super.invoke(m);
}
finally {
if(put_lock.holds() > 0)
put_lock.release();
removeFqnFromRemoveList(fqn, remove_lock);
}
}
}
return super.invoke(m);
}
private void findAndBlockOnPut(Fqn fqn, ReentrantLock lock) throws InterruptedException {
Fqn tmp;
while(true) {
lock.acquire();
tmp=findFqnInPutList(fqn);
if(tmp == null) return;
if(log.isTraceEnabled())
log.trace("found " + tmp + " in put-list, waiting");
synchronized(tmp) {
lock.release();
tmp.wait();
}
if(log.isTraceEnabled())
log.trace("wait() for put-list on " + tmp + " got notified");
}
}
private void findAndBlockOnRemove(Fqn fqn, ReentrantLock lock) throws InterruptedException {
Fqn tmp;
while(true) {
try {
lock.acquire();
tmp=findFqnInRemoveList(fqn);
if(tmp == null) return;
if(log.isTraceEnabled())
log.trace("found " + tmp + " in remove-list, waiting");
synchronized(tmp) {
lock.release();
tmp.wait();
}
if(log.isTraceEnabled())
log.trace("wait() for remove-list on " + tmp + " got notified");
}
finally {
if(lock.holds() > 0)
lock.release();
}
}
}
private Fqn findFqnInPutList(Fqn fqn) {
Fqn tmp;
for(Iterator it=put_list.iterator(); it.hasNext();) {
tmp=(Fqn)it.next();
if(tmp.isChildOf(fqn) || tmp.equals(fqn)) return tmp;
}
return null;
}
private Fqn findFqnInRemoveList(Fqn fqn) {
Fqn tmp;
for(Iterator it=remove_list.iterator(); it.hasNext();) {
tmp=(Fqn)it.next();
if(fqn.isChildOf(tmp) || fqn.equals(tmp)) return tmp;
}
return null;
}
private void addFqnToPutList(Fqn fqn, ReentrantLock lock) {
try {
lock.acquire();
if(!put_list.contains(fqn)) {
put_list.add(fqn);
if(log.isTraceEnabled())
log.trace("adding " + fqn + " to put-list (size=" + put_list.size() + ")");
}
}
catch(InterruptedException e) {
}
finally {
if(lock.holds() > 0)
lock.release();
}
}
private void addFqnToRemoveList(Fqn fqn, ReentrantLock lock) {
try {
lock.acquire();
if(!remove_list.contains(fqn)) {
remove_list.add(fqn);
if(log.isTraceEnabled())
log.trace("adding " + fqn + " to remove-list (size=" + remove_list.size() + ")");
}
}
catch(InterruptedException e) {
}
finally {
if(lock.holds() > 0)
lock.release();
}
}
private void removeFqnFromPutList(Fqn fqn, ReentrantLock lock) {
try {
if(log.isTraceEnabled())
log.trace("removing " + fqn + " from put-list (size=" + put_list.size() + ")");
lock.acquire();
put_list.remove(fqn);
lock.release();
synchronized(fqn) {
fqn.notifyAll();
}
}
catch(InterruptedException e) {
}
finally {
if(lock.holds() > 0)
lock.release();
}
}
private void removeFqnFromRemoveList(Fqn fqn, ReentrantLock lock) {
try {
if(log.isTraceEnabled())
log.trace("removing " + fqn + " from remove-list (size=" + remove_list.size() + ")");
lock.acquire();
remove_list.remove(fqn);
lock.release();
synchronized(fqn) {
fqn.notifyAll();
}
}
catch(InterruptedException e) {
}
finally {
if(lock.holds() > 0)
lock.release();
}
}
private void createNode(Fqn fqn, GlobalTransaction tx) {
Node n, child_node;
Object child_name;
Fqn tmp_fqn=new Fqn(), copy;
if(fqn == null) return;
synchronized(this) {
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) {
copy=(Fqn)tmp_fqn.clone();
child_node=n.createChild(child_name, copy, n);
if(tx != null) {
cache.addNode(tx, (Fqn)tmp_fqn.clone());
}
cache.notifyNodeCreated(copy);
}
n=child_node;
}
}
}
}