package org.infinispan.tree.impl; import java.util.Map; import java.util.Set; import org.infinispan.AdvancedCache; import org.infinispan.Cache; import org.infinispan.atomic.AtomicMap; import org.infinispan.commons.CacheConfigurationException; import org.infinispan.commons.CacheException; import org.infinispan.context.Flag; import org.infinispan.tree.Fqn; import org.infinispan.tree.Node; import org.infinispan.tree.NodeNotExistsException; import org.infinispan.tree.TreeCache; import org.infinispan.util.logging.Log; import org.infinispan.util.logging.LogFactory; /** * @author Manik Surtani (<a href="mailto:manik AT jboss DOT org">manik AT jboss DOT org</a>) * @since 4.0 */ public class TreeCacheImpl<K, V> extends TreeStructureSupport implements TreeCache<K, V> { private static final Log log = LogFactory.getLog(TreeCacheImpl.class); private static final boolean trace = log.isTraceEnabled(); public TreeCacheImpl(Cache<?, ?> cache) { this(cache.getAdvancedCache()); } public TreeCacheImpl(AdvancedCache<?, ?> cache) { super(cache, cache.getBatchContainer()); if (cache.getCacheConfiguration().indexing().index().isEnabled()) throw new CacheConfigurationException("TreeCache cannot be used with a Cache instance configured to use indexing!"); assertBatchingSupported(cache.getCacheConfiguration()); createRoot(); } @Override public Node<K, V> getRoot() { return getRoot(cache); } @Override public Node<K, V> getRoot(Flag... flags) { return getRoot(cache.withFlags(flags)); } private Node<K, V> getRoot(AdvancedCache<NodeKey, AtomicMap<?, ?>> cache) { return new NodeImpl<K, V>(Fqn.ROOT, cache, batchContainer); } @Override public V put(String fqn, K key, V value) { return put(cache, Fqn.fromString(fqn), key, value); } @Override public V put(String fqn, K key, V value, Flag... flags) { return put(cache.withFlags(flags), Fqn.fromString(fqn), key, value); } @Override public void put(Fqn fqn, Map<? extends K, ? extends V> data) { put(cache, fqn, data); } @Override public void put(Fqn fqn, Map<? extends K, ? extends V> data, Flag... flags) { put(cache.withFlags(flags), fqn, data); } private void put(AdvancedCache<NodeKey, AtomicMap<?, ?>> cache, Fqn fqn, Map<? extends K, ? extends V> data) { startAtomic(); try { Node<K, V> n = getNode(cache, fqn); if (n == null) createNodeInCache(cache, fqn); n = getNode(cache, fqn); n.putAll(data); } finally { endAtomic(); } } @Override public void put(String fqn, Map<? extends K, ? extends V> data) { put(cache, Fqn.fromString(fqn), data); } @Override public void put(String fqn, Map<? extends K, ? extends V> data, Flag... flags) { put(cache.withFlags(flags), Fqn.fromString(fqn), data); } @Override public V remove(Fqn fqn, K key) { return remove(cache, fqn, key); } @Override public V remove(Fqn fqn, K key, Flag... flags) { return remove(cache.withFlags(flags), fqn, key); } private V remove(AdvancedCache<NodeKey, AtomicMap<?, ?>> cache, Fqn fqn, K key) { startAtomic(); try { AtomicMap<K, V> map = getAtomicMap(cache, new NodeKey(fqn, NodeKey.Type.DATA)); return map == null ? null : map.remove(key); } finally { endAtomic(); } } @Override public V remove(String fqn, K key) { return remove(cache, Fqn.fromString(fqn), key); } @Override public V remove(String fqn, K key, Flag... flags) { return remove(cache.withFlags(flags), Fqn.fromString(fqn), key); } @Override public boolean removeNode(Fqn fqn) { return removeNode(cache, fqn); } @Override public boolean removeNode(Fqn fqn, Flag... flags) { return removeNode(cache.withFlags(flags), fqn); } private boolean removeNode(AdvancedCache<NodeKey, AtomicMap<?, ?>> cache, Fqn fqn) { if (fqn.isRoot()) return false; startAtomic(); boolean result; try { if (trace) log.tracef("About to remove node %s", fqn); Node<K, V> n = getNode(cache, fqn.getParent()); result = n != null && n.removeChild(fqn.getLastElement()); } finally { endAtomic(); } if (trace) log.trace("Node successfully removed"); return result; } @Override public boolean removeNode(String fqn) { return removeNode(cache, Fqn.fromString(fqn)); } @Override public boolean removeNode(String fqn, Flag... flags) { return removeNode(cache.withFlags(flags), Fqn.fromString(fqn)); } @Override public Node<K, V> getNode(Fqn fqn) { return getNode(cache, fqn); } @Override public Node<K, V> getNode(Fqn fqn, Flag... flags) { return getNode(cache.withFlags(flags), fqn); } private Node<K, V> getNode(AdvancedCache<NodeKey, AtomicMap<?, ?>> cache, Fqn fqn) { startAtomic(); try { if (exists(cache, fqn)) return new NodeImpl<K, V>(fqn, cache, batchContainer); else return null; } finally { endAtomic(); } } @Override public Node<K, V> getNode(String fqn) { return getNode(cache, Fqn.fromString(fqn)); } @Override public Node<K, V> getNode(String fqn, Flag... flags) { return getNode(cache.withFlags(flags), Fqn.fromString(fqn)); } @Override public V get(Fqn fqn, K key) { return get(cache, fqn, key); } @Override public V get(Fqn fqn, K key, Flag... flags) { return get(cache.withFlags(flags), fqn, key); } private V get(AdvancedCache<NodeKey, AtomicMap<?, ?>> cache, Fqn fqn, K key) { Map<K, V> m = getAtomicMap(cache, new NodeKey(fqn, NodeKey.Type.DATA)); if (m == null) return null; return m.get(key); } @Override public boolean exists(String f) { return exists(cache, Fqn.fromString(f)); } @Override public boolean exists(String fqn, Flag... flags) { return exists(cache.withFlags(flags), Fqn.fromString(fqn)); } @Override public boolean exists(Fqn fqn, Flag... flags) { return exists(cache.withFlags(flags), fqn); } @Override public V get(String fqn, K key) { return get(cache, Fqn.fromString(fqn), key); } @Override public V get(String fqn, K key, Flag... flags) { return get(cache.withFlags(flags), Fqn.fromString(fqn), key); } @Override public void move(Fqn nodeToMoveFqn, Fqn newParentFqn) throws NodeNotExistsException { move(cache, nodeToMoveFqn, newParentFqn); } @Override public void move(Fqn nodeToMoveFqn, Fqn newParentFqn, Flag... flags) throws NodeNotExistsException { move(cache.withFlags(flags), nodeToMoveFqn, newParentFqn); } private void move(AdvancedCache<NodeKey, AtomicMap<?, ?>> cache, Fqn nodeToMoveFqn, Fqn newParentFqn) throws NodeNotExistsException { if (trace) log.tracef("Moving node '%s' to '%s'", nodeToMoveFqn, newParentFqn); if (nodeToMoveFqn == null || newParentFqn == null) throw new NullPointerException("Cannot accept null parameters!"); if (nodeToMoveFqn.getParent().equals(newParentFqn)) { if (trace) log.trace("Not doing anything as this node is equal with its parent"); // moving onto self! Do nothing! return; } // Depth first. Lets start with getting the node we want. startAtomic(); boolean success = false; try { // Use the FORCE_WRITE_LOCK for the first read operation on the source, the source's parent, // and the destination. AdvancedCache<NodeKey, AtomicMap<?, ?>> cacheForWrite = cache.withFlags(Flag.FORCE_WRITE_LOCK); // check that parent's structure map contains the node to be moved. in case of optimistic locking this // ensures the write skew is properly detected if some other thread removes the child Node<K, V> parent = getNode(cacheForWrite, nodeToMoveFqn.getParent()); if (!parent.hasChild(nodeToMoveFqn.getLastElement())) { if (trace) log.trace("The parent does not have the child that needs to be moved. Returning..."); return; } Node<K, V> nodeToMove = getNode(cacheForWrite, nodeToMoveFqn); if (nodeToMove == null) { if (trace) log.trace("Did not find the node that needs to be moved. Returning..."); return; // nothing to do here! } if (!exists(cacheForWrite, newParentFqn)) { // then we need to silently create the new parent createNodeInCache(cache, newParentFqn); if (trace) log.tracef("The new parent (%s) did not exists, was created", newParentFqn); } // create an empty node for this new parent Fqn newFqn = Fqn.fromRelativeElements(newParentFqn, nodeToMoveFqn.getLastElement()); createNodeInCache(cache, newFqn); Node<K, V> newNode = getNode(cache, newFqn); Map<K, V> oldData = nodeToMove.getData(); if (oldData != null && !oldData.isEmpty()) newNode.putAll(oldData); for (Object child : nodeToMove.getChildrenNames()) { // move kids if (trace) log.tracef("Moving child %s", child); Fqn oldChildFqn = Fqn.fromRelativeElements(nodeToMoveFqn, child); move(cache, oldChildFqn, newFqn); } removeNode(cache, nodeToMoveFqn); success = true; } finally { if (success) { endAtomic(); } else { failAtomic(); } } log.tracef("Successfully moved node '%s' to '%s'", nodeToMoveFqn, newParentFqn); } @Override public void move(String nodeToMove, String newParent) throws NodeNotExistsException { move(cache, Fqn.fromString(nodeToMove), Fqn.fromString(newParent)); } @Override public void move(String nodeToMove, String newParent, Flag... flags) throws NodeNotExistsException { move(cache.withFlags(flags), Fqn.fromString(nodeToMove), Fqn.fromString(newParent)); } @Override public Map<K, V> getData(Fqn fqn) { return getData(cache, fqn); } @Override public Map<K, V> getData(Fqn fqn, Flag... flags) { return getData(cache.withFlags(flags), fqn); } private Map<K, V> getData(AdvancedCache<NodeKey, AtomicMap<?, ?>> cache, Fqn fqn) { startAtomic(); try { Node<K, V> node = getNode(cache, fqn); if (node == null) return null; else return node.getData(); } finally { endAtomic(); } } @Override public Set<K> getKeys(String fqn) { return getKeys(cache, Fqn.fromString(fqn)); } @Override public Set<K> getKeys(String fqn, Flag... flags) { return getKeys(cache.withFlags(flags), Fqn.fromString(fqn)); } @Override public Set<K> getKeys(Fqn fqn) { return getKeys(cache, fqn); } @Override public Set<K> getKeys(Fqn fqn, Flag... flags) { return getKeys(cache.withFlags(flags), fqn); } private Set<K> getKeys(AdvancedCache<NodeKey, AtomicMap<?, ?>> cache, Fqn fqn) { startAtomic(); try { Node<K, V> node = getNode(cache, fqn); if (node == null) return null; else return node.getKeys(); } finally { endAtomic(); } } @Override public void clearData(String fqn) { clearData(cache, Fqn.fromString(fqn)); } @Override public void clearData(String fqn, Flag... flags) { clearData(cache.withFlags(flags), Fqn.fromString(fqn)); } @Override public void clearData(Fqn fqn) { clearData(cache, fqn); } @Override public void clearData(Fqn fqn, Flag... flags) { clearData(cache.withFlags(flags), fqn); } public void clearData(AdvancedCache<NodeKey, AtomicMap<?, ?>> cache, Fqn fqn) { startAtomic(); try { Node<K, V> node = getNode(cache, fqn); if (node != null) node.clearData(); } finally { endAtomic(); } } @Override public V put(Fqn fqn, K key, V value) { return put(cache, fqn, key, value); } @Override public V put(Fqn fqn, K key, V value, Flag... flags) { return put(cache.withFlags(flags), fqn, key, value); } private V put(AdvancedCache<NodeKey, AtomicMap<?, ?>> cache, Fqn fqn, K key, V value) { startAtomic(); try { createNodeInCache(cache, fqn); Map<K, V> m = getAtomicMap(cache, new NodeKey(fqn, NodeKey.Type.DATA)); return m.put(key, value); } finally { endAtomic(); } } @Override public Cache<?, ?> getCache() { // Retrieve the advanced cache as a way to retrieve // the cache behind the cache adapter. return cache.getAdvancedCache(); } // ------------------ nothing different; just delegate to the cache @Override public void start() throws CacheException { cache.start(); createRoot(); } @Override public void stop() { cache.stop(); } private void createRoot() { if (!exists(Fqn.ROOT)) createNodeInCache(Fqn.ROOT); } public String toString() { return cache.toString(); } }