/*
* JBoss, Home of Professional Open Source
* Copyright 2009 Red Hat Inc. and/or its affiliates and other
* contributors as indicated by the @author tags. All rights reserved.
* See the copyright.txt in the distribution for a full listing of
* individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.infinispan.tree;
import org.infinispan.AdvancedCache;
import org.infinispan.Cache;
import org.infinispan.CacheException;
import org.infinispan.atomic.AtomicMap;
import org.infinispan.config.ConfigurationException;
import org.infinispan.context.Flag;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import java.util.Map;
import java.util.Set;
/**
* @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.getConfiguration().isIndexingEnabled())
throw new ConfigurationException("TreeCache cannot be used with a Cache instance configured to use indexing!");
assertBatchingSupported(cache.getConfiguration());
createRoot();
}
@Override
public Node<K, V> getRoot() {
return new NodeImpl<K, V>(Fqn.ROOT, cache, batchContainer);
}
@Override
public Node<K, V> getRoot(Flag... flags) {
tcc.setFlags(flags);
try {
return getRoot();
} finally {
tcc.suspend();
}
}
@Override
public V put(String fqn, K key, V value) {
return put(Fqn.fromString(fqn), key, value);
}
@Override
public V put(String fqn, K key, V value, Flag... flags) {
tcc.setFlags(flags);
try {
return put(fqn, key, value);
} finally {
tcc.suspend();
}
}
@Override
public void put(Fqn fqn, Map<? extends K, ? extends V> data) {
startAtomic();
try {
Node<K, V> n = getNode(fqn);
if (n == null) createNodeInCache(fqn);
n = getNode(fqn);
n.putAll(data);
} finally {
endAtomic();
}
}
@Override
public void put(Fqn fqn, Map<? extends K, ? extends V> data, Flag... flags) {
tcc.setFlags(flags);
try {
put(fqn, data);
} finally {
tcc.suspend();
}
}
@Override
public void put(String fqn, Map<? extends K, ? extends V> data) {
put(Fqn.fromString(fqn), data);
}
@Override
public void put(String fqn, Map<? extends K, ? extends V> data, Flag... flags) {
tcc.setFlags(flags);
try {
put(fqn, data);
} finally {
tcc.suspend();
}
}
@Override
public V remove(Fqn fqn, K key) {
startAtomic();
try {
AtomicMap<K, V> map = getAtomicMap(new NodeKey(fqn, NodeKey.Type.DATA));
return map == null ? null : map.remove(key);
} finally {
endAtomic();
}
}
@Override
public V remove(Fqn fqn, K key, Flag... flags) {
tcc.setFlags(flags);
try {
return remove(fqn, key);
} finally {
tcc.suspend();
}
}
@Override
public V remove(String fqn, K key) {
return remove(Fqn.fromString(fqn), key);
}
@Override
public V remove(String fqn, K key, Flag... flags) {
tcc.setFlags(flags);
try {
return remove(fqn, key);
} finally {
tcc.suspend();
}
}
@Override
public boolean removeNode(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(fqn.getParent());
result = n != null && n.removeChild(fqn.getLastElement());
} finally {
endAtomic();
}
if (trace) log.trace("Node successfully removed");
return result;
}
@Override
public boolean removeNode(Fqn fqn, Flag... flags) {
tcc.setFlags(flags);
try {
return removeNode(fqn);
} finally {
tcc.suspend();
}
}
@Override
public boolean removeNode(String fqn) {
return removeNode(Fqn.fromString(fqn));
}
@Override
public boolean removeNode(String fqn, Flag... flags) {
tcc.setFlags(flags);
try {
return removeNode(fqn);
} finally {
tcc.suspend();
}
}
@Override
public Node<K, V> getNode(Fqn fqn) {
startAtomic();
try {
if (exists(fqn))
return new NodeImpl<K, V>(fqn, cache, batchContainer);
else return null;
} finally {
endAtomic();
}
}
@Override
public Node<K, V> getNode(Fqn fqn, Flag... flags) {
tcc.setFlags(flags);
try {
return getNode(fqn);
} finally {
tcc.suspend();
}
}
@Override
public Node<K, V> getNode(String fqn) {
return getNode(Fqn.fromString(fqn));
}
@Override
public Node<K, V> getNode(String fqn, Flag... flags) {
tcc.setFlags(flags);
try {
return getNode(fqn);
} finally {
tcc.suspend();
}
}
@Override
public V get(Fqn fqn, K key) {
Map<K, V> m = getAtomicMap(new NodeKey(fqn, NodeKey.Type.DATA));
if (m == null) return null;
return m.get(key);
}
@Override
public V get(Fqn fqn, K key, Flag... flags) {
tcc.setFlags(flags);
try {
return get(fqn, key);
} finally {
tcc.suspend();
}
}
@Override
public boolean exists(String f) {
return exists(Fqn.fromString(f));
}
@Override
public boolean exists(String fqn, Flag... flags) {
tcc.setFlags(flags);
try {
return exists(fqn);
} finally {
tcc.suspend();
}
}
@Override
public boolean exists(Fqn fqn, Flag... flags) {
tcc.setFlags(flags);
try {
return exists(fqn);
} finally {
tcc.suspend();
}
}
@Override
public V get(String fqn, K key) {
return get(Fqn.fromString(fqn), key);
}
@Override
public V get(String fqn, K key, Flag... flags) {
tcc.setFlags(flags);
try {
return get(fqn, key);
} finally {
tcc.suspend();
}
}
@Override
public void move(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();
try {
Node<K, V> nodeToMove = getNode(nodeToMoveFqn, Flag.FORCE_WRITE_LOCK);
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(newParentFqn)) {
// then we need to silently create the new parent
createNodeInCache(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(newFqn);
Node<K, V> newNode = getNode(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(oldChildFqn, newFqn);
}
removeNode(nodeToMoveFqn);
} finally {
endAtomic();
}
log.tracef("Successfully moved node '%s' to '%s'", nodeToMoveFqn, newParentFqn);
}
@Override
public void move(Fqn nodeToMove, Fqn newParent, Flag... flags) throws NodeNotExistsException {
tcc.setFlags(flags);
try {
move(nodeToMove, newParent);
} finally {
tcc.suspend();
}
}
@Override
public void move(String nodeToMove, String newParent) throws NodeNotExistsException {
move(Fqn.fromString(nodeToMove), Fqn.fromString(newParent));
}
@Override
public void move(String nodeToMove, String newParent, Flag... flags) throws NodeNotExistsException {
tcc.setFlags(flags);
try {
move(nodeToMove, newParent);
} finally {
tcc.suspend();
}
}
@Override
public Map<K, V> getData(Fqn fqn) {
startAtomic();
try {
Node<K, V> node = getNode(fqn);
if (node == null)
return null;
else
return node.getData();
} finally {
endAtomic();
}
}
@Override
public Map<K, V> getData(Fqn fqn, Flag... flags) {
tcc.setFlags(flags);
try {
return getData(fqn);
} finally {
tcc.suspend();
}
}
@Override
public Set<K> getKeys(String fqn) {
return getKeys(Fqn.fromString(fqn));
}
@Override
public Set<K> getKeys(String fqn, Flag... flags) {
tcc.setFlags(flags);
try {
return getKeys(fqn);
} finally {
tcc.suspend();
}
}
@Override
public Set<K> getKeys(Fqn fqn) {
startAtomic();
try {
Node<K, V> node = getNode(fqn);
if (node == null)
return null;
else
return node.getKeys();
} finally {
endAtomic();
}
}
@Override
public Set<K> getKeys(Fqn fqn, Flag... flags) {
tcc.setFlags(flags);
try {
return getKeys(fqn);
} finally {
tcc.suspend();
}
}
@Override
public void clearData(String fqn) {
clearData(Fqn.fromString(fqn));
}
@Override
public void clearData(String fqn, Flag... flags) {
tcc.setFlags(flags);
try {
clearData(fqn);
} finally {
tcc.suspend();
}
}
@Override
public void clearData(Fqn fqn) {
startAtomic();
try {
Node<K, V> node = getNode(fqn);
if (node != null)
node.clearData();
} finally {
endAtomic();
}
}
@Override
public void clearData(Fqn fqn, Flag... flags) {
tcc.setFlags(flags);
try {
clearData(fqn);
} finally {
tcc.suspend();
}
}
@Override
public V put(Fqn fqn, K key, V value) {
if (trace) log.tracef("Start: Putting value under key [%s] for node [%s]", key, fqn);
startAtomic();
try {
createNodeInCache(fqn);
Map<K, V> m = getAtomicMap(new NodeKey(fqn, NodeKey.Type.DATA));
return m.put(key, value);
} finally {
endAtomic();
if (trace) log.tracef("End: Putting value under key [%s] for node [%s]", key, fqn);
}
}
@Override
public V put(Fqn fqn, K key, V value, Flag... flags) {
tcc.setFlags(flags);
try {
return put(fqn, key, value);
} finally {
tcc.suspend();
}
}
@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();
}
}