package org.jboss.cache.invocation;
import org.jboss.cache.CacheException;
import org.jboss.cache.CacheSPI;
import org.jboss.cache.Fqn;
import org.jboss.cache.Node;
import org.jboss.cache.NodeNotValidException;
import org.jboss.cache.NodeSPI;
import org.jboss.cache.UnversionedNode;
import org.jboss.cache.config.Option;
import org.jboss.cache.lock.NodeLock;
import org.jboss.cache.optimistic.DataVersion;
import org.jboss.cache.transaction.GlobalTransaction;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* The delegate that users (and interceptor authors) interact with when they obtain a node from the cache or another node.
* This wrapper delegates calls down the interceptor chain.
*
* @author Manik Surtani (<a href="mailto:manik@jboss.org">manik@jboss.org</a>)
* @since 2.1.0
*/
@SuppressWarnings("unchecked")
public class NodeInvocationDelegate<K, V> extends AbstractInvocationDelegate implements NodeSPI<K, V>
{
private final UnversionedNode node;
private CacheSPI<K, V> spi;
public NodeInvocationDelegate(UnversionedNode node)
{
this.node = node;
}
public Object getDelegationTarget()
{
return node;
}
public void injectDependencies(CacheSPI spi)
{
this.spi = spi;
}
public boolean isChildrenLoaded()
{
return node.isChildrenLoaded();
}
public void setChildrenLoaded(boolean loaded)
{
node.setChildrenLoaded(loaded);
}
public boolean isDataLoaded()
{
return node.isDataLoaded();
}
public void setDataLoaded(boolean dataLoaded)
{
node.setDataLoaded(dataLoaded);
}
public Map<Object, Node<K, V>> getChildrenMapDirect()
{
return node.getChildrenMapDirect();
}
public void setChildrenMapDirect(Map<Object, Node<K, V>> children)
{
node.setChildrenMapDirect(children);
}
public NodeSPI<K, V> getOrCreateChild(Object name, GlobalTransaction tx)
{
return node.getOrCreateChild(name, tx, true);
}
public NodeLock getLock()
{
return node.getLock();
}
public void setFqn(Fqn<?> f)
{
node.setFqn(f);
}
public boolean isDeleted()
{
return node.isDeleted();
}
public void markAsDeleted(boolean marker)
{
node.markAsDeleted(marker);
}
public void markAsDeleted(boolean marker, boolean recursive)
{
node.markAsDeleted(marker, recursive);
}
public void addChild(Object nodeName, Node<K, V> nodeToAdd)
{
node.addChild(nodeName, nodeToAdd);
}
public void printDetails(StringBuilder sb, int indent)
{
node.printDetails(sb, indent);
}
public void print(StringBuilder sb, int indent)
{
throw new CacheException("This method is deprecated!");
}
public void setVersion(DataVersion version)
{
node.setVersion(version);
}
public DataVersion getVersion()
{
return node.getVersion();
}
public Set<NodeSPI<K, V>> getChildrenDirect()
{
return node.getChildrenDirect();
}
public void removeChildrenDirect()
{
node.removeChildrenDirect();
}
public Set<NodeSPI<K, V>> getChildrenDirect(boolean includeMarkedAsDeleted)
{
return node.getChildrenDirect(includeMarkedAsDeleted);
}
public NodeSPI<K, V> getChildDirect(Object childName)
{
return node.getChildDirect(childName);
}
public NodeSPI<K, V> addChildDirect(Fqn childName)
{
return node.addChildDirect(childName);
}
public NodeSPI<K, V> addChildDirect(Fqn f, boolean notify)
{
return node.addChildDirect(f, notify);
}
public NodeSPI<K, V> addChildDirect(Object childName, boolean notify)
{
return node.addChildDirect(childName, notify);
}
public void addChildDirect(NodeSPI<K, V> child)
{
node.addChildDirect(child);
}
public NodeSPI<K, V> getChildDirect(Fqn childName)
{
return node.getChildDirect(childName);
}
public boolean removeChildDirect(Fqn fqn)
{
return node.removeChildDirect(fqn);
}
public boolean removeChildDirect(Object childName)
{
return node.removeChildDirect(childName);
}
public V removeDirect(K key)
{
return (V) node.removeDirect(key);
}
public V putDirect(K key, V value)
{
return (V) node.putDirect(key, value);
}
public void putAllDirect(Map<K, V> data)
{
node.putAllDirect(data);
}
public Map<K, V> getDataDirect()
{
return node.getDataDirect();
}
public V getDirect(K key)
{
return (V) node.getDirect(key);
}
public void clearDataDirect()
{
node.clearDataDirect();
}
public Set<K> getKeysDirect()
{
return node.getKeysDirect();
}
public Set<Object> getChildrenNamesDirect()
{
return node.getChildrenNamesDirect();
}
public CacheSPI<K, V> getCache()
{
return spi;
}
public NodeSPI<K, V> getParent()
{
return node.getParent();
}
public Set<Node<K, V>> getChildren()
{
assertValid();
if (spi == null) return Collections.emptySet();
Set<Node<K, V>> children = new HashSet<Node<K, V>>();
for (Object c : spi.getChildrenNames(getFqn()))
{
Node n = spi.getNode(Fqn.fromRelativeElements(getFqn(), c));
if (n != null) children.add(n);
}
return Collections.unmodifiableSet(children);
}
public Set<Object> getChildrenNames()
{
assertValid();
return spi.getChildrenNames(getFqn());
}
public Map<K, V> getData()
{
assertValid();
if (spi == null) return Collections.emptyMap();
return spi.getData(getFqn());
}
public Set<K> getKeys()
{
assertValid();
Set keys = spi.getKeys(getFqn());
return keys == null ? Collections.emptySet() : Collections.unmodifiableSet(keys);
}
public Fqn getFqn()
{
return node.getFqn();
}
public Node<K, V> addChild(Fqn<?> f)
{
// TODO: Revisit. Is this really threadsafe? See comment in putIfAbsent() - same solution should be applied here too.
assertValid();
Fqn nf = Fqn.fromRelativeFqn(getFqn(), f);
Option o1;
try
{
o1 = spi.getInvocationContext().getOptionOverrides().clone();
}
catch (CloneNotSupportedException e)
{
// should never happen
throw new RuntimeException(e);
}
Node<K, V> child = getChild(f);
if (child == null)
{
Option o2;
try
{
o2 = o1.clone();
}
catch (CloneNotSupportedException e)
{
// should never happen
throw new RuntimeException(e);
}
spi.getInvocationContext().setOptionOverrides(o1);
spi.put(nf, null);
spi.getInvocationContext().setOptionOverrides(o2);
child = getChild(f);
}
return child;
}
public boolean removeChild(Fqn<?> f)
{
assertValid();
return spi.removeNode(Fqn.fromRelativeFqn(getFqn(), f));
}
public boolean removeChild(Object childName)
{
assertValid();
return spi.removeNode(Fqn.fromRelativeElements(getFqn(), childName));
}
public Node<K, V> getChild(Fqn<?> f)
{
assertValid();
return spi.getNode(Fqn.fromRelativeFqn(getFqn(), f));
}
public Node<K, V> getChild(Object name)
{
assertValid();
return spi.getNode(Fqn.fromRelativeElements(getFqn(), name));
}
public V put(K key, V value)
{
assertValid();
return spi.put(getFqn(), key, value);
}
public V putIfAbsent(K k, V v)
{
assertValid();
// TODO: Refactor this!! Synchronized block here sucks, this could lead to a deadlock since the locking interceptors will not use the same mutex.
// will only work once we make calls directly on the UnversionedNode in the CallInterceptor rather than multiple calls via the CacheImpl.
synchronized (this)
{
if (!getKeys().contains(k))
return put(k, v);
else
return get(k);
}
}
public V replace(K key, V value)
{
assertValid();
// TODO: Refactor this!! Synchronized block here sucks, this could lead to a deadlock since the locking interceptors will not use the same mutex.
// will only work once we make calls directly on the UnversionedNode in the CallInterceptor rather than multiple calls via the CacheImpl.
synchronized (this)
{
if (getKeys().contains(key))
{
return put(key, value);
}
else
return null;
}
}
public boolean replace(K key, V oldValue, V newValue)
{
assertValid();
// TODO: Refactor this!! Synchronized block here sucks, this could lead to a deadlock since the locking interceptors will not use the same mutex.
// will only work once we make calls directly on the UnversionedNode in the CallInterceptor rather than multiple calls via the CacheImpl.
synchronized (this)
{
if (oldValue.equals(get(key)))
{
put(key, newValue);
return true;
}
else
return false;
}
}
public void putAll(Map<K, V> data)
{
assertValid();
spi.put(getFqn(), data);
}
public void replaceAll(Map<K, V> data)
{
assertValid();
spi.clearData(getFqn());
spi.put(getFqn(), data);
}
public V get(K key)
{
assertValid();
return spi.get(getFqn(), key);
}
public V remove(K key)
{
assertValid();
return spi.remove(getFqn(), key);
}
public void clearData()
{
assertValid();
spi.clearData(getFqn());
}
public int dataSize()
{
assertValid();
return spi.getKeys(getFqn()).size();
}
public boolean hasChild(Fqn<?> f)
{
// TODO: This could be made more efficient when calls are made directly on the node
assertValid();
return getChild(f) != null;
}
public boolean hasChild(Object o)
{
// TODO: This could be made more efficient when calls are made directly on the node
assertValid();
return getChild(o) != null;
}
public boolean isValid()
{
return node.isValid();
}
public boolean isResident()
{
return node.isResident();
}
public void setResident(boolean resident)
{
node.setResident(resident);
}
public boolean isLockForChildInsertRemove()
{
return node.isLockForChildInsertRemove();
}
public void setLockForChildInsertRemove(boolean lockForChildInsertRemove)
{
node.setLockForChildInsertRemove(lockForChildInsertRemove);
}
public void releaseObjectReferences(boolean recursive)
{
node.releaseObjectReferences(recursive);
}
public boolean hasChildrenDirect()
{
return node.hasChildrenDirect();
}
public Map getInternalState(boolean onlyInternalState)
{
return node.getInternalState(onlyInternalState);
}
public void setInternalState(Map state)
{
node.setInternalState(state);
}
public void setValid(boolean valid, boolean recursive)
{
node.setValid(valid, recursive);
}
protected void assertValid()
{
if (!getFqn().isRoot() && !node.isValid())
throw new NodeNotValidException("Node " + getFqn() + " is not valid. Perhaps it has been moved or removed.");
}
@Override
public String toString()
{
return node == null ? "null" : node.toString();
}
}