/*
* JBoss, Home of Professional Open Source
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jboss.cache.loader;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.cache.Fqn;
import org.jboss.cache.Modification;
import org.jboss.cache.TreeCache;
import org.jboss.cache.CacheException;
import org.jboss.cache.buddyreplication.BuddyManager;
import org.jboss.cache.config.CacheLoaderConfig.IndividualCacheLoaderConfig;
import java.util.*;
import EDU.oswego.cs.dl.util.concurrent.ConcurrentHashMap;
/**
* Dummy cache loader that stores data in memory
*
* @author <a href="mailto:manik@jboss.org">Manik Surtani (manik@jboss.org)</a>
* @author <a href="mailto:galder.zamarreno@jboss.com">Galder Zamarreno</a>
*/
public class DummyInMemoryCacheLoader implements CacheLoader
{
protected Map nodes = new ConcurrentHashMap();
protected Log log = LogFactory.getLog(DummyInMemoryCacheLoader.class);
protected Map transactions = new ConcurrentHashMap();
protected TreeCache cache;
public void setConfig(IndividualCacheLoaderConfig config)
{
}
public IndividualCacheLoaderConfig getConfig()
{
return null;
}
public void setConfig(Properties properties)
{
}
public void setCache(TreeCache c)
{
cache = c;
}
public Set getChildrenNames(Fqn fqn) throws Exception
{
if (log.isDebugEnabled()) log.debug("Calling getChildrenNames on Fqn " + fqn + ". Data map = " + nodes);
if (!nodes.containsKey(fqn))
{
log.debug("node not in loader");
return null;
}
Set children = findChildren(fqn);
log.debug("Fqn " + fqn + " has children " + children);
// to keep in line with the CacheLoader interface contract for this method.
return children.size() == 0 ? null : children;
}
private Set findChildren(Fqn p)
{
Set c = new HashSet();
Fqn f;
for(Iterator it = nodes.keySet().iterator(); it.hasNext();)
{
f = (Fqn)it.next();
if (!f.isRoot() && f.getParent().equals(p))
{
c.add(f.getLast());
}
}
return c;
}
public Map get(Fqn name) throws Exception
{
DummyNode dn = (DummyNode)nodes.get(name);
Map d = dn != null ? dn.data : null;
if (log.isDebugEnabled())
{
try {
log.debug("Getting data for fqn " + name + " = " + d );
} catch (Exception e) {} //concurrent modification may appear here
}
return d;
}
public boolean exists(Fqn name) throws Exception
{
return nodes.containsKey(name);
}
public Object put(Fqn name, Object key, Object value) throws Exception
{
DummyNode n = (DummyNode)nodes.get(name);
if (n == null)
{
n = new DummyNode(name);
}
Object old = n.data.put(key, value);
nodes.put(name, n);
// we need to make sure parents get put in as well.
recursivelyPutParentsIfNeeded(name);
if (log.isDebugEnabled()) log.debug("Did a put on " + name + ", data is " + n.data);
return old;
}
public void put(Fqn name, Map attributes) throws Exception
{
DummyNode n = (DummyNode)nodes.get(name);
if (n == null)
{
n = new DummyNode(name);
}
if (attributes != null) n.data.putAll(attributes);
nodes.put(name, n);
// we need to make sure parents get put in as well.
recursivelyPutParentsIfNeeded(name);
if (log.isDebugEnabled()) log.debug("Did a put on " + name + ", data is " + n.data);
}
public void put(List modifications) throws Exception
{
Modification m;
for(Iterator it = modifications.iterator(); it.hasNext();)
{
m = (Modification)it.next();
switch (m.getType())
{
case Modification.PUT_DATA:
put(m.getFqn(), m.getData());
break;
case Modification.PUT_DATA_ERASE:
removeData(m.getFqn());
put(m.getFqn(), m.getData());
break;
case Modification.PUT_KEY_VALUE:
put(m.getFqn(), m.getKey(), m.getValue());
break;
case Modification.REMOVE_DATA:
removeData(m.getFqn());
break;
case Modification.REMOVE_KEY_VALUE:
remove(m.getFqn(), m.getKey());
break;
case Modification.REMOVE_NODE:
remove(m.getFqn());
break;
default:
throw new CacheException("Unknown modificatiobn " + m.getType());
}
}
}
private void recursivelyPutParentsIfNeeded(Fqn node)
{
Fqn parent = node.getParent();
if (nodes.containsKey(parent)) return; // nothing to do.
// else put the parent in.
nodes.put(parent, new DummyNode(parent));
recursivelyPutParentsIfNeeded(parent);
}
public Object remove(Fqn fqn, Object key) throws Exception
{
log.debug("Removing data from " + fqn);
DummyNode n = (DummyNode)nodes.get(fqn);
if (n == null) n = new DummyNode(fqn);
Object old = n.data.remove(key);
nodes.put(fqn, n);
return old;
}
public void remove(Fqn fqn) throws Exception
{
log.debug("Removing fqn " + fqn);
nodes.remove(fqn);
// remove children.
recursivelyRemoveChildren(fqn);
}
private void recursivelyRemoveChildren(Fqn removedParent)
{
Fqn f;
for(Iterator it = nodes.keySet().iterator(); it.hasNext();)
{
f = (Fqn)it.next();
if (f.getParent().equals(removedParent))
{
// remove the child node too
nodes.remove(f);
// and it's children. Depth first.
recursivelyRemoveChildren(f);
}
}
}
public void removeData(Fqn fqn) throws Exception
{
log.debug("Removing data from " + fqn);
DummyNode n = (DummyNode)nodes.get(fqn);
if (n == null) n = new DummyNode(fqn);
n.data.clear();
nodes.put(fqn, n);
}
public void prepare(Object tx, List modifications, boolean one_phase) throws Exception
{
if (one_phase)
{
put(modifications);
}
else
{
transactions.put(tx, modifications);
}
}
public void commit(Object tx) throws Exception
{
List modifications = (List)transactions.remove(tx);
if (modifications == null)
{
throw new Exception("transaction " + tx + " not found in transaction table");
}
put(modifications);
}
public void rollback(Object tx)
{
transactions.remove(tx);
}
public byte[] loadEntireState() throws Exception
{
return loadState(Fqn.ROOT);
}
public byte[] loadState(Fqn subtree) throws Exception
{
List list = new LinkedList();
getNodeDataList(subtree, list);
if (log.isTraceEnabled()) log.trace("Loading state of " + list.size() + " nodes into stream");
// cache.getMarshaller().objectToObjectStream(list, out, fqn);
return cache.getMarshaller().objectToByteBuffer(list);
}
protected void getNodeDataList(Fqn fqn, List list) throws Exception
{
Map attrs;
Set children_names;
String child_name;
Fqn tmp_fqn;
NodeData nd;
// first handle the current node
attrs = get(fqn);
if (attrs == null || attrs.size() == 0)
{
nd = new NodeData(fqn);
}
else
{
nd = new NodeData(fqn, attrs);
}
//out.writeObject(nd);
list.add(nd);
// then visit the children
children_names = getChildrenNames(fqn);
if (children_names == null)
{
return;
}
for(Iterator it = children_names.iterator(); it.hasNext();)
{
child_name = (String) it.next();
tmp_fqn = new Fqn(fqn, child_name);
//loadStateHelper(tmp_fqn, out);
getNodeDataList(tmp_fqn, list);
}
}
public void storeEntireState(byte[] state) throws Exception
{
storeState(Fqn.ROOT, state);
}
public void storeState(Fqn subtree, byte[] state) throws Exception
{
// remove entire existing state
this.remove(subtree);
boolean moveToBuddy = subtree.isChildOf(BuddyManager.BUDDY_BACKUP_SUBTREE_FQN) && subtree.size() > 1;
// store new state
Fqn fqn = null;
//NodeData nd = null;
Object objectFromStream = cache.getMarshaller().objectFromByteBuffer(state);
List nodeData = (List) objectFromStream;
for(Iterator it = nodeData.iterator(); it.hasNext();)
{
NodeData nd = (NodeData) it.next();
if (moveToBuddy)
{
fqn = BuddyManager.getBackupFqn(subtree, nd.getFqn());
}
else
{
fqn = nd.getFqn();
}
if (nd.getAttributes() != null)
{
removeData(fqn);
this.put(fqn, nd.getAttributes());// creates a node with 0 or more attributes
}
else
{
this.put(fqn, null);// creates a node with null attributes
}
}
}
public void create() throws Exception
{
}
public void start() throws Exception
{
}
public void stop()
{
}
public void destroy()
{
}
public class DummyNode
{
Map data = new HashMap();
Fqn fqn;
public DummyNode(Fqn fqn)
{
this.fqn = fqn;
}
public String toString()
{
return "Node{" +
"data=" + data +
", fqn=" + fqn +
'}';
}
}
public String toString()
{
return "DummyInMemoryCacheLoader{" +
"nodes=" + nodes +
'}';
}
}