diff --git a/src/helma/framework/core/Application.java b/src/helma/framework/core/Application.java
index d4171bc4..77a66d2a 100644
--- a/src/helma/framework/core/Application.java
+++ b/src/helma/framework/core/Application.java
@@ -274,7 +274,9 @@ public final class Application implements IPathElement, Runnable {
* Get the application ready to run, initializing the evaluators and type manager.
*/
public synchronized void init()
- throws DatabaseException, ScriptingException, MalformedURLException {
+ throws DatabaseException, MalformedURLException,
+ IllegalAccessException, InstantiationException,
+ ClassNotFoundException {
// create and init type mananger
typemgr = new TypeManager(this);
diff --git a/src/helma/objectmodel/ObjectCache.java b/src/helma/objectmodel/ObjectCache.java
new file mode 100644
index 00000000..d19a2205
--- /dev/null
+++ b/src/helma/objectmodel/ObjectCache.java
@@ -0,0 +1,108 @@
+/*
+ * Helma License Notice
+ *
+ * The contents of this file are subject to the Helma License
+ * Version 2.0 (the "License"). You may not use this file except in
+ * compliance with the License. A copy of the License is available at
+ * http://adele.helma.org/download/helma/license.txt
+ *
+ * Copyright 1998-2003 Helma Software. All Rights Reserved.
+ *
+ * $RCSfile$
+ * $Author$
+ * $Revision$
+ * $Date$
+ */
+
+package helma.objectmodel;
+
+import helma.framework.core.Application;
+
+import java.util.Properties;
+
+/**
+ * Interface Helma object cache classes need to implement.
+ *
+ */
+public interface ObjectCache {
+
+ /**
+ * Set the {@link helma.framework.core.Application Application} instance
+ * for the cache.
+ * @param app the app instance
+ */
+ void init(Application app);
+
+ /**
+ * Called when the application's properties have been updated to let
+ * the cache implementation update its settings.
+ * @param props
+ */
+ void updateProperties(Properties props);
+
+ /**
+ * Returns true if the collection contains an element for the key.
+ *
+ * @param key the key that we are looking for
+ */
+ boolean containsKey(Object key);
+
+ /**
+ * Returns the number of keys in object array keys
that
+ * were not found in the Map.
+ * Those keys that are contained in the Map are nulled out in the array.
+ * @param keys an array of key objects we are looking for
+ * @see ObjectCache#containsKey
+ */
+ int containsKeys(Object[] keys);
+
+ /**
+ * Gets the object associated with the specified key in the
+ * hashtable.
+ * @param key the specified key
+ * @return the element for the key or null if the key
+ * is not defined in the hash table.
+ * @see ObjectCache#put
+ */
+ Object get(Object key);
+
+ /**
+ * Puts the specified element into the hashtable, using the specified
+ * key. The element may be retrieved by doing a get() with the same key.
+ * The key and the element cannot be null.
+ * @param key the specified key in the hashtable
+ * @param value the specified element
+ * @exception NullPointerException If the value of the element
+ * is equal to null.
+ * @see ObjectCache#get
+ * @return the old value of the key, or null if it did not have one.
+ */
+ Object put(Object key, Object value);
+
+ /**
+ * Removes the element corresponding to the key. Does nothing if the
+ * key is not present.
+ * @param key the key that needs to be removed
+ * @return the value of key, or null if the key was not found.
+ */
+ Object remove(Object key);
+
+ /**
+ * Removes all items currently stored in the cache.
+ *
+ * @return true if the operation succeeded
+ */
+ boolean clear();
+
+ /**
+ * Return the number of objects currently stored in the cache.
+ * @return the number of cached items
+ */
+ int size();
+
+ /**
+ * Return an array with all objects currently contained in the cache.
+ */
+ Object[] getCachedObjects();
+
+}
diff --git a/src/helma/objectmodel/db/Node.java b/src/helma/objectmodel/db/Node.java
index 46e4a9a7..d4306b46 100644
--- a/src/helma/objectmodel/db/Node.java
+++ b/src/helma/objectmodel/db/Node.java
@@ -542,6 +542,15 @@ public final class Node implements INode, Serializable {
return dbmap;
}
+ /**
+ *
+ *
+ * @param nmgr
+ */
+ public void setWrappedNodeManager(WrappedNodeManager nmgr) {
+ this.nmgr = nmgr;
+ }
+
/**
*
*
diff --git a/src/helma/objectmodel/db/NodeManager.java b/src/helma/objectmodel/db/NodeManager.java
index 2006e85b..3c4ee872 100644
--- a/src/helma/objectmodel/db/NodeManager.java
+++ b/src/helma/objectmodel/db/NodeManager.java
@@ -35,7 +35,7 @@ import java.util.*;
public final class NodeManager {
protected Application app;
- private CacheMap cache;
+ private ObjectCache cache;
private Replicator replicator;
protected IDatabase db;
protected IDGenerator idgen;
@@ -51,22 +51,17 @@ public final class NodeManager {
* created in dbHome if one doesn't already exist.
*/
public NodeManager(Application app, String dbHome, Properties props)
- throws DatabaseException {
+ throws DatabaseException, ClassNotFoundException,
+ IllegalAccessException, InstantiationException {
this.app = app;
- int cacheSize = Integer.parseInt(props.getProperty("cachesize", "1000"));
+ String cacheImpl = props.getProperty("cacheimpl", "helma.util.CacheMap");
- // Make actual cache size bigger, since we use it only up to the threshold
- // cache = new CacheMap ((int) Math.ceil (cacheSize/0.75f), 0.75f);
- cache = new CacheMap(cacheSize, 0.75f);
- cache.setApplication(app);
- if (cacheSize != 1000) {
- app.logEvent("Setting cache size for "+app.getName()+" to " + cacheSize);
- }
+ cache = (ObjectCache) Class.forName(cacheImpl).newInstance();
+ cache.init(app);
safe = new WrappedNodeManager(this);
- // nullNode = new Node ();
logSql = "true".equalsIgnoreCase(props.getProperty("logsql"));
logReplication = "true".equalsIgnoreCase(props.getProperty("logReplication"));
@@ -102,9 +97,8 @@ public final class NodeManager {
* app.properties file has been updated. Reread some settings.
*/
public void updateProperties(Properties props) {
- int cacheSize = Integer.parseInt(props.getProperty("cachesize", "1000"));
-
- cache.setCapacity(cacheSize);
+ // notify the cache about the properties update
+ cache.updateProperties(props);
logSql = "true".equalsIgnoreCase(props.getProperty("logsql"));
logReplication = "true".equalsIgnoreCase(props.getProperty("logReplication"));
}
@@ -1788,7 +1782,7 @@ public final class NodeManager {
* Get an array of the the keys currently held in the object cache
*/
public Object[] getCacheEntries() {
- return cache.getEntryArray();
+ return cache.getCachedObjects();
}
/**
diff --git a/src/helma/objectmodel/db/Transactor.java b/src/helma/objectmodel/db/Transactor.java
index f0789944..2d67f14f 100644
--- a/src/helma/objectmodel/db/Transactor.java
+++ b/src/helma/objectmodel/db/Transactor.java
@@ -241,11 +241,13 @@ public class Transactor extends Thread {
int nstate = node.getState();
if (nstate == Node.NEW) {
- nmgr.registerNode(node); // register node with nodemanager cache
nmgr.insertNode(nmgr.db, txn, node);
dirtyDbMappings.add(node.getDbMapping());
node.setState(Node.CLEAN);
+ // register node with nodemanager cache
+ nmgr.registerNode(node);
+
if (replicator != null) {
replicator.addNewNode(node);
}
@@ -260,6 +262,9 @@ public class Transactor extends Thread {
}
node.setState(Node.CLEAN);
+ // update node with nodemanager cache
+ nmgr.registerNode(node);
+
if (replicator != null) {
replicator.addModifiedNode(node);
}
@@ -270,6 +275,8 @@ public class Transactor extends Thread {
} else if (nstate == Node.DELETED) {
nmgr.deleteNode(nmgr.db, txn, node);
dirtyDbMappings.add(node.getDbMapping());
+
+ // remove node from nodemanager cache
nmgr.evictNode(node);
if (replicator != null) {
diff --git a/src/helma/util/CacheMap.java b/src/helma/util/CacheMap.java
index 8513ebe4..322f1abf 100644
--- a/src/helma/util/CacheMap.java
+++ b/src/helma/util/CacheMap.java
@@ -30,7 +30,10 @@
package helma.util;
import java.util.HashMap;
+import java.util.Properties;
+
import helma.framework.core.Application;
+import helma.objectmodel.ObjectCache;
/// A Hashtable that expires least-recently-used objects.
@@ -44,7 +47,7 @@ import helma.framework.core.Application;
//
// @see java.util.Hashtable
-public class CacheMap {
+public class CacheMap implements ObjectCache {
// Load factor.
private float loadFactor;
@@ -62,6 +65,14 @@ public class CacheMap {
// the application to output messages to
private Application app = null;
+ /**
+ * Zero argument constructor. Creates a CacheMap with capacity of 1000
+ * and load factor 0.75
+ */
+ public CacheMap() {
+ this(1000, 0.75f);
+ }
+
/// Constructs a new, empty hashtable with the specified initial
// capacity and the specified load factor.
// Unlike a plain Hashtable, an LruHashtable will never grow or
@@ -73,25 +84,25 @@ public class CacheMap {
// is less than or equal to zero.
// @exception IllegalArgumentException If the load factor is
// less than or equal to zero.
- public CacheMap (int initialCapacity, float loadFactor) {
- // We have to call a superclass constructor, but we're not actually
- // going to use it at all. The only reason we want to extend Hashtable
- // is for type conformance. So, make a parent hash table of minimum
- // size and then ignore it.
- if ( initialCapacity <= 0 || loadFactor <= 0.0 )
- throw new IllegalArgumentException();
- this.loadFactor = loadFactor;
- // table rotation threshold: we allow each table to gain
- // initialCapacity/2 entries.
- threshold = initialCapacity / 2;
- // We deliberately choose the initial capacity of tables large
- // enough that it can hold threshold entries without being rehashed,
- // in other words, make sure our threshold for table rotation is lower
- // than that of the underlying HashMap for table rehashing.
- eachCapacity = (int) (threshold / loadFactor) + 2;
- // create tables
- oldTable = new HashMap ();
- newTable = new HashMap (eachCapacity, loadFactor);
+ public CacheMap(int initialCapacity, float loadFactor) {
+ // We have to call a superclass constructor, but we're not actually
+ // going to use it at all. The only reason we want to extend Hashtable
+ // is for type conformance. So, make a parent hash table of minimum
+ // size and then ignore it.
+ if (initialCapacity <= 0 || loadFactor <= 0.0)
+ throw new IllegalArgumentException();
+ this.loadFactor = loadFactor;
+ // table rotation threshold: we allow each table to gain
+ // initialCapacity/2 entries.
+ threshold = initialCapacity / 2;
+ // We deliberately choose the initial capacity of tables large
+ // enough that it can hold threshold entries without being rehashed,
+ // in other words, make sure our threshold for table rotation is lower
+ // than that of the underlying HashMap for table rehashing.
+ eachCapacity = (int) (threshold / loadFactor) + 2;
+ // create tables
+ oldTable = new HashMap();
+ newTable = new HashMap(eachCapacity, loadFactor);
}
/// Constructs a new, empty hashtable with the specified initial
@@ -99,41 +110,41 @@ public class CacheMap {
// Unlike a plain Hashtable, an LruHashtable will never grow or
// shrink from this initial capacity.
// @param initialCapacity the initial number of buckets
- public CacheMap (int initialCapacity) {
- this (initialCapacity, 0.75F);
+ public CacheMap(int initialCapacity) {
+ this(initialCapacity, 0.75F);
}
/// Returns the number of elements contained in the hashtable.
public int size() {
- return newTable.size() + oldTable.size();
+ return newTable.size() + oldTable.size();
}
/// Returns true if the hashtable contains no elements.
public boolean isEmpty() {
- return size() == 0;
+ return size() == 0;
}
/// Set the capacity of the CacheMap
public void setCapacity(int capacity) {
- // table rotation threshold: we allow each table to gain
- // initialCapacity/2 entries.
- int newThreshold = capacity / 2;
- if (newThreshold != threshold) {
- if (app != null)
- app.logEvent ("Setting cache capacity to "+capacity);
- updateThreshold (newThreshold);
- }
+ // table rotation threshold: we allow each table to gain
+ // initialCapacity/2 entries.
+ int newThreshold = capacity / 2;
+ if (newThreshold != threshold) {
+ if (app != null)
+ app.logEvent("Setting cache capacity to " + capacity);
+ updateThreshold(newThreshold);
+ }
}
- private synchronized void updateThreshold (int newThreshold) {
- threshold = newThreshold;
- eachCapacity = (int) (threshold / loadFactor) + 2;
- // if newtable is larger than threshold, rotate.
- if (newTable.size() > threshold) {
- oldTable = newTable;
- newTable = new HashMap (eachCapacity, loadFactor);
- }
+ private synchronized void updateThreshold(int newThreshold) {
+ threshold = newThreshold;
+ eachCapacity = (int) (threshold / loadFactor) + 2;
+ // if newtable is larger than threshold, rotate.
+ if (newTable.size() > threshold) {
+ oldTable = newTable;
+ newTable = new HashMap(eachCapacity, loadFactor);
+ }
}
/// Returns true if the specified object is an element of the hashtable.
@@ -142,35 +153,35 @@ public class CacheMap {
// @exception NullPointerException If the value being searched
// for is equal to null.
// @see LruHashtable#containsKey
- public synchronized boolean containsValue (Object value) {
- if (newTable.containsValue (value))
- return true;
- if (oldTable.containsValue (value)) {
- // We would like to move the object from the old table to the
- // new table. However, we need keys to re-add the objects, and
- // there's no good way to find all the keys for the given object.
- // We'd have to enumerate through all the keys and check each
- // one. Yuck. For now we just punt. Anyway, contains() is
- // probably not a commonly-used operation.
- return true;
- }
- return false;
+ public synchronized boolean containsValue(Object value) {
+ if (newTable.containsValue(value))
+ return true;
+ if (oldTable.containsValue(value)) {
+ // We would like to move the object from the old table to the
+ // new table. However, we need keys to re-add the objects, and
+ // there's no good way to find all the keys for the given object.
+ // We'd have to enumerate through all the keys and check each
+ // one. Yuck. For now we just punt. Anyway, contains() is
+ // probably not a commonly-used operation.
+ return true;
+ }
+ return false;
}
/// Returns true if the collection contains an element for the key.
// @param key the key that we are looking for
// @see LruHashtable#contains
- public synchronized boolean containsKey (Object key) {
- if (newTable.containsKey(key))
- return true;
- if (oldTable.containsKey (key)) {
- // Move object from old table to new table.
- Object value = oldTable.get (key);
- newTable.put (key, value);
- oldTable.remove (key);
- return true;
- }
- return false;
+ public synchronized boolean containsKey(Object key) {
+ if (newTable.containsKey(key))
+ return true;
+ if (oldTable.containsKey(key)) {
+ // Move object from old table to new table.
+ Object value = oldTable.get(key);
+ newTable.put(key, value);
+ oldTable.remove(key);
+ return true;
+ }
+ return false;
}
/// Returns the number of keys in object array keys
that
@@ -178,21 +189,21 @@ public class CacheMap {
// Those keys that are contained in the Map are nulled out in the array.
// @param keys an array of key objects we are looking for
// @see LruHashtable#contains
- public synchronized int containsKeys (Object[] keys) {
- int notfound = 0;
- for (int i=0; i