package org.infinispan.tree.impl; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.infinispan.AdvancedCache; import org.infinispan.atomic.AtomicMap; import org.infinispan.atomic.impl.AtomicHashMapProxy; import org.infinispan.batch.BatchContainer; import org.infinispan.commons.util.Immutables; import org.infinispan.commons.util.Util; import org.infinispan.context.Flag; import org.infinispan.tree.Fqn; import org.infinispan.tree.Node; /** * Implementation backed by an {@link AtomicMap} * * @author Manik Surtani (<a href="mailto:manik AT jboss DOT org">manik AT jboss DOT org</a>) * @since 4.0 */ public class NodeImpl<K, V> extends TreeStructureSupport implements Node<K, V> { Fqn fqn; NodeKey dataKey, structureKey; public NodeImpl(Fqn fqn, AdvancedCache<?, ?> cache, BatchContainer batchContainer) { super(cache, batchContainer); this.fqn = fqn; dataKey = new NodeKey(fqn, NodeKey.Type.DATA); structureKey = new NodeKey(fqn, NodeKey.Type.STRUCTURE); } @Override public Node<K, V> getParent() { return getParent(cache); } @Override public Node<K, V> getParent(Flag... flags) { return getParent(cache.withFlags(flags)); } private Node<K, V> getParent(AdvancedCache<?, ?> cache) { if (fqn.isRoot()) return this; return new NodeImpl<K, V>(fqn.getParent(), cache, batchContainer); } @Override public Set<Node<K, V>> getChildren() { return getChildren(cache); } @Override public Set<Node<K, V>> getChildren(Flag... flags) { return getChildren(cache.withFlags(flags)); } private Set<Node<K, V>> getChildren(AdvancedCache<?, ?> cache) { startAtomic(); try { Set<Node<K, V>> result = new HashSet<Node<K, V>>(); for (Fqn f : getStructure().values()) { NodeImpl<K, V> n = new NodeImpl<K, V>(f, cache, batchContainer); result.add(n); } return Immutables.immutableSetWrap(result); } finally { endAtomic(); } } @Override public Set<Object> getChildrenNames() { return getChildrenNames(cache); } @Override public Set<Object> getChildrenNames(Flag... flags) { return getChildrenNames(cache.withFlags(flags)); } private Set<Object> getChildrenNames(AdvancedCache<?, ?> cache) { return Immutables.immutableSetCopy(getStructure(cache).keySet()); } @Override public Map<K, V> getData() { return getData(cache); } @Override public Map<K, V> getData(Flag... flags) { return getData(cache.withFlags(flags)); } private Map<K, V> getData(AdvancedCache<?, ?> cache) { return Collections.unmodifiableMap(new HashMap<K, V>(getDataInternal(cache))); } @Override public Set<K> getKeys() { return getKeys(cache); } @Override public Set<K> getKeys(Flag... flags) { return getKeys(cache.withFlags(flags)); } private Set<K> getKeys(AdvancedCache<?, ?> cache) { startAtomic(); try { return getData(cache).keySet(); } finally { endAtomic(); } } @Override public Fqn getFqn() { return fqn; } @Override public Node<K, V> addChild(Fqn f) { return addChild(cache, f); } @Override public Node<K, V> addChild(Fqn f, Flag... flags) { return addChild(cache.withFlags(flags), f); } private Node<K, V> addChild(AdvancedCache<?, ?> cache, Fqn f) { startAtomic(); try { Fqn absoluteChildFqn = Fqn.fromRelativeFqn(fqn, f); //1) first register it with the parent AtomicMap<Object, Fqn> structureMap = getStructure(cache); structureMap.put(f.getLastElement(), absoluteChildFqn); //2) then create the structure and data maps createNodeInCache(cache, absoluteChildFqn); return new NodeImpl<K, V>(absoluteChildFqn, cache, batchContainer); } finally { endAtomic(); } } @Override public boolean removeChild(Fqn f) { return removeChild(cache, f); } @Override public boolean removeChild(Fqn f, Flag... flags) { return removeChild(cache.withFlags(flags), f); } public boolean removeChild(AdvancedCache<?, ?> cache, Fqn f) { return removeChild(cache, f.getLastElement()); } @Override public boolean removeChild(Object childName) { return removeChild(cache, childName); } @Override public boolean removeChild(Object childName, Flag... flags) { return removeChild(cache.withFlags(flags), childName); } private boolean removeChild(AdvancedCache cache, Object childName) { startAtomic(); try { AtomicMap<Object, Fqn> s = getStructure(cache); Fqn childFqn = s.remove(childName); if (childFqn != null) { Node<K, V> child = new NodeImpl<K, V>(childFqn, cache, batchContainer); child.removeChildren(); child.clearData(); // this is necessary in case we have a remove and then an add on the same node, in the same tx. cache.remove(new NodeKey(childFqn, NodeKey.Type.DATA)); cache.remove(new NodeKey(childFqn, NodeKey.Type.STRUCTURE)); return true; } return false; } finally { endAtomic(); } } @Override public Node<K, V> getChild(Fqn f) { return getChild(cache, f); } @Override public Node<K, V> getChild(Fqn f, Flag... flags) { return getChild(cache.withFlags(flags), f); } private Node<K, V> getChild(AdvancedCache cache, Fqn f) { startAtomic(); try { if (hasChild(f)) return new NodeImpl<K, V>(Fqn.fromRelativeFqn(fqn, f), cache, batchContainer); else return null; } finally { endAtomic(); } } @Override public Node<K, V> getChild(Object name) { return getChild(cache, name); } @Override public Node<K, V> getChild(Object name, Flag... flags) { return getChild(cache.withFlags(flags), name); } private Node<K, V> getChild(AdvancedCache cache, Object name) { startAtomic(); try { if (hasChild(name)) return new NodeImpl<K, V>(Fqn.fromRelativeElements(fqn, name), cache, batchContainer); else return null; } finally { endAtomic(); } } @Override public V put(K key, V value) { return put(cache, key, value); } @Override public V put(K key, V value, Flag... flags) { return put(cache.withFlags(flags), key, value); } private V put(AdvancedCache cache, K key, V value) { startAtomic(); try { AtomicHashMapProxy<K, V> map = (AtomicHashMapProxy<K, V>) getDataInternal(cache); return map.put(key, value); } finally { endAtomic(); } } @Override public V putIfAbsent(K key, V value) { return putIfAbsent(cache, key, value); } @Override public V putIfAbsent(K key, V value, Flag... flags) { return putIfAbsent(cache.withFlags(flags), key, value); } private V putIfAbsent(AdvancedCache<?, ?> cache, K key, V value) { startAtomic(); try { AtomicMap<K, V> data = getDataInternal(cache); if (!data.containsKey(key)) return data.put(key, value); return data.get(key); } finally { endAtomic(); } } @Override public V replace(K key, V value) { return replace(cache, key, value); } @Override public V replace(K key, V value, Flag... flags) { return replace(cache.withFlags(flags), key, value); } private V replace(AdvancedCache<?, ?> cache, K key, V value) { startAtomic(); try { AtomicMap<K, V> map = getAtomicMap(cache, dataKey); if (map.containsKey(key)) return map.put(key, value); else return null; } finally { endAtomic(); } } @Override public boolean replace(K key, V oldValue, V newValue) { return replace(cache, key, oldValue, newValue); } @Override public boolean replace(K key, V oldValue, V newValue, Flag... flags) { return replace(cache.withFlags(flags), key, oldValue, newValue); } private boolean replace(AdvancedCache<?, ?> cache, K key, V oldValue, V newValue) { startAtomic(); try { AtomicMap<K, V> data = getDataInternal(cache); V old = data.get(key); if (Util.safeEquals(oldValue, old)) { data.put(key, newValue); return true; } return false; } finally { endAtomic(); } } @Override public void putAll(Map<? extends K, ? extends V> map) { putAll(cache, map); } @Override public void putAll(Map<? extends K, ? extends V> map, Flag... flags) { putAll(cache.withFlags(flags), map); } private void putAll(AdvancedCache cache, Map<? extends K, ? extends V> map) { startAtomic(); try { getDataInternal(cache).putAll(map); } finally { endAtomic(); } } @Override public void replaceAll(Map<? extends K, ? extends V> map) { replaceAll(cache, map); } @Override public void replaceAll(Map<? extends K, ? extends V> map, Flag... flags) { replaceAll(cache.withFlags(flags), map); } private void replaceAll(AdvancedCache cache, Map<? extends K, ? extends V> map) { startAtomic(); try { AtomicMap<K, V> data = getDataInternal(cache); data.clear(); data.putAll(map); } finally { endAtomic(); } } @Override public V get(K key) { return get(cache, key); } @Override public V get(K key, Flag... flags) { return get(cache.withFlags(flags), key); } private V get(AdvancedCache cache, K key) { return getData(cache).get(key); } @Override public V remove(K key) { return remove(cache, key); } @Override public V remove(K key, Flag... flags) { return remove(cache.withFlags(flags), key); } private V remove(AdvancedCache cache, K key) { startAtomic(); try { return getDataInternal(cache).remove(key); } finally { endAtomic(); } } @Override public void clearData() { clearData(cache); } @Override public void clearData(Flag... flags) { clearData(cache.withFlags(flags)); } private void clearData(AdvancedCache<?, ?> cache) { getDataInternal(cache).clear(); } @Override public int dataSize() { return dataSize(cache); } @Override public int dataSize(Flag... flags) { return dataSize(cache.withFlags(flags)); } private int dataSize(AdvancedCache<?, ?> cache) { return getData(cache).size(); } @Override public boolean hasChild(Fqn f) { return hasChild(cache, f); } @Override public boolean hasChild(Fqn f, Flag... flags) { return hasChild(cache.withFlags(flags), f); } private boolean hasChild(AdvancedCache<?, ?> cache, Fqn f) { if (f.size() > 1) { // indirect child. Fqn absoluteFqn = Fqn.fromRelativeFqn(fqn, f); return exists(cache, absoluteFqn); } else { return hasChild(f.getLastElement()); } } @Override public boolean hasChild(Object o) { return hasChild(cache, o); } @Override public boolean hasChild(Object o, Flag... flags) { return hasChild(cache.withFlags(flags), o); } private boolean hasChild(AdvancedCache<?, ?> cache, Object o) { return getStructure(cache).containsKey(o); } @Override public boolean isValid() { return cache.containsKey(dataKey); } @Override public void removeChildren() { removeChildren(cache); } @Override public void removeChildren(Flag... flags) { removeChildren(cache.withFlags(flags)); } private void removeChildren(AdvancedCache<?, ?> cache) { startAtomic(); try { Map<Object, Fqn> s = getStructure(cache); for (Object o : Immutables.immutableSetCopy(s.keySet())) removeChild(cache, o); } finally { endAtomic(); } } AtomicMap<K, V> getDataInternal(AdvancedCache<?, ?> cache) { return getAtomicMap(cache, dataKey); } AtomicMap<Object, Fqn> getStructure() { return getAtomicMap(structureKey); } private AtomicMap<Object, Fqn> getStructure(AdvancedCache<?, ?> cache) { return getAtomicMap(cache, structureKey); } public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; NodeImpl<?, ?> node = (NodeImpl<?, ?>) o; if (fqn != null ? !fqn.equals(node.fqn) : node.fqn != null) return false; return true; } public int hashCode() { return (fqn != null ? fqn.hashCode() : 0); } @Override public String toString() { return "NodeImpl{" + "fqn=" + fqn + '}'; } }