JBoss Cache SVN: r7665 - in core/branches/flat/src: test/java/org/horizon/util and 1 other directory.
by jbosscache-commits@lists.jboss.org
Author: manik.surtani(a)jboss.com
Date: 2009-02-08 06:42:01 -0500 (Sun, 08 Feb 2009)
New Revision: 7665
Modified:
core/branches/flat/src/main/java/org/horizon/container/UnsortedDataContainer.java
core/branches/flat/src/test/java/org/horizon/util/BidirectionalLinkedHashMapTest.java
Log:
Fixed broken tests
Modified: core/branches/flat/src/main/java/org/horizon/container/UnsortedDataContainer.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/container/UnsortedDataContainer.java 2009-02-08 11:24:01 UTC (rev 7664)
+++ core/branches/flat/src/main/java/org/horizon/container/UnsortedDataContainer.java 2009-02-08 11:42:01 UTC (rev 7665)
@@ -193,9 +193,9 @@
Map.Entry<K, ExpirableCachedValue<V>> entry = iter.next();
ExpirableCachedValue<?> cv = entry.getValue();
if (cv.expired()) {
- iter.remove();
expireOnCacheLoader(entry.getKey());
purged.add(entry.getKey());
+ iter.remove();
}
}
return purged;
Modified: core/branches/flat/src/test/java/org/horizon/util/BidirectionalLinkedHashMapTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/util/BidirectionalLinkedHashMapTest.java 2009-02-08 11:24:01 UTC (rev 7664)
+++ core/branches/flat/src/test/java/org/horizon/util/BidirectionalLinkedHashMapTest.java 2009-02-08 11:42:01 UTC (rev 7665)
@@ -69,13 +69,19 @@
int index = 0;
while (it.hasNext()) assert it.next() == index++;
- assert index == 999 : "Was expecting 999, was " + index;
+ assert index == 1000 : "Was expecting 1000, was " + index;
// now check the reverse iterator.
+ System.out.println("Keys: " + map.keySet());
it = map.keySet().reverseIterator();
- while (it.hasNext()) assert it.next() == index--;
+ index = 999;
+ while (it.hasNext()) {
+ int expected = index--;
+ int actual = it.next();
+ assert actual == expected : "Was expecting " + expected + " but was " + actual;
+ }
- assert index == 0 : "Was expecting 0, was " + index;
+ assert index == -1 : "Was expecting -1, was " + index;
}
15 years, 10 months
JBoss Cache SVN: r7664 - core/branches/flat/src/test/java/org/horizon/config/parsing.
by jbosscache-commits@lists.jboss.org
Author: manik.surtani(a)jboss.com
Date: 2009-02-08 06:24:01 -0500 (Sun, 08 Feb 2009)
New Revision: 7664
Added:
core/branches/flat/src/test/java/org/horizon/config/parsing/XmlFileParsingTest.java
Log:
Restored test
Added: core/branches/flat/src/test/java/org/horizon/config/parsing/XmlFileParsingTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/config/parsing/XmlFileParsingTest.java (rev 0)
+++ core/branches/flat/src/test/java/org/horizon/config/parsing/XmlFileParsingTest.java 2009-02-08 11:24:01 UTC (rev 7664)
@@ -0,0 +1,164 @@
+package org.horizon.config.parsing;
+
+import org.horizon.config.Configuration;
+import org.horizon.config.GlobalConfiguration;
+import org.horizon.lock.IsolationLevel;
+import org.testng.annotations.Test;
+
+import java.io.IOException;
+import java.util.Map;
+
+@Test(groups = "unit")
+public class XmlFileParsingTest {
+ public void testNamedCacheFile() throws IOException {
+ XmlConfigurationParser parser = new XmlConfigurationParserImpl("configs/named-cache-test.xml");
+
+ GlobalConfiguration gc = parser.parseGlobalConfiguration();
+
+ assert gc.getAsyncListenerExecutorFactoryClass().equals("org.horizon.executors.DefaultExecutorFactory");
+ assert gc.getAsyncListenerExecutorProperties().getProperty("maxThreads").equals("5");
+ assert gc.getAsyncListenerExecutorProperties().getProperty("threadNamePrefix").equals("AsyncListenerThread");
+
+ assert gc.getAsyncSerializationExecutorFactoryClass().equals("org.horizon.executors.DefaultExecutorFactory");
+ assert gc.getAsyncSerializationExecutorProperties().getProperty("maxThreads").equals("25");
+ assert gc.getAsyncSerializationExecutorProperties().getProperty("threadNamePrefix").equals("AsyncSerializationThread");
+
+ assert gc.getEvictionScheduledExecutorFactoryClass().equals("org.horizon.executors.DefaultScheduledExecutorFactory");
+ assert gc.getEvictionScheduledExecutorProperties().getProperty("threadNamePrefix").equals("EvictionThread");
+
+ assert gc.getReplicationQueueScheduledExecutorFactoryClass().equals("org.horizon.executors.DefaultScheduledExecutorFactory");
+ assert gc.getReplicationQueueScheduledExecutorProperties().getProperty("threadNamePrefix").equals("ReplicationQueueThread");
+
+ assert gc.getTransportClass().equals("org.horizon.remoting.transport.jgroups.JGroupsTransport");
+ assert gc.getTransportProperties().isEmpty();
+
+ assert gc.getMarshallerClass().equals("org.horizon.marshall.VersionAwareMarshaller");
+ assert gc.getMarshallVersionString().equals("1.0");
+ assert gc.getObjectOutputStreamPoolSize() == 100;
+ assert gc.getObjectInputStreamPoolSize() == 100;
+
+ Configuration defaultConfiguration = parser.parseDefaultConfiguration();
+
+ assert defaultConfiguration.getLockAcquisitionTimeout() == 1000;
+ assert defaultConfiguration.getConcurrencyLevel() == 100;
+ assert defaultConfiguration.getIsolationLevel() == IsolationLevel.READ_COMMITTED;
+
+ Map<String, Configuration> namedCaches = parser.parseNamedConfigurations();
+
+ Configuration c = namedCaches.get("transactional");
+
+ assert c.getTransactionManagerLookupClass().equals("org.horizon.transaction.GenericTransactionManagerLookup");
+
+ c = namedCaches.get("syncRepl");
+
+ assert c.getCacheMode() == Configuration.CacheMode.REPL_SYNC;
+ assert c.isFetchInMemoryState();
+ assert c.getStateRetrievalTimeout() == 15000;
+ assert c.getSyncReplTimeout() == 15000;
+
+ c = namedCaches.get("asyncRepl");
+
+ assert c.getCacheMode() == Configuration.CacheMode.REPL_ASYNC;
+ assert !c.isUseReplQueue();
+ assert !c.isUseAsyncSerialization();
+ assert c.isFetchInMemoryState();
+ assert c.getStateRetrievalTimeout() == 15000;
+
+ c = namedCaches.get("asyncReplQueue");
+
+ assert c.getCacheMode() == Configuration.CacheMode.REPL_ASYNC;
+ assert c.isUseReplQueue();
+ assert c.isUseAsyncSerialization();
+ assert c.isFetchInMemoryState();
+ assert c.getStateRetrievalTimeout() == 15000;
+
+ c = namedCaches.get("txSyncRepl");
+
+ assert c.getTransactionManagerLookupClass().equals("org.horizon.transaction.GenericTransactionManagerLookup");
+ assert c.getCacheMode() == Configuration.CacheMode.REPL_SYNC;
+ assert c.isFetchInMemoryState();
+ assert c.getStateRetrievalTimeout() == 15000;
+ assert c.getSyncReplTimeout() == 15000;
+
+ c = namedCaches.get("overriding");
+
+ assert c.getTransactionManagerLookupClass() == null;
+ assert c.getCacheMode() == Configuration.CacheMode.LOCAL;
+ assert c.getLockAcquisitionTimeout() == 20000;
+ assert c.getConcurrencyLevel() == 1000;
+ assert c.getIsolationLevel() == IsolationLevel.REPEATABLE_READ;
+ }
+
+ public void testConfigurationMerging() throws IOException {
+ XmlConfigurationParser parser = new XmlConfigurationParserImpl("configs/named-cache-test.xml");
+ Configuration defaultCfg = parser.parseDefaultConfiguration();
+ Map<String, Configuration> namedCaches = parser.parseNamedConfigurations();
+
+ Configuration c = defaultCfg.clone();
+ c.applyOverrides(namedCaches.get("transactional"));
+
+ assert c.getCacheMode() == Configuration.CacheMode.LOCAL;
+ assert c.getTransactionManagerLookupClass().equals("org.horizon.transaction.GenericTransactionManagerLookup");
+ assert c.getLockAcquisitionTimeout() == 1000;
+ assert c.getConcurrencyLevel() == 100;
+ assert c.getIsolationLevel() == IsolationLevel.READ_COMMITTED;
+
+ c = defaultCfg.clone();
+ c.applyOverrides(namedCaches.get("syncRepl"));
+
+ assert c.getTransactionManagerLookupClass() == null;
+ assert c.getCacheMode() == Configuration.CacheMode.REPL_SYNC;
+ assert c.isFetchInMemoryState();
+ assert c.getStateRetrievalTimeout() == 15000;
+ assert c.getSyncReplTimeout() == 15000;
+ assert c.getLockAcquisitionTimeout() == 1000;
+ assert c.getIsolationLevel() == IsolationLevel.READ_COMMITTED;
+ assert c.getConcurrencyLevel() == 100;
+
+ c = defaultCfg.clone();
+ c.applyOverrides(namedCaches.get("asyncRepl"));
+
+ assert c.getTransactionManagerLookupClass() == null;
+ assert c.getCacheMode() == Configuration.CacheMode.REPL_ASYNC;
+ assert !c.isUseReplQueue();
+ assert !c.isUseAsyncSerialization();
+ assert c.isFetchInMemoryState();
+ assert c.getStateRetrievalTimeout() == 15000;
+ assert c.getLockAcquisitionTimeout() == 1000;
+ assert c.getIsolationLevel() == IsolationLevel.READ_COMMITTED;
+ assert c.getConcurrencyLevel() == 100;
+
+ c = defaultCfg.clone();
+ c.applyOverrides(namedCaches.get("asyncReplQueue"));
+
+ assert c.getTransactionManagerLookupClass() == null;
+ assert c.getCacheMode() == Configuration.CacheMode.REPL_ASYNC;
+ assert c.isUseReplQueue();
+ assert c.isUseAsyncSerialization();
+ assert c.isFetchInMemoryState();
+ assert c.getStateRetrievalTimeout() == 15000;
+ assert c.getLockAcquisitionTimeout() == 1000;
+ assert c.getIsolationLevel() == IsolationLevel.READ_COMMITTED;
+ assert c.getConcurrencyLevel() == 100;
+
+ c = defaultCfg.clone();
+ c.applyOverrides(namedCaches.get("txSyncRepl"));
+ assert c.getTransactionManagerLookupClass().equals("org.horizon.transaction.GenericTransactionManagerLookup");
+ assert c.getCacheMode() == Configuration.CacheMode.REPL_SYNC;
+ assert c.isFetchInMemoryState();
+ assert c.getStateRetrievalTimeout() == 15000;
+ assert c.getSyncReplTimeout() == 15000;
+ assert c.getLockAcquisitionTimeout() == 1000;
+ assert c.getIsolationLevel() == IsolationLevel.READ_COMMITTED;
+ assert c.getConcurrencyLevel() == 100;
+
+ c = defaultCfg.clone();
+ c.applyOverrides(namedCaches.get("overriding"));
+
+ assert c.getTransactionManagerLookupClass() == null;
+ assert c.getCacheMode() == Configuration.CacheMode.LOCAL;
+ assert c.getLockAcquisitionTimeout() == 20000;
+ assert c.getConcurrencyLevel() == 1000;
+ assert c.getIsolationLevel() == IsolationLevel.REPEATABLE_READ;
+ }
+}
15 years, 10 months
JBoss Cache SVN: r7663 - core/branches/flat/src/test/java/org/horizon/config/parsing.
by jbosscache-commits@lists.jboss.org
Author: manik.surtani(a)jboss.com
Date: 2009-02-08 06:21:51 -0500 (Sun, 08 Feb 2009)
New Revision: 7663
Removed:
core/branches/flat/src/test/java/org/horizon/config/parsing/XmlFileParsingTest.java
Log:
Expiry stuff
Deleted: core/branches/flat/src/test/java/org/horizon/config/parsing/XmlFileParsingTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/config/parsing/XmlFileParsingTest.java 2009-02-08 11:20:49 UTC (rev 7662)
+++ core/branches/flat/src/test/java/org/horizon/config/parsing/XmlFileParsingTest.java 2009-02-08 11:21:51 UTC (rev 7663)
@@ -1,164 +0,0 @@
-package org.horizon.config.parsing;
-
-import org.horizon.config.Configuration;
-import org.horizon.config.GlobalConfiguration;
-import org.horizon.lock.IsolationLevel;
-import org.testng.annotations.Test;
-
-import java.io.IOException;
-import java.util.Map;
-
-@Test(groups = "unit")
-public class XmlFileParsingTest {
- public void testNamedCacheFile() throws IOException {
- XmlConfigurationParser parser = new XmlConfigurationParserImpl("configs/named-cache-test.xml");
-
- GlobalConfiguration gc = parser.parseGlobalConfiguration();
-
- assert gc.getAsyncListenerExecutorFactoryClass().equals("org.horizon.executors.DefaultExecutorFactory");
- assert gc.getAsyncListenerExecutorProperties().getProperty("maxThreads").equals("5");
- assert gc.getAsyncListenerExecutorProperties().getProperty("threadNamePrefix").equals("AsyncListenerThread");
-
- assert gc.getAsyncSerializationExecutorFactoryClass().equals("org.horizon.executors.DefaultExecutorFactory");
- assert gc.getAsyncSerializationExecutorProperties().getProperty("maxThreads").equals("25");
- assert gc.getAsyncSerializationExecutorProperties().getProperty("threadNamePrefix").equals("AsyncSerializationThread");
-
- assert gc.getEvictionScheduledExecutorFactoryClass().equals("org.horizon.executors.DefaultScheduledExecutorFactory");
- assert gc.getEvictionScheduledExecutorProperties().getProperty("threadNamePrefix").equals("EvictionThread");
-
- assert gc.getReplicationQueueScheduledExecutorFactoryClass().equals("org.horizon.executors.DefaultScheduledExecutorFactory");
- assert gc.getReplicationQueueScheduledExecutorProperties().getProperty("threadNamePrefix").equals("ReplicationQueueThread");
-
- assert gc.getTransportClass().equals("org.horizon.remoting.transport.jgroups.JGroupsTransport");
- assert gc.getTransportProperties().isEmpty();
-
- assert gc.getMarshallerClass().equals("org.horizon.marshall.VersionAwareMarshaller");
- assert gc.getMarshallVersionString().equals("1.0");
- assert gc.getObjectOutputStreamPoolSize() == 100;
- assert gc.getObjectInputStreamPoolSize() == 100;
-
- Configuration defaultConfiguration = parser.parseDefaultConfiguration();
-
- assert defaultConfiguration.getLockAcquisitionTimeout() == 1000;
- assert defaultConfiguration.getConcurrencyLevel() == 100;
- assert defaultConfiguration.getIsolationLevel() == IsolationLevel.READ_COMMITTED;
-
- Map<String, Configuration> namedCaches = parser.parseNamedConfigurations();
-
- Configuration c = namedCaches.get("transactional");
-
- assert c.getTransactionManagerLookupClass().equals("org.horizon.transaction.GenericTransactionManagerLookup");
-
- c = namedCaches.get("syncRepl");
-
- assert c.getCacheMode() == Configuration.CacheMode.REPL_SYNC;
- assert c.isFetchInMemoryState();
- assert c.getStateRetrievalTimeout() == 15000;
- assert c.getSyncReplTimeout() == 15000;
-
- c = namedCaches.get("asyncRepl");
-
- assert c.getCacheMode() == Configuration.CacheMode.REPL_ASYNC;
- assert !c.isUseReplQueue();
- assert !c.isUseAsyncSerialization();
- assert c.isFetchInMemoryState();
- assert c.getStateRetrievalTimeout() == 15000;
-
- c = namedCaches.get("asyncReplQueue");
-
- assert c.getCacheMode() == Configuration.CacheMode.REPL_ASYNC;
- assert c.isUseReplQueue();
- assert c.isUseAsyncSerialization();
- assert c.isFetchInMemoryState();
- assert c.getStateRetrievalTimeout() == 15000;
-
- c = namedCaches.get("txSyncRepl");
-
- assert c.getTransactionManagerLookupClass().equals("org.horizon.transaction.GenericTransactionManagerLookup");
- assert c.getCacheMode() == Configuration.CacheMode.REPL_SYNC;
- assert c.isFetchInMemoryState();
- assert c.getStateRetrievalTimeout() == 15000;
- assert c.getSyncReplTimeout() == 15000;
-
- c = namedCaches.get("overriding");
-
- assert c.getTransactionManagerLookupClass() == null;
- assert c.getCacheMode() == Configuration.CacheMode.LOCAL;
- assert c.getLockAcquisitionTimeout() == 20000;
- assert c.getConcurrencyLevel() == 1000;
- assert c.getIsolationLevel() == IsolationLevel.REPEATABLE_READ;
- }
-
- public void testConfigurationMerging() throws IOException {
- XmlConfigurationParser parser = new XmlConfigurationParserImpl("configs/named-cache-test.xml");
- Configuration defaultCfg = parser.parseDefaultConfiguration();
- Map<String, Configuration> namedCaches = parser.parseNamedConfigurations();
-
- Configuration c = defaultCfg.clone();
- c.applyOverrides(namedCaches.get("transactional"));
-
- assert c.getCacheMode() == Configuration.CacheMode.LOCAL;
- assert c.getTransactionManagerLookupClass().equals("org.horizon.transaction.GenericTransactionManagerLookup");
- assert c.getLockAcquisitionTimeout() == 1000;
- assert c.getConcurrencyLevel() == 100;
- assert c.getIsolationLevel() == IsolationLevel.READ_COMMITTED;
-
- c = defaultCfg.clone();
- c.applyOverrides(namedCaches.get("syncRepl"));
-
- assert c.getTransactionManagerLookupClass() == null;
- assert c.getCacheMode() == Configuration.CacheMode.REPL_SYNC;
- assert c.isFetchInMemoryState();
- assert c.getStateRetrievalTimeout() == 15000;
- assert c.getSyncReplTimeout() == 15000;
- assert c.getLockAcquisitionTimeout() == 1000;
- assert c.getIsolationLevel() == IsolationLevel.READ_COMMITTED;
- assert c.getConcurrencyLevel() == 100;
-
- c = defaultCfg.clone();
- c.applyOverrides(namedCaches.get("asyncRepl"));
-
- assert c.getTransactionManagerLookupClass() == null;
- assert c.getCacheMode() == Configuration.CacheMode.REPL_ASYNC;
- assert !c.isUseReplQueue();
- assert !c.isUseAsyncSerialization();
- assert c.isFetchInMemoryState();
- assert c.getStateRetrievalTimeout() == 15000;
- assert c.getLockAcquisitionTimeout() == 1000;
- assert c.getIsolationLevel() == IsolationLevel.READ_COMMITTED;
- assert c.getConcurrencyLevel() == 100;
-
- c = defaultCfg.clone();
- c.applyOverrides(namedCaches.get("asyncReplQueue"));
-
- assert c.getTransactionManagerLookupClass() == null;
- assert c.getCacheMode() == Configuration.CacheMode.REPL_ASYNC;
- assert c.isUseReplQueue();
- assert c.isUseAsyncSerialization();
- assert c.isFetchInMemoryState();
- assert c.getStateRetrievalTimeout() == 15000;
- assert c.getLockAcquisitionTimeout() == 1000;
- assert c.getIsolationLevel() == IsolationLevel.READ_COMMITTED;
- assert c.getConcurrencyLevel() == 100;
-
- c = defaultCfg.clone();
- c.applyOverrides(namedCaches.get("txSyncRepl"));
- assert c.getTransactionManagerLookupClass().equals("org.horizon.transaction.GenericTransactionManagerLookup");
- assert c.getCacheMode() == Configuration.CacheMode.REPL_SYNC;
- assert c.isFetchInMemoryState();
- assert c.getStateRetrievalTimeout() == 15000;
- assert c.getSyncReplTimeout() == 15000;
- assert c.getLockAcquisitionTimeout() == 1000;
- assert c.getIsolationLevel() == IsolationLevel.READ_COMMITTED;
- assert c.getConcurrencyLevel() == 100;
-
- c = defaultCfg.clone();
- c.applyOverrides(namedCaches.get("overriding"));
-
- assert c.getTransactionManagerLookupClass() == null;
- assert c.getCacheMode() == Configuration.CacheMode.LOCAL;
- assert c.getLockAcquisitionTimeout() == 20000;
- assert c.getConcurrencyLevel() == 1000;
- assert c.getIsolationLevel() == IsolationLevel.REPEATABLE_READ;
- }
-}
15 years, 10 months
JBoss Cache SVN: r7662 - in core/branches/flat/src: main/java/org/horizon/commands and 12 other directories.
by jbosscache-commits@lists.jboss.org
Author: manik.surtani(a)jboss.com
Date: 2009-02-08 06:20:49 -0500 (Sun, 08 Feb 2009)
New Revision: 7662
Added:
core/branches/flat/src/main/java/org/horizon/eviction/events/EvictionEvent.java
core/branches/flat/src/main/java/org/horizon/eviction/events/PurgedDataEndEvent.java
core/branches/flat/src/test/java/org/horizon/container/
core/branches/flat/src/test/java/org/horizon/container/DataContainerTest.java
core/branches/flat/src/test/java/org/horizon/expiry/
core/branches/flat/src/test/java/org/horizon/expiry/ExpiryTest.java
Removed:
core/branches/flat/src/main/java/org/horizon/eviction/events/EvictionEvent.java
core/branches/flat/src/test/java/org/horizon/BasicTest.java
core/branches/flat/src/test/java/org/horizon/SkipListTest.java
Modified:
core/branches/flat/src/main/java/org/horizon/Cache.java
core/branches/flat/src/main/java/org/horizon/CacheDelegate.java
core/branches/flat/src/main/java/org/horizon/commands/CommandsFactory.java
core/branches/flat/src/main/java/org/horizon/commands/CommandsFactoryImpl.java
core/branches/flat/src/main/java/org/horizon/commands/write/PutKeyValueCommand.java
core/branches/flat/src/main/java/org/horizon/commands/write/PutMapCommand.java
core/branches/flat/src/main/java/org/horizon/commands/write/ReplaceCommand.java
core/branches/flat/src/main/java/org/horizon/container/DataContainer.java
core/branches/flat/src/main/java/org/horizon/container/MVCCEntry.java
core/branches/flat/src/main/java/org/horizon/container/ReadCommittedEntry.java
core/branches/flat/src/main/java/org/horizon/container/RepeatableReadEntry.java
core/branches/flat/src/main/java/org/horizon/container/UnsortedDataContainer.java
core/branches/flat/src/main/java/org/horizon/eviction/EvictionAlgorithm.java
core/branches/flat/src/main/java/org/horizon/eviction/EvictionManagerImpl.java
core/branches/flat/src/main/java/org/horizon/eviction/algorithms/BaseEvictionAlgorithm.java
core/branches/flat/src/main/java/org/horizon/factories/EntryFactory.java
core/branches/flat/src/main/java/org/horizon/factories/EntryFactoryImpl.java
core/branches/flat/src/main/java/org/horizon/interceptors/CacheLoaderInterceptor.java
core/branches/flat/src/main/java/org/horizon/interceptors/LockingInterceptor.java
core/branches/flat/src/main/java/org/horizon/util/AbstractMap.java
core/branches/flat/src/main/java/org/horizon/util/FastCopyHashMap.java
core/branches/flat/src/test/java/org/horizon/api/CacheAPITest.java
Log:
Expiry stuff
Modified: core/branches/flat/src/main/java/org/horizon/Cache.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/Cache.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/Cache.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -28,7 +28,9 @@
import org.horizon.manager.CacheManager;
import org.horizon.notifications.Listenable;
+import java.util.Map;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.TimeUnit;
/**
* @author Mircea.Markus(a)jboss.com
@@ -123,6 +125,65 @@
*/
void removeInterceptor(Class<? extends CommandInterceptor> interceptorType);
+ /**
+ * Retrieves the cache manager responsible for creating this cache instance.
+ *
+ * @return a cache manager
+ */
+ CacheManager getCacheManager();
- CacheManager getCacheManager();
+ /**
+ * An overloaded form of {@link #put(Object, Object)}, which takes in lifespan parameters.
+ *
+ * @param key key to use
+ * @param value value to store
+ * @param lifespan lifespan of the entry. Negative values are intepreted as unlimited lifespan.
+ * @param unit unit of measurement for the lifespan
+ * @return the value being replaced, or null if nothing is being replaced.
+ */
+ V put(K key, V value, long lifespan, TimeUnit unit);
+
+ /**
+ * An overloaded form of {@link #putIfAbsent(Object, Object)}, which takes in lifespan parameters.
+ *
+ * @param key key to use
+ * @param value value to store
+ * @param lifespan lifespan of the entry. Negative values are intepreted as unlimited lifespan.
+ * @param unit unit of measurement for the lifespan
+ * @return the value being replaced, or null if nothing is being replaced.
+ */
+ V putIfAbsent(K key, V value, long lifespan, TimeUnit unit);
+
+ /**
+ * An overloaded form of {@link #putAll(java.util.Map)}, which takes in lifespan parameters. Note that the lifespan
+ * is applied to all mappings in the map passed in.
+ *
+ * @param map map containing mappings to enter
+ * @param lifespan lifespan of the entry. Negative values are intepreted as unlimited lifespan.
+ * @param unit unit of measurement for the lifespan
+ */
+ void putAll(Map<? extends K, ? extends V> map, long lifespan, TimeUnit unit);
+
+ /**
+ * An overloaded form of {@link #replace(Object, Object)}, which takes in lifespan parameters.
+ *
+ * @param key key to use
+ * @param value value to store
+ * @param lifespan lifespan of the entry. Negative values are intepreted as unlimited lifespan.
+ * @param unit unit of measurement for the lifespan
+ * @return the value being replaced, or null if nothing is being replaced.
+ */
+ V replace(K key, V value, long lifespan, TimeUnit unit);
+
+ /**
+ * An overloaded form of {@link #replace(Object, Object, Object)}, which takes in lifespan parameters.
+ *
+ * @param key key to use
+ * @param oldValue value to replace
+ * @param value value to store
+ * @param lifespan lifespan of the entry. Negative values are intepreted as unlimited lifespan.
+ * @param unit unit of measurement for the lifespan
+ * @return true if the value was replaced, false otherwise
+ */
+ boolean replace(K key, V oldValue, V value, long lifespan, TimeUnit unit);
}
Modified: core/branches/flat/src/main/java/org/horizon/CacheDelegate.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/CacheDelegate.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/CacheDelegate.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -63,6 +63,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.TimeUnit;
/**
* @author Mircea.Markus(a)jboss.com
@@ -396,4 +397,30 @@
public CacheManager getCacheManager() {
return cacheManager;
}
+
+ public V put(K key, V value, long lifespan, TimeUnit unit) {
+ PutKeyValueCommand command = commandsFactory.buildPutKeyValueCommand(key, value, unit.toMillis(lifespan));
+ return (V) invoker.invoke(buildCtx(), command);
+ }
+
+ public V putIfAbsent(K key, V value, long lifespan, TimeUnit unit) {
+ PutKeyValueCommand command = commandsFactory.buildPutKeyValueCommand(key, value, unit.toMillis(lifespan));
+ command.setPutIfAbsent(true);
+ return (V) invoker.invoke(buildCtx(), command);
+ }
+
+ public void putAll(Map<? extends K, ? extends V> map, long lifespan, TimeUnit unit) {
+ PutMapCommand command = commandsFactory.buildPutMapCommand(map, unit.toMillis(lifespan));
+ invoker.invoke(buildCtx(), command);
+ }
+
+ public V replace(K key, V value, long lifespan, TimeUnit unit) {
+ ReplaceCommand command = commandsFactory.buildReplaceCommand(key, null, value, unit.toMillis(lifespan));
+ return (V) invoker.invoke(buildCtx(), command);
+ }
+
+ public boolean replace(K key, V oldValue, V value, long lifespan, TimeUnit unit) {
+ ReplaceCommand command = commandsFactory.buildReplaceCommand(key, oldValue, value, unit.toMillis(lifespan));
+ return (Boolean) invoker.invoke(buildCtx(), command);
+ }
}
Modified: core/branches/flat/src/main/java/org/horizon/commands/CommandsFactory.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/commands/CommandsFactory.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/commands/CommandsFactory.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -48,20 +48,27 @@
*/
@Scope(Scopes.NAMED_CACHE)
public interface CommandsFactory {
+
PutKeyValueCommand buildPutKeyValueCommand(Object key, Object value);
+ PutKeyValueCommand buildPutKeyValueCommand(Object key, Object value, long lifespanMillis);
+
RemoveCommand buildRemoveCommand(Object key, Object value);
InvalidateCommand buildInvalidateCommand(Object... keys);
ReplaceCommand buildReplaceCommand(Object key, Object oldValue, Object newValue);
+ ReplaceCommand buildReplaceCommand(Object key, Object oldValue, Object newValue, long lifespanMillis);
+
SizeCommand buildSizeCommand();
GetKeyValueCommand buildGetKeyValueCommand(Object key);
PutMapCommand buildPutMapCommand(Map t);
+ PutMapCommand buildPutMapCommand(Map t, long lifespanMillis);
+
ClearCommand buildClearCommand();
EvictCommand buildEvictCommand(Object key);
Modified: core/branches/flat/src/main/java/org/horizon/commands/CommandsFactoryImpl.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/commands/CommandsFactoryImpl.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/commands/CommandsFactoryImpl.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -67,6 +67,10 @@
return new PutKeyValueCommand(key, value, false, notifier);
}
+ public PutKeyValueCommand buildPutKeyValueCommand(Object key, Object value, long lifespanMillis) {
+ return new PutKeyValueCommand(key, value, false, notifier, lifespanMillis);
+ }
+
public RemoveCommand buildRemoveCommand(Object key, Object value) {
return new RemoveCommand(key, value, notifier);
}
@@ -79,6 +83,10 @@
return new ReplaceCommand(key, oldValue, newValue);
}
+ public ReplaceCommand buildReplaceCommand(Object key, Object oldValue, Object newValue, long lifespan) {
+ return new ReplaceCommand(key, oldValue, newValue, lifespan);
+ }
+
public SizeCommand buildSizeCommand() {
if (cachedSizeCommand == null) {
cachedSizeCommand = new SizeCommand(dataContainer);
@@ -94,6 +102,10 @@
return new PutMapCommand(map, notifier);
}
+ public PutMapCommand buildPutMapCommand(Map map, long lifespan) {
+ return new PutMapCommand(map, notifier, lifespan);
+ }
+
public ClearCommand buildClearCommand() {
return new ClearCommand();
}
Modified: core/branches/flat/src/main/java/org/horizon/commands/write/PutKeyValueCommand.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/commands/write/PutKeyValueCommand.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/commands/write/PutKeyValueCommand.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -42,7 +42,16 @@
protected boolean putIfAbsent;
private CacheNotifier notifier;
boolean successful = true;
+ long lifespanMillis = -1;
+ public PutKeyValueCommand(Object key, Object value, boolean putIfAbsent, CacheNotifier notifier, long lifespanMillis) {
+ super(key);
+ this.value = value;
+ this.putIfAbsent = putIfAbsent;
+ this.notifier = notifier;
+ this.lifespanMillis = lifespanMillis;
+ }
+
public PutKeyValueCommand(Object key, Object value, boolean putIfAbsent, CacheNotifier notifier) {
super(key);
this.value = value;
@@ -99,13 +108,18 @@
}
public Object[] getParameters() {
- return new Object[]{key, value};
+ if (lifespanMillis < 0)
+ return new Object[]{key, value, false};
+ else
+ return new Object[]{key, value, true, lifespanMillis};
}
public void setParameters(int commandId, Object[] parameters) {
if (commandId != METHOD_ID) throw new IllegalStateException("Invalid method id");
key = parameters[0];
value = parameters[1];
+ boolean setLifespan = (Boolean) parameters[2];
+ if (setLifespan) lifespanMillis = (Long) parameters[3];
}
public boolean isPutIfAbsent() {
@@ -116,31 +130,40 @@
this.putIfAbsent = putIfAbsent;
}
+ public long getLifespanMillis() {
+ return lifespanMillis;
+ }
+
+ @Override
public boolean equals(Object o) {
if (this == o) return true;
- if (!(o instanceof PutKeyValueCommand)) return false;
+ if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
PutKeyValueCommand that = (PutKeyValueCommand) o;
+ if (lifespanMillis != that.lifespanMillis) return false;
if (putIfAbsent != that.putIfAbsent) return false;
if (value != null ? !value.equals(that.value) : that.value != null) return false;
return true;
}
+ @Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (value != null ? value.hashCode() : 0);
result = 31 * result + (putIfAbsent ? 1 : 0);
+ result = 31 * result + (int) (lifespanMillis ^ (lifespanMillis >>> 32));
return result;
}
+ @Override
public String toString() {
return "PutKeyValueCommand{" +
- "key= " + key +
+ "lifespanMillis=" + lifespanMillis +
+ ", putIfAbsent=" + putIfAbsent +
", value=" + value +
- ", putIfAbsent=" + putIfAbsent +
'}';
}
Modified: core/branches/flat/src/main/java/org/horizon/commands/write/PutMapCommand.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/commands/write/PutMapCommand.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/commands/write/PutMapCommand.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -38,7 +38,14 @@
private Map<Object, Object> map;
private CacheNotifier notifier;
+ private long lifespanMillis = -1;
+ public PutMapCommand(Map map, CacheNotifier notifier, long lifespanMillis) {
+ this.map = map;
+ this.notifier = notifier;
+ this.lifespanMillis = lifespanMillis;
+ }
+
public PutMapCommand(Map map, CacheNotifier notifier) {
this.map = map;
this.notifier = notifier;
@@ -79,37 +86,51 @@
}
public Object[] getParameters() {
- return new Object[]{map};
+ if (lifespanMillis < 0)
+ return new Object[]{map, false};
+ else
+ return new Object[]{map, true, lifespanMillis};
}
public void setParameters(int commandId, Object[] parameters) {
- if (commandId != METHOD_ID) throw new IllegalStateException("Invalid method id");
map = (Map) parameters[0];
+ boolean setLifespan = (Boolean) parameters[1];
+ if (setLifespan) lifespanMillis = (Long) parameters[2];
}
+ @Override
public boolean equals(Object o) {
if (this == o) return true;
- if (!(o instanceof PutMapCommand)) return false;
+ if (o == null || getClass() != o.getClass()) return false;
PutMapCommand that = (PutMapCommand) o;
+ if (lifespanMillis != that.lifespanMillis) return false;
if (map != null ? !map.equals(that.map) : that.map != null) return false;
return true;
}
+ @Override
public int hashCode() {
- return (map != null ? map.hashCode() : 0);
+ int result = map != null ? map.hashCode() : 0;
+ result = 31 * result + (int) (lifespanMillis ^ (lifespanMillis >>> 32));
+ return result;
}
-
+ @Override
public String toString() {
return "PutMapCommand{" +
"map=" + map +
+ ", lifespanMillis=" + lifespanMillis +
'}';
}
public boolean isSuccessful() {
return true;
}
+
+ public long getLifespanMillis() {
+ return lifespanMillis;
+ }
}
Modified: core/branches/flat/src/main/java/org/horizon/commands/write/ReplaceCommand.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/commands/write/ReplaceCommand.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/commands/write/ReplaceCommand.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -36,8 +36,16 @@
protected Object oldValue;
protected Object newValue;
+ protected long lifespanMillis = -1;
boolean successful = true;
+ public ReplaceCommand(Object key, Object oldValue, Object newValue, long lifespanMillis) {
+ super(key);
+ this.oldValue = oldValue;
+ this.newValue = newValue;
+ this.lifespanMillis = lifespanMillis;
+ }
+
public ReplaceCommand(Object key, Object oldValue, Object newValue) {
super(key);
this.oldValue = oldValue;
@@ -86,7 +94,10 @@
}
public Object[] getParameters() {
- return new Object[]{key, oldValue, newValue};
+ if (lifespanMillis < 0)
+ return new Object[]{key, oldValue, newValue, false};
+ else
+ return new Object[]{key, oldValue, newValue, true, lifespanMillis};
}
public void setParameters(int commandId, Object[] parameters) {
@@ -94,37 +105,39 @@
key = parameters[0];
oldValue = parameters[1];
newValue = parameters[2];
+ boolean setLifespan = (Boolean) parameters[3];
+ if (setLifespan) lifespanMillis = (Long) parameters[4];
}
+ @Override
public boolean equals(Object o) {
if (this == o) return true;
- if (!(o instanceof ReplaceCommand)) return false;
+ if (o == null || getClass() != o.getClass()) return false;
if (!super.equals(o)) return false;
ReplaceCommand that = (ReplaceCommand) o;
+ if (lifespanMillis != that.lifespanMillis) return false;
if (newValue != null ? !newValue.equals(that.newValue) : that.newValue != null) return false;
if (oldValue != null ? !oldValue.equals(that.oldValue) : that.oldValue != null) return false;
return true;
}
+ @Override
public int hashCode() {
int result = super.hashCode();
result = 31 * result + (oldValue != null ? oldValue.hashCode() : 0);
result = 31 * result + (newValue != null ? newValue.hashCode() : 0);
+ result = 31 * result + (int) (lifespanMillis ^ (lifespanMillis >>> 32));
return result;
}
- public String toString() {
- return "ReplaceCommand{" +
- "key=" + key +
- ", oldValue=" + oldValue +
- ", newValue=" + newValue +
- '}';
- }
-
public boolean isSuccessful() {
return successful;
}
+
+ public long getLifespanMillis() {
+ return lifespanMillis;
+ }
}
Modified: core/branches/flat/src/main/java/org/horizon/container/DataContainer.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/container/DataContainer.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/container/DataContainer.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -27,7 +27,7 @@
import java.util.Set;
/**
- * // TODO: MANIK: Document this
+ * The main internal data structure which stores entries
*
* @author Manik Surtani (<a href="mailto:manik@jboss.org">manik(a)jboss.org</a>)
* @since 1.0
@@ -36,7 +36,7 @@
public interface DataContainer<K, V> {
V get(K k);
- void put(K k, V v);
+ void put(K k, V v, long lifespan);
boolean containsKey(K k);
@@ -48,7 +48,12 @@
Set<K> keySet();
- long getCreatedTimestamp(K key);
+ long getModifiedTimestamp(K key);
- long getModifiedTimestamp(K key);
+ /**
+ * Purges entries that have passed their expiry time, returning a set of keys that have been purged.
+ *
+ * @return set of keys that have been purged.
+ */
+ Set<K> purgeExpiredEntries();
}
Modified: core/branches/flat/src/main/java/org/horizon/container/MVCCEntry.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/container/MVCCEntry.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/container/MVCCEntry.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -21,8 +21,6 @@
*/
package org.horizon.container;
-import org.horizon.context.InvocationContext;
-
import java.util.Map.Entry;
/**
@@ -36,7 +34,7 @@
void copyForUpdate(DataContainer container, boolean writeSkewCheck);
- void commitUpdate(InvocationContext ctx, DataContainer container);
+ void commitUpdate(DataContainer container);
void rollbackUpdate();
Modified: core/branches/flat/src/main/java/org/horizon/container/ReadCommittedEntry.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/container/ReadCommittedEntry.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/container/ReadCommittedEntry.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -22,7 +22,6 @@
package org.horizon.container;
import static org.horizon.container.ReadCommittedEntry.Flags.*;
-import org.horizon.context.InvocationContext;
import org.horizon.logging.Log;
import org.horizon.logging.LogFactory;
@@ -38,16 +37,18 @@
protected Object key, value, oldValue;
protected byte flags = 0;
+ private long lifespan;
protected ReadCommittedEntry() {
setValid(true);
}
- public ReadCommittedEntry(Object key, Object value) {
+ public ReadCommittedEntry(Object key, Object value, long lifespan) {
setValid(true);
this.key = key;
this.value = value;
+ this.lifespan = lifespan;
}
public Object getKey() {
@@ -115,16 +116,15 @@
}
@SuppressWarnings("unchecked")
- public void commitUpdate(InvocationContext ctx, DataContainer container) {
+ public void commitUpdate(DataContainer container) {
// only do stuff if there are changes.
if (isFlagSet(CHANGED)) {
if (trace)
- log.trace("Updating entry [" + getKey() + "]. deleted=" + isDeleted() + " valid=" + isValid() + " changed=" + isChanged() + " created=" + isFlagSet(CREATED));
+ log.trace("Updating entry [" + getKey() + "]. deleted=" + isDeleted() + " valid=" + isValid() + " changed=" + isChanged() + " created=" + isFlagSet(CREATED) + " value=" + value);
if (isFlagSet(DELETED)) {
container.remove(key);
-
} else {
- container.put(key, value);
+ container.put(key, value, lifespan);
}
reset();
}
Modified: core/branches/flat/src/main/java/org/horizon/container/RepeatableReadEntry.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/container/RepeatableReadEntry.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/container/RepeatableReadEntry.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -35,8 +35,8 @@
public class RepeatableReadEntry extends ReadCommittedEntry {
private static final Log log = LogFactory.getLog(RepeatableReadEntry.class);
- public RepeatableReadEntry(Object key, Object value) {
- super(key, value);
+ public RepeatableReadEntry(Object key, Object value, long lifespan) {
+ super(key, value, lifespan);
}
@Override
Modified: core/branches/flat/src/main/java/org/horizon/container/UnsortedDataContainer.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/container/UnsortedDataContainer.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/container/UnsortedDataContainer.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -21,8 +21,16 @@
*/
package org.horizon.container;
+import org.horizon.factories.annotations.Inject;
+import org.horizon.loader.CacheLoader;
+import org.horizon.loader.CacheLoaderManager;
+
import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.HashSet;
import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -34,9 +42,31 @@
* @since 1.0
*/
public class UnsortedDataContainer<K, V> implements DataContainer<K, V> {
- private final ConcurrentMap<Object, CachedValue<V>> data = new ConcurrentHashMap<Object, CachedValue<V>>();
+ // data with expiry and without expiry are stored in different maps, for efficiency. E.g., so that when purging expired
+ // stuff, we don't need to iterate through immortal data.
+ final ConcurrentMap<K, CachedValue<V>> immortalData = new ConcurrentHashMap<K, CachedValue<V>>();
+ final ConcurrentMap<K, ExpirableCachedValue<V>> expirableData = new ConcurrentHashMap<K, ExpirableCachedValue<V>>();
private static final Object NULL = new Object();
+ private CacheLoaderManager clm;
+ private CacheLoader cacheLoader;
+ @Inject
+ public void injectDependencies(CacheLoaderManager clm) {
+ this.clm = clm;
+ }
+
+ private void expire(K key) {
+ expirableData.remove(key);
+ expireOnCacheLoader(key);
+ }
+
+ private void expireOnCacheLoader(K key) {
+ if (cacheLoader == null && clm != null) cacheLoader = clm.getCacheLoader();
+ if (cacheLoader != null) {
+ cacheLoader.remove(key);
+ }
+ }
+
@SuppressWarnings("unchecked")
private K maskNullKey(K o) {
return o == null ? (K) NULL : o;
@@ -48,54 +78,105 @@
@SuppressWarnings("unchecked")
public V get(K k) {
- CachedValue<V> cv = data.get(maskNullKey(k));
+ K maskedKey = maskNullKey(k);
+ CachedValue<V> cv = immortalData.get(maskedKey);
if (cv != null) {
cv.touch();
return cv.value;
} else {
- return null;
+ ExpirableCachedValue<V> ecv = expirableData.get(maskedKey);
+ if (ecv != null) {
+ if (ecv.expired()) {
+ expire(maskedKey);
+ } else {
+ ecv.touch();
+ return ecv.value;
+ }
+ }
}
+
+ return null;
}
- public void put(K k, V v) {
+ public void put(K k, V v, long lifespan) {
K maskedKey = maskNullKey(k);
- CachedValue<V> cv = data.get(maskedKey);
- if (cv == null) {
- cv = new CachedValue<V>(v);
- data.put(maskedKey, cv);
+ CachedValue<V> cv = immortalData.get(maskedKey);
+ ExpirableCachedValue<V> ecv;
+ if (cv != null) {
+ // do we need to move this to expirable?
+ if (lifespan < 0) {
+ // no.
+ cv.value = v;
+ cv.touch();
+ } else {
+ ecv = new ExpirableCachedValue<V>(v, lifespan);
+ immortalData.remove(maskedKey);
+ expirableData.put(maskedKey, ecv);
+ }
+ } else if ((ecv = expirableData.get(maskedKey)) != null) {
+ // do we need to move this to immortal?
+ if (lifespan < 0) {
+ // yes.
+ cv = new CachedValue<V>(v);
+ expirableData.remove(maskedKey);
+ immortalData.put(maskedKey, cv);
+ } else {
+ ecv.value = v;
+ ecv.touch();
+ }
} else {
- cv.touch();
- cv.value = v;
+ // does not exist anywhere!
+ if (lifespan < 0) {
+ cv = new CachedValue<V>(v);
+ immortalData.put(maskedKey, cv);
+ } else {
+ ecv = new ExpirableCachedValue<V>(v, lifespan);
+ expirableData.put(maskedKey, ecv);
+ }
}
}
public boolean containsKey(K k) {
- return data.containsKey(maskNullKey(k));
+ K maskedKey = maskNullKey(k);
+ if (!immortalData.containsKey(maskedKey)) {
+ ExpirableCachedValue<V> ecv = expirableData.get(maskedKey);
+ if (ecv == null) return false;
+ if (ecv.expired()) {
+ expire(maskedKey);
+ return false;
+ }
+ }
+ return true;
}
public long getModifiedTimestamp(K key) {
- CachedValue cv = data.get(maskNullKey(key));
+ K maskedKey = maskNullKey(key);
+ CachedValue cv = immortalData.get(maskedKey);
+ if (cv == null) cv = expirableData.get(maskedKey);
return cv == null ? -1 : cv.modified;
}
- public long getCreatedTimestamp(K key) {
- CachedValue cv = data.get(maskNullKey(key));
- return cv == null ? -1 : cv.created;
- }
-
@SuppressWarnings("unchecked")
public V remove(K k) {
- CachedValue<V> cv = data.remove(maskNullKey(k));
- return cv == null ? null : cv.value;
+ K maskedKey = maskNullKey(k);
+ CachedValue<V> cv = immortalData.remove(maskedKey);
+ if (cv == null) cv = expirableData.remove(maskedKey);
+
+ if (cv == null) {
+ return null;
+ } else {
+ return cv.value;
+ }
}
public int size() {
- return data.size();
+ return immortalData.size() + expirableData.size();
}
public void clear() {
- data.clear();
+ immortalData.clear();
+ expirableData.clear();
}
public Set<K> keySet() {
@@ -103,18 +184,34 @@
}
public String toString() {
- return data.toString();
+ return "Immortal Data: " + immortalData.toString() + "\n" + "Expirable Data: " + expirableData.toString();
}
+ public Set<K> purgeExpiredEntries() {
+ Set<K> purged = new HashSet<K>();
+ for (Iterator<Map.Entry<K, ExpirableCachedValue<V>>> iter = expirableData.entrySet().iterator(); iter.hasNext();) {
+ Map.Entry<K, ExpirableCachedValue<V>> entry = iter.next();
+ ExpirableCachedValue<?> cv = entry.getValue();
+ if (cv.expired()) {
+ iter.remove();
+ expireOnCacheLoader(entry.getKey());
+ purged.add(entry.getKey());
+ }
+ }
+ return purged;
+ }
+
private class KeySet extends AbstractSet<K> {
- Set<Object> realSet;
+ Set<K> immortalKeys;
+ Set<K> expirableKeys;
public KeySet() {
- this.realSet = data.keySet();
+ immortalKeys = immortalData.keySet();
+ expirableKeys = expirableData.keySet();
}
public Iterator<K> iterator() {
- return new KeyIterator(realSet.iterator());
+ return new KeyIterator(immortalKeys.iterator(), expirableKeys.iterator());
}
public void clear() {
@@ -122,7 +219,8 @@
}
public boolean contains(Object o) {
- return realSet.contains(maskNullKey((K) o));
+ K maskedKey = maskNullKey((K) o);
+ return immortalKeys.contains(maskedKey) || expirableKeys.contains(maskedKey);
}
public boolean remove(Object o) {
@@ -130,24 +228,37 @@
}
public int size() {
- return realSet.size();
+ return immortalKeys.size() + expirableKeys.size();
}
}
private class KeyIterator implements Iterator<K> {
- Iterator<Object> realIterator;
+ Iterator<Iterator<K>> metaIterator;
+ Iterator<K> immortalIterator;
+ Iterator<K> expirableIterator;
+ Iterator<K> currentIterator;
- private KeyIterator(Iterator<Object> realIterator) {
- this.realIterator = realIterator;
+ private KeyIterator(Iterator<K> immortalIterator, Iterator<K> expirableIterator) {
+ List<Iterator<K>> iterators = new ArrayList<Iterator<K>>(2);
+ iterators.add(immortalIterator);
+ iterators.add(expirableIterator);
+ metaIterator = iterators.iterator();
+
+ if (metaIterator.hasNext()) currentIterator = metaIterator.next();
}
public boolean hasNext() {
- return realIterator.hasNext();
+ boolean hasNext = currentIterator.hasNext();
+ while (!hasNext && metaIterator.hasNext()) {
+ currentIterator = metaIterator.next();
+ hasNext = currentIterator.hasNext();
+ }
+ return hasNext;
}
@SuppressWarnings("unchecked")
public K next() {
- return unmaskNullKey((K) realIterator.next());
+ return unmaskNullKey(currentIterator.next());
}
public void remove() {
@@ -157,22 +268,34 @@
private static class CachedValue<V> {
V value;
- long created;
long modified;
- public CachedValue(V value) {
+ private CachedValue(V value) {
this.value = value;
- created = modified = System.currentTimeMillis();
+ modified = System.currentTimeMillis();
}
- public CachedValue(V value, long created, long modified) {
- this.value = value;
- this.created = created;
- this.modified = modified;
+ void touch() {
+ modified = System.currentTimeMillis();
}
+ }
- public void touch() {
- modified = System.currentTimeMillis();
+ private static class ExpirableCachedValue<V> extends CachedValue<V> {
+ long created;
+ long expiryTime;
+
+ private ExpirableCachedValue(V value, long lifespan) {
+ super(value);
+ created = modified;
+ setLifespan(lifespan);
}
+
+ private boolean expired() {
+ return expiryTime >= 0 && System.currentTimeMillis() > expiryTime;
+ }
+
+ private void setLifespan(long lifespan) {
+ expiryTime = lifespan < 0 ? -1 : lifespan + created;
+ }
}
}
\ No newline at end of file
Modified: core/branches/flat/src/main/java/org/horizon/eviction/EvictionAlgorithm.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/EvictionAlgorithm.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/eviction/EvictionAlgorithm.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -41,7 +41,7 @@
public interface EvictionAlgorithm extends Lifecycle {
/**
* Entry point for eviction algorithm. Invoking this will cause the algorithm to process the queue of {@link
- * EvictionEvent}s passed in.
+ * org.horizon.eviction.events.EvictionEvent}s passed in.
*
* @param queue blocking queue of {@link org.horizon.eviction.events.EvictionEvent}s to process.
* @throws EvictionException if there is a problem processing any of these events
Modified: core/branches/flat/src/main/java/org/horizon/eviction/EvictionManagerImpl.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/EvictionManagerImpl.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/eviction/EvictionManagerImpl.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -8,6 +8,7 @@
import org.horizon.container.DataContainer;
import org.horizon.eviction.events.EvictionEvent;
import org.horizon.eviction.events.InUseEvictionEvent;
+import org.horizon.eviction.events.PurgedDataEndEvent;
import org.horizon.factories.KnownComponentNames;
import org.horizon.factories.annotations.ComponentName;
import org.horizon.factories.annotations.Inject;
@@ -17,6 +18,7 @@
import org.horizon.logging.LogFactory;
import org.horizon.util.Util;
+import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
@@ -87,6 +89,9 @@
class ScheduledTask implements Runnable {
public void run() {
+ registerEvictionEvent(new EvictionEvent(null, EvictionEvent.Type.EXPIRED_DATA_PURGE_START));
+ Set purgedKeys = dataContainer.purgeExpiredEntries();
+ registerEvictionEvent(new PurgedDataEndEvent(purgedKeys));
processEvictionQueues();
}
}
Modified: core/branches/flat/src/main/java/org/horizon/eviction/algorithms/BaseEvictionAlgorithm.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/algorithms/BaseEvictionAlgorithm.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/eviction/algorithms/BaseEvictionAlgorithm.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -32,12 +32,15 @@
import org.horizon.eviction.events.EvictionEvent;
import org.horizon.eviction.events.EvictionEvent.Type;
import org.horizon.eviction.events.InUseEvictionEvent;
+import org.horizon.eviction.events.PurgedDataEndEvent;
import org.horizon.logging.Log;
import org.horizon.logging.LogFactory;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@@ -171,12 +174,14 @@
EvictionEvent event;
int count = 0;
long startTime = System.currentTimeMillis();
-
+ Set<Object> keysToRetainInQueue = null;
while ((event = nextEvent(queue)) != null) {
if (trace) count++;
switch (event.getEventType()) {
case ADD_ENTRY_EVENT:
- processAddedEntries(event.getKey());
+ Object key = event.getKey();
+ processAddedEntries(key);
+ recordEventKey(keysToRetainInQueue, key);
break;
case REMOVE_ENTRY_EVENT:
processRemovedEntries(event.getKey());
@@ -193,6 +198,14 @@
case UNMARK_IN_USE_EVENT:
processUnmarkInUseNodes(event.getKey());
break;
+ case EXPIRED_DATA_PURGE_START:
+ if (keysToRetainInQueue == null) keysToRetainInQueue = new HashSet<Object>();
+ break;
+ case EXPIRED_DATA_PURGE_END:
+ Set<Object> keysPurged = ((PurgedDataEndEvent) event).getKeysPurged();
+ if (keysToRetainInQueue != null) keysPurged.removeAll(keysToRetainInQueue);
+ for (Object o : keysPurged) evictionQueue.remove(o);
+ break;
default:
throw new EvictionException("Illegal eviction event type " + event.getEventType());
}
@@ -201,6 +214,10 @@
log.trace("processed {0} eviction events in {1} millis", count, System.currentTimeMillis() - startTime);
}
+ private void recordEventKey(Set<Object> setToRecordIn, Object key) {
+ if (setToRecordIn != null) setToRecordIn.add(key);
+ }
+
protected void evictOrRecycle(Object key) {
log.trace("Attempting to evict {0}", key);
if (!action.evict(key)) {
Deleted: core/branches/flat/src/main/java/org/horizon/eviction/events/EvictionEvent.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/events/EvictionEvent.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/eviction/events/EvictionEvent.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -1,98 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file 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.horizon.eviction.events;
-
-/**
- * An eviction event records activity on nodes in the cache. These are recorded for processing later.
- *
- * @author (various)
- * @since 1.0
- */
-public class EvictionEvent {
- Object key;
- Type type;
-
- public static enum Type {
- ADD_ENTRY_EVENT,
- REMOVE_ENTRY_EVENT,
- VISIT_ENTRY_EVENT,
- CLEAR_CACHE_EVENT,
- MARK_IN_USE_EVENT,
- UNMARK_IN_USE_EVENT
- }
-
- public EvictionEvent(Object key, Type type) {
- this.key = key;
- this.type = type;
- }
-
- public Object getKey() {
- return key;
- }
-
- public void setKey(Object key) {
- this.key = key;
- }
-
- public void setEventType(Type event) {
- type = event;
- }
-
- public Type getEventType() {
- return type;
- }
-
- @Override
- public String toString() {
- return "EvictedEventNode[key=" + key + " event=" + type + "]";
- }
-
- /**
- * Copies this evicted event node to create a new one with the same values, except with a new Fqn root.
- *
- * @param key new Fqn root to use
- * @return a new EvictedEventNode instance
- */
- public EvictionEvent copy(Object key) {
- return new EvictionEvent(key, type);
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- EvictionEvent that = (EvictionEvent) o;
-
- if (key != null ? !key.equals(that.key) : that.key != null) return false;
- if (type != that.type) return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int result = key != null ? key.hashCode() : 0;
- result = 31 * result + (type != null ? type.hashCode() : 0);
- return result;
- }
-}
Added: core/branches/flat/src/main/java/org/horizon/eviction/events/EvictionEvent.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/events/EvictionEvent.java (rev 0)
+++ core/branches/flat/src/main/java/org/horizon/eviction/events/EvictionEvent.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -0,0 +1,100 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file 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.horizon.eviction.events;
+
+/**
+ * An eviction event records activity on nodes in the cache. These are recorded for processing later.
+ *
+ * @author (various)
+ * @since 1.0
+ */
+public class EvictionEvent {
+ Object key;
+ Type type;
+
+ public static enum Type {
+ ADD_ENTRY_EVENT,
+ REMOVE_ENTRY_EVENT,
+ VISIT_ENTRY_EVENT,
+ CLEAR_CACHE_EVENT,
+ MARK_IN_USE_EVENT,
+ UNMARK_IN_USE_EVENT,
+ EXPIRED_DATA_PURGE_START, // internal marker to denote when purging of expired data starts
+ EXPIRED_DATA_PURGE_END // internal marker to denote when purging of expired data ends
+ }
+
+ public EvictionEvent(Object key, Type type) {
+ this.key = key;
+ this.type = type;
+ }
+
+ public Object getKey() {
+ return key;
+ }
+
+ public void setKey(Object key) {
+ this.key = key;
+ }
+
+ public void setEventType(Type event) {
+ type = event;
+ }
+
+ public Type getEventType() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ return "EvictedEventNode[key=" + key + " event=" + type + "]";
+ }
+
+ /**
+ * Copies this evicted event node to create a new one with the same values, except with a new Fqn root.
+ *
+ * @param key new Fqn root to use
+ * @return a new EvictedEventNode instance
+ */
+ public EvictionEvent copy(Object key) {
+ return new EvictionEvent(key, type);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ EvictionEvent that = (EvictionEvent) o;
+
+ if (key != null ? !key.equals(that.key) : that.key != null) return false;
+ if (type != that.type) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = key != null ? key.hashCode() : 0;
+ result = 31 * result + (type != null ? type.hashCode() : 0);
+ return result;
+ }
+}
Property changes on: core/branches/flat/src/main/java/org/horizon/eviction/events/EvictionEvent.java
___________________________________________________________________
Name: svn:executable
+ *
Added: core/branches/flat/src/main/java/org/horizon/eviction/events/PurgedDataEndEvent.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/events/PurgedDataEndEvent.java (rev 0)
+++ core/branches/flat/src/main/java/org/horizon/eviction/events/PurgedDataEndEvent.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -0,0 +1,49 @@
+package org.horizon.eviction.events;
+
+import java.util.Set;
+
+/**
+ * To be put on an eviction event queue after expired data has been purged
+ *
+ * @author Manik Surtani
+ * @since 1.0
+ */
+public class PurgedDataEndEvent extends EvictionEvent {
+ Set<Object> keysPurged;
+
+ public PurgedDataEndEvent(Set<Object> keysPurged) {
+ super(null, EvictionEvent.Type.EXPIRED_DATA_PURGE_END);
+ this.keysPurged = keysPurged;
+ }
+
+ public Set<Object> getKeysPurged() {
+ return keysPurged;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ if (!super.equals(o)) return false;
+
+ PurgedDataEndEvent that = (PurgedDataEndEvent) o;
+
+ if (keysPurged != null ? !keysPurged.equals(that.keysPurged) : that.keysPurged != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = super.hashCode();
+ result = 31 * result + (keysPurged != null ? keysPurged.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "PurgedDataEndEvent{" +
+ "keysPurged=" + keysPurged +
+ '}';
+ }
+}
Modified: core/branches/flat/src/main/java/org/horizon/factories/EntryFactory.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/factories/EntryFactory.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/factories/EntryFactory.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -47,11 +47,11 @@
*/
boolean acquireLock(InvocationContext ctx, Object key) throws InterruptedException, TimeoutException;
- MVCCEntry wrapEntryForWriting(InvocationContext ctx, Object key, boolean createIfAbsent, boolean forceLockIfAbsent) throws InterruptedException;
+ MVCCEntry wrapEntryForWriting(InvocationContext ctx, Object key, boolean createIfAbsent, boolean forceLockIfAbsent, long lifespan) throws InterruptedException;
MVCCEntry wrapEntryForReading(InvocationContext ctx, Object key, boolean putInContext, boolean forceWriteLock) throws InterruptedException;
MVCCEntry wrapEntryForReading(InvocationContext ctx, Object key, boolean putInContext) throws InterruptedException;
- MVCCEntry createWrappedEntry(Object key, Object value, boolean isForInsert);
+ MVCCEntry createWrappedEntry(Object key, Object value, boolean isForInsert, long lifespan);
}
Modified: core/branches/flat/src/main/java/org/horizon/factories/EntryFactoryImpl.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/factories/EntryFactoryImpl.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/factories/EntryFactoryImpl.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -71,10 +71,10 @@
writeSkewCheck = configuration.isWriteSkewCheck();
}
- public MVCCEntry createWrappedEntry(Object key, Object value, boolean isForInsert) {
+ public MVCCEntry createWrappedEntry(Object key, Object value, boolean isForInsert, long lifespan) {
if (value == null && !isForInsert) return useRepeatableRead ? NULL_MARKER : null;
- MVCCEntry mvccEntry = useRepeatableRead ? new RepeatableReadEntry(key, value) : new ReadCommittedEntry(key, value);
+ MVCCEntry mvccEntry = useRepeatableRead ? new RepeatableReadEntry(key, value, lifespan) : new ReadCommittedEntry(key, value, lifespan);
return mvccEntry;
}
@@ -89,12 +89,12 @@
MVCCEntry mvccEntry;
if (forceWriteLock) {
if (trace) log.trace("Forcing lock on reading");
- return wrapEntryForWriting(ctx, key, false, false);
+ return wrapEntryForWriting(ctx, key, false, false, -1);
} else if ((mvccEntry = ctx.lookupEntry(key)) == null) {
if (trace) log.trace("Key " + key + " is not in context, fetching from container.");
// simple implementation. Peek the node, wrap it, put wrapped node in the context.
Object value = container.get(key);
- mvccEntry = createWrappedEntry(key, value, false);
+ mvccEntry = createWrappedEntry(key, value, false, -1);
if (mvccEntry != null && putInContext) ctx.putLookedUpEntry(key, mvccEntry);
return mvccEntry;
} else {
@@ -103,7 +103,7 @@
}
}
- public MVCCEntry wrapEntryForWriting(InvocationContext ctx, Object key, boolean createIfAbsent, boolean forceLockIfAbsent) throws InterruptedException {
+ public MVCCEntry wrapEntryForWriting(InvocationContext ctx, Object key, boolean createIfAbsent, boolean forceLockIfAbsent, long lifespan) throws InterruptedException {
MVCCEntry mvccEntry = ctx.lookupEntry(key);
if (createIfAbsent && mvccEntry != null && mvccEntry.isNullEntry()) mvccEntry = null;
if (mvccEntry != null) // exists in context! Just acquire lock if needed, and wrap.
@@ -128,17 +128,16 @@
// do we need a lock?
boolean needToCopy = false;
if (acquireLock(ctx, key)) needToCopy = true;
- mvccEntry = createWrappedEntry(key, value, false);
+ mvccEntry = createWrappedEntry(key, value, false, lifespan);
ctx.putLookedUpEntry(key, mvccEntry);
if (needToCopy) mvccEntry.copyForUpdate(container, writeSkewCheck);
- } else if (createIfAbsent) // else, do we need to create one?
- {
+ } else if (createIfAbsent) {
// this is the *only* point where new entries can be created!!
if (trace) log.trace("Creating new entry.");
// now to lock and create the node. Lock first to prevent concurrent creation!
acquireLock(ctx, key);
notifier.notifyCacheEntryCreated(key, true, ctx);
- mvccEntry = createWrappedEntry(key, value, true);
+ mvccEntry = createWrappedEntry(key, value, true, lifespan);
mvccEntry.setCreated(true);
ctx.putLookedUpEntry(key, mvccEntry);
mvccEntry.copyForUpdate(container, writeSkewCheck);
Modified: core/branches/flat/src/main/java/org/horizon/interceptors/CacheLoaderInterceptor.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/interceptors/CacheLoaderInterceptor.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/interceptors/CacheLoaderInterceptor.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -138,7 +138,7 @@
}
// Reuse the lock and create a new entry for loading
- MVCCEntry n = entryFactory.wrapEntryForWriting(ctx, key, true, false);
+ MVCCEntry n = entryFactory.wrapEntryForWriting(ctx, key, true, false, -1); // TODO: handle expiry information from loaded data
n = loadEntry(ctx, key, n);
}
Modified: core/branches/flat/src/main/java/org/horizon/interceptors/LockingInterceptor.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/interceptors/LockingInterceptor.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/interceptors/LockingInterceptor.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -133,7 +133,7 @@
public Object visitClearCommand(InvocationContext ctx, ClearCommand command) throws Throwable {
try {
// get a snapshot of all keys in the data container
- for (Object key : dataContainer.keySet()) entryFactory.wrapEntryForWriting(ctx, key, false, false);
+ for (Object key : dataContainer.keySet()) entryFactory.wrapEntryForWriting(ctx, key, false, false, -1);
return invokeNextInterceptor(ctx, command);
}
@@ -152,7 +152,7 @@
public Object visitInvalidateCommand(InvocationContext ctx, InvalidateCommand command) throws Throwable {
try {
if (command.getKeys() != null) {
- for (Object key : command.getKeys()) entryFactory.wrapEntryForWriting(ctx, key, false, true);
+ for (Object key : command.getKeys()) entryFactory.wrapEntryForWriting(ctx, key, false, true, -1);
}
return invokeNextInterceptor(ctx, command);
}
@@ -164,7 +164,7 @@
@Override
public Object visitPutKeyValueCommand(InvocationContext ctx, PutKeyValueCommand command) throws Throwable {
try {
- entryFactory.wrapEntryForWriting(ctx, command.getKey(), true, false);
+ entryFactory.wrapEntryForWriting(ctx, command.getKey(), true, false, command.getLifespanMillis());
Object o = invokeNextInterceptor(ctx, command);
return o;
}
@@ -177,7 +177,7 @@
public Object visitPutMapCommand(InvocationContext ctx, PutMapCommand command) throws Throwable {
try {
for (Object key : command.getMap().keySet()) {
- entryFactory.wrapEntryForWriting(ctx, key, true, false);
+ entryFactory.wrapEntryForWriting(ctx, key, true, false, command.getLifespanMillis());
}
return invokeNextInterceptor(ctx, command);
}
@@ -189,7 +189,7 @@
@Override
public Object visitRemoveCommand(InvocationContext ctx, RemoveCommand command) throws Throwable {
try {
- entryFactory.wrapEntryForWriting(ctx, command.getKey(), false, true);
+ entryFactory.wrapEntryForWriting(ctx, command.getKey(), false, true, -1);
return invokeNextInterceptor(ctx, command);
}
finally {
@@ -200,7 +200,7 @@
@Override
public Object visitReplaceCommand(InvocationContext ctx, ReplaceCommand command) throws Throwable {
try {
- entryFactory.wrapEntryForWriting(ctx, command.getKey(), false, true);
+ entryFactory.wrapEntryForWriting(ctx, command.getKey(), false, true, command.getLifespanMillis());
return invokeNextInterceptor(ctx, command);
}
finally {
@@ -248,7 +248,7 @@
Object key = it.previous();
MVCCEntry entry = ctx.lookupEntry(key);
// could be null with read-committed
- if (entry != null) entry.commitUpdate(ctx, dataContainer);
+ if (entry != null) entry.commitUpdate(dataContainer);
// and then unlock
if (trace) log.trace("Releasing lock on [" + key + "] for owner " + owner);
lockManager.unlock(key, owner);
Modified: core/branches/flat/src/main/java/org/horizon/util/AbstractMap.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/util/AbstractMap.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/util/AbstractMap.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -43,52 +43,52 @@
return o1 == o2 || (o1 != null && o1.equals(o2));
}
-//
-// protected static class SimpleEntry<K, V> implements Map.Entry<K, V> {
-// private K key;
-// private V value;
-//
-// SimpleEntry(K key, V value) {
-// this.key = key;
-// this.value = value;
-// }
-//
-// SimpleEntry(Map.Entry<K, V> entry) {
-// this.key = entry.getKey();
-// this.value = entry.getValue();
-// }
-//
-// public K getKey() {
-// return key;
-// }
-//
-// public V getValue() {
-// return value;
-// }
-//
-// public V setValue(V value) {
-// V old = this.value;
-// this.value = value;
-// return old;
-// }
-//
-// public boolean equals(Object o) {
-// if (this == o)
-// return true;
-//
-// if (!(o instanceof Map.Entry))
-// return false;
-// Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
-// return eq(key, e.getKey()) && eq(value, e.getValue());
-// }
-//
-// public int hashCode() {
-// return hash(key) ^
-// (value == null ? 0 : hash(value));
-// }
-//
-// public String toString() {
-// return getKey() + "=" + getValue();
-// }
-// }
+
+ protected static class SimpleEntry<K, V> implements Map.Entry<K, V> {
+ private K key;
+ private V value;
+
+ SimpleEntry(K key, V value) {
+ this.key = key;
+ this.value = value;
+ }
+
+ SimpleEntry(Map.Entry<K, V> entry) {
+ this.key = entry.getKey();
+ this.value = entry.getValue();
+ }
+
+ public K getKey() {
+ return key;
+ }
+
+ public V getValue() {
+ return value;
+ }
+
+ public V setValue(V value) {
+ V old = this.value;
+ this.value = value;
+ return old;
+ }
+
+ public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (!(o instanceof Map.Entry))
+ return false;
+ Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
+ return eq(key, e.getKey()) && eq(value, e.getValue());
+ }
+
+ public int hashCode() {
+ return hash(key) ^
+ (value == null ? 0 : hash(value));
+ }
+
+ public String toString() {
+ return getKey() + "=" + getValue();
+ }
+ }
}
Modified: core/branches/flat/src/main/java/org/horizon/util/FastCopyHashMap.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/util/FastCopyHashMap.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/main/java/org/horizon/util/FastCopyHashMap.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -538,7 +538,7 @@
}
private class EntryIterator extends FasyCopyHashMapIterator<Map.Entry<K, V>> {
- private class WriteThroughEntry extends java.util.AbstractMap.SimpleEntry<K, V> {
+ private class WriteThroughEntry extends AbstractMap.SimpleEntry<K, V> {
WriteThroughEntry(K key, V value) {
super(key, value);
}
Deleted: core/branches/flat/src/test/java/org/horizon/BasicTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/BasicTest.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/test/java/org/horizon/BasicTest.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -1,124 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source
- * Copyright 2008, Red Hat Middleware LLC, and individual contributors
- * by the @authors tag. 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.horizon;
-
-import org.horizon.config.Configuration;
-import org.horizon.config.GlobalConfiguration;
-import org.horizon.logging.Log;
-import org.horizon.logging.LogFactory;
-import org.horizon.manager.CacheManager;
-import org.horizon.manager.DefaultCacheManager;
-import org.horizon.manager.NamedCacheNotFoundException;
-import org.horizon.util.TestingUtil;
-import org.testng.annotations.Test;
-
-@Test(groups = "functional")
-public class BasicTest {
- public static final Log log = LogFactory.getLog(BasicTest.class);
-
- public void basicTest() throws Exception {
- // create a cache manager
- Configuration c = new Configuration(); // LOCAL mode
- c.setFetchInMemoryState(false);
- DefaultCacheManager cm = new DefaultCacheManager(c);
- try {
- cm.start();
- Cache cache = cm.getCache("test");
- String key = "key", value = "value";
-
- assert cache.isEmpty();
- assert cache.size() == 0;
- assert !cache.containsKey(key);
-
- cache.put(key, value);
- assert cache.size() == 1;
- assert cache.containsKey(key);
- assert !cache.isEmpty();
-
- assert cache.remove(key).equals(value);
-
- assert cache.isEmpty();
- assert cache.size() == 0;
- assert !cache.containsKey(key);
- }
- finally {
- cm.stop();
- }
- }
-
- public void testBasicReplication() throws NamedCacheNotFoundException {
- Configuration configuration = new Configuration();
- configuration.setCacheMode(Configuration.CacheMode.REPL_SYNC);
-
- CacheManager firstManager = new DefaultCacheManager(GlobalConfiguration.getClusteredDefault(), configuration);
- CacheManager secondManager = new DefaultCacheManager(GlobalConfiguration.getClusteredDefault(), configuration);
-
- try {
- CacheSPI firstCache = (CacheSPI) firstManager.getCache();
- CacheSPI secondCache = (CacheSPI) secondManager.getCache();
- TestingUtil.blockUntilViewsReceived(60000, firstManager, secondManager);
-
- firstCache.put("key", "value");
-
- assert secondCache.get("key").equals("value");
- assert firstCache.get("key").equals("value");
- secondCache.put("key", "value2");
- assert firstCache.get("key").equals("value2");
- firstCache.remove("key");
- assert secondCache.get("key") == null;
- }
- finally {
- TestingUtil.killCacheManagers(firstManager, secondManager);
- }
- }
-
- public void concurrentMapMethodTest() {
- CacheManager cm = null;
- Cache<String, String> c = null;
- try {
- cm = new DefaultCacheManager();
- c = cm.getCache();
-
- assert c.putIfAbsent("A", "B") == null;
- assert c.putIfAbsent("A", "C").equals("B");
- assert c.get("A").equals("B");
-
- assert !c.remove("A", "C");
- assert c.containsKey("A");
- assert c.remove("A", "B");
- assert !c.containsKey("A");
-
- c.put("A", "B");
-
- assert !c.replace("A", "D", "C");
- assert c.get("A").equals("B");
- assert c.replace("A", "B", "C");
- assert c.get("A").equals("C");
-
- assert c.replace("A", "X").equals("C");
- assert c.replace("X", "A") == null;
- assert !c.containsKey("X");
- } finally {
- TestingUtil.killCacheManagers(cm);
- }
- }
-}
Deleted: core/branches/flat/src/test/java/org/horizon/SkipListTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/SkipListTest.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/test/java/org/horizon/SkipListTest.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -1,64 +0,0 @@
-package org.horizon;
-
-import org.testng.annotations.Test;
-
-import java.util.TreeMap;
-
-/**
- * // TODO: Manik: Document this!
- *
- * @author Manik Surtani
- */
-@Test
-public class SkipListTest {
-
- public void test() {
- TreeMap m = new TreeMap();
- m.put(new Thing(50), "x");
- Thing t = new Thing(20);
- m.put(t, "x");
- m.put(new Thing(40), "x");
- m.put(new Thing(70), "x");
-
- System.out.println(m);
-
- t.number = 99;
- System.out.println(m);
- }
-
- public static class Thing implements Comparable {
- Integer number;
-
- public Thing(Integer number) {
- this.number = number;
- }
-
- public int compareTo(Object o) {
- return number.compareTo(((Thing) o).number);
- }
-
- @Override
- public String toString() {
- return "Thing{" +
- "number=" + number +
- '}';
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- Thing thing = (Thing) o;
-
- if (number != null ? !number.equals(thing.number) : thing.number != null) return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- return number != null ? number.hashCode() : 0;
- }
- }
-}
Modified: core/branches/flat/src/test/java/org/horizon/api/CacheAPITest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/api/CacheAPITest.java 2009-02-06 18:10:10 UTC (rev 7661)
+++ core/branches/flat/src/test/java/org/horizon/api/CacheAPITest.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -1,5 +1,6 @@
package org.horizon.api;
+import org.horizon.Cache;
import org.horizon.CacheSPI;
import org.horizon.UnitTestCacheFactory;
import org.horizon.config.Configuration;
@@ -201,4 +202,71 @@
assert cache.get(key).equals(value);
assert 1 == cache.size();
}
+
+ public void testConcurrentMapMethods() {
+ CacheSPI<String, String> c = cacheTL.get();
+
+ assert c.putIfAbsent("A", "B") == null;
+ assert c.putIfAbsent("A", "C").equals("B");
+ assert c.get("A").equals("B");
+
+ assert !c.remove("A", "C");
+ assert c.containsKey("A");
+ assert c.remove("A", "B");
+ assert !c.containsKey("A");
+
+ c.put("A", "B");
+
+ assert !c.replace("A", "D", "C");
+ assert c.get("A").equals("B");
+ assert c.replace("A", "B", "C");
+ assert c.get("A").equals("C");
+
+ assert c.replace("A", "X").equals("C");
+ assert c.replace("X", "A") == null;
+ assert !c.containsKey("X");
+ }
+
+ public void testSizeAndContents() throws Exception {
+ Cache cache = cacheTL.get();
+ String key = "key", value = "value";
+
+ assert cache.isEmpty();
+ assert cache.size() == 0;
+ assert !cache.containsKey(key);
+
+ cache.put(key, value);
+ assert cache.size() == 1;
+ assert cache.containsKey(key);
+ assert !cache.isEmpty();
+
+ assert cache.remove(key).equals(value);
+
+ assert cache.isEmpty();
+ assert cache.size() == 0;
+ assert !cache.containsKey(key);
+
+ Map<String, String> m = new HashMap<String, String>();
+ m.put("1", "one");
+ m.put("2", "two");
+ m.put("3", "three");
+ cache.putAll(m);
+
+ assert cache.get("1").equals("one");
+ assert cache.get("2").equals("two");
+ assert cache.get("3").equals("three");
+ assert cache.size() == 3;
+
+ m = new HashMap<String, String>();
+ m.put("1", "newvalue");
+ m.put("4", "four");
+
+ cache.putAll(m);
+
+ assert cache.get("1").equals("newvalue");
+ assert cache.get("2").equals("two");
+ assert cache.get("3").equals("three");
+ assert cache.get("4").equals("four");
+ assert cache.size() == 4;
+ }
}
Added: core/branches/flat/src/test/java/org/horizon/container/DataContainerTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/container/DataContainerTest.java (rev 0)
+++ core/branches/flat/src/test/java/org/horizon/container/DataContainerTest.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -0,0 +1,70 @@
+package org.horizon.container;
+
+import org.testng.annotations.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+@Test(groups = "unit")
+public class DataContainerTest {
+ public void testExpiredData() throws InterruptedException {
+ DataContainer dc = new UnsortedDataContainer();
+ dc.put("k", "v", -1);
+ Thread.sleep(1);
+
+ assert dc.getModifiedTimestamp("k") <= System.currentTimeMillis();
+
+ dc.get("k");
+ assert dc.purgeExpiredEntries().isEmpty();
+
+ dc.put("k", "v", 0);
+ Thread.sleep(1);
+ assert dc.size() == 1;
+ assert dc.getModifiedTimestamp("k") <= System.currentTimeMillis();
+
+ assert dc.get("k") == null;
+ assert dc.size() == 0;
+
+ dc.put("k", "v", 0);
+ Thread.sleep(1);
+ assert dc.size() == 1;
+ assert dc.getModifiedTimestamp("k") <= System.currentTimeMillis();
+
+ assert dc.purgeExpiredEntries().contains("k");
+ assert dc.size() == 0;
+ }
+
+ public void testExpirableToImmortal() {
+ UnsortedDataContainer dc = new UnsortedDataContainer();
+ dc.put("k", "v", 6000000);
+ assert dc.containsKey("k");
+ assert !dc.immortalData.containsKey("k");
+ assert dc.expirableData.containsKey("k");
+
+ dc.put("k", "v2", -1);
+ assert dc.containsKey("k");
+ assert dc.immortalData.containsKey("k");
+ assert !dc.expirableData.containsKey("k");
+
+ dc.put("k", "v3", 700000);
+ assert dc.containsKey("k");
+ assert !dc.immortalData.containsKey("k");
+ assert dc.expirableData.containsKey("k");
+ }
+
+ public void testKeySet() {
+ UnsortedDataContainer dc = new UnsortedDataContainer();
+ dc.put("k1", "v", 6000000);
+ dc.put("k2", "v", -1);
+
+ Set expected = new HashSet();
+ expected.add("k1");
+ expected.add("k2");
+
+ for (Object o : dc.keySet()) {
+ assert expected.remove(o);
+ }
+
+ assert expected.isEmpty();
+ }
+}
Added: core/branches/flat/src/test/java/org/horizon/expiry/ExpiryTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/expiry/ExpiryTest.java (rev 0)
+++ core/branches/flat/src/test/java/org/horizon/expiry/ExpiryTest.java 2009-02-08 11:20:49 UTC (rev 7662)
@@ -0,0 +1,118 @@
+package org.horizon.expiry;
+
+import org.horizon.Cache;
+import org.horizon.manager.CacheManager;
+import org.horizon.manager.DefaultCacheManager;
+import org.horizon.util.TestingUtil;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+@Test(groups = "functional", sequential = true)
+public class ExpiryTest {
+
+ CacheManager cm;
+
+ @BeforeMethod
+ public void setUp() {
+ cm = new DefaultCacheManager();
+ }
+
+ @AfterMethod
+ public void tearDown() {
+ TestingUtil.killCacheManagers(cm);
+ }
+
+ public void testExpiryInPut() throws InterruptedException {
+ Cache cache = cm.getCache();
+ long startTime = System.currentTimeMillis();
+ long lifespan = 1000;
+ cache.put("k", "v", lifespan, TimeUnit.MILLISECONDS);
+ while (System.currentTimeMillis() < startTime + lifespan + 100) {
+ if (System.currentTimeMillis() < startTime + lifespan) {
+ assert cache.get("k").equals("v");
+ } else {
+ assert cache.get("k") == null;
+ }
+ Thread.sleep(50);
+ }
+ }
+
+ public void testExpiryInPutAll() throws InterruptedException {
+ Cache cache = cm.getCache();
+ long startTime = System.currentTimeMillis();
+ long lifespan = 1000;
+ Map m = new HashMap();
+ m.put("k1", "v");
+ m.put("k2", "v");
+ cache.putAll(m, lifespan, TimeUnit.MILLISECONDS);
+ while (System.currentTimeMillis() < startTime + lifespan + 100) {
+ if (System.currentTimeMillis() < startTime + lifespan) {
+ assert cache.get("k1").equals("v");
+ assert cache.get("k2").equals("v");
+ } else {
+ assert cache.get("k1") == null;
+ assert cache.get("k2") == null;
+ }
+ Thread.sleep(50);
+ }
+ }
+
+ public void testExpiryInPutIfAbsent() throws InterruptedException {
+ Cache cache = cm.getCache();
+ long startTime = System.currentTimeMillis();
+ long lifespan = 1000;
+ assert cache.putIfAbsent("k", "v", lifespan, TimeUnit.MILLISECONDS) == null;
+ while (System.currentTimeMillis() < startTime + lifespan + 100) {
+ if (System.currentTimeMillis() < startTime + lifespan) {
+ assert cache.get("k").equals("v");
+ } else {
+ assert cache.get("k") == null;
+ }
+ Thread.sleep(50);
+ }
+
+ cache.put("k", "v");
+ assert cache.putIfAbsent("k", "v", lifespan, TimeUnit.MILLISECONDS) != null;
+ }
+
+ public void testExpiryInReplace() throws InterruptedException {
+ Cache cache = cm.getCache();
+ long lifespan = 1000;
+ assert cache.get("k") == null;
+ assert cache.replace("k", "v", lifespan, TimeUnit.MILLISECONDS) == null;
+ assert cache.get("k") == null;
+ cache.put("k", "v-old");
+ assert cache.get("k").equals("v-old");
+ long startTime = System.currentTimeMillis();
+ assert cache.replace("k", "v", lifespan, TimeUnit.MILLISECONDS) != null;
+ assert cache.get("k").equals("v");
+ while (System.currentTimeMillis() < startTime + lifespan + 100) {
+ if (System.currentTimeMillis() < startTime + lifespan) {
+ assert cache.get("k").equals("v");
+ } else {
+ assert cache.get("k") == null;
+ }
+ Thread.sleep(50);
+ }
+
+ startTime = System.currentTimeMillis();
+ cache.put("k", "v");
+ assert cache.replace("k", "v", "v2", lifespan, TimeUnit.MILLISECONDS);
+ while (System.currentTimeMillis() < startTime + lifespan + 100) {
+ if (System.currentTimeMillis() < startTime + lifespan) {
+ assert cache.get("k").equals("v2");
+ } else {
+ assert cache.get("k") == null;
+ }
+ Thread.sleep(50);
+ }
+ }
+
+ // TODO test that expiry lifespans survive eviction + subsequent cache loading
+ // TODO same for passivation
+}
15 years, 10 months
JBoss Cache SVN: r7661 - in core/branches/flat/src: test/java/org/horizon/eviction/algorithms/lfu and 1 other directory.
by jbosscache-commits@lists.jboss.org
Author: manik.surtani(a)jboss.com
Date: 2009-02-06 13:10:10 -0500 (Fri, 06 Feb 2009)
New Revision: 7661
Modified:
core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lfu/LFUQueue.java
core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lfu/LfuQueueTest.java
Log:
Better LFU impl
Modified: core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lfu/LFUQueue.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lfu/LFUQueue.java 2009-02-06 17:46:20 UTC (rev 7660)
+++ core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lfu/LFUQueue.java 2009-02-06 18:10:10 UTC (rev 7661)
@@ -21,136 +21,129 @@
*/
package org.horizon.eviction.algorithms.lfu;
-import org.horizon.CacheException;
import org.horizon.eviction.algorithms.BaseEvictionQueue;
+import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
-import java.util.PriorityQueue;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
/**
* Least frequently used queue for use with the {@link org.horizon.eviction.algorithms.lfu.LFUAlgorithm}. This
- * implementation guarantees O(ln n) for put and remove operations, and 2O(ln n) for visits since this requires a
- * re-sort.
+ * implementation guarantees O (ln n) for put and remove, visit an iteration operations.
* <p/>
* // TODO investigate using a Fibonacci Heap or a Brodal Queue (http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.43.8133)
- * instead of a TreeMap to implement // Since this will give us O(1) for insert, iteration and O(ln n) for remove and
- * visit
+ * // another potential is Bernard Chazelle's SoftHeap
*
* @author Manik Surtani
* @since 1.0
*/
public class LFUQueue extends BaseEvictionQueue {
- PriorityQueue<VisitableKey> keys = new PriorityQueue<VisitableKey>();
- HashMap<Object, VisitableKey> keyMap = new HashMap<Object, VisitableKey>();
+ Map<Object, Integer> visitLog = new HashMap<Object, Integer>();
+ Map<Integer, Set<Object>> iterationMap = new TreeMap<Integer, Set<Object>>(new Comparator<Integer>() {
+ public int compare(Integer o1, Integer o2) {
+ return (o1 < o2 ? 1 : (o1 == o2 ? 0 : -1));
+ }
+ });
- public void visit(Object key) {
- VisitableKey vk = keyMap.get(key);
- if (vk != null) {
+ final Set<Object> getKeySet(int numVisits) {
+ Set<Object> set = iterationMap.get(numVisits);
+ if (set == null) {
+ set = new HashSet<Object>();
+ iterationMap.put(numVisits, set);
+ }
+ return set;
+ }
- // need to remove and add to reorder the tree
- keys.remove(vk);
- vk.visits++;
- keys.add(vk);
+ final void addToKeySet(Object key, int numVisits) {
+ getKeySet(numVisits).add(key);
+ }
+
+ final void removeFromKeySet(Object key, int numVisits) {
+ Set<Object> set = iterationMap.get(numVisits);
+ if (set != null) {
+ set.remove(key);
+ if (set.isEmpty()) iterationMap.remove(numVisits);
}
}
+ public void visit(Object key) {
+ Integer numVisits = visitLog.get(key);
+ if (numVisits != null) {
+ removeFromKeySet(key, numVisits);
+ numVisits++;
+ addToKeySet(key, numVisits);
+ visitLog.put(key, numVisits);
+ }
+ }
+
public boolean contains(Object key) {
- return keyMap.containsKey(key);
+ return visitLog.containsKey(key);
}
public void remove(Object key) {
- VisitableKey vk = keyMap.remove(key);
- if (vk != null) {
- if (!keys.remove(vk)) throw new CacheException("Problem removing key " + key);
+ Integer numVisits = visitLog.remove(key);
+ if (numVisits != null) {
+ removeFromKeySet(key, numVisits);
}
}
public void add(Object key) {
- if (!keyMap.containsKey(key)) {
- VisitableKey vk = new VisitableKey();
- vk.key = key;
- keyMap.put(key, vk);
- keys.add(vk);
+ if (!visitLog.containsKey(key)) {
+ // new, so numVisits is 1.
+ getKeySet(1).add(key);
+ visitLog.put(key, 1);
}
}
public int size() {
- return keyMap.size();
+ return visitLog.size();
}
@Override
public boolean isEmpty() {
- return keyMap.size() == 0 && keys.size() == 0;
+ return visitLog.size() == 0;
}
public void clear() {
- keyMap.clear();
- keys.clear();
+ visitLog.clear();
+ visitLog = new HashMap<Object, Integer>();
+ iterationMap.clear();
+ iterationMap = new HashMap<Integer, Set<Object>>();
}
public Iterator<Object> iterator() {
return new Iterator<Object>() {
+ Iterator<Set<Object>> setIterator = iterationMap.values().iterator();
+ Set<Object> currentKeySet;
+ Iterator<Object> keyIterator;
+ Object currentKey;
- Iterator<VisitableKey> vkIter = keys.iterator();
- Object lastKey;
-
public boolean hasNext() {
- return vkIter.hasNext();
+ while (keyIterator == null || !keyIterator.hasNext()) {
+ if (setIterator.hasNext()) {
+ currentKeySet = setIterator.next();
+ keyIterator = currentKeySet.iterator();
+ } else return false;
+ }
+ return true;
}
public Object next() {
- lastKey = vkIter.next().key;
- return lastKey;
+ if (keyIterator == null && !hasNext()) throw new IllegalStateException("Out of bounds");
+ return (currentKey = keyIterator.next());
}
public void remove() {
- if (lastKey == null) throw new IllegalStateException("Call next() before remove()!");
- vkIter.remove();
- keyMap.remove(lastKey);
+ if (currentKey == null) throw new IllegalStateException("Call next() before remove()!");
+ keyIterator.remove();
+ if (currentKeySet.isEmpty()) setIterator.remove();
+ visitLog.remove(currentKey);
}
};
}
-
- class VisitableKey implements Comparable {
- Object key;
- int visits;
-
- public int compareTo(Object o) {
- if (o == null) return -1;
- if (o instanceof VisitableKey) {
- VisitableKey other = (VisitableKey) o;
- return other.visits - visits;
- } else {
- return -1;
- }
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- VisitableKey that = (VisitableKey) o;
-
- if (key != null ? !key.equals(that.key) : that.key != null) return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- return key != null ? key.hashCode() : 0;
- }
-
- @Override
- public String toString() {
- return "VisitableKey{" +
- "key=" + key +
- ", visits=" + visits +
- '}';
- }
- }
-
}
Modified: core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lfu/LfuQueueTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lfu/LfuQueueTest.java 2009-02-06 17:46:20 UTC (rev 7660)
+++ core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lfu/LfuQueueTest.java 2009-02-06 18:10:10 UTC (rev 7661)
@@ -27,6 +27,10 @@
return s;
}
+ private int numVisits(LFUQueue queue, Object key) {
+ return queue.visitLog.get(key);
+ }
+
public void testOrder() throws Exception {
LFUQueue queue = (LFUQueue) getNewEvictionQueue();
fillQueue(queue, 500);
@@ -37,15 +41,16 @@
assert allKeys.isEmpty() : "Not all keys were encountered. Remaining keys " + allKeys;
for (int i = 1; i < 500; i += 2) queue.visit(i); // visit all the even keys
- System.out.println("Set: " + queue.keys);
- System.out.println("KeyMap: " + queue.keyMap);
+ for (Object key : queue) System.out.println("Key: " + key);
- assert (Integer) getFirstEntry(queue) % 2 == 1 : "We don't know which odd key this would be";
+ assert (Integer) getFirstEntry(queue) % 2 == 1 : "First key should be odd. Was " + getFirstEntry(queue);
+ System.out.println(queue.visitLog);
+
// now check the sort order.
for (Object key : queue) {
Integer k = (Integer) key;
- assert queue.keyMap.get(key).visits == k % 2 : "Expecting " + (k % 2) + " visits on key " + key + " but it was " + queue.keyMap.get(key).visits;
+ assert numVisits(queue, key) == 1 + (k % 2) : "Expecting " + (1 + (k % 2)) + " visits on key " + key + " but it was " + numVisits(queue, key);
}
int k = 0;
@@ -60,8 +65,7 @@
for (int i = 0; i < 500; i += 2) {
queue.visit(i);
- LFUQueue.VisitableKey data = queue.keyMap.get(i);
- assert 1 == data.visits : "Expected visit count to be 1 but it was " + data.visits + " on key " + i;
+ assert 2 == numVisits(queue, i) : "Expected visit count to be 2 but it was " + numVisits(queue, i) + " on key " + i;
if (i > 250) queue.visit(i); // visit again!
}
@@ -70,9 +74,9 @@
for (Object key : queue) {
k = (Integer) key;
if (k <= 250)
- assert 1 == queue.keyMap.get(key).visits : "Expected visit count to be 1 but it was " + queue.keyMap.get(key).visits + " on key " + key;
+ assert 2 == numVisits(queue, key) : "Expected visit count to be 2 but it was " + numVisits(queue, key) + " on key " + key;
else
- assert 2 == queue.keyMap.get(key).visits : "Expected visit count to be 2 but it was " + queue.keyMap.get(key).visits + " on key " + key;
+ assert 3 == numVisits(queue, key) : "Expected visit count to be 3 but it was " + numVisits(queue, key) + " on key " + key;
}
}
@@ -81,7 +85,6 @@
fillQueue(queue, 500);
assert 500 == queue.size();
- assert 500 == queue.keyMap.size();
int i = 0;
for (Iterator it = queue.iterator(); it.hasNext() && it.next() != null;) {
15 years, 10 months
JBoss Cache SVN: r7660 - core/trunk/src/test/java/org/jboss/cache/statetransfer.
by jbosscache-commits@lists.jboss.org
Author: mircea.markus
Date: 2009-02-06 12:46:20 -0500 (Fri, 06 Feb 2009)
New Revision: 7660
Modified:
core/trunk/src/test/java/org/jboss/cache/statetransfer/StateTransferConcurrencyTest.java
Log:
fixed UT
Modified: core/trunk/src/test/java/org/jboss/cache/statetransfer/StateTransferConcurrencyTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/statetransfer/StateTransferConcurrencyTest.java 2009-02-06 15:36:50 UTC (rev 7659)
+++ core/trunk/src/test/java/org/jboss/cache/statetransfer/StateTransferConcurrencyTest.java 2009-02-06 17:46:20 UTC (rev 7660)
@@ -470,7 +470,7 @@
RegionImpl region = (RegionImpl) cache2.getRegion(Fqn.ROOT, false);
// We expect a VISIT event for / and ADD events for /a, /a/b and /a/b/c
int nodeEventQueueSize = region.getEvictionEventQueue().size();
- assertTrue("Saw the expected number of node events", nodeEventQueueSize > 5); //one event happens on read root
+ assertTrue("Saw the expected number of node events", nodeEventQueueSize >= 4); //one event happens on read root
}
/**
15 years, 10 months
JBoss Cache SVN: r7659 - core/trunk/src/main/java/org/jboss/cache.
by jbosscache-commits@lists.jboss.org
Author: mircea.markus
Date: 2009-02-06 10:36:50 -0500 (Fri, 06 Feb 2009)
New Revision: 7659
Modified:
core/trunk/src/main/java/org/jboss/cache/RPCManagerImpl.java
Log:
fixed issue: for NBST, member list was retreived *before* connecting to the channel, which was incorrect
Modified: core/trunk/src/main/java/org/jboss/cache/RPCManagerImpl.java
===================================================================
--- core/trunk/src/main/java/org/jboss/cache/RPCManagerImpl.java 2009-02-06 14:42:45 UTC (rev 7658)
+++ core/trunk/src/main/java/org/jboss/cache/RPCManagerImpl.java 2009-02-06 15:36:50 UTC (rev 7659)
@@ -87,7 +87,7 @@
{
private Channel channel;
private final Log log = LogFactory.getLog(RPCManagerImpl.class);
- private List<Address> members;
+ private volatile List<Address> members;
private long replicationCount;
private long replicationFailures;
private boolean statisticsEnabled = false;
@@ -210,12 +210,10 @@
}
- List<Address> members = getMembers();
-
long start = System.currentTimeMillis();
if (nonBlocking)
{
- startNonBlockStateTransfer(members);
+ startNonBlockStateTransfer(getMembers());
}
else
{
@@ -227,7 +225,7 @@
log.info("Cache local address is " + getLocalAddress());
}
- if (members.size() > 1)
+ if (getMembers().size() > 1)
{
messageListener.waitForState();
}
15 years, 10 months
JBoss Cache SVN: r7658 - core/trunk/src/test/java/org/jboss/cache/statetransfer.
by jbosscache-commits@lists.jboss.org
Author: mircea.markus
Date: 2009-02-06 09:42:45 -0500 (Fri, 06 Feb 2009)
New Revision: 7658
Modified:
core/trunk/src/test/java/org/jboss/cache/statetransfer/NonBlockingStateTransferTest.java
Log:
added test name and better cleanup in case of failure
Modified: core/trunk/src/test/java/org/jboss/cache/statetransfer/NonBlockingStateTransferTest.java
===================================================================
--- core/trunk/src/test/java/org/jboss/cache/statetransfer/NonBlockingStateTransferTest.java 2009-02-06 12:27:16 UTC (rev 7657)
+++ core/trunk/src/test/java/org/jboss/cache/statetransfer/NonBlockingStateTransferTest.java 2009-02-06 14:42:45 UTC (rev 7658)
@@ -15,6 +15,8 @@
import java.io.Serializable;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.List;
+import java.util.ArrayList;
import org.jboss.cache.Cache;
import org.jboss.cache.CacheSPI;
@@ -25,8 +27,9 @@
import org.jboss.cache.factories.UnitTestConfigurationFactory;
import org.jboss.cache.util.TestingUtil;
import org.testng.annotations.Test;
+import org.testng.annotations.AfterMethod;
-@Test(groups="functional")
+@Test(groups="functional", testName = "statetransfer.NonBlockingStateTransferTest")
public class NonBlockingStateTransferTest
{
public static final Fqn A = Fqn.fromString("/a");
@@ -43,6 +46,18 @@
public static final Integer TWENTY = 20;
public static final Integer FORTY = 40;
+ private List<Cache> createdCaches = new ArrayList<Cache>();
+
+ @AfterMethod
+ public void clearCaches()
+ {
+ for (Cache c : createdCaches)
+ {
+ TestingUtil.killCaches(c);
+ }
+ createdCaches.clear();
+ }
+
public static class DelayTransfer implements Serializable
{
private transient int count;
@@ -128,7 +143,9 @@
cache.create();
cache.start();
+ createdCaches.add(cache);
return cache;
+
}
public void testInitialStateTransfer() throws Exception
@@ -143,8 +160,6 @@
TestingUtil.blockUntilViewsReceived(new CacheSPI[]{cache1, cache2}, 60000);
verifyInitialData(cache2);
-
- TestingUtil.killCaches(cache1, cache2);
}
@@ -197,8 +212,6 @@
for (int c = 0; c < count; c++)
assertEquals(c, cache2.get("/test" + c, "test"));
-
- TestingUtil.killCaches(cache1, cache2, cache3);
}
private void verifyInitialData(CacheSPI<Object, Object> cache2)
@@ -244,7 +257,5 @@
for (int c = 0; c < count; c++)
assertEquals(c, cache2.get("/test" + c, "test"));
-
- TestingUtil.killCaches(cache1, cache2);
}
}
15 years, 10 months
JBoss Cache SVN: r7657 - in core/branches/flat/src: main/java/org/horizon/eviction and 17 other directories.
by jbosscache-commits@lists.jboss.org
Author: manik.surtani(a)jboss.com
Date: 2009-02-06 07:27:16 -0500 (Fri, 06 Feb 2009)
New Revision: 7657
Added:
core/branches/flat/src/main/java/org/horizon/container/CachedEntry.java
core/branches/flat/src/main/java/org/horizon/eviction/events/
core/branches/flat/src/main/java/org/horizon/eviction/events/EvictionEvent.java
core/branches/flat/src/main/java/org/horizon/eviction/events/InUseEvictionEvent.java
core/branches/flat/src/main/java/org/horizon/util/AbstractMap.java
core/branches/flat/src/main/java/org/horizon/util/BidirectionalLinkedHashMap.java
core/branches/flat/src/main/java/org/horizon/util/ReversibleSet.java
core/branches/flat/src/main/java/org/horizon/util/VisitableLinkedHashSet.java
core/branches/flat/src/test/java/org/horizon/SkipListTest.java
core/branches/flat/src/test/java/org/horizon/util/BidirectionalLinkedHashMapTest.java
core/branches/flat/src/test/java/org/horizon/util/VisitableLinkedHashSetTest.java
Removed:
core/branches/flat/src/main/java/org/horizon/eviction/EntryEvictionData.java
core/branches/flat/src/main/java/org/horizon/eviction/EvictionEvent.java
Modified:
core/branches/flat/src/main/java/org/horizon/container/DataContainer.java
core/branches/flat/src/main/java/org/horizon/container/UnsortedDataContainer.java
core/branches/flat/src/main/java/org/horizon/eviction/EvictionAlgorithm.java
core/branches/flat/src/main/java/org/horizon/eviction/EvictionManager.java
core/branches/flat/src/main/java/org/horizon/eviction/EvictionManagerImpl.java
core/branches/flat/src/main/java/org/horizon/eviction/EvictionQueue.java
core/branches/flat/src/main/java/org/horizon/eviction/algorithms/BaseEvictionAlgorithm.java
core/branches/flat/src/main/java/org/horizon/eviction/algorithms/BaseEvictionQueue.java
core/branches/flat/src/main/java/org/horizon/eviction/algorithms/fifo/FIFOAlgorithm.java
core/branches/flat/src/main/java/org/horizon/eviction/algorithms/fifo/FIFOQueue.java
core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lfu/LFUAlgorithm.java
core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lfu/LFUQueue.java
core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lru/LRUAlgorithm.java
core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lru/LRUQueue.java
core/branches/flat/src/main/java/org/horizon/eviction/algorithms/mru/MRUAlgorithm.java
core/branches/flat/src/main/java/org/horizon/eviction/algorithms/mru/MRUQueue.java
core/branches/flat/src/main/java/org/horizon/eviction/algorithms/nullalgo/NullEvictionAlgorithm.java
core/branches/flat/src/main/java/org/horizon/eviction/algorithms/nullalgo/NullEvictionQueue.java
core/branches/flat/src/main/java/org/horizon/interceptors/EvictionInterceptor.java
core/branches/flat/src/main/java/org/horizon/util/FastCopyHashMap.java
core/branches/flat/src/test/java/org/horizon/eviction/EvictionFunctionalTest.java
core/branches/flat/src/test/java/org/horizon/eviction/EvictionManagerTest.java
core/branches/flat/src/test/java/org/horizon/eviction/algorithms/BaseAlgorithmTest.java
core/branches/flat/src/test/java/org/horizon/eviction/algorithms/BaseQueueTest.java
core/branches/flat/src/test/java/org/horizon/eviction/algorithms/fifo/FifoAlgorithmTest.java
core/branches/flat/src/test/java/org/horizon/eviction/algorithms/fifo/FifoQueueTest.java
core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lfu/LfuAlgorithmTest.java
core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lfu/LfuQueueTest.java
core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lru/LruAlgorithmTest.java
core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lru/LruQueueTest.java
core/branches/flat/src/test/java/org/horizon/eviction/algorithms/mru/MruAlgorithmTest.java
core/branches/flat/src/test/java/org/horizon/eviction/algorithms/mru/MruQueueTest.java
Log:
Rewrote eviction
Added: core/branches/flat/src/main/java/org/horizon/container/CachedEntry.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/container/CachedEntry.java (rev 0)
+++ core/branches/flat/src/main/java/org/horizon/container/CachedEntry.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -0,0 +1,9 @@
+package org.horizon.container;
+
+/**
+ * This is a wrapper for a cached entry, containing a reference to key, value and metadata.
+ *
+ * @author Manik Surtani
+ * @since 1.0
+ */
+
Modified: core/branches/flat/src/main/java/org/horizon/container/DataContainer.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/container/DataContainer.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/container/DataContainer.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -47,4 +47,8 @@
void clear();
Set<K> keySet();
+
+ long getCreatedTimestamp(K key);
+
+ long getModifiedTimestamp(K key);
}
Modified: core/branches/flat/src/main/java/org/horizon/container/UnsortedDataContainer.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/container/UnsortedDataContainer.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/container/UnsortedDataContainer.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -34,34 +34,60 @@
* @since 1.0
*/
public class UnsortedDataContainer<K, V> implements DataContainer<K, V> {
- private final ConcurrentMap<Object, Object> data = new ConcurrentHashMap<Object, Object>();
+ private final ConcurrentMap<Object, CachedValue<V>> data = new ConcurrentHashMap<Object, CachedValue<V>>();
private static final Object NULL = new Object();
@SuppressWarnings("unchecked")
- private Object maskNull(Object o) {
- return o == null ? (K) NULL : (K) o;
+ private K maskNullKey(K o) {
+ return o == null ? (K) NULL : o;
}
- private Object unmaskNull(Object o) {
+ private K unmaskNullKey(K o) {
return (o == NULL) ? null : o;
}
@SuppressWarnings("unchecked")
public V get(K k) {
- return (V) unmaskNull(data.get(maskNull(k)));
+ CachedValue<V> cv = data.get(maskNullKey(k));
+ if (cv != null) {
+ cv.touch();
+ return cv.value;
+ } else {
+ return null;
+ }
}
public void put(K k, V v) {
- data.put(maskNull(k), maskNull(v));
+ K maskedKey = maskNullKey(k);
+
+ CachedValue<V> cv = data.get(maskedKey);
+ if (cv == null) {
+ cv = new CachedValue<V>(v);
+ data.put(maskedKey, cv);
+ } else {
+ cv.touch();
+ cv.value = v;
+ }
}
public boolean containsKey(K k) {
- return data.containsKey(maskNull(k));
+ return data.containsKey(maskNullKey(k));
}
+ public long getModifiedTimestamp(K key) {
+ CachedValue cv = data.get(maskNullKey(key));
+ return cv == null ? -1 : cv.modified;
+ }
+
+ public long getCreatedTimestamp(K key) {
+ CachedValue cv = data.get(maskNullKey(key));
+ return cv == null ? -1 : cv.created;
+ }
+
@SuppressWarnings("unchecked")
public V remove(K k) {
- return (V) unmaskNull(data.remove(maskNull(k)));
+ CachedValue<V> cv = data.remove(maskNullKey(k));
+ return cv == null ? null : cv.value;
}
public int size() {
@@ -96,7 +122,7 @@
}
public boolean contains(Object o) {
- return realSet.contains(maskNull(o));
+ return realSet.contains(maskNullKey((K) o));
}
public boolean remove(Object o) {
@@ -121,11 +147,32 @@
@SuppressWarnings("unchecked")
public K next() {
- return (K) unmaskNull(realIterator.next());
+ return unmaskNullKey((K) realIterator.next());
}
public void remove() {
throw new UnsupportedOperationException();
}
}
-}
+
+ private static class CachedValue<V> {
+ V value;
+ long created;
+ long modified;
+
+ public CachedValue(V value) {
+ this.value = value;
+ created = modified = System.currentTimeMillis();
+ }
+
+ public CachedValue(V value, long created, long modified) {
+ this.value = value;
+ this.created = created;
+ this.modified = modified;
+ }
+
+ public void touch() {
+ modified = System.currentTimeMillis();
+ }
+ }
+}
\ No newline at end of file
Deleted: core/branches/flat/src/main/java/org/horizon/eviction/EntryEvictionData.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/EntryEvictionData.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/eviction/EntryEvictionData.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -1,167 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file 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.horizon.eviction;
-
-/**
- * Value object used in queue
- *
- * @author Ben Wang 2-2004
- * @author Daniel Huang - dhuang(a)jboss.org
- * @since 1.0
- */
-public class EntryEvictionData {
- private long modifiedTimeStamp;
- private long creationTimeStamp;
- private int numberOfVisits;
- private Object key;
-
- private long inUseTimeoutTimestamp;
- private boolean currentlyInUse = false;
-
- /**
- * Private constructor that automatically sets the creation time stamp of the node entry.
- */
- private EntryEvictionData() {
- this.creationTimeStamp = System.currentTimeMillis();
- }
-
- public EntryEvictionData(Object key) {
- this();
- setKey(key);
- }
-
- public EntryEvictionData(int numberOfVisits, long modifiedTimeStamp, Object key) {
- this(key);
- this.numberOfVisits = numberOfVisits;
- this.modifiedTimeStamp = modifiedTimeStamp;
- }
-
- /**
- * Is the node currently in use.
- *
- * @return true if the node is currently marked as in use.
- */
- public boolean isCurrentlyInUse() {
- return currentlyInUse;
- }
-
- public void setCurrentlyInUse(boolean currentlyInUse, long inUseTimeout) {
- this.currentlyInUse = currentlyInUse;
- if (inUseTimeout > 0) {
- this.inUseTimeoutTimestamp = System.currentTimeMillis() + inUseTimeout;
- }
- }
-
- public long getInUseTimeoutTimestamp() {
- return this.inUseTimeoutTimestamp;
- }
-
- /**
- * Get modified time stamp. This stamp is created during the node is processed so it has some fuzy tolerance in
- * there.
- *
- * @return The last modified time stamp
- */
- public long getModifiedTimeStamp() {
- return modifiedTimeStamp;
- }
-
- public void setModifiedTimeStamp(long modifiedTimeStamp) {
- this.modifiedTimeStamp = modifiedTimeStamp;
- }
-
- /**
- * Get the time stamp for when the node entry was created.
- *
- * @return The node entry creation time stamp
- */
- public long getCreationTimeStamp() {
- return creationTimeStamp;
- }
-
- public void setCreationTimeStamp(long creationTimeStamp) {
- this.creationTimeStamp = creationTimeStamp;
- }
-
- public int getNumberOfVisits() {
- return numberOfVisits;
- }
-
- public void setNumberOfVisits(int numberOfVisits) {
- this.numberOfVisits = numberOfVisits;
- }
-
- public Object getKey() {
- return key;
- }
-
- void setKey(Object key) {
- this.key = key;
- }
-
- @Override
- public int hashCode() {
- return key.hashCode();
- }
-
- public boolean isNodeInUseAndNotTimedOut() {
- if (isCurrentlyInUse()) {
- if (getInUseTimeoutTimestamp() == 0) {
- return true;
- }
-
- if (System.currentTimeMillis() < getInUseTimeoutTimestamp()) {
- return true;
- }
- }
- return false;
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof EntryEvictionData))
- return false;
- EntryEvictionData ne = (EntryEvictionData) o;
- return key.equals(ne.getKey());
- }
-
- @Override
- public String toString() {
- StringBuilder output = new StringBuilder();
- output.append("Fqn: ");
- if (key != null) {
- output.append(key);
- } else {
- output.append(" null");
- }
-
- output.append(" CreateTime: ").append(this.getCreationTimeStamp());
- output.append(" Visits: ").append(this.getNumberOfVisits());
- output.append(" ModifiedTime: ").append(this.getModifiedTimeStamp());
- output.append(" CurrentlyInUse: ").append(this.isCurrentlyInUse());
- return output.toString();
- }
-
- public void incrementNumberOfVisits() {
- numberOfVisits++;
- }
-}
Modified: core/branches/flat/src/main/java/org/horizon/eviction/EvictionAlgorithm.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/EvictionAlgorithm.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/eviction/EvictionAlgorithm.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -23,58 +23,58 @@
import org.horizon.Cache;
import org.horizon.config.EvictionAlgorithmConfig;
-import org.horizon.eviction.EvictionEvent.Type;
+import org.horizon.container.DataContainer;
+import org.horizon.eviction.events.EvictionEvent;
+import org.horizon.eviction.events.EvictionEvent.Type;
+import org.horizon.lifecycle.Lifecycle;
import java.util.concurrent.BlockingQueue;
/**
- * Interface for all eviction algorithms.
- * <p/>
- * Note: None of the Eviction classes are thread safe. It is assumed that an individual instance of an EvictionPolicy/
- * EvictionAlgorithm/EvictionQueue/EvictionConfiguration are only operated on by one thread at any given time.
+ * Interface for all eviction algorithms. There is no requirement for implementations to be thread safe, as the {@link
+ * org.horizon.eviction.EvictionManager} will guarantee that only a single thread will access the algorithm
+ * implementation at any given time.
*
- * @author (various)
+ * @author Manik Surtani
* @since 1.0
*/
-public interface EvictionAlgorithm {
+public interface EvictionAlgorithm extends Lifecycle {
/**
* Entry point for eviction algorithm. Invoking this will cause the algorithm to process the queue of {@link
* EvictionEvent}s passed in.
*
- * @param queue blocking queue of {@link org.horizon.eviction.EvictionEvent}s to process.
+ * @param queue blocking queue of {@link org.horizon.eviction.events.EvictionEvent}s to process.
* @throws EvictionException if there is a problem processing any of these events
*/
void process(BlockingQueue<EvictionEvent> queue) throws EvictionException;
/**
- * Reset the whole eviction queue. The queue may need to be reset due to corrupted state, for example.
+ * Reset the eviction queue.
*/
void resetEvictionQueue();
/**
- * Get the EvictionQueue implementation used by this algorithm.
+ * Sets the eviction action policy, so the algorithm knows what to do when an entry is to be evicted.
*
- * @return an EvictionQueue instance that is able to sort keys for eviction as dictated by the eviction algorithm
- */
- EvictionQueue getEvictionQueue();
-
- /**
- * Sets the eviction action policy, so the algorithm knows what to do when a node is to be evicted.
- *
* @param evictionAction eviction action instance to use
*/
void setEvictionAction(EvictionAction evictionAction);
/**
- * Assigns the algorithm instance to a given Cache.
+ * Initializes the algorithm instance by passing in the cache instance, data container and configuration
*
* @param cache cache to work with
+ * @param dataContiner the cache's data container
* @param evictionAlgorithmConfig algorithm configuration to use
*/
- void assignToCache(Cache<?, ?> cache, EvictionAlgorithmConfig evictionAlgorithmConfig);
+ void init(Cache<?, ?> cache, DataContainer<?, ?> dataContiner,
+ EvictionAlgorithmConfig evictionAlgorithmConfig);
/**
- * Tests whether the algorithm would ignore certain event types on certain Fqns.
+ * Tests whether the algorithm would ignore certain event types. This is an optimization on the {@link
+ * org.horizon.eviction.EvictionManager} so that events that would eventually be ignored in {@link
+ * #process(java.util.concurrent.BlockingQueue)} would not be added to the event queue, keeping the queue smaller and
+ * leaving more space to deal with more important event types.
*
* @param eventType event type to test for
* @return true if the event representing the parameters would be ignored by this algorithm or not.
@@ -82,11 +82,6 @@
boolean canIgnoreEvent(Type eventType);
/**
- * Invoked by the region manager when the enclosing region is initialized.
- */
- void initialize();
-
- /**
* @return the type of the {@link org.horizon.config.EvictionAlgorithmConfig} bean used to configure this
* implementation of {@link org.horizon.eviction.EvictionAlgorithm}
*/
Deleted: core/branches/flat/src/main/java/org/horizon/eviction/EvictionEvent.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/EvictionEvent.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/eviction/EvictionEvent.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -1,107 +0,0 @@
-/*
- * JBoss, Home of Professional Open Source.
- * Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors
- * as indicated by the @author tags. See the copyright.txt file 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.horizon.eviction;
-
-/**
- * An eviction event records activity on nodes in the cache. These are recorded for processing later.
- *
- * @author (various)
- * @since 1.0
- */
-public class EvictionEvent {
- private Object key;
- private Type type;
-
- private long inUseTimeout;
- private long creationTimestamp;
-
- public EvictionEvent() {
- }
-
- public static enum Type {
- ADD_ENTRY_EVENT,
- REMOVE_ENTRY_EVENT,
- VISIT_ENTRY_EVENT,
- CLEAR_CACHE_EVENT,
- MARK_IN_USE_EVENT,
- UNMARK_IN_USE_EVENT
- }
-
- public EvictionEvent(Object key, Type type) {
- this.key = key;
- this.type = type;
- this.creationTimestamp = System.currentTimeMillis();
- }
-
- public EvictionEvent(Object key, Type type, long creationTimestamp) {
- this.key = key;
- this.type = type;
- this.creationTimestamp = creationTimestamp;
- }
-
- public long getCreationTimestamp() {
- return creationTimestamp;
- }
-
- public void setCreationTimestamp(long creationTimestamp) {
- this.creationTimestamp = creationTimestamp;
- }
-
- public long getInUseTimeout() {
- return inUseTimeout;
- }
-
- public void setInUseTimeout(long inUseTimeout) {
- this.inUseTimeout = inUseTimeout;
- }
-
- public Object getKey() {
- return key;
- }
-
- public void setKey(Object key) {
- this.key = key;
- }
-
- public void setEventType(Type event) {
- type = event;
- }
-
- public Type getEventType() {
- return type;
- }
-
- @Override
- public String toString() {
- return "EvictedEventNode[key=" + key + " event=" + type + "]";
- }
-
- /**
- * Copies this evicted event node to create a new one with the same values, except with a new Fqn root.
- *
- * @param key new Fqn root to use
- * @return a new EvictedEventNode instance
- */
- public EvictionEvent copy(Object key) {
- return new EvictionEvent(key, type);
- }
-}
Modified: core/branches/flat/src/main/java/org/horizon/eviction/EvictionManager.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/EvictionManager.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/eviction/EvictionManager.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -1,6 +1,7 @@
package org.horizon.eviction;
import net.jcip.annotations.ThreadSafe;
+import org.horizon.eviction.events.EvictionEvent;
import org.horizon.factories.annotations.NonVolatile;
import org.horizon.factories.scopes.Scope;
import org.horizon.factories.scopes.Scopes;
@@ -9,7 +10,23 @@
import java.util.concurrent.TimeUnit;
/**
- * Central component that deals with eviction of cache entries
+ * Central component that deals with eviction of cache entries. This component manages a queue of events taking place
+ * on the cache. This queue is typically populated by the {@link org.horizon.interceptors.EvictionInterceptor} calling
+ * {@link #registerEvictionEvent(Object, org.horizon.eviction.events.EvictionEvent.Type)}.
+ * <p/>
+ * In certain special cases, this component may be manipulated by direct interaction with end user code. An example of
+ * this is the use of the {@link #markKeyCurrentlyInUse(Object, long, java.util.concurrent.TimeUnit)} and {@link #
+ * markKeyCurrentlyInUse (Object, long, java.util.concurrent.TimeUnit)} methods, to prevent certain entries from being
+ * evicted even if their "time is up".
+ * <p/>
+ * Another case where direct interaction may take place is where no eviction thread is configured (e.g., with
+ * <tt>wakeUpInterval</tt> set to <tt>0</tt>, the same as {@link org.horizon.config.EvictionConfig#setWakeUpInterval(long)}
+ * being set to <tt>0</tt>).
+ * <p/>
+ * In such cases, user code may want to manually process the eviction queue, by calling {@link
+ * #processEvictionQueues()}. This is sometimes desirable, such as when user code already has a maintenance thread
+ * periodically running and does not want to incur the cost of an additional eviction thread.
+ * <p/>
*
* @author Mircea.Markus(a)jboss.com
* @author Manik Surtani
@@ -21,17 +38,17 @@
public interface EvictionManager extends Lifecycle {
/**
- * Processes the eviction event queue by passing it to the configured eviction algorithm
+ * Processes the eviction event queue.
*/
void processEvictionQueues();
/**
- * Clears the eviction event queue used for processing eviction.
+ * Clears the eviction event queue.
*/
void resetEvictionQueues();
/**
- * Registers an eviction event eviction event queue for later processing by {@link #processEvictionQueues()}.
+ * Registers an event on the eviction event queue for later processing by {@link #processEvictionQueues()}.
*
* @param key key of the cache entry to register an event for
* @param eventType type of event
@@ -41,19 +58,19 @@
/**
* Marks a key as being currently in use, so that it is not considered for eviction even if the condifured algorithm
- * selects it for eviction.
+ * selects it for eviction. It may be considered for eviction when the queue is next processed though.
*
* @param key entry key to mark
* @param timeout duration for which the entry should be considered as in-use
* @param unit time unit
*/
- void markNodeCurrentlyInUse(Object key, long timeout, TimeUnit unit);
+ void markKeyCurrentlyInUse(Object key, long timeout, TimeUnit unit);
/**
- * Un-marks a key as being currently in use, if it was marked using {@link #markNodeCurrentlyInUse(Object, long,
- * java.util.concurrent.TimeUnit)} Unmarking makes the node vulnerable to eviction again.
+ * Un-marks a key as being currently in use, if it was marked using {@link #markKeyCurrentlyInUse(Object, long,
+ * java.util.concurrent.TimeUnit)} Unmarking makes the entry vulnerable to eviction again.
*
* @param key entry key to unmark
*/
- void unmarkNodeCurrentlyInUse(Object key);
+ void unmarkKeyCurrentlyInUse(Object key);
}
Modified: core/branches/flat/src/main/java/org/horizon/eviction/EvictionManagerImpl.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/EvictionManagerImpl.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/eviction/EvictionManagerImpl.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -5,6 +5,9 @@
import org.horizon.config.Configuration;
import org.horizon.config.EvictionAlgorithmConfig;
import org.horizon.config.EvictionConfig;
+import org.horizon.container.DataContainer;
+import org.horizon.eviction.events.EvictionEvent;
+import org.horizon.eviction.events.InUseEvictionEvent;
import org.horizon.factories.KnownComponentNames;
import org.horizon.factories.annotations.ComponentName;
import org.horizon.factories.annotations.Inject;
@@ -35,20 +38,22 @@
ScheduledFuture evictionTask;
// event queue
- private BlockingQueue<EvictionEvent> evictionEventQueue;
+ BlockingQueue<EvictionEvent> evictionEventQueue;
int capacityWarnThreshold = 0;
// components to be injected
ScheduledExecutorService executor;
Configuration configuration;
Cache cache;
+ private DataContainer<?, ?> dataContainer;
@Inject
public void initialize((a)ComponentName(KnownComponentNames.EVICTION_SCHEDULED_EXECUTOR) ScheduledExecutorService executor,
- Configuration configuration, Cache cache) {
+ Configuration configuration, Cache cache, DataContainer dataContainer) {
this.executor = executor;
this.configuration = configuration;
this.cache = cache;
+ this.dataContainer = dataContainer;
}
@Start
@@ -68,24 +73,28 @@
// 2. now ensure we instantiate the eviction algorithm class
evictionAlgorithmConfig = evictionConfig.getAlgorithmConfig();
evictionAlgorithm = createEvictionAlgorithm(evictionAlgorithmConfig, evictionConfig.getActionPolicyClass());
+ evictionAlgorithm.start();
// 3. And finally set up the eviction timer task
if (evictionConfig.getWakeUpInterval() <= 0) {
log.info("wakeUpInterval is <= 0, not starting eviction thread");
} else {
- evictionTask = executor.scheduleWithFixedDelay(new Runnable() {
- public void run() {
- processEvictionQueues();
- }
- }, evictionConfig.getWakeUpInterval(), evictionConfig.getWakeUpInterval(), TimeUnit.MILLISECONDS);
+ evictionTask = executor.scheduleWithFixedDelay(new ScheduledTask(), evictionConfig.getWakeUpInterval(),
+ evictionConfig.getWakeUpInterval(), TimeUnit.MILLISECONDS);
}
}
}
+ class ScheduledTask implements Runnable {
+ public void run() {
+ processEvictionQueues();
+ }
+ }
+
@Stop
public void stop() {
if (evictionTask != null) evictionTask.cancel(true);
- if (evictionAlgorithm != null) evictionAlgorithm.resetEvictionQueue();
+ if (evictionAlgorithm != null) evictionAlgorithm.stop();
evictionAlgorithm = null;
if (evictionEventQueue != null) evictionEventQueue.clear();
evictionEventQueue = null;
@@ -106,11 +115,11 @@
return ee;
}
- public void markNodeCurrentlyInUse(Object key, long duration, TimeUnit unit) {
- registerEvictionEvent(key, EvictionEvent.Type.MARK_IN_USE_EVENT).setInUseTimeout(unit.toMillis(duration));
+ public void markKeyCurrentlyInUse(Object key, long duration, TimeUnit unit) {
+ registerEvictionEvent(new InUseEvictionEvent(key, unit.toMillis(duration)));
}
- public void unmarkNodeCurrentlyInUse(Object key) {
+ public void unmarkKeyCurrentlyInUse(Object key) {
registerEvictionEvent(key, EvictionEvent.Type.UNMARK_IN_USE_EVENT);
}
@@ -145,7 +154,7 @@
if (trace) log.trace("Instantiating {0}", algoConfig.getEvictionAlgorithmClassName());
EvictionAlgorithm algorithm = (EvictionAlgorithm) Util.getInstance(algoConfig.getEvictionAlgorithmClassName());
algorithm.setEvictionAction(evictionAction);
- algorithm.assignToCache(cache, algoConfig);
+ algorithm.init(cache, dataContainer, algoConfig);
return algorithm;
} catch (Exception e) {
log.fatal("Unable to instantiate eviction algorithm {0}", e, algoConfig.getEvictionAlgorithmClassName());
Modified: core/branches/flat/src/main/java/org/horizon/eviction/EvictionQueue.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/EvictionQueue.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/eviction/EvictionQueue.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -22,15 +22,23 @@
package org.horizon.eviction;
/**
- * Eviction Queue interface defines a contract for the Eviction Queue implementations used by EvictionPolicies.
+ * The eviction queue interface defines a contract for the queue implementations used by {@link EvictionAlgorithm}
+ * implementations. Queues are meant to be sorted in order of preference of keys for eviction, such that by using the
+ * iterator exposed by the queue, the first element would be the most preferable to evict.
* <p/>
- * Note: None of the Eviction classes are thread safe. It is assumed that an individual instance of an EvictionPolicy/
- * EvictionAlgorithm/EvictionQueue/EvictionConfiguration are only operated on by one thread at any given time.
+ * The iterator provided should support {@link java.util.Iterator#remove()}.
+ * <p/>
+ * Note that there is no requirement that implementations should be thread safe, as the {@link
+ * org.horizon.eviction.EvictionManager} would guarantee that only a single thread ever acccess the eviction queue at
+ * any given time.
+ * <p/>
+ * Note that this is not to be confused with a JDK {@link java.util.Queue}, as it has no bearing or relationship to one,
+ * although implementations may choose to use a JDK {@link java.util.Queue} as an underlying data structure.
*
- * @author Daniel Huang (dhuang(a)jboss.org)
+ * @author Manik Surtani
* @since 1.0
*/
-public interface EvictionQueue extends Iterable<EntryEvictionData> {
+public interface EvictionQueue extends Iterable<Object> {
/**
* Tests whether queue is empty
@@ -39,16 +47,13 @@
*/
boolean isEmpty();
- //
/**
- * Retrieve eviction entry data representing a specific key
- * <p/>
- * This will return null if the entry is not found.
+ * Informs the queue that an entry has been visited. Implementations may choose to ignore this invocation, or use it
+ * to update internal ordering based on the type of queue.
*
- * @param key key to find
- * @return eviction entry data representing the specified key
+ * @param key key visited
*/
- EntryEvictionData get(Object key);
+ void visit(Object key);
/**
@@ -69,9 +74,9 @@
/**
* Add entry eviction data to the queue
*
- * @param entryEvictionData to add. Must not be null.
+ * @param key key to add. Must not be null.
*/
- void add(EntryEvictionData entryEvictionData);
+ void add(Object key);
/**
* Get the size of the queue
Modified: core/branches/flat/src/main/java/org/horizon/eviction/algorithms/BaseEvictionAlgorithm.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/algorithms/BaseEvictionAlgorithm.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/eviction/algorithms/BaseEvictionAlgorithm.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -23,28 +23,28 @@
import org.horizon.Cache;
import org.horizon.config.EvictionAlgorithmConfig;
-import org.horizon.eviction.EntryEvictionData;
+import org.horizon.container.DataContainer;
import org.horizon.eviction.EvictionAction;
import org.horizon.eviction.EvictionAlgorithm;
import org.horizon.eviction.EvictionAlgorithmConfigBase;
-import org.horizon.eviction.EvictionEvent;
-import org.horizon.eviction.EvictionEvent.Type;
import org.horizon.eviction.EvictionException;
import org.horizon.eviction.EvictionQueue;
+import org.horizon.eviction.events.EvictionEvent;
+import org.horizon.eviction.events.EvictionEvent.Type;
+import org.horizon.eviction.events.InUseEvictionEvent;
import org.horizon.logging.Log;
import org.horizon.logging.LogFactory;
+import java.util.HashMap;
import java.util.Iterator;
+import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
- * Abstract Event Processing Eviction Algorithm. This class is used to implement basic event processing for Eviction
- * Algorithms. To extend this abstract class to make an Eviction Algorithm, implement the abstract methods and a
- * policy.
+ * Abstract Eviction Algorithm. This class is used to implement basic event processing for eviction algorithms.
*
- * @author (various)
* @author <a href="mailto:galder.zamarreno@jboss.com">Galder Zamarreno</a>
* @author Manik Surtani
* @since 1.0
@@ -53,58 +53,71 @@
private static final Log log = LogFactory.getLog(BaseEvictionAlgorithm.class);
private static final boolean trace = log.isTraceEnabled();
- protected EvictionAction evictionAction;
- protected EvictionAlgorithmConfigBase evictionAlgorithmConfig;
- protected BlockingQueue recycleQueue;
+ protected EvictionAction action;
+ protected EvictionAlgorithmConfigBase config;
+ // blocking queue of cache keys
+ protected BlockingQueue<Object> recycleQueue;
protected EvictionQueue evictionQueue;
protected Cache cache;
+ protected DataContainer dataContainer;
+ // cache keys and expiry time
+ protected Map<Object, Long> keysInUse = new HashMap<Object, Long>();
/**
- * This method will create an EvictionQueue implementation and prepare it for use.
+ * This method will create a new EvictionQueue instance and prepare it for use.
*
- * @return The created EvictionQueue to be used as the eviction queue for this algorithm.
- * @see org.horizon.eviction.EvictionQueue
+ * @return A new EvictionQueue
*/
- protected abstract EvictionQueue setupEvictionQueue() throws EvictionException;
+ protected abstract EvictionQueue createEvictionQueue();
/**
- * This method will check whether the given entry should be evicted or not.
+ * This method tests whether the next entry should be evicted due to cache size constraints being hit.
*
- * @param entry entry to consider
- * @return true if the entry is to be evicted, false otherwise
+ * @return true if the entry should be evicted, false if no more evictions are needed.
*/
- protected boolean shouldEvictEntry(EntryEvictionData entry, int originalQueueSize) {
- // first test max and min entries
- if (evictionAlgorithmConfig.getMaxEntries() > -1) {
- int currentSize = evictionQueue.size();
+ protected boolean needToEvict(int originalQueueSize) {
+ // test max and min entries
+ if (config.getMaxEntries() > -1) {
// entry count eviction is enabled!
- if (evictionAlgorithmConfig.getMaxEntries() < currentSize) return true;
- if (evictionAlgorithmConfig.getMaxEntries() < originalQueueSize &&
- evictionAlgorithmConfig.getMinEntries() > -1 &&
- evictionAlgorithmConfig.getMinEntries() < currentSize)
+ int currentSize = evictionQueue.size();
+
+ // exceeded max entries!
+ if (config.getMaxEntries() < currentSize) return true;
+
+ // below max entries now, but the current run exceeded, and we need to get down to minEntries (if configured)
+ if (config.getMaxEntries() < originalQueueSize &&
+ config.getMinEntries() > -1 &&
+ config.getMinEntries() < currentSize)
return true;
}
-
+ // configured to hold unlimited entries, or we haven't hit any thresholds yet
return false;
}
protected BaseEvictionAlgorithm() {
- recycleQueue = new LinkedBlockingQueue(500000);
+ // size of the recycle queue
+ // TODO make this configurable!!
+ recycleQueue = new LinkedBlockingQueue<Object>(500000);
}
- public synchronized void initialize() {
- if (evictionQueue == null) {
- evictionQueue = setupEvictionQueue();
- }
+ public synchronized void start() {
+ if (evictionQueue == null) evictionQueue = createEvictionQueue();
}
+ public synchronized void stop() {
+ if (evictionQueue != null) evictionQueue.clear();
+ keysInUse.clear();
+ recycleQueue.clear();
+ }
+
public void setEvictionAction(EvictionAction evictionAction) {
- this.evictionAction = evictionAction;
+ this.action = evictionAction;
}
- public void assignToCache(Cache<?, ?> cache, EvictionAlgorithmConfig evictionAlgorithmConfig) {
+ public void init(Cache<?, ?> cache, DataContainer<?, ?> dataContainer, EvictionAlgorithmConfig evictionAlgorithmConfig) {
this.cache = cache;
- this.evictionAlgorithmConfig = (EvictionAlgorithmConfigBase) evictionAlgorithmConfig;
+ this.dataContainer = dataContainer;
+ this.config = (EvictionAlgorithmConfigBase) evictionAlgorithmConfig;
}
public boolean canIgnoreEvent(Type eventType) {
@@ -112,24 +125,20 @@
}
/**
- * Process the given eviction event queue. Eviction Processing encompasses the following:
- * <p/>
- * - Add/Remove/Visit Nodes - Prune according to Eviction Algorithm - Empty/Retry the recycle queue of previously
- * evicted but locked (during actual cache eviction) nodes.
+ * Process the given eviction event queue. Eviction Pprocessing encompasses the following:
*
* @param eventQueue queue containing eviction events
* @throws EvictionException
*/
public void process(BlockingQueue<EvictionEvent> eventQueue) throws EvictionException {
if (trace) log.trace("processing eviction event queue");
- initialize();
processQueues(eventQueue);
emptyRecycleQueue();
prune();
}
public void resetEvictionQueue() {
- // a no-op
+ if (evictionQueue != null) evictionQueue.clear();
}
/**
@@ -139,10 +148,10 @@
* @see EvictionQueue
*/
public EvictionQueue getEvictionQueue() {
- return this.evictionQueue;
+ return evictionQueue;
}
- protected EvictionEvent getNextInQueue(BlockingQueue<EvictionEvent> queue) {
+ protected EvictionEvent nextEvent(BlockingQueue<EvictionEvent> queue) {
try {
return queue.poll(0, TimeUnit.SECONDS);
}
@@ -153,11 +162,7 @@
}
/**
- * Event processing for Evict/Add/Visiting of nodes.
- * <p/>
- * - On AddEvents a new element is added into the eviction queue - On RemoveEvents, the removed element is removed
- * from the eviction queue. - On VisitEvents, the visited node has its eviction statistics updated (idleTime,
- * numberOfVisists, etc..)
+ * Process the event queue
*
* @param queue queue to inspect
* @throws EvictionException in the event of problems
@@ -165,139 +170,88 @@
protected void processQueues(BlockingQueue<EvictionEvent> queue) throws EvictionException {
EvictionEvent event;
int count = 0;
- while ((event = getNextInQueue(queue)) != null) {
- count++;
+ long startTime = System.currentTimeMillis();
+
+ while ((event = nextEvent(queue)) != null) {
+ if (trace) count++;
switch (event.getEventType()) {
case ADD_ENTRY_EVENT:
- processAddedEntries(event);
+ processAddedEntries(event.getKey());
break;
case REMOVE_ENTRY_EVENT:
- processRemovedEntries(event);
+ processRemovedEntries(event.getKey());
break;
case VISIT_ENTRY_EVENT:
- processVisitedEntries(event);
+ processVisitedEntries(event.getKey());
break;
case CLEAR_CACHE_EVENT:
processClearCacheEvent();
break;
case MARK_IN_USE_EVENT:
- processMarkInUseNodes(event.getKey(), event.getInUseTimeout());
+ processMarkInUseNodes(event.getKey(), ((InUseEvictionEvent) event).getInUseTimeout());
break;
case UNMARK_IN_USE_EVENT:
processUnmarkInUseNodes(event.getKey());
break;
default:
- throw new RuntimeException("Illegal Eviction Event type " + event.getEventType());
+ throw new EvictionException("Illegal eviction event type " + event.getEventType());
}
}
- log.trace("processed {0} eviction events", count);
+ if (trace)
+ log.trace("processed {0} eviction events in {1} millis", count, System.currentTimeMillis() - startTime);
}
- protected boolean evict(EntryEvictionData data) {
- if (data != null) {
- log.trace("Attempting to evict {0}", data);
- if (!evictionAction.evict(data.getKey())) {
- try {
- boolean result = recycleQueue.offer(data.getKey(), 5, TimeUnit.SECONDS);
- if (!result) {
- log.warn("Unable to add key {0} to the recycle queue." +
- "This is often sign that " +
- "evictions are not occurring and nodes that should be " +
- "evicted are piling up waiting to be evicted.", data.getKey());
- }
+ protected void evictOrRecycle(Object key) {
+ log.trace("Attempting to evict {0}", key);
+ if (!action.evict(key)) {
+ try {
+ boolean result = recycleQueue.offer(key, 5, TimeUnit.SECONDS);
+ if (!result) {
+ log.warn("Unable to add key {0} to the recycle queue." +
+ "This is often sign that " +
+ "evictions are not occurring and nodes that should be " +
+ "evicted are piling up waiting to be evicted.", key);
}
- catch (InterruptedException e) {
- log.debug("InterruptedException", e);
- }
}
- return true;
- } else {
- return false;
+ catch (InterruptedException e) {
+ log.debug("InterruptedException", e);
+ }
}
}
protected void processMarkInUseNodes(Object key, long inUseTimeout) throws EvictionException {
log.trace("Marking {0} as in use with a usage timeout of {1}", key, inUseTimeout);
-
- EntryEvictionData ne = evictionQueue.get(key);
- if (ne != null) {
- ne.setCurrentlyInUse(true, inUseTimeout);
- }
+ keysInUse.put(key, inUseTimeout + System.currentTimeMillis());
}
protected void processUnmarkInUseNodes(Object key) throws EvictionException {
log.trace("Unmarking node {0} as in use", key);
-
- EntryEvictionData ne = evictionQueue.get(key);
- if (ne != null) {
- ne.setCurrentlyInUse(false, 0);
- }
+ keysInUse.remove(key);
}
- protected void processAddedEntries(EvictionEvent evictedEventNode) throws EvictionException {
- Object key = evictedEventNode.getKey();
+ protected void processAddedEntries(Object key) throws EvictionException {
log.trace("Adding entry {0} to eviction queue", key);
- EntryEvictionData data = evictionQueue.get(key);
- if (data != null) {
- data.setModifiedTimeStamp(evictedEventNode.getCreationTimestamp());
- data.incrementNumberOfVisits();
- log.trace("Queue already contains key. Processing it as visited.");
- processVisitedEntries(evictedEventNode);
+ if (evictionQueue.contains(key)) {
+ evictionQueue.visit(key);
+ log.trace("Key already exists so treating as a visit");
} else {
- data = new EntryEvictionData(1, evictedEventNode.getCreationTimestamp(), key);
- data.setCreationTimeStamp(evictedEventNode.getCreationTimestamp());
- evictionQueue.add(data);
- log.trace("Added successfully to eviction queue");
+ evictionQueue.add(key);
}
}
- protected void processRemovedEntries(EvictionEvent evictedEventNode) throws EvictionException {
- Object key = evictedEventNode.getKey();
-
+ protected void processRemovedEntries(Object key) throws EvictionException {
log.trace("Removing key {0} from eviction queue and attempting eviction", key);
-
- EntryEvictionData data = evictionQueue.get(key);
- if (data != null) {
- evictionQueue.remove(key);
- } else {
- log.trace("Can't find entry eviction data associated with key {0}. Could have been evicted earlier.", key);
- return;
- }
-
- log.trace("Removed from eviction queue");
+ evictionQueue.remove(key);
}
protected void processClearCacheEvent() throws EvictionException {
log.trace("Clearing eviction queue");
evictionQueue.clear();
- log.trace("Cleared eviction queue");
}
-
- /**
- * Visit a node in cache.
- * <p/>
- * This method will update the numVisits and modifiedTimestamp properties. These properties are used as statistics to
- * determine eviction (LRU, LFU, MRU, etc..)
- * <p/>
- * *Note* that this method updates entries by reference and does not put them back into the queue. For some sorted
- * collections, a remove, and a re-add is required to maintain the sorted order of the elements.
- *
- * @throws EvictionException
- */
- protected void processVisitedEntries(EvictionEvent evictionEvent) throws EvictionException {
- Object key = evictionEvent.getKey();
- EntryEvictionData data = evictionQueue.get(key);
- if (data == null) {
- if (trace) log.trace("Visiting entry {0} that has not added to eviction queues before.", key);
- this.processAddedEntries(evictionEvent);
- return;
- }
- // note this method will visit and modify the node statistics by reference!
- // if a collection is only guaranteed sort order by adding to the collection,
- // this implementation will not guarantee sort order.
- data.incrementNumberOfVisits();
- data.setModifiedTimeStamp(evictionEvent.getCreationTimestamp());
+ protected void processVisitedEntries(Object key) throws EvictionException {
+ log.trace("Visiting entry {0}", key);
+ evictionQueue.visit(key);
}
/**
@@ -326,7 +280,7 @@
if (trace) log.trace("emptying recycle bin. Evict key " + key);
// Still doesn't work
- if (!evictionAction.evict(key)) {
+ if (!action.evict(key)) {
try {
recycleQueue.put(key);
}
@@ -340,10 +294,13 @@
protected void prune() throws EvictionException {
int originalQueueSize = evictionQueue.size();
- for (Iterator<EntryEvictionData> it = evictionQueue.iterator(); it.hasNext();) {
- EntryEvictionData data = it.next();
- if (data != null && shouldEvictEntry(data, originalQueueSize)) {
- if (shouldNotOverrideEviction(data) && evict(data)) it.remove();
+ for (Iterator<Object> it = evictionQueue.iterator(); it.hasNext();) {
+ Object key = it.next();
+ if (key != null && needToEvict(originalQueueSize)) {
+ if (shouldNotOverrideEviction(key) && isNotMarkedInUse(key)) {
+ evictOrRecycle(key);
+ it.remove();
+ }
} else {
break; // assume the rest won't need to be evicted either
}
@@ -361,11 +318,17 @@
/**
* A helper for implementations that support the minimum time to live property
*
- * @param data eviction data on the entry to consider
+ * @param key key to consider
* @return true if the entry is younger than the minimum time to live, false otherwise.
*/
- protected boolean shouldNotOverrideEviction(EntryEvictionData data) {
- long minTTL = evictionAlgorithmConfig.getMinTimeToLive();
- return minTTL < 1 || (data.getModifiedTimeStamp() + minTTL < System.currentTimeMillis());
+ private boolean shouldNotOverrideEviction(Object key) {
+ long minTTL = config.getMinTimeToLive();
+ long lastModified = dataContainer.getModifiedTimestamp(key);
+ return minTTL < 1 || lastModified < 0 || (lastModified + minTTL < System.currentTimeMillis());
}
+
+ private boolean isNotMarkedInUse(Object key) {
+ Long expiryTime = keysInUse.get(key);
+ return expiryTime == null || (expiryTime < System.currentTimeMillis() && keysInUse.remove(key) != null);
+ }
}
Modified: core/branches/flat/src/main/java/org/horizon/eviction/algorithms/BaseEvictionQueue.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/algorithms/BaseEvictionQueue.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/eviction/algorithms/BaseEvictionQueue.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -3,7 +3,12 @@
import org.horizon.eviction.EvictionQueue;
public abstract class BaseEvictionQueue implements EvictionQueue {
+
public boolean isEmpty() {
return size() == 0;
}
+
+ public void visit(Object key) {
+ // default impl to ignore this
+ }
}
Modified: core/branches/flat/src/main/java/org/horizon/eviction/algorithms/fifo/FIFOAlgorithm.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/algorithms/fifo/FIFOAlgorithm.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/eviction/algorithms/fifo/FIFOAlgorithm.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -26,8 +26,6 @@
import org.horizon.eviction.EvictionException;
import org.horizon.eviction.EvictionQueue;
import org.horizon.eviction.algorithms.BaseEvictionAlgorithm;
-import org.horizon.logging.Log;
-import org.horizon.logging.LogFactory;
/**
* First-in-first-out algorithm used to evict cache entries
@@ -37,10 +35,7 @@
*/
@NotThreadSafe
public class FIFOAlgorithm extends BaseEvictionAlgorithm {
- private static final Log log = LogFactory.getLog(FIFOAlgorithm.class);
-
- @Override
- protected EvictionQueue setupEvictionQueue() throws EvictionException {
+ protected EvictionQueue createEvictionQueue() throws EvictionException {
return new FIFOQueue();
}
Modified: core/branches/flat/src/main/java/org/horizon/eviction/algorithms/fifo/FIFOQueue.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/algorithms/fifo/FIFOQueue.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/eviction/algorithms/fifo/FIFOQueue.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -21,53 +21,49 @@
*/
package org.horizon.eviction.algorithms.fifo;
-import org.horizon.eviction.EntryEvictionData;
import org.horizon.eviction.algorithms.BaseEvictionQueue;
import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.Map;
+import java.util.LinkedHashSet;
/**
- * FIFO Eviction Queue implementation for FIFO Policy.
+ * First in, first out eviction queue implementation for the {@link FIFOAlgorithm}. Guarantees O(1) for put, remove,
+ * and the iterator. Ignores {@link org.horizon.eviction.EvictionQueue#visit(Object)} as it doesn't order on visitor
+ * count.
*
- * @author Daniel Huang (dhuang(a)jboss.org)
+ * @author Manik Surtani
* @since 1.0
*/
public class FIFOQueue extends BaseEvictionQueue {
- private Map<Object, EntryEvictionData> keyMap;
+ private LinkedHashSet<Object> orderedKeys;
protected FIFOQueue() {
- keyMap = new LinkedHashMap<Object, EntryEvictionData>();
- // We use a LinkedHashMap here because we want to maintain FIFO ordering and still get the benefits of
- // O(n) = 1 for add/remove/search.
+ orderedKeys = new LinkedHashSet<Object>();
+ // We use a LinkedHashSet here because we want to maintain FIFO ordering and still get the benefits of
+ // O(1) for put/remove/iterate
}
- public EntryEvictionData get(Object key) {
- return keyMap.get(key);
- }
-
public boolean contains(Object key) {
- return keyMap.containsKey(key);
+ return orderedKeys.contains(key);
}
public void remove(Object key) {
- keyMap.remove(key);
+ orderedKeys.remove(key);
}
- public void add(EntryEvictionData entryEvictionData) {
- if (!keyMap.containsKey(entryEvictionData.getKey())) keyMap.put(entryEvictionData.getKey(), entryEvictionData);
+ public void add(Object key) {
+ if (!orderedKeys.contains(key)) orderedKeys.add(key);
}
public int size() {
- return keyMap.size();
+ return orderedKeys.size();
}
public void clear() {
- keyMap.clear();
+ orderedKeys.clear();
}
- public Iterator<EntryEvictionData> iterator() {
- return keyMap.values().iterator();
+ public Iterator<Object> iterator() {
+ return orderedKeys.iterator();
}
}
Modified: core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lfu/LFUAlgorithm.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lfu/LFUAlgorithm.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lfu/LFUAlgorithm.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -23,98 +23,23 @@
import net.jcip.annotations.NotThreadSafe;
import org.horizon.config.EvictionAlgorithmConfig;
-import org.horizon.eviction.EvictionEvent;
import org.horizon.eviction.EvictionException;
import org.horizon.eviction.EvictionQueue;
import org.horizon.eviction.algorithms.BaseEvictionAlgorithm;
-import org.horizon.logging.Log;
-import org.horizon.logging.LogFactory;
-import java.util.concurrent.BlockingQueue;
-
/**
- * Least Frequently Used algorithm for cache eviction. Note that this algorithm is not thread-safe.
- * <p/>
- * This algorithm relies on maxNodes and minNodes to operate correctly. Eviction takes place using Least Frequently Used
- * algorithm. A node A that is used less than a node B is evicted sooner.
- * <p/>
- * The minNodes property defines a threshold for eviction. If minNodes = 100, the LFUAlgorithm will not evict the cache
- * to anything less than 100 elements still left in cache. The maxNodes property defines the maximum number of nodes the
- * cache will accept before eviction. maxNodes = 0 means that this region is unbounded. minNodes = 0 means that the
- * eviction queue will attempt to bring the cache of this region to 0 elements (evict all elements) whenever it is run.
- * <p/>
- * This algorithm uses a sorted eviction queue. The eviction queue is sorted in ascending order based on the number of
- * times a node is visited. The more frequently a node is visited, the less likely it will be evicted.
+ * Least Frequently Used algorithm for cache eviction.
*
* @author Manik Surtani
* @since 1.0
*/
@NotThreadSafe
public class LFUAlgorithm extends BaseEvictionAlgorithm {
- private static final Log log = LogFactory.getLog(LFUAlgorithm.class);
- private static final boolean trace = log.isTraceEnabled();
- boolean evictionQueueRequiresSort = false;
-
- /**
- * Will create a LFUQueue to be used as the underlying eviction queue.
- *
- * @return The created LFUQueue.
- * @throws org.horizon.eviction.EvictionException
- *
- */
- @Override
- protected EvictionQueue setupEvictionQueue() throws EvictionException {
+ protected EvictionQueue createEvictionQueue() throws EvictionException {
return new LFUQueue();
}
- @Override
- protected void prune() throws EvictionException {
- super.prune();
-
- // clean up the Queue's eviction removals
- ((LFUQueue) this.evictionQueue).doBatchRemove();
- }
-
public Class<? extends EvictionAlgorithmConfig> getConfigurationClass() {
return LFUAlgorithmConfig.class;
}
-
- @Override
- protected void processQueues(BlockingQueue<EvictionEvent> queue) throws EvictionException {
- evictionQueueRequiresSort = false;
- try {
- super.processQueues(queue);
- if (evictionQueueRequiresSort) {
- if (trace) log.trace("Eviction queue requires re-sort. Re-sorting.");
- resortEvictionQueue();
- }
- } finally {
- evictionQueueRequiresSort = false;
- }
- }
-
- @Override
- protected void processAddedEntries(EvictionEvent event) {
- evictionQueueRequiresSort = true;
- super.processAddedEntries(event);
- }
-
- @Override
- protected void processVisitedEntries(EvictionEvent event) {
- evictionQueueRequiresSort = true;
- super.processVisitedEntries(event);
- }
-
- /**
- * This method is called to resort the queue after add or visit events have occurred.
- */
- protected void resortEvictionQueue() {
- long begin = System.currentTimeMillis();
- ((LFUQueue) evictionQueue).reSortEvictionQueue();
-
- if (trace) {
- log.trace("Took {0} millis to re-sort queue with {1} elements",
- System.currentTimeMillis() - begin, getEvictionQueue().size());
- }
- }
}
Modified: core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lfu/LFUQueue.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lfu/LFUQueue.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lfu/LFUQueue.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -21,41 +21,38 @@
*/
package org.horizon.eviction.algorithms.lfu;
-import org.horizon.eviction.EntryEvictionData;
+import org.horizon.CacheException;
import org.horizon.eviction.algorithms.BaseEvictionQueue;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.Map;
-import java.util.Set;
+import java.util.PriorityQueue;
/**
- * LFUQueue EvictionQueue implementation for LFU Policy.
+ * Least frequently used queue for use with the {@link org.horizon.eviction.algorithms.lfu.LFUAlgorithm}. This
+ * implementation guarantees O(ln n) for put and remove operations, and 2O(ln n) for visits since this requires a
+ * re-sort.
* <p/>
- * The queue is sorted in least frequently used order.
+ * // TODO investigate using a Fibonacci Heap or a Brodal Queue (http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.43.8133)
+ * instead of a TreeMap to implement // Since this will give us O(1) for insert, iteration and O(ln n) for remove and
+ * visit
*
- * @author Daniel Huang (dhuang(a)jboss.org)
+ * @author Manik Surtani
* @since 1.0
*/
public class LFUQueue extends BaseEvictionQueue {
- Map<Object, EntryEvictionData> keyMap;
- LinkedList<EntryEvictionData> evictionList;
- Set<EntryEvictionData> removalQueue;
- private Comparator<EntryEvictionData> comparator;
+ PriorityQueue<VisitableKey> keys = new PriorityQueue<VisitableKey>();
+ HashMap<Object, VisitableKey> keyMap = new HashMap<Object, VisitableKey>();
- protected LFUQueue() {
- keyMap = new HashMap<Object, EntryEvictionData>();
- comparator = new LFUComparator();
- evictionList = new LinkedList<EntryEvictionData>();
- removalQueue = new HashSet<EntryEvictionData>();
- }
+ public void visit(Object key) {
+ VisitableKey vk = keyMap.get(key);
+ if (vk != null) {
- public EntryEvictionData get(Object key) {
- return keyMap.get(key);
+ // need to remove and add to reorder the tree
+ keys.remove(vk);
+ vk.visits++;
+ keys.add(vk);
+ }
}
public boolean contains(Object key) {
@@ -63,22 +60,18 @@
}
public void remove(Object key) {
- EntryEvictionData data = keyMap.remove(key);
- if (data != null) {
- // don't remove directly from the LinkedList otherwise we will incur a O(n) = n
- // performance penalty for every removal! In the prune method for LFU, we will iterate the LinkedList through ONCE
- // doing a single O(n) = n operation and removal. This is much preferred over running O(n) = n every single time
- // remove is called. There is also special logic in the getFirstNodeEntry that will know to check
- // the removalQueue before returning.
- this.removalQueue.add(data);
+ VisitableKey vk = keyMap.remove(key);
+ if (vk != null) {
+ if (!keys.remove(vk)) throw new CacheException("Problem removing key " + key);
}
}
- public void add(EntryEvictionData entryEvictionData) {
- if (!keyMap.containsKey(entryEvictionData)) {
- Object key = entryEvictionData.getKey();
- keyMap.put(key, entryEvictionData);
- evictionList.add(entryEvictionData);
+ public void add(Object key) {
+ if (!keyMap.containsKey(key)) {
+ VisitableKey vk = new VisitableKey();
+ vk.key = key;
+ keyMap.put(key, vk);
+ keys.add(vk);
}
}
@@ -86,76 +79,78 @@
return keyMap.size();
}
+ @Override
+ public boolean isEmpty() {
+ return keyMap.size() == 0 && keys.size() == 0;
+ }
+
public void clear() {
keyMap.clear();
- evictionList.clear();
- removalQueue.clear();
+ keys.clear();
}
- public void reSortEvictionQueue() {
- Collections.sort(evictionList, comparator);
- }
+ public Iterator<Object> iterator() {
+ return new Iterator<Object>() {
- /**
- * Anything removed using {@link #remove(Object)} or removing using the iterator does not actually get removed until
- * this method is called.
- */
- public void doBatchRemove() {
- Iterator<EntryEvictionData> it = evictionList.iterator();
- while (it.hasNext() && removalQueue.size() > 0) {
- if (removalQueue.remove(it.next())) it.remove();
- }
- }
+ Iterator<VisitableKey> vkIter = keys.iterator();
+ Object lastKey;
- public Iterator<EntryEvictionData> iterator() {
- //return evictionList.iterator();
- return new Iterator<EntryEvictionData>() {
- Iterator<EntryEvictionData> delegate = evictionList.iterator();
- EntryEvictionData current;
-
public boolean hasNext() {
- return delegate.hasNext();
+ return vkIter.hasNext();
}
- public EntryEvictionData next() {
- current = delegate.next();
- return current;
+ public Object next() {
+ lastKey = vkIter.next().key;
+ return lastKey;
}
public void remove() {
- if (current != null) LFUQueue.this.remove(current.getKey());
+ if (lastKey == null) throw new IllegalStateException("Call next() before remove()!");
+ vkIter.remove();
+ keyMap.remove(lastKey);
}
};
}
- /**
- * Comparator class for LFU.
- * <p/>
- * This class will sort the eviction queue in the correct eviction order. The top of the list should evict before the
- * bottom of the list.
- * <p/>
- * The sort is based on ascending order of visits.
- * <p/>
- * This class has a natural ordering that is inconsistent with equals as defined by the java.lang.Comparator
- * contract.
- */
- protected static class LFUComparator implements Comparator<EntryEvictionData> {
+ class VisitableKey implements Comparable {
+ Object key;
+ int visits;
- public int compare(EntryEvictionData data1, EntryEvictionData data2) {
- if (data1.equals(data2)) return 0;
-
- int neNodeHits = data1.getNumberOfVisits();
- int ne2NodeHits = data2.getNumberOfVisits();
-
- if (neNodeHits > ne2NodeHits) {
- return 1;
- } else if (neNodeHits < ne2NodeHits) {
+ public int compareTo(Object o) {
+ if (o == null) return -1;
+ if (o instanceof VisitableKey) {
+ VisitableKey other = (VisitableKey) o;
+ return other.visits - visits;
+ } else {
return -1;
- } else if (neNodeHits == ne2NodeHits) {
- return 0;
}
- throw new RuntimeException("Should never reach this condition");
}
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ VisitableKey that = (VisitableKey) o;
+
+ if (key != null ? !key.equals(that.key) : that.key != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return key != null ? key.hashCode() : 0;
+ }
+
+ @Override
+ public String toString() {
+ return "VisitableKey{" +
+ "key=" + key +
+ ", visits=" + visits +
+ '}';
+ }
}
+
}
Modified: core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lru/LRUAlgorithm.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lru/LRUAlgorithm.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lru/LRUAlgorithm.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -23,84 +23,22 @@
import net.jcip.annotations.NotThreadSafe;
import org.horizon.config.EvictionAlgorithmConfig;
-import org.horizon.eviction.EntryEvictionData;
import org.horizon.eviction.EvictionException;
import org.horizon.eviction.EvictionQueue;
import org.horizon.eviction.algorithms.BaseEvictionAlgorithm;
-import org.horizon.logging.Log;
-import org.horizon.logging.LogFactory;
-import java.util.Iterator;
-
/**
- * Least recently Used algorithm to evict cache entries
+ * Least recently used algorithm to evict cache entries
*
* @author Manik Surtani
* @since 1.0
*/
@NotThreadSafe
public class LRUAlgorithm extends BaseEvictionAlgorithm {
- private static final Log log = LogFactory.getLog(LRUAlgorithm.class);
- private static final boolean trace = log.isTraceEnabled();
-
- @Override
- protected EvictionQueue setupEvictionQueue() throws EvictionException {
+ protected EvictionQueue createEvictionQueue() throws EvictionException {
return new LRUQueue();
}
- @Override
- protected void prune() throws EvictionException {
- LRUQueue lruQueue = (LRUQueue) evictionQueue;
- int origSize = evictionQueue.size();
-
- EntryEvictionData data;
- Iterator<EntryEvictionData> it = lruQueue.iterateLRUQueue();
- while (it.hasNext()) {
- data = it.next();
- if (data.isNodeInUseAndNotTimedOut()) continue;
- if (shouldEvictEntry(data, origSize) && shouldNotOverrideEviction(data)) {
- it.remove();
- lruQueue.removeNodeEntryFromMaxAge(data);
- this.evict(data);
- } else {
- break;
- }
- }
-
- it = lruQueue.iterateMaxAgeQueue();
- while (it.hasNext()) {
- data = it.next();
- if (data.isNodeInUseAndNotTimedOut()) continue;
- if (shouldEvictEntry(data, origSize) && shouldNotOverrideEviction(data)) {
- it.remove();
- lruQueue.removeNodeEntryFromLRU(data);
- this.evict(data);
- } else {
- break;
- }
- }
-
- int maxNodes = evictionAlgorithmConfig.getMaxEntries();
- if (maxNodes <= 0) {
- return;
- }
-
- it = lruQueue.iterateLRUQueue();
- while (evictionQueue.size() > maxNodes && it.hasNext()) {
- data = it.next();
- if (trace) {
- log.trace("Node " + data.getKey() + " will be evicted because of exceeding the maxNode limit." +
- " maxNode: " + maxNodes + " but current queue size is: " + evictionQueue.size());
- }
-
- if (!data.isNodeInUseAndNotTimedOut() && shouldNotOverrideEviction(data)) {
- it.remove();
- lruQueue.removeNodeEntryFromMaxAge(data);
- this.evict(data);
- }
- }
- }
-
public Class<? extends EvictionAlgorithmConfig> getConfigurationClass() {
return LRUAlgorithmConfig.class;
}
Modified: core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lru/LRUQueue.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lru/LRUQueue.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/eviction/algorithms/lru/LRUQueue.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -21,110 +21,56 @@
*/
package org.horizon.eviction.algorithms.lru;
-import org.horizon.eviction.EntryEvictionData;
import org.horizon.eviction.algorithms.BaseEvictionQueue;
+import org.horizon.util.VisitableLinkedHashSet;
import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.Map;
/**
- * LRU Eviction Queue implementation.
- * <p/>
- * This eviction queue will iterate properly through two sorted lists. One sorted by maxAge and the other sorted by
- * idleTime.
+ * Least Recently Used eviction queue implementation for the {@link LRUAlgorithm}. Guarantees O(1) for put, remove, and
+ * the iterator.
*
- * @author Daniel Huang (dhuang(a)jboss.org)
+ * @author Manik Surtani
* @since 1.0
*/
public class LRUQueue extends BaseEvictionQueue {
- Map<Object, EntryEvictionData> maxAgeQueue;
- Map<Object, EntryEvictionData> lruQueue;
- protected LRUQueue() {
- maxAgeQueue = new LinkedHashMap<Object, EntryEvictionData>();
- lruQueue = new LinkedHashMap<Object, EntryEvictionData>(16, 0.75f, true);
- }
+ protected VisitableLinkedHashSet<Object> keys = new VisitableLinkedHashSet<Object>();
- public EntryEvictionData get(Object key) {
- return lruQueue.get(key);
+ @Override
+ public void visit(Object key) {
+ keys.visit(key);
}
public boolean contains(Object key) {
- return maxAgeQueue.containsKey(key);
+ return keys.contains(key);
}
public void remove(Object key) {
- if (contains(key)) {
- EntryEvictionData data1 = lruQueue.remove(key);
- EntryEvictionData data2 = maxAgeQueue.remove(key);
- if (data1 == null || data2 == null) {
- throw new RuntimeException("The queues are out of sync.");
- }
- }
+ keys.remove(key);
}
- public void add(EntryEvictionData entryEvictionData) {
- if (!contains(entryEvictionData)) {
- Object key = entryEvictionData.getKey();
- maxAgeQueue.put(key, entryEvictionData);
- lruQueue.put(key, entryEvictionData);
- }
+ public void add(Object key) {
+ if (keys.contains(key))
+ keys.visit(key);
+ else
+ keys.add(key);
}
public int size() {
- return maxAgeQueue.size();
+ return keys.size();
}
public void clear() {
- maxAgeQueue.clear();
- lruQueue.clear();
+ keys.clear();
}
- public Iterator<EntryEvictionData> iterator() {
- return new LRUQueueIterator(lruQueue, maxAgeQueue);
+ public Iterator<Object> iterator() {
+ return keys.iterator();
}
- public Iterator<EntryEvictionData> iterateLRUQueue() {
- return new LRUQueueIterator(lruQueue, maxAgeQueue);
+ @Override
+ public String toString() {
+ return keys.toString();
}
-
- public void removeNodeEntryFromMaxAge(EntryEvictionData data) {
- maxAgeQueue.remove(data.getKey());
- }
-
- public Iterator<EntryEvictionData> iterateMaxAgeQueue() {
- return new LRUQueueIterator(maxAgeQueue, lruQueue);
- }
-
- public void removeNodeEntryFromLRU(EntryEvictionData data) {
- lruQueue.remove(data.getKey());
- }
-
- private class LRUQueueIterator implements Iterator<EntryEvictionData> {
- Iterator<EntryEvictionData> delegate;
- Map<Object, EntryEvictionData> secondaryStructure;
- EntryEvictionData current;
-
- private LRUQueueIterator(Map<Object, EntryEvictionData> primaryStructure, Map<Object, EntryEvictionData> secondaryStructure) {
- delegate = primaryStructure.values().iterator();
- this.secondaryStructure = secondaryStructure;
- }
-
- public boolean hasNext() {
- return delegate.hasNext();
- }
-
- public EntryEvictionData next() {
- current = delegate.next();
- return current;
- }
-
- public void remove() {
- delegate.remove();
- if (current != null) {
- maxAgeQueue.remove(current.getKey());
- }
- }
- }
}
Modified: core/branches/flat/src/main/java/org/horizon/eviction/algorithms/mru/MRUAlgorithm.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/algorithms/mru/MRUAlgorithm.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/eviction/algorithms/mru/MRUAlgorithm.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -23,32 +23,22 @@
import net.jcip.annotations.NotThreadSafe;
import org.horizon.config.EvictionAlgorithmConfig;
-import org.horizon.eviction.EvictionEvent;
import org.horizon.eviction.EvictionException;
import org.horizon.eviction.EvictionQueue;
import org.horizon.eviction.algorithms.BaseEvictionAlgorithm;
/**
- * Most Recently Used Algorithm.
- * <p/>
- * This algorithm will evict the most recently used cache entries from cache.
+ * Most recently used algorithm. This algorithm will evict the most recently used cache entries from cache.
*
* @author Manik Surtani
* @since 1.0
*/
@NotThreadSafe
public class MRUAlgorithm extends BaseEvictionAlgorithm {
- @Override
- protected EvictionQueue setupEvictionQueue() throws EvictionException {
+ protected EvictionQueue createEvictionQueue() throws EvictionException {
return new MRUQueue();
}
- @Override
- protected void processVisitedEntries(EvictionEvent evictedEventNode) throws EvictionException {
- super.processVisitedEntries(evictedEventNode);
- ((MRUQueue) evictionQueue).moveToTopOfStack(evictedEventNode.getKey());
- }
-
public Class<? extends EvictionAlgorithmConfig> getConfigurationClass() {
return MRUAlgorithmConfig.class;
}
Modified: core/branches/flat/src/main/java/org/horizon/eviction/algorithms/mru/MRUQueue.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/algorithms/mru/MRUQueue.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/eviction/algorithms/mru/MRUQueue.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -21,107 +21,21 @@
*/
package org.horizon.eviction.algorithms.mru;
-import org.horizon.eviction.EntryEvictionData;
-import org.horizon.eviction.algorithms.BaseEvictionQueue;
+import org.horizon.eviction.algorithms.lru.LRUQueue;
-import java.util.HashMap;
import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.Map;
/**
- * MRU Eviction Queue implementation.
- * <p/>
- * This nodeMap is sorted by MRU. The first entry in the nodeMap will also be the most recently used entry. The sort is
- * implicit based on a Stack that we can implicitly sort to the top by moving a node that is used to the top of the
- * eviction stack.
+ * Most Recently Used eviction queue implementation for the {@link MRUAlgorithm}. Guarantees O(1) for put, remove, and
+ * the iterator.
*
- * @author Daniel Huang (dhuang(a)jboss.org)
+ * @author Manik Surtani
* @since 1.0
*/
-public class MRUQueue extends BaseEvictionQueue {
- // we use our own Stack/Linked List implementation here because it guarantees O(n) = 1 for add, remove, get and
- // we can sort it in order of MRU implicitly while still getting O(n) = 1 access time
- // throughout.
- Map<Object, EntryEvictionData> keyMap;
- LinkedList<EntryEvictionData> list;
+public class MRUQueue extends LRUQueue {
- protected MRUQueue() {
- keyMap = new HashMap<Object, EntryEvictionData>();
- list = new LinkedList<EntryEvictionData>();
- }
-
- /**
- * This call moves an entry to the top of the stack.
- * <p/>
- * When an entry is visited this method should be called to guarantee MRU ordering.
- *
- * @param key entry key to move to the top of the stack.
- */
- protected void moveToTopOfStack(Object key) {
- EntryEvictionData data = keyMap.get(key);
- if (data != null) {
- list.remove(data);
- list.addFirst(data);
- }
- }
-
- public EntryEvictionData get(Object key) {
- return keyMap.get(key);
- }
-
- public boolean contains(Object key) {
- return keyMap.containsKey(key);
- }
-
- public void remove(Object key) {
- EntryEvictionData data = keyMap.remove(key);
- if (data != null) list.remove(data);
- }
-
- public void add(EntryEvictionData entryEvictionData) {
- if (!keyMap.containsKey(entryEvictionData.getKey())) {
- list.addLast(entryEvictionData);
- keyMap.put(entryEvictionData.getKey(), entryEvictionData);
- }
- }
-
- public int size() {
- return list.size();
- }
-
- public void clear() {
- keyMap.clear();
- list.clear();
- }
-
- public Iterator<EntryEvictionData> iterator() {
- return new Iterator<EntryEvictionData>() {
- Iterator<EntryEvictionData> delegate = list.iterator();
- EntryEvictionData current;
-
- public boolean hasNext() {
- return delegate.hasNext();
- }
-
- public EntryEvictionData next() {
- current = delegate.next();
- return current;
- }
-
- public void remove() {
- // this will remove it from the list
- delegate.remove();
- // now to take care of the key map
- if (current != null) {
- keyMap.remove(current.getKey());
- }
- }
- };
- }
-
@Override
- public String toString() {
- return list.toString();
+ public Iterator<Object> iterator() {
+ return keys.reverseIterator();
}
}
Modified: core/branches/flat/src/main/java/org/horizon/eviction/algorithms/nullalgo/NullEvictionAlgorithm.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/algorithms/nullalgo/NullEvictionAlgorithm.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/eviction/algorithms/nullalgo/NullEvictionAlgorithm.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -23,12 +23,13 @@
import org.horizon.Cache;
import org.horizon.config.EvictionAlgorithmConfig;
+import org.horizon.container.DataContainer;
import org.horizon.eviction.EvictionAction;
import org.horizon.eviction.EvictionAlgorithm;
-import org.horizon.eviction.EvictionEvent;
-import org.horizon.eviction.EvictionEvent.Type;
import org.horizon.eviction.EvictionException;
import org.horizon.eviction.EvictionQueue;
+import org.horizon.eviction.events.EvictionEvent;
+import org.horizon.eviction.events.EvictionEvent.Type;
import java.util.concurrent.BlockingQueue;
@@ -65,7 +66,7 @@
// no-op
}
- public void assignToCache(Cache<?, ?> cache, EvictionAlgorithmConfig evictionAlgorithmConfig) {
+ public void init(Cache<?, ?> cache, DataContainer<?, ?> dataContainer, EvictionAlgorithmConfig evictionAlgorithmConfig) {
// no-op
}
@@ -81,11 +82,15 @@
return true; // always ignore everything!
}
- public void initialize() {
+ public Class<? extends EvictionAlgorithmConfig> getConfigurationClass() {
+ return NullEvictionAlgorithmConfig.class;
+ }
+
+ public void start() {
// no-op
}
- public Class<? extends EvictionAlgorithmConfig> getConfigurationClass() {
- return NullEvictionAlgorithmConfig.class;
+ public void stop() {
+ // no-op
}
}
Modified: core/branches/flat/src/main/java/org/horizon/eviction/algorithms/nullalgo/NullEvictionQueue.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/algorithms/nullalgo/NullEvictionQueue.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/eviction/algorithms/nullalgo/NullEvictionQueue.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -21,7 +21,6 @@
*/
package org.horizon.eviction.algorithms.nullalgo;
-import org.horizon.eviction.EntryEvictionData;
import org.horizon.eviction.algorithms.BaseEvictionQueue;
import java.util.Iterator;
@@ -48,7 +47,7 @@
/**
* No-op
*/
- public void add(EntryEvictionData entryEvictionData) {
+ public void add(Object key) {
// no-op
}
@@ -59,10 +58,6 @@
// no-op
}
- public EntryEvictionData get(Object key) {
- return null; // no-op
- }
-
/**
* Returns <code>false</code>
*/
@@ -80,7 +75,7 @@
/**
* Returns an <code>Iterator</code> whose <code>hasNext()</code> returns <code>false</code>.
*/
- public Iterator<EntryEvictionData> iterator() {
+ public Iterator<Object> iterator() {
return NullQueueIterator.INSTANCE;
}
@@ -91,7 +86,7 @@
// no-op
}
- static class NullQueueIterator implements Iterator<EntryEvictionData> {
+ static class NullQueueIterator implements Iterator<Object> {
private static final NullQueueIterator INSTANCE = new NullQueueIterator();
private NullQueueIterator() {
@@ -101,7 +96,7 @@
return false;
}
- public EntryEvictionData next() {
+ public Object next() {
throw new NoSuchElementException("No more elements");
}
Copied: core/branches/flat/src/main/java/org/horizon/eviction/events/EvictionEvent.java (from rev 7633, core/branches/flat/src/main/java/org/horizon/eviction/EvictionEvent.java)
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/events/EvictionEvent.java (rev 0)
+++ core/branches/flat/src/main/java/org/horizon/eviction/events/EvictionEvent.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -0,0 +1,98 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2000 - 2008, Red Hat Middleware LLC, and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file 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.horizon.eviction.events;
+
+/**
+ * An eviction event records activity on nodes in the cache. These are recorded for processing later.
+ *
+ * @author (various)
+ * @since 1.0
+ */
+public class EvictionEvent {
+ Object key;
+ Type type;
+
+ public static enum Type {
+ ADD_ENTRY_EVENT,
+ REMOVE_ENTRY_EVENT,
+ VISIT_ENTRY_EVENT,
+ CLEAR_CACHE_EVENT,
+ MARK_IN_USE_EVENT,
+ UNMARK_IN_USE_EVENT
+ }
+
+ public EvictionEvent(Object key, Type type) {
+ this.key = key;
+ this.type = type;
+ }
+
+ public Object getKey() {
+ return key;
+ }
+
+ public void setKey(Object key) {
+ this.key = key;
+ }
+
+ public void setEventType(Type event) {
+ type = event;
+ }
+
+ public Type getEventType() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ return "EvictedEventNode[key=" + key + " event=" + type + "]";
+ }
+
+ /**
+ * Copies this evicted event node to create a new one with the same values, except with a new Fqn root.
+ *
+ * @param key new Fqn root to use
+ * @return a new EvictedEventNode instance
+ */
+ public EvictionEvent copy(Object key) {
+ return new EvictionEvent(key, type);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ EvictionEvent that = (EvictionEvent) o;
+
+ if (key != null ? !key.equals(that.key) : that.key != null) return false;
+ if (type != that.type) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = key != null ? key.hashCode() : 0;
+ result = 31 * result + (type != null ? type.hashCode() : 0);
+ return result;
+ }
+}
Property changes on: core/branches/flat/src/main/java/org/horizon/eviction/events/EvictionEvent.java
___________________________________________________________________
Name: svn:executable
+ *
Name: svn:keywords
+ Id Revision
Name: svn:eol-style
+ LF
Added: core/branches/flat/src/main/java/org/horizon/eviction/events/InUseEvictionEvent.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/eviction/events/InUseEvictionEvent.java (rev 0)
+++ core/branches/flat/src/main/java/org/horizon/eviction/events/InUseEvictionEvent.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -0,0 +1,52 @@
+package org.horizon.eviction.events;
+
+/**
+ * An eviction event used to mark an entry as in-use.
+ *
+ * @author Manik Surtani
+ * @since 1.0
+ */
+public class InUseEvictionEvent extends EvictionEvent {
+
+ long inUseTimeout;
+
+ public InUseEvictionEvent(Object key, long inUseTimeout) {
+ super(key, Type.MARK_IN_USE_EVENT);
+ this.inUseTimeout = inUseTimeout;
+ }
+
+ public long getInUseTimeout() {
+ return inUseTimeout;
+ }
+
+ public void setInUseTimeout(long inUseTimeout) {
+ this.inUseTimeout = inUseTimeout;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ InUseEvictionEvent that = (InUseEvictionEvent) o;
+ if (!super.equals(o)) return false;
+ if (inUseTimeout != that.inUseTimeout) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int hc = super.hashCode();
+ return hc * 31 + (int) (inUseTimeout ^ (inUseTimeout >>> 32));
+ }
+
+ @Override
+ public String toString() {
+ return "InUseEvictionEvent{" +
+ "key=" + key +
+ ", type=" + type +
+ ", inUseTimeout=" + inUseTimeout +
+ '}';
+ }
+}
Modified: core/branches/flat/src/main/java/org/horizon/interceptors/EvictionInterceptor.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/interceptors/EvictionInterceptor.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/interceptors/EvictionInterceptor.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -28,9 +28,9 @@
import org.horizon.commands.write.RemoveCommand;
import org.horizon.commands.write.ReplaceCommand;
import org.horizon.context.InvocationContext;
-import org.horizon.eviction.EvictionEvent;
-import static org.horizon.eviction.EvictionEvent.Type.*;
import org.horizon.eviction.EvictionManager;
+import org.horizon.eviction.events.EvictionEvent;
+import static org.horizon.eviction.events.EvictionEvent.Type.*;
import org.horizon.factories.annotations.Inject;
import org.horizon.interceptors.base.CommandInterceptor;
Added: core/branches/flat/src/main/java/org/horizon/util/AbstractMap.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/util/AbstractMap.java (rev 0)
+++ core/branches/flat/src/main/java/org/horizon/util/AbstractMap.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -0,0 +1,94 @@
+package org.horizon.util;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Similar to the JDK's AbstractMap, this provides common functionality for custom map implementations.
+ *
+ * @author Manik Surtani
+ * @since 1.0
+ */
+public abstract class AbstractMap<K, V> implements Map<K, V> {
+
+ /**
+ * Marks null keys.
+ */
+ private static final Object NULL = new Object();
+
+ // views
+ protected transient Set<Map.Entry<K, V>> entrySet = null;
+ protected transient Set<K> keySet = null;
+ protected transient Collection<V> values = null;
+
+
+ // The normal bit spreader...
+ protected static final int hash(Object key) {
+ int h = key.hashCode();
+ h ^= (h >>> 20) ^ (h >>> 12);
+ return h ^ (h >>> 7) ^ (h >>> 4);
+ }
+
+ @SuppressWarnings("unchecked")
+ protected static final <K> K maskNull(K key) {
+ return key == null ? (K) NULL : key;
+ }
+
+ protected static final <K> K unmaskNull(K key) {
+ return key == NULL ? null : key;
+ }
+
+ protected static final boolean eq(Object o1, Object o2) {
+ return o1 == o2 || (o1 != null && o1.equals(o2));
+ }
+
+//
+// protected static class SimpleEntry<K, V> implements Map.Entry<K, V> {
+// private K key;
+// private V value;
+//
+// SimpleEntry(K key, V value) {
+// this.key = key;
+// this.value = value;
+// }
+//
+// SimpleEntry(Map.Entry<K, V> entry) {
+// this.key = entry.getKey();
+// this.value = entry.getValue();
+// }
+//
+// public K getKey() {
+// return key;
+// }
+//
+// public V getValue() {
+// return value;
+// }
+//
+// public V setValue(V value) {
+// V old = this.value;
+// this.value = value;
+// return old;
+// }
+//
+// public boolean equals(Object o) {
+// if (this == o)
+// return true;
+//
+// if (!(o instanceof Map.Entry))
+// return false;
+// Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
+// return eq(key, e.getKey()) && eq(value, e.getValue());
+// }
+//
+// public int hashCode() {
+// return hash(key) ^
+// (value == null ? 0 : hash(value));
+// }
+//
+// public String toString() {
+// return getKey() + "=" + getValue();
+// }
+// }
+}
Added: core/branches/flat/src/main/java/org/horizon/util/BidirectionalLinkedHashMap.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/util/BidirectionalLinkedHashMap.java (rev 0)
+++ core/branches/flat/src/main/java/org/horizon/util/BidirectionalLinkedHashMap.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -0,0 +1,800 @@
+package org.horizon.util;
+
+import java.util.AbstractCollection;
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+/**
+ * A LinkedHashMap that allows an optional direction flag for the doubly linked list connecting pointers. The only real
+ * difference between this implementation and the JDK's {@link java.util.LinkedHashMap} is the provision of the // TODO
+ *
+ * @author Manik Surtani
+ */
+public class BidirectionalLinkedHashMap<K, V> extends AbstractMap<K, V> {
+
+ /**
+ * The head of the doubly linked list.
+ */
+ private transient LinkedEntry<K, V> header;
+
+ /**
+ * The iteration ordering method for this linked hash map: <tt>true</tt> for access-order, <tt>false</tt> for
+ * insertion-order.
+ *
+ * @serial
+ */
+ private final boolean accessOrder;
+
+
+ /**
+ * The default initial capacity - MUST be a power of two.
+ */
+ static final int DEFAULT_INITIAL_CAPACITY = 16;
+
+ /**
+ * The maximum capacity, used if a higher value is implicitly specified by either of the constructors with arguments.
+ * MUST be a power of two <= 1<<30.
+ */
+ static final int MAXIMUM_CAPACITY = 1 << 30;
+
+ /**
+ * The load factor used when none specified in constructor.
+ */
+ static final float DEFAULT_LOAD_FACTOR = 0.75f;
+
+ /**
+ * The table, resized as necessary. Length MUST Always be a power of two.
+ */
+ transient LinkedEntry[] table;
+
+ /**
+ * The number of key-value mappings contained in this map.
+ */
+ transient int size;
+
+ /**
+ * The next size value at which to resize (capacity * load factor).
+ *
+ * @serial
+ */
+ int threshold;
+
+ /**
+ * The load factor for the hash table.
+ *
+ * @serial
+ */
+ final float loadFactor;
+
+ /**
+ * The number of times this HashMap has been structurally modified Structural modifications are those that change the
+ * number of mappings in the HashMap or otherwise modify its internal structure (e.g., rehash). This field is used
+ * to make iterators on Collection-views of the HashMap fail-fast. (See ConcurrentModificationException).
+ */
+ transient volatile int modCount;
+
+ /**
+ * Constructs an empty <tt>HashMap</tt> with the specified initial capacity and load factor.
+ *
+ * @param initialCapacity the initial capacity
+ * @param loadFactor the load factor
+ * @throws IllegalArgumentException if the initial capacity is negative or the load factor is nonpositive
+ */
+ public BidirectionalLinkedHashMap(int initialCapacity, float loadFactor) {
+ this(initialCapacity, loadFactor, false);
+ }
+
+ /**
+ * Constructs an empty <tt>LinkedHashMap</tt> instance with the specified initial capacity, load factor and ordering
+ * mode.
+ *
+ * @param initialCapacity the initial capacity
+ * @param loadFactor the load factor
+ * @param accessOrder the ordering mode - <tt>true</tt> for access-order, <tt>false</tt> for insertion-order
+ * @throws IllegalArgumentException if the initial capacity is negative or the load factor is nonpositive
+ */
+ public BidirectionalLinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
+ if (initialCapacity < 0)
+ throw new IllegalArgumentException("Illegal initial capacity: " +
+ initialCapacity);
+ if (initialCapacity > MAXIMUM_CAPACITY)
+ initialCapacity = MAXIMUM_CAPACITY;
+ if (loadFactor <= 0 || Float.isNaN(loadFactor))
+ throw new IllegalArgumentException("Illegal load factor: " +
+ loadFactor);
+
+ // Find a power of 2 >= initialCapacity
+ int capacity = 1;
+ while (capacity < initialCapacity)
+ capacity <<= 1;
+
+ this.loadFactor = loadFactor;
+ threshold = (int) (capacity * loadFactor);
+ table = new LinkedEntry[capacity];
+ this.accessOrder = accessOrder;
+ init();
+ }
+
+ /**
+ * Constructs an empty <tt>HashMap</tt> with the specified initial capacity and the default load factor (0.75).
+ *
+ * @param initialCapacity the initial capacity.
+ * @throws IllegalArgumentException if the initial capacity is negative.
+ */
+ public BidirectionalLinkedHashMap(int initialCapacity) {
+ this(initialCapacity, DEFAULT_LOAD_FACTOR);
+ }
+
+ /**
+ * Constructs an empty <tt>HashMap</tt> with the default initial capacity (16) and the default load factor (0.75).
+ */
+ public BidirectionalLinkedHashMap() {
+ this.loadFactor = DEFAULT_LOAD_FACTOR;
+ threshold = (int) (DEFAULT_INITIAL_CAPACITY * DEFAULT_LOAD_FACTOR);
+ table = new LinkedEntry[DEFAULT_INITIAL_CAPACITY];
+ accessOrder = false;
+ init();
+ }
+
+ /**
+ * Constructs a new <tt>HashMap</tt> with the same mappings as the specified <tt>Map</tt>. The <tt>HashMap</tt> is
+ * created with default load factor (0.75) and an initial capacity sufficient to hold the mappings in the specified
+ * <tt>Map</tt>.
+ *
+ * @param m the map whose mappings are to be placed in this map
+ * @throws NullPointerException if the specified map is null
+ */
+ public BidirectionalLinkedHashMap(Map<? extends K, ? extends V> m) {
+ this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1,
+ DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR, false);
+ putAllForCreate(m);
+ }
+
+ // internal utilities
+
+ /**
+ * Returns index for hash code h.
+ */
+ static int indexFor(int h, int length) {
+ return h & (length - 1);
+ }
+
+ /**
+ * Returns the number of key-value mappings in this map.
+ *
+ * @return the number of key-value mappings in this map
+ */
+ //@Override
+ public int size() {
+ return size;
+ }
+
+ /**
+ * Returns <tt>true</tt> if this map contains no key-value mappings.
+ *
+ * @return <tt>true</tt> if this map contains no key-value mappings
+ */
+ //@Override
+ public boolean isEmpty() {
+ return size == 0;
+ }
+
+ /**
+ * Returns <tt>true</tt> if this map contains a mapping for the specified key.
+ *
+ * @param key The key whose presence in this map is to be tested
+ * @return <tt>true</tt> if this map contains a mapping for the specified key.
+ */
+ public boolean containsKey(Object key) {
+ return getEntry(key) != null;
+ }
+
+ /**
+ * Returns the entry associated with the specified key in the HashMap. Returns null if the HashMap contains no
+ * mapping for the key.
+ */
+ final LinkedEntry<K, V> getEntry(Object key) {
+ int hash = (key == null) ? 0 : hash(key.hashCode());
+ for (LinkedEntry<K, V> e = table[indexFor(hash, table.length)];
+ e != null;
+ e = e.next) {
+ Object k;
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k))))
+ return e;
+ }
+ return null;
+ }
+
+
+ /**
+ * Associates the specified value with the specified key in this map. If the map previously contained a mapping for
+ * the key, the old value is replaced.
+ *
+ * @param key key with which the specified value is to be associated
+ * @param value value to be associated with the specified key
+ * @return the previous value associated with <tt>key</tt>, or <tt>null</tt> if there was no mapping for
+ * <tt>key</tt>. (A <tt>null</tt> return can also indicate that the map previously associated <tt>null</tt>
+ * with <tt>key</tt>.)
+ */
+ public V put(K key, V value) {
+ if (key == null)
+ return putForNullKey(value);
+ int hash = hash(key.hashCode());
+ int i = indexFor(hash, table.length);
+ for (LinkedEntry<K, V> e = table[i]; e != null; e = e.next) {
+ Object k;
+ if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
+ V oldValue = e.value;
+ e.value = value;
+ e.recordAccess(this);
+ return oldValue;
+ }
+ }
+
+ modCount++;
+ addEntry(hash, key, value, i);
+ return null;
+ }
+
+ /**
+ * Offloaded version of put for null keys
+ */
+ private V putForNullKey(V value) {
+ for (LinkedEntry<K, V> e = table[0]; e != null; e = e.next) {
+ if (e.key == null) {
+ V oldValue = e.value;
+ e.value = value;
+ e.recordAccess(this);
+ return oldValue;
+ }
+ }
+ modCount++;
+ addEntry(0, null, value, 0);
+ return null;
+ }
+
+ /**
+ * This method is used instead of put by constructors and pseudoconstructors (clone, readObject). It does not resize
+ * the table, check for comodification, etc. It calls createEntry rather than addEntry.
+ */
+ private void putForCreate(K key, V value) {
+ int hash = (key == null) ? 0 : hash(key.hashCode());
+ int i = indexFor(hash, table.length);
+
+ /**
+ * Look for preexisting entry for key. This will never happen for
+ * clone or deserialize. It will only happen for construction if the
+ * input Map is a sorted map whose ordering is inconsistent w/ equals.
+ */
+ for (LinkedEntry<K, V> e = table[i]; e != null; e = e.next) {
+ Object k;
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k)))) {
+ e.value = value;
+ return;
+ }
+ }
+
+ createEntry(hash, key, value, i);
+ }
+
+ private void putAllForCreate(Map<? extends K, ? extends V> m) {
+ for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext();) {
+ Map.Entry<? extends K, ? extends V> e = i.next();
+ putForCreate(e.getKey(), e.getValue());
+ }
+ }
+
+ /**
+ * Rehashes the contents of this map into a new array with a larger capacity. This method is called automatically
+ * when the number of keys in this map reaches its threshold.
+ * <p/>
+ * If current capacity is MAXIMUM_CAPACITY, this method does not resize the map, but sets threshold to
+ * Integer.MAX_VALUE. This has the effect of preventing future calls.
+ *
+ * @param newCapacity the new capacity, MUST be a power of two; must be greater than current capacity unless current
+ * capacity is MAXIMUM_CAPACITY (in which case value is irrelevant).
+ */
+ void resize(int newCapacity) {
+ LinkedEntry[] oldTable = table;
+ int oldCapacity = oldTable.length;
+ if (oldCapacity == MAXIMUM_CAPACITY) {
+ threshold = Integer.MAX_VALUE;
+ return;
+ }
+
+ LinkedEntry[] newTable = new LinkedEntry[newCapacity];
+ transfer(newTable);
+ table = newTable;
+ threshold = (int) (newCapacity * loadFactor);
+ }
+
+ /**
+ * Copies all of the mappings from the specified map to this map. These mappings will replace any mappings that this
+ * map had for any of the keys currently in the specified map.
+ *
+ * @param m mappings to be stored in this map
+ * @throws NullPointerException if the specified map is null
+ */
+ public void putAll(Map<? extends K, ? extends V> m) {
+ int numKeysToBeAdded = m.size();
+ if (numKeysToBeAdded == 0)
+ return;
+
+ /*
+ * Expand the map if the map if the number of mappings to be added
+ * is greater than or equal to threshold. This is conservative; the
+ * obvious condition is (m.size() + size) >= threshold, but this
+ * condition could result in a map with twice the appropriate capacity,
+ * if the keys to be added overlap with the keys already in this map.
+ * By using the conservative calculation, we subject ourself
+ * to at most one extra resize.
+ */
+ if (numKeysToBeAdded > threshold) {
+ int targetCapacity = (int) (numKeysToBeAdded / loadFactor + 1);
+ if (targetCapacity > MAXIMUM_CAPACITY)
+ targetCapacity = MAXIMUM_CAPACITY;
+ int newCapacity = table.length;
+ while (newCapacity < targetCapacity)
+ newCapacity <<= 1;
+ if (newCapacity > table.length)
+ resize(newCapacity);
+ }
+
+ for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = m.entrySet().iterator(); i.hasNext();) {
+ Map.Entry<? extends K, ? extends V> e = i.next();
+ put(e.getKey(), e.getValue());
+ }
+ }
+
+ /**
+ * Removes the mapping for the specified key from this map if present.
+ *
+ * @param key key whose mapping is to be removed from the map
+ * @return the previous value associated with <tt>key</tt>, or <tt>null</tt> if there was no mapping for
+ * <tt>key</tt>. (A <tt>null</tt> return can also indicate that the map previously associated <tt>null</tt>
+ * with <tt>key</tt>.)
+ */
+ public V remove(Object key) {
+ LinkedEntry<K, V> e = removeEntryForKey(key);
+ return (e == null ? null : e.value);
+ }
+
+ /**
+ * Removes and returns the entry associated with the specified key in the HashMap. Returns null if the HashMap
+ * contains no mapping for this key.
+ */
+ final LinkedEntry<K, V> removeEntryForKey(Object key) {
+ int hash = (key == null) ? 0 : hash(key.hashCode());
+ int i = indexFor(hash, table.length);
+ LinkedEntry<K, V> prev = table[i];
+ LinkedEntry<K, V> e = prev;
+
+ while (e != null) {
+ LinkedEntry<K, V> next = e.next;
+ Object k;
+ if (e.hash == hash &&
+ ((k = e.key) == key || (key != null && key.equals(k)))) {
+ modCount++;
+ size--;
+ if (prev == e)
+ table[i] = next;
+ else
+ prev.next = next;
+ e.recordRemoval(this);
+ return e;
+ }
+ prev = e;
+ e = next;
+ }
+
+ return e;
+ }
+
+ /**
+ * Special version of remove for EntrySet.
+ */
+ final LinkedEntry<K, V> removeMapping(Object o) {
+ if (!(o instanceof Map.Entry))
+ return null;
+
+ Map.Entry<K, V> entry = (Map.Entry<K, V>) o;
+ Object key = entry.getKey();
+ int hash = (key == null) ? 0 : hash(key.hashCode());
+ int i = indexFor(hash, table.length);
+ LinkedEntry<K, V> prev = table[i];
+ LinkedEntry<K, V> e = prev;
+
+ while (e != null) {
+ LinkedEntry<K, V> next = e.next;
+ if (e.hash == hash && e.equals(entry)) {
+ modCount++;
+ size--;
+ if (prev == e)
+ table[i] = next;
+ else
+ prev.next = next;
+ e.recordRemoval(this);
+ return e;
+ }
+ prev = e;
+ e = next;
+ }
+
+ return e;
+ }
+
+ /**
+ * Removes all of the mappings from this map. The map will be empty after this call returns.
+ */
+ public void clear() {
+ modCount++;
+ LinkedEntry[] tab = table;
+ for (int i = 0; i < tab.length; i++)
+ tab[i] = null;
+ size = 0;
+ header.before = header.after = header;
+ }
+
+ static class LinkedEntry<K, V> implements Map.Entry<K, V> {
+ final K key;
+ V value;
+ LinkedEntry<K, V> next;
+ final int hash;
+
+ // These fields comprise the doubly linked list used for iteration.
+ LinkedEntry<K, V> before, after;
+
+ /**
+ * Creates new entry.
+ */
+ LinkedEntry(int h, K k, V v, LinkedEntry<K, V> n) {
+ value = v;
+ next = n;
+ key = k;
+ hash = h;
+ }
+
+ /**
+ * Removes this entry from the linked list.
+ */
+ private void remove() {
+ before.after = after;
+ after.before = before;
+ }
+
+ /**
+ * Inserts this entry before the specified existing entry in the list.
+ */
+ private void addBefore(LinkedEntry<K, V> existingEntry) {
+ after = existingEntry;
+ before = existingEntry.before;
+ before.after = this;
+ after.before = this;
+ }
+
+ /**
+ * This method is invoked by the superclass whenever the value of a pre-existing entry is read by Map.get or
+ * modified by Map.set. If the enclosing Map is access-ordered, it moves the entry to the end of the list;
+ * otherwise, it does nothing.
+ */
+ void recordAccess(BidirectionalLinkedHashMap<K, V> lm) {
+ if (lm.accessOrder) {
+ lm.modCount++;
+ remove();
+ addBefore(lm.header);
+ }
+ }
+
+ void recordRemoval(BidirectionalLinkedHashMap<K, V> m) {
+ remove();
+ }
+
+ public final K getKey() {
+ return key;
+ }
+
+ public final V getValue() {
+ return value;
+ }
+
+ public final V setValue(V newValue) {
+ V oldValue = value;
+ value = newValue;
+ return oldValue;
+ }
+
+ public final boolean equals(Object o) {
+ if (!(o instanceof Map.Entry))
+ return false;
+ Map.Entry e = (Map.Entry) o;
+ Object k1 = getKey();
+ Object k2 = e.getKey();
+ if (k1 == k2 || (k1 != null && k1.equals(k2))) {
+ Object v1 = getValue();
+ Object v2 = e.getValue();
+ if (v1 == v2 || (v1 != null && v1.equals(v2)))
+ return true;
+ }
+ return false;
+ }
+
+ public final int hashCode() {
+ return (key == null ? 0 : key.hashCode()) ^
+ (value == null ? 0 : value.hashCode());
+ }
+
+ public final String toString() {
+ return getKey() + "=" + getValue();
+ }
+ }
+
+ // These methods are used when serializing HashSets
+ int capacity() {
+ return table.length;
+ }
+
+ float loadFactor() {
+ return loadFactor;
+ }
+
+ /**
+ * Called by superclass constructors and pseudoconstructors (clone, readObject) before any entries are inserted into
+ * the map. Initializes the chain.
+ */
+ void init() {
+ header = new LinkedEntry<K, V>(-1, null, null, null);
+ header.before = header.after = header;
+ }
+
+ /**
+ * Transfers all entries to new table array. This method is called by superclass resize. It is overridden for
+ * performance, as it is faster to iterate using our linked list.
+ */
+ void transfer(LinkedEntry[] newTable) {
+ int newCapacity = newTable.length;
+ for (LinkedEntry<K, V> e = header.after; e != header; e = e.after) {
+ int index = indexFor(e.hash, newCapacity);
+ e.next = newTable[index];
+ newTable[index] = e;
+ }
+ }
+
+
+ /**
+ * Returns <tt>true</tt> if this map maps one or more keys to the specified value.
+ *
+ * @param value value whose presence in this map is to be tested
+ * @return <tt>true</tt> if this map maps one or more keys to the specified value
+ */
+ public boolean containsValue(Object value) {
+ // Overridden to take advantage of faster iterator
+ if (value == null) {
+ for (LinkedEntry e = header.after; e != header; e = e.after)
+ if (e.value == null)
+ return true;
+ } else {
+ for (LinkedEntry e = header.after; e != header; e = e.after)
+ if (value.equals(e.value))
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns the value to which the specified key is mapped, or {@code null} if this map contains no mapping for the
+ * key.
+ * <p/>
+ * <p>More formally, if this map contains a mapping from a key {@code k} to a value {@code v} such that {@code
+ * (key==null ? k==null : key.equals(k))}, then this method returns {@code v}; otherwise it returns {@code null}.
+ * (There can be at most one such mapping.)
+ * <p/>
+ * <p>A return value of {@code null} does not <i>necessarily</i> indicate that the map contains no mapping for the
+ * key; it's also possible that the map explicitly maps the key to {@code null}. The {@link #containsKey containsKey}
+ * operation may be used to distinguish these two cases.
+ */
+ public V get(Object key) {
+ LinkedEntry<K, V> e = getEntry(key);
+ if (e == null)
+ return null;
+ e.recordAccess(this);
+ return e.value;
+ }
+
+ private abstract class LinkedHashIterator<T> implements Iterator<T> {
+ LinkedEntry<K, V> nextEntry;
+ LinkedEntry<K, V> lastReturned = null;
+ boolean directionReversed;
+
+ protected LinkedHashIterator(boolean directionReversed) {
+ this.directionReversed = directionReversed;
+ nextEntry = directionReversed ? header.before : header.after;
+// nextEntry = header;
+ }
+
+ /**
+ * The modCount value that the iterator believes that the backing List should have. If this expectation is
+ * violated, the iterator has detected concurrent modification.
+ */
+ int expectedModCount = modCount;
+
+ public boolean hasNext() {
+ return nextEntry != header;
+ }
+
+ public void remove() {
+ if (lastReturned == null)
+ throw new IllegalStateException();
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+
+ BidirectionalLinkedHashMap.this.remove(lastReturned.key);
+ lastReturned = null;
+ expectedModCount = modCount;
+ }
+
+ LinkedEntry<K, V> nextEntry() {
+ if (modCount != expectedModCount)
+ throw new ConcurrentModificationException();
+ if (nextEntry == header)
+ throw new NoSuchElementException();
+
+ LinkedEntry<K, V> e = lastReturned = nextEntry;
+ nextEntry = directionReversed ? e.before : e.after;
+ return e;
+ }
+ }
+
+ private class KeyIterator extends LinkedHashIterator<K> {
+ protected KeyIterator(boolean directionReversed) {
+ super(directionReversed);
+ }
+
+ public K next() {
+ return nextEntry().getKey();
+ }
+ }
+
+ private class ValueIterator extends LinkedHashIterator<V> {
+ protected ValueIterator(boolean directionReversed) {
+ super(directionReversed);
+ }
+
+ public V next() {
+ return nextEntry().value;
+ }
+ }
+
+ private class EntryIterator extends LinkedHashIterator<Map.Entry<K, V>> {
+ protected EntryIterator(boolean directionReversed) {
+ super(directionReversed);
+ }
+
+ public Map.Entry<K, V> next() {
+ return nextEntry();
+ }
+ }
+
+ /**
+ * This override alters behavior of superclass put method. It causes newly allocated entry to get inserted at the end
+ * of the linked list and removes the eldest entry if appropriate.
+ */
+ void addEntry(int hash, K key, V value, int bucketIndex) {
+ createEntry(hash, key, value, bucketIndex);
+
+ // Grow capacity if appropriate
+ if (size >= threshold) resize(2 * table.length);
+ }
+
+ /**
+ * This override differs from addEntry in that it doesn't resize the table or remove the eldest entry.
+ */
+ void createEntry(int hash, K key, V value, int bucketIndex) {
+ LinkedEntry<K, V> old = table[bucketIndex];
+ LinkedEntry<K, V> e = new LinkedEntry<K, V>(hash, key, value, old);
+ table[bucketIndex] = e;
+ e.addBefore(header);
+ size++;
+ }
+
+ public Collection<V> values() {
+ if (values == null) values = new Values();
+ return values;
+ }
+
+ private final class Values extends AbstractCollection<V> {
+ public Iterator<V> iterator() {
+ return new ValueIterator(false);
+ }
+
+ public int size() {
+ return BidirectionalLinkedHashMap.this.size();
+ }
+
+ public boolean contains(Object o) {
+ return containsValue(o);
+ }
+
+ public void clear() {
+ BidirectionalLinkedHashMap.this.clear();
+ }
+ }
+
+ public ReversibleSet<K> keySet() {
+ if (keySet == null) keySet = new KeySet();
+ return (ReversibleSet<K>) keySet;
+ }
+
+ private class KeySet extends AbstractSet<K>
+ implements ReversibleSet<K> {
+
+ public Iterator<K> reverseIterator() {
+ return new KeyIterator(true);
+ }
+
+ public Iterator<K> iterator() {
+ return new KeyIterator(false);
+ }
+
+ public void clear() {
+ BidirectionalLinkedHashMap.this.clear();
+ }
+
+ public boolean contains(Object o) {
+ return containsKey(o);
+ }
+
+ public boolean remove(Object o) {
+ int size = size();
+ BidirectionalLinkedHashMap.this.remove(o);
+ return size() < size;
+ }
+
+ public int size() {
+ return BidirectionalLinkedHashMap.this.size();
+ }
+ }
+
+ public ReversibleSet<Map.Entry<K, V>> entrySet() {
+ if (entrySet == null) entrySet = new EntrySet();
+ return (ReversibleSet<Map.Entry<K, V>>) entrySet;
+ }
+
+ private final class EntrySet extends AbstractSet<Map.Entry<K, V>>
+ implements ReversibleSet<Map.Entry<K, V>> {
+
+ public Iterator<Map.Entry<K, V>> reverseIterator() {
+ return new EntryIterator(true);
+ }
+
+ public Iterator<Map.Entry<K, V>> iterator() {
+ return new EntryIterator(false);
+ }
+
+ @SuppressWarnings("unchecked")
+ public boolean contains(Object o) {
+ if (!(o instanceof Map.Entry))
+ return false;
+ Map.Entry<K, V> e = (Map.Entry<K, V>) o;
+ LinkedEntry<K, V> candidate = getEntry(e.getKey());
+ return candidate != null && candidate.equals(e);
+ }
+
+ public boolean remove(Object o) {
+ return removeMapping(o) != null;
+ }
+
+ public int size() {
+ return size;
+ }
+
+ public void clear() {
+ BidirectionalLinkedHashMap.this.clear();
+ }
+ }
+}
Modified: core/branches/flat/src/main/java/org/horizon/util/FastCopyHashMap.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/util/FastCopyHashMap.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/main/java/org/horizon/util/FastCopyHashMap.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -24,7 +24,6 @@
import java.io.IOException;
import java.io.Serializable;
import java.util.AbstractCollection;
-import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.Collection;
import java.util.ConcurrentModificationException;
@@ -41,11 +40,6 @@
*/
public class FastCopyHashMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable, Serializable {
/**
- * Marks null keys.
- */
- private static final Object NULL = new Object();
-
- /**
* Serialization ID
*/
private static final long serialVersionUID = 10929568968762L;
@@ -90,11 +84,6 @@
*/
private transient int modCount;
- // Cached views
- private transient KeySet keySet;
- private transient Values values;
- private transient EntrySet entrySet;
-
public FastCopyHashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Can not have a negative size table!");
@@ -142,31 +131,11 @@
this(DEFAULT_CAPACITY);
}
- // The normal bit spreader...
- private static final int hash(Object key) {
- int h = key.hashCode();
- h ^= (h >>> 20) ^ (h >>> 12);
- return h ^ (h >>> 7) ^ (h >>> 4);
- }
-
- @SuppressWarnings("unchecked")
- private static final <K> K maskNull(K key) {
- return key == null ? (K) NULL : key;
- }
-
- private static final <K> K unmaskNull(K key) {
- return key == NULL ? null : key;
- }
-
private int nextIndex(int index, int length) {
index = (index >= length - 1) ? 0 : index + 1;
return index;
}
- private static final boolean eq(Object o1, Object o2) {
- return o1 == o2 || (o1 != null && o1.equals(o2));
- }
-
private static final int index(int hashCode, int length) {
return hashCode & (length - 1);
}
@@ -412,27 +381,6 @@
System.out.println(" Max Distance: " + maxSkew);
}
- public Set<Map.Entry<K, V>> entrySet() {
- if (entrySet == null)
- entrySet = new EntrySet();
-
- return entrySet;
- }
-
- public Set<K> keySet() {
- if (keySet == null)
- keySet = new KeySet();
-
- return keySet;
- }
-
- public Collection<V> values() {
- if (values == null)
- values = new Values();
-
- return values;
- }
-
@SuppressWarnings("unchecked")
private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
@@ -478,18 +426,6 @@
}
}
- private static final class Entry<K, V> {
- final K key;
- final int hash;
- final V value;
-
- Entry(K key, int hash, V value) {
- this.key = key;
- this.hash = hash;
- this.value = value;
- }
- }
-
private abstract class FasyCopyHashMapIterator<E> implements Iterator<E> {
private int next = 0;
private int expectedCount = modCount;
@@ -589,7 +525,6 @@
}
}
-
private class KeyIterator extends FasyCopyHashMapIterator<K> {
public K next() {
return unmaskNull(nextEntry().key);
@@ -603,7 +538,7 @@
}
private class EntryIterator extends FasyCopyHashMapIterator<Map.Entry<K, V>> {
- private class WriteThroughEntry extends SimpleEntry<K, V> {
+ private class WriteThroughEntry extends java.util.AbstractMap.SimpleEntry<K, V> {
WriteThroughEntry(K key, V value) {
super(key, value);
}
@@ -620,9 +555,36 @@
Entry<K, V> e = nextEntry();
return new WriteThroughEntry(unmaskNull(e.key), e.value);
}
+ }
+ public Collection<V> values() {
+ if (values == null) values = new Values();
+ return values;
}
+ private final class Values extends AbstractCollection<V> {
+ public Iterator<V> iterator() {
+ return new ValueIterator();
+ }
+
+ public int size() {
+ return FastCopyHashMap.this.size();
+ }
+
+ public boolean contains(Object o) {
+ return containsValue(o);
+ }
+
+ public void clear() {
+ FastCopyHashMap.this.clear();
+ }
+ }
+
+ public Set<K> keySet() {
+ if (keySet == null) keySet = new KeySet();
+ return keySet;
+ }
+
private class KeySet extends AbstractSet<K> {
public Iterator<K> iterator() {
return new KeyIterator();
@@ -647,18 +609,9 @@
}
}
- private class Values extends AbstractCollection<V> {
- public Iterator<V> iterator() {
- return new ValueIterator();
- }
-
- public void clear() {
- FastCopyHashMap.this.clear();
- }
-
- public int size() {
- return FastCopyHashMap.this.size();
- }
+ public Set<Map.Entry<K, V>> entrySet() {
+ if (entrySet == null) entrySet = new EntrySet();
+ return entrySet;
}
private class EntrySet extends AbstractSet<Map.Entry<K, V>> {
@@ -688,51 +641,15 @@
}
}
- protected static class SimpleEntry<K, V> implements Map.Entry<K, V> {
- private K key;
- private V value;
+ private static final class Entry<K, V> {
+ final K key;
+ final int hash;
+ final V value;
- SimpleEntry(K key, V value) {
+ Entry(K key, int hash, V value) {
this.key = key;
+ this.hash = hash;
this.value = value;
}
-
- SimpleEntry(Map.Entry<K, V> entry) {
- this.key = entry.getKey();
- this.value = entry.getValue();
- }
-
- public K getKey() {
- return key;
- }
-
- public V getValue() {
- return value;
- }
-
- public V setValue(V value) {
- V old = this.value;
- this.value = value;
- return old;
- }
-
- public boolean equals(Object o) {
- if (this == o)
- return true;
-
- if (!(o instanceof Map.Entry))
- return false;
- Map.Entry<?, ?> e = (Map.Entry<?, ?>) o;
- return eq(key, e.getKey()) && eq(value, e.getValue());
- }
-
- public int hashCode() {
- return hash(key) ^
- (value == null ? 0 : hash(value));
- }
-
- public String toString() {
- return getKey() + "=" + getValue();
- }
}
}
Added: core/branches/flat/src/main/java/org/horizon/util/ReversibleSet.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/util/ReversibleSet.java (rev 0)
+++ core/branches/flat/src/main/java/org/horizon/util/ReversibleSet.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -0,0 +1,15 @@
+package org.horizon.util;
+
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * A set that allows reverse iteration of the set elements, exposed via the {@link #reverseIterator()} method. This
+ * only really makes sense for ordered Set implementations.
+ *
+ * @author Manik Surtani
+ * @since 1.0
+ */
+public interface ReversibleSet<E> extends Set<E> {
+ Iterator<E> reverseIterator();
+}
Added: core/branches/flat/src/main/java/org/horizon/util/VisitableLinkedHashSet.java
===================================================================
--- core/branches/flat/src/main/java/org/horizon/util/VisitableLinkedHashSet.java (rev 0)
+++ core/branches/flat/src/main/java/org/horizon/util/VisitableLinkedHashSet.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -0,0 +1,148 @@
+package org.horizon.util;
+
+import java.util.AbstractSet;
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * Similar to the JDK's {@link java.util.LinkedHashSet} except that it sets the underlying {@link
+ * java.util.LinkedHashMap}'s <tt>accessOrder</tt> constructor parameter to <tt>true</tt>, allowing for recording of
+ * visits. To do this, this implementation exposes a {@link #visit(Object)} method to visit a key.
+ *
+ * @author Manik Surtani
+ * @since 1.0
+ */
+public class VisitableLinkedHashSet<E> extends AbstractSet<E> implements ReversibleSet<E> {
+
+ private transient BidirectionalLinkedHashMap<E, Object> map;
+
+ // Dummy value to associate with an Object in the backing Map
+ private static final Object DUMMY_VALUE = new Object();
+
+
+ /**
+ * Constructs a new, empty linked hash set with the specified initial capacity and load factor.
+ *
+ * @param initialCapacity the initial capacity of the linked hash set
+ * @param loadFactor the load factor of the linked hash set
+ * @throws IllegalArgumentException if the initial capacity is less than zero, or if the load factor is nonpositive
+ */
+ public VisitableLinkedHashSet(int initialCapacity, float loadFactor) {
+ map = new BidirectionalLinkedHashMap<E, Object>(initialCapacity, loadFactor, true);
+ }
+
+ /**
+ * Constructs a new, empty linked hash set with the specified initial capacity and the default load factor (0.75).
+ *
+ * @param initialCapacity the initial capacity of the LinkedHashSet
+ * @throws IllegalArgumentException if the initial capacity is less than zero
+ */
+ public VisitableLinkedHashSet(int initialCapacity) {
+ this(initialCapacity, .75f);
+ }
+
+ /**
+ * Constructs a new, empty linked hash set with the default initial capacity (16) and load factor (0.75).
+ */
+ public VisitableLinkedHashSet() {
+ this(16, .75f);
+ }
+
+ /**
+ * Constructs a new linked hash set with the same elements as the specified collection. The linked hash set is
+ * created with an initial capacity sufficient to hold the elements in the specified collection and the default load
+ * factor (0.75).
+ *
+ * @param c the collection whose elements are to be placed into this set
+ * @throws NullPointerException if the specified collection is null
+ */
+ public VisitableLinkedHashSet(Collection<? extends E> c) {
+ this(Math.max(2 * c.size(), 11), .75f);
+ addAll(c);
+ }
+
+ /**
+ * Returns an iterator over the elements in this set. The elements are returned in no particular order.
+ *
+ * @return an Iterator over the elements in this set
+ * @see java.util.ConcurrentModificationException
+ */
+ public Iterator<E> iterator() {
+ return map.keySet().iterator();
+ }
+
+ public Iterator<E> reverseIterator() {
+ return map.keySet().reverseIterator();
+ }
+
+ /**
+ * Returns the number of elements in this set (its cardinality).
+ *
+ * @return the number of elements in this set (its cardinality)
+ */
+ public int size() {
+ return map.size();
+ }
+
+ /**
+ * Returns <tt>true</tt> if this set contains no elements.
+ *
+ * @return <tt>true</tt> if this set contains no elements
+ */
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ /**
+ * Returns <tt>true</tt> if this set contains the specified element. More formally, returns <tt>true</tt> if and only
+ * if this set contains an element <tt>e</tt> such that <tt>(o==null ? e==null : o.equals(e))</tt>.
+ *
+ * @param o element whose presence in this set is to be tested
+ * @return <tt>true</tt> if this set contains the specified element
+ */
+ public boolean contains(Object o) {
+ return map.containsKey(o);
+ }
+
+ /**
+ * Adds the specified element to this set if it is not already present. More formally, adds the specified element
+ * <tt>e</tt> to this set if this set contains no element <tt>e2</tt> such that <tt>(e==null ? e2==null : e.equals(e2))</tt>.
+ * If this set already contains the element, the call leaves the set unchanged and returns <tt>false</tt>.
+ *
+ * @param e element to be added to this set
+ * @return <tt>true</tt> if this set did not already contain the specified element
+ */
+ public boolean add(E e) {
+ return map.put(e, DUMMY_VALUE) == null;
+ }
+
+ /**
+ * Removes the specified element from this set if it is present. More formally, removes an element <tt>e</tt> such
+ * that <tt>(o==null ? e==null : o.equals(e))</tt>, if this set contains such an element. Returns
+ * <tt>true</tt> if this set contained the element (or equivalently, if this set changed as a result of the call).
+ * (This set will not contain the element once the call returns.)
+ *
+ * @param o object to be removed from this set, if present
+ * @return <tt>true</tt> if the set contained the specified element
+ */
+ public boolean remove(Object o) {
+ return map.remove(o) == DUMMY_VALUE;
+ }
+
+ /**
+ * Visits the key in the underlying Map, by performing a {@link java.util.Map#get(Object)}. This records the access
+ * and updates the ordering accordingly.
+ *
+ * @param key key to visit
+ */
+ public void visit(E key) {
+ map.get(key);
+ }
+
+ /**
+ * Removes all of the elements from this set. The set will be empty after this call returns.
+ */
+ public void clear() {
+ map.clear();
+ }
+}
Added: core/branches/flat/src/test/java/org/horizon/SkipListTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/SkipListTest.java (rev 0)
+++ core/branches/flat/src/test/java/org/horizon/SkipListTest.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -0,0 +1,64 @@
+package org.horizon;
+
+import org.testng.annotations.Test;
+
+import java.util.TreeMap;
+
+/**
+ * // TODO: Manik: Document this!
+ *
+ * @author Manik Surtani
+ */
+@Test
+public class SkipListTest {
+
+ public void test() {
+ TreeMap m = new TreeMap();
+ m.put(new Thing(50), "x");
+ Thing t = new Thing(20);
+ m.put(t, "x");
+ m.put(new Thing(40), "x");
+ m.put(new Thing(70), "x");
+
+ System.out.println(m);
+
+ t.number = 99;
+ System.out.println(m);
+ }
+
+ public static class Thing implements Comparable {
+ Integer number;
+
+ public Thing(Integer number) {
+ this.number = number;
+ }
+
+ public int compareTo(Object o) {
+ return number.compareTo(((Thing) o).number);
+ }
+
+ @Override
+ public String toString() {
+ return "Thing{" +
+ "number=" + number +
+ '}';
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Thing thing = (Thing) o;
+
+ if (number != null ? !number.equals(thing.number) : thing.number != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return number != null ? number.hashCode() : 0;
+ }
+ }
+}
Modified: core/branches/flat/src/test/java/org/horizon/eviction/EvictionFunctionalTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/eviction/EvictionFunctionalTest.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/test/java/org/horizon/eviction/EvictionFunctionalTest.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -7,16 +7,19 @@
import org.horizon.manager.CacheManager;
import org.horizon.manager.DefaultCacheManager;
import org.horizon.notifications.Listener;
+import org.horizon.notifications.cachelistener.annotation.CacheEntryEvicted;
import org.horizon.notifications.cachelistener.event.CacheEntryEvictedEvent;
import org.horizon.util.TestingUtil;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
+import java.util.HashSet;
+import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-@Test(groups = "functional", sequential = true, enabled = false)
+@Test(groups = "functional", sequential = true, enabled = true)
public class EvictionFunctionalTest {
Cache cache;
EvictionListener el;
@@ -47,25 +50,31 @@
public void testEviction() throws InterruptedException {
el.latch = new CountDownLatch(1);
- for (int i = 0; i < 11; i++) {
+ for (int i = 0; i < 20; i++) {
cache.put(i, "value");
}
assert el.latch.await(60, TimeUnit.SECONDS);
- for (int i = 1; i < 11; i++) {
- assert cache.containsKey(i);
- }
+ for (int i = 10; i < 20; i++) assert cache.containsKey(i);
- assert !cache.containsKey(0);
+ for (int i = 0; i < 10; i++) assert !cache.containsKey(i);
}
@Listener
public static class EvictionListener {
+ Set<Integer> expectedKeys = new HashSet<Integer>();
CountDownLatch latch;
+ public EvictionListener() {
+ for (int i = 0; i < 10; i++) expectedKeys.add(i);
+ }
+
+ @CacheEntryEvicted
public void handle(CacheEntryEvictedEvent event) {
+ System.out.println("Saw event " + event);
if (event.isPre()) {
- latch.countDown();
+ assert expectedKeys.remove(event.getKey());
+ if (expectedKeys.isEmpty()) latch.countDown();
}
}
}
Modified: core/branches/flat/src/test/java/org/horizon/eviction/EvictionManagerTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/eviction/EvictionManagerTest.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/test/java/org/horizon/eviction/EvictionManagerTest.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -1,7 +1,76 @@
package org.horizon.eviction;
+import org.easymock.EasyMock;
+import static org.easymock.EasyMock.*;
+import org.horizon.config.Configuration;
+import org.horizon.config.EvictionConfig;
+import org.horizon.eviction.algorithms.fifo.FIFOAlgorithmConfig;
+import org.horizon.eviction.events.EvictionEvent;
+import org.horizon.eviction.events.InUseEvictionEvent;
import org.testng.annotations.Test;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
@Test(groups = "unit")
public class EvictionManagerTest {
+
+ private Configuration getCfg() {
+ Configuration cfg = new Configuration();
+ EvictionConfig ecfg = new EvictionConfig();
+ ecfg.setAlgorithmConfig(new FIFOAlgorithmConfig()); // for now
+ cfg.setEvictionConfig(ecfg);
+ return cfg;
+ }
+
+ public void testNoEvictionThread() {
+ EvictionManagerImpl em = new EvictionManagerImpl();
+ Configuration cfg = getCfg();
+ cfg.getEvictionConfig().setWakeUpInterval(0);
+
+ ScheduledExecutorService mockService = EasyMock.createMock(ScheduledExecutorService.class);
+ em.initialize(mockService, cfg, null, null);
+ replay(mockService);
+ em.start();
+
+ assert em.evictionTask == null : "Eviction task is not null! Should not have scheduled anything!";
+ verify(mockService); // expect that the executor was never used!!
+ }
+
+ public void testWakeupInterval() {
+ EvictionManagerImpl em = new EvictionManagerImpl();
+ Configuration cfg = getCfg();
+ cfg.getEvictionConfig().setWakeUpInterval(789);
+
+ ScheduledExecutorService mockService = EasyMock.createMock(ScheduledExecutorService.class);
+ em.initialize(mockService, cfg, null, null);
+
+ ScheduledFuture mockFuture = createNiceMock(ScheduledFuture.class);
+ expect(mockService.scheduleWithFixedDelay(isA(EvictionManagerImpl.ScheduledTask.class), eq((long) 789),
+ eq((long) 789), eq(TimeUnit.MILLISECONDS)))
+ .andReturn(mockFuture).once();
+ replay(mockService);
+ em.start();
+
+ assert em.evictionTask == mockFuture;
+ verify(mockService); // expect that the executor was never used!!
+ }
+
+ public void testMarkInUse() throws InterruptedException {
+ EvictionManagerImpl em = new EvictionManagerImpl();
+ em.evictionEventQueue = createMock(BlockingQueue.class);
+ em.evictionAlgorithm = createNiceMock(EvictionAlgorithm.class);
+ expect(em.evictionEventQueue.size()).andReturn(0).anyTimes();
+ em.evictionEventQueue.put(eq(new InUseEvictionEvent("x", 7000)));
+ expectLastCall().once();
+ em.evictionEventQueue.put(eq(new EvictionEvent("x", EvictionEvent.Type.UNMARK_IN_USE_EVENT)));
+ expectLastCall().once();
+
+ replay(em.evictionEventQueue);
+ em.markKeyCurrentlyInUse("x", 7, TimeUnit.SECONDS);
+ em.unmarkKeyCurrentlyInUse("x");
+ verify(em.evictionEventQueue);
+ }
}
Modified: core/branches/flat/src/test/java/org/horizon/eviction/algorithms/BaseAlgorithmTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/eviction/algorithms/BaseAlgorithmTest.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/test/java/org/horizon/eviction/algorithms/BaseAlgorithmTest.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -3,12 +3,13 @@
import org.easymock.EasyMock;
import static org.easymock.EasyMock.*;
import org.horizon.config.EvictionAlgorithmConfig;
-import org.horizon.eviction.EntryEvictionData;
+import org.horizon.container.DataContainer;
import org.horizon.eviction.EvictionAction;
import org.horizon.eviction.EvictionAlgorithm;
import org.horizon.eviction.EvictionAlgorithmConfigBase;
-import org.horizon.eviction.EvictionEvent;
-import static org.horizon.eviction.EvictionEvent.Type.*;
+import org.horizon.eviction.events.EvictionEvent;
+import static org.horizon.eviction.events.EvictionEvent.Type.*;
+import org.horizon.eviction.events.InUseEvictionEvent;
import org.horizon.util.Util;
import org.testng.annotations.Test;
@@ -21,9 +22,17 @@
protected abstract EvictionAlgorithmConfigBase getNewEvictionAlgorithmConfig();
- protected EvictionAlgorithm createAndInit(EvictionAlgorithmConfig cfg) throws Exception {
- EvictionAlgorithm a = (EvictionAlgorithm) Util.getInstance(cfg.getEvictionAlgorithmClassName());
- a.assignToCache(null, cfg);
+ protected BaseEvictionAlgorithm createAndInit(EvictionAlgorithmConfig cfg) throws Exception {
+ DataContainer dc = createNiceMock(DataContainer.class);
+ expect(dc.getModifiedTimestamp(anyObject())).andReturn(System.currentTimeMillis()).anyTimes();
+ replay(dc);
+ return createAndInit(cfg, dc);
+ }
+
+ protected BaseEvictionAlgorithm createAndInit(EvictionAlgorithmConfig cfg, DataContainer dc) throws Exception {
+ BaseEvictionAlgorithm a = (BaseEvictionAlgorithm) Util.getInstance(cfg.getEvictionAlgorithmClassName());
+ a.init(null, dc, cfg);
+ a.start();
return a;
}
@@ -31,8 +40,11 @@
EvictionAlgorithmConfigBase cfg = getNewEvictionAlgorithmConfig();
cfg.setMinTimeToLive(2 * 60 * 60 * 1000); // something enormous - 2 hrs
cfg.setMaxEntries(5);
- EvictionAlgorithm a = createAndInit(cfg);
- EvictionAction mockAction = EasyMock.createMock(EvictionAction.class);
+ DataContainer dc = createMock(DataContainer.class);
+ expect(dc.getModifiedTimestamp(anyObject())).andReturn(System.currentTimeMillis()).anyTimes();
+ replay(dc);
+ BaseEvictionAlgorithm a = createAndInit(cfg, dc);
+ EvictionAction mockAction = createMock(EvictionAction.class);
a.setEvictionAction(mockAction);
BlockingQueue<EvictionEvent> eventQueue = new LinkedBlockingQueue<EvictionEvent>();
for (int i = 0; i < 10; i++) eventQueue.put(new EvictionEvent(i, EvictionEvent.Type.ADD_ENTRY_EVENT));
@@ -45,18 +57,19 @@
a.process(eventQueue);
verify(mockAction);
- for (EntryEvictionData data : a.getEvictionQueue()) {
+
+ reset(dc);
+ for (Object k : a.getEvictionQueue()) {
// change the creation timestamp to before 2 hrs in the past
// for all even keys
- Integer key = (Integer) data.getKey();
+ Integer key = (Integer) k;
if (key % 2 == 0) {
- data.setCreationTimeStamp(1);
- data.setModifiedTimeStamp(1);
+ expect(dc.getModifiedTimestamp(eq(key))).andReturn((long) 1).once();
EvictionEvent e = new EvictionEvent(key, EvictionEvent.Type.VISIT_ENTRY_EVENT);
- e.setCreationTimestamp(1);
eventQueue.put(e);
} else {
+ expect(dc.getModifiedTimestamp(eq(key))).andReturn(System.currentTimeMillis()).once();
eventQueue.put(new EvictionEvent(key, EvictionEvent.Type.VISIT_ENTRY_EVENT));
}
}
@@ -70,11 +83,19 @@
expect(mockAction.evict(eq(4))).andReturn(true).once();
expect(mockAction.evict(eq(6))).andReturn(true).once();
expect(mockAction.evict(eq(8))).andReturn(true).once();
- replay(mockAction);
+ replay(mockAction, dc);
a.process(eventQueue);
verify(mockAction);
}
+ protected boolean timeOrderedQueue() {
+ return true;
+ }
+
+ protected boolean reverseOrder() {
+ return false;
+ }
+
public void testNumEntries1() throws Exception {
EvictionAlgorithmConfigBase config = getNewEvictionAlgorithmConfig();
config.setMaxEntries(4);
@@ -86,16 +107,26 @@
eventQueue.put(new EvictionEvent("four", ADD_ENTRY_EVENT));
eventQueue.put(new EvictionEvent("five", ADD_ENTRY_EVENT));
EvictionAlgorithm algo = createAndInit(config);
- EvictionAction mockAction = EasyMock.createMock(EvictionAction.class);
+ EvictionAction mockAction = createMock(EvictionAction.class);
algo.setEvictionAction(mockAction);
// should prune down to 2 entries
- EasyMock.expect(mockAction.evict(eq("one"))).andReturn(true).once();
- EasyMock.expect(mockAction.evict(eq("two"))).andReturn(true).once();
- EasyMock.expect(mockAction.evict(eq("three"))).andReturn(true).once();
- EasyMock.replay(mockAction);
+ if (timeOrderedQueue()) {
+ if (reverseOrder()) {
+ expect(mockAction.evict(eq("five"))).andReturn(true).once();
+ expect(mockAction.evict(eq("four"))).andReturn(true).once();
+ expect(mockAction.evict(eq("three"))).andReturn(true).once();
+ } else {
+ expect(mockAction.evict(eq("one"))).andReturn(true).once();
+ expect(mockAction.evict(eq("two"))).andReturn(true).once();
+ expect(mockAction.evict(eq("three"))).andReturn(true).once();
+ }
+ } else {
+ expect(mockAction.evict(anyObject())).andReturn(true).times(3);
+ }
+ replay(mockAction);
algo.process(eventQueue);
- EasyMock.verify(mockAction);
+ verify(mockAction);
}
@@ -107,7 +138,7 @@
eventQueue.put(new EvictionEvent("one", ADD_ENTRY_EVENT));
eventQueue.put(new EvictionEvent("two", ADD_ENTRY_EVENT));
EvictionAlgorithm algo = createAndInit(config);
- EvictionAction mockAction = EasyMock.createMock(EvictionAction.class);
+ EvictionAction mockAction = createMock(EvictionAction.class);
algo.setEvictionAction(mockAction);
// should prune down to 2 entries
@@ -128,11 +159,20 @@
eventQueue.put(new EvictionEvent("three", ADD_ENTRY_EVENT));
eventQueue.put(new EvictionEvent("four", ADD_ENTRY_EVENT));
EvictionAlgorithm algo = createAndInit(config);
- EvictionAction mockAction = EasyMock.createMock(EvictionAction.class);
+ EvictionAction mockAction = createMock(EvictionAction.class);
algo.setEvictionAction(mockAction);
// should prune down to equal to maxEntries
- EasyMock.expect(mockAction.evict(eq("one"))).andReturn(true).once();
+ if (timeOrderedQueue()) {
+ if (reverseOrder()) {
+ EasyMock.expect(mockAction.evict(eq("four"))).andReturn(true).once();
+ } else {
+ EasyMock.expect(mockAction.evict(eq("one"))).andReturn(true).once();
+ }
+ } else {
+ EasyMock.expect(mockAction.evict(anyObject())).andReturn(true).once();
+ }
+
EasyMock.replay(mockAction);
algo.process(eventQueue);
EasyMock.verify(mockAction);
@@ -148,7 +188,7 @@
eventQueue.put(new EvictionEvent("three", ADD_ENTRY_EVENT));
eventQueue.put(new EvictionEvent("four", ADD_ENTRY_EVENT));
EvictionAlgorithm algo = createAndInit(config);
- EvictionAction mockAction = EasyMock.createMock(EvictionAction.class);
+ EvictionAction mockAction = createMock(EvictionAction.class);
algo.setEvictionAction(mockAction);
// expecting nothing to be evicted
@@ -167,11 +207,19 @@
eventQueue.put(new EvictionEvent("three", ADD_ENTRY_EVENT));
eventQueue.put(new EvictionEvent("four", ADD_ENTRY_EVENT));
EvictionAlgorithm algo = createAndInit(config);
- EvictionAction mockAction = EasyMock.createMock(EvictionAction.class);
+ EvictionAction mockAction = createMock(EvictionAction.class);
algo.setEvictionAction(mockAction);
// should prune down to equal to maxEntries
- EasyMock.expect(mockAction.evict(eq("one"))).andReturn(true).once();
+ if (timeOrderedQueue()) {
+ if (reverseOrder()) {
+ EasyMock.expect(mockAction.evict(eq("four"))).andReturn(true).once();
+ } else {
+ EasyMock.expect(mockAction.evict(eq("one"))).andReturn(true).once();
+ }
+ } else {
+ EasyMock.expect(mockAction.evict(anyObject())).andReturn(true).once();
+ }
EasyMock.replay(mockAction);
algo.process(eventQueue);
EasyMock.verify(mockAction);
@@ -185,7 +233,7 @@
eventQueue.put(new EvictionEvent("two", ADD_ENTRY_EVENT));
eventQueue.put(new EvictionEvent("three", ADD_ENTRY_EVENT));
eventQueue.put(new EvictionEvent("four", ADD_ENTRY_EVENT));
- EvictionAlgorithm algo = createAndInit(config);
+ BaseEvictionAlgorithm algo = createAndInit(config);
EvictionAction mockAction = EasyMock.createNiceMock(EvictionAction.class);
algo.setEvictionAction(mockAction);
@@ -194,12 +242,12 @@
EasyMock.replay(mockAction);
algo.process(eventQueue);
- assert algo.getEvictionQueue().size() == 4;
+ assert algo.evictionQueue.size() == 4;
eventQueue.put(new EvictionEvent("three", REMOVE_ENTRY_EVENT));
algo.process(eventQueue);
assert algo.getEvictionQueue().size() == 3;
- assert algo.getEvictionQueue().get("three") == null;
+ assert !algo.getEvictionQueue().contains("three");
}
public void testClearEvent() throws Exception {
@@ -210,7 +258,7 @@
eventQueue.put(new EvictionEvent("two", ADD_ENTRY_EVENT));
eventQueue.put(new EvictionEvent("three", ADD_ENTRY_EVENT));
eventQueue.put(new EvictionEvent("four", ADD_ENTRY_EVENT));
- EvictionAlgorithm algo = createAndInit(config);
+ BaseEvictionAlgorithm algo = createAndInit(config);
EvictionAction mockAction = EasyMock.createNiceMock(EvictionAction.class);
algo.setEvictionAction(mockAction);
@@ -224,6 +272,76 @@
eventQueue.put(new EvictionEvent("three", CLEAR_CACHE_EVENT));
algo.process(eventQueue);
assert algo.getEvictionQueue().size() == 0;
- assert algo.getEvictionQueue().get("three") == null;
+ assert !algo.getEvictionQueue().contains("three");
}
+
+ public void testMarkInUseControl() throws Exception {
+ EvictionAlgorithmConfigBase config = getNewEvictionAlgorithmConfig();
+ config.setMaxEntries(1);
+ BlockingQueue<EvictionEvent> eventQueue = new LinkedBlockingQueue<EvictionEvent>();
+ eventQueue.put(new EvictionEvent("one", ADD_ENTRY_EVENT));
+ eventQueue.put(new EvictionEvent("two", ADD_ENTRY_EVENT));
+ BaseEvictionAlgorithm algo = createAndInit(config);
+ EvictionAction mockAction = EasyMock.createNiceMock(EvictionAction.class);
+ algo.setEvictionAction(mockAction);
+
+ // should prune down to equal to maxEntries
+ if (timeOrderedQueue()) {
+ if (reverseOrder()) {
+ expect(mockAction.evict(eq("two"))).andReturn(true).once();
+ } else {
+ expect(mockAction.evict(eq("one"))).andReturn(true).once();
+ }
+ } else {
+ expect(mockAction.evict(anyObject())).andReturn(true).once();
+ }
+
+ replay(mockAction);
+ algo.process(eventQueue);
+ verify(mockAction);
+
+ assert algo.keysInUse.isEmpty();
+ }
+
+ public void testMarkInUse() throws Exception {
+ EvictionAlgorithmConfigBase config = getNewEvictionAlgorithmConfig();
+ config.setMaxEntries(1);
+ BlockingQueue<EvictionEvent> eventQueue = new LinkedBlockingQueue<EvictionEvent>();
+ eventQueue.put(new EvictionEvent("one", ADD_ENTRY_EVENT));
+ eventQueue.put(new EvictionEvent("two", ADD_ENTRY_EVENT));
+ eventQueue.put(new InUseEvictionEvent("one", 200000));
+ eventQueue.put(new InUseEvictionEvent("two", 200000));
+ BaseEvictionAlgorithm algo = createAndInit(config);
+ EvictionAction mockAction = EasyMock.createNiceMock(EvictionAction.class);
+ algo.setEvictionAction(mockAction);
+
+ // Nothing should happen
+ replay(mockAction);
+ algo.process(eventQueue);
+ verify(mockAction);
+
+ assert algo.keysInUse.size() == 2;
+
+ eventQueue = new LinkedBlockingQueue<EvictionEvent>();
+ eventQueue.put(new EvictionEvent("one", UNMARK_IN_USE_EVENT));
+ eventQueue.put(new EvictionEvent("two", UNMARK_IN_USE_EVENT));
+
+ reset(mockAction);
+ // should prune down to equal to maxEntries
+ if (timeOrderedQueue()) {
+ if (reverseOrder()) {
+ expect(mockAction.evict(eq("two"))).andReturn(true).once();
+ } else {
+ expect(mockAction.evict(eq("one"))).andReturn(true).once();
+ }
+ } else {
+ expect(mockAction.evict(anyObject())).andReturn(true).once();
+ }
+
+ replay(mockAction);
+ algo.process(eventQueue);
+ verify(mockAction);
+
+ assert algo.keysInUse.isEmpty();
+ }
}
Modified: core/branches/flat/src/test/java/org/horizon/eviction/algorithms/BaseQueueTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/eviction/algorithms/BaseQueueTest.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/test/java/org/horizon/eviction/algorithms/BaseQueueTest.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -1,21 +1,16 @@
package org.horizon.eviction.algorithms;
-import org.horizon.eviction.EntryEvictionData;
import org.horizon.eviction.EvictionQueue;
import org.testng.annotations.Test;
@Test(groups = "unit")
public abstract class BaseQueueTest {
- protected EntryEvictionData getFirstEntry(EvictionQueue q) {
+ protected Object getFirstEntry(EvictionQueue q) {
return q.iterator().next();
}
- protected Object getFirstEntryKey(EvictionQueue q) {
- return q.iterator().next().getKey();
- }
-
protected void fillQueue(EvictionQueue q, int numEntries) {
- for (int i = 0; i < numEntries; i++) q.add(new EntryEvictionData(i));
+ for (int i = 0; i < numEntries; i++) q.add(i);
}
protected abstract EvictionQueue getNewEvictionQueue();
@@ -35,16 +30,29 @@
assert q.contains(999);
assert !q.contains(1000);
- for (int i = 0; i < 1000; i++) {
- assert q.get(i).getKey().equals(i);
- }
+ testContents(q);
for (int i = 0; i < 1000; i++) {
assert q.size() == 1000 - i;
q.remove(i);
}
+ assert q.size() == 0 : "Was expecting size=0 but it was " + q.size();
assert q.isEmpty();
- assert q.size() == 0;
}
+
+ protected int[] expectedKeysForSizingTest() {
+ int[] ints = new int[1000];
+ for (int i = 0; i < 1000; i++) ints[i] = i;
+ return ints;
+ }
+
+ protected void testContents(EvictionQueue q) {
+ int i = 0;
+ int[] expected = expectedKeysForSizingTest();
+ for (Object key : q) {
+ assert key.equals(expected[i]);
+ if (i < 1000) i++;
+ }
+ }
}
Modified: core/branches/flat/src/test/java/org/horizon/eviction/algorithms/fifo/FifoAlgorithmTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/eviction/algorithms/fifo/FifoAlgorithmTest.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/test/java/org/horizon/eviction/algorithms/fifo/FifoAlgorithmTest.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -1,62 +1,10 @@
package org.horizon.eviction.algorithms.fifo;
-import org.easymock.EasyMock;
-import static org.easymock.EasyMock.*;
-import org.horizon.eviction.EntryEvictionData;
-import org.horizon.eviction.EvictionAction;
-import org.horizon.eviction.EvictionEvent;
import org.horizon.eviction.algorithms.BaseAlgorithmTest;
import org.testng.annotations.Test;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.LinkedBlockingQueue;
-
@Test(groups = "unit")
public class FifoAlgorithmTest extends BaseAlgorithmTest {
-
- public void testMaxEntries() throws Exception {
- FIFOAlgorithmConfig config = new FIFOAlgorithmConfig();
- config.setMaxEntries(5);
-
- FIFOAlgorithm algo = (FIFOAlgorithm) createAndInit(config);
- EvictionAction mockAction = EasyMock.createMock(EvictionAction.class);
- algo.setEvictionAction(mockAction);
-
- BlockingQueue<EvictionEvent> eventQueue = new LinkedBlockingQueue<EvictionEvent>();
- for (int i = 0; i < 10; i++) eventQueue.put(new EvictionEvent(i, EvictionEvent.Type.ADD_ENTRY_EVENT));
-
- for (int i = 0; i < 5; i++) expect(mockAction.evict(eq(i))).andReturn(true).once();
- replay(mockAction);
- algo.process(eventQueue);
- verify(mockAction);
-
- // now verify the order.
- int index = 5;
- for (EntryEvictionData data : algo.getEvictionQueue()) {
- assert data.getKey().equals(index);
- index++;
- }
-
- // now verify the same order still exists after visiting the entries
- for (int i = 5; i < 10; i++) {
- eventQueue.put(new EvictionEvent(i, EvictionEvent.Type.VISIT_ENTRY_EVENT));
- }
- for (int i = 5; i < 10; i++) {
- eventQueue.put(new EvictionEvent(i, EvictionEvent.Type.VISIT_ENTRY_EVENT));
- }
-
- algo.process(eventQueue);
-
- assert 5 == algo.getEvictionQueue().size();
-
- // now verify the order.
- index = 5;
- for (EntryEvictionData data : algo.getEvictionQueue()) {
- assert data.getKey().equals(index);
- index++;
- }
- }
-
protected FIFOAlgorithmConfig getNewEvictionAlgorithmConfig() {
return new FIFOAlgorithmConfig();
}
Modified: core/branches/flat/src/test/java/org/horizon/eviction/algorithms/fifo/FifoQueueTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/eviction/algorithms/fifo/FifoQueueTest.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/test/java/org/horizon/eviction/algorithms/fifo/FifoQueueTest.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -1,6 +1,5 @@
package org.horizon.eviction.algorithms.fifo;
-import org.horizon.eviction.EntryEvictionData;
import org.horizon.eviction.EvictionQueue;
import org.horizon.eviction.algorithms.BaseQueueTest;
import org.testng.annotations.Test;
@@ -9,33 +8,34 @@
@Test(groups = "unit")
public class FifoQueueTest extends BaseQueueTest {
+
public void testOrder() throws Exception {
FIFOQueue queue = new FIFOQueue();
fillQueue(queue, 5000);
- assert getFirstEntryKey(queue).equals(0);
+ assert getFirstEntry(queue).equals(0);
// now make sure the ordering is correct.
int k = 0;
- for (Iterator<EntryEvictionData> i = queue.iterator(); i.hasNext();) {
- EntryEvictionData data = i.next();
- assert data.getKey().equals(k);
+ for (Iterator<Object> i = queue.iterator(); i.hasNext();) {
+ Object key = i.next();
+ assert key.equals(k);
i.remove();
k++;
if (k == 2500) break;
}
- assert getFirstEntryKey(queue).equals(2500);
+ assert getFirstEntry(queue).equals(2500);
assert 2500 == queue.size();
assert !queue.isEmpty();
k = 2500;
- for (Iterator<EntryEvictionData> i = queue.iterator(); i.hasNext();) {
- EntryEvictionData data = i.next();
- assert data.getKey().equals(k);
+ for (Iterator<Object> i = queue.iterator(); i.hasNext();) {
+ Object key = i.next();
+ assert key.equals(k);
i.remove();
k++;
}
Modified: core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lfu/LfuAlgorithmTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lfu/LfuAlgorithmTest.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lfu/LfuAlgorithmTest.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -5,55 +5,11 @@
@Test(groups = "unit")
public class LfuAlgorithmTest extends BaseAlgorithmTest {
-// public void testMinTimeToLive() throws Exception {
-// LFUAlgorithmConfig cfg = getNewEvictionAlgorithmConfig();
-// cfg.setMinTimeToLive(2 * 60 * 60 * 1000); // something enormous - 2 hrs
-// cfg.setMaxEntries(5);
-// EvictionAlgorithm a = createAndInit(cfg);
-// EvictionAction mockAction = EasyMock.createMock(EvictionAction.class);
-// a.setEvictionAction(mockAction);
-// BlockingQueue<EvictionEvent> eventQueue = new LinkedBlockingQueue<EvictionEvent>();
-// for (int i = 0; i < 10; i++) eventQueue.put(new EvictionEvent(i, ADD_ENTRY_EVENT));
-//
-// assert eventQueue.size() == 10;
-//
-// // what do we expect to happen on the eviction action class?
-// // nothing at this stage.
-// replay(mockAction);
-// a.process(eventQueue);
-// verify(mockAction);
-//
-// for (EntryEvictionData data : a.getEvictionQueue()) {
-// // change the creation timestamp to before 2 hrs in the past
-// // for all even keys
-// Integer key = (Integer) data.getKey();
-//
-// if (key % 2 == 0) {
-// EvictionEvent e = new EvictionEvent(key, EvictionEvent.Type.VISIT_ENTRY_EVENT);
-// e.setCreationTimestamp(1);
-// data.setCreationTimeStamp(1);
-// data.setModifiedTimeStamp(1);
-// eventQueue.put(e);
-// } else {
-// eventQueue.put(new EvictionEvent(key, EvictionEvent.Type.VISIT_ENTRY_EVENT));
-// }
-// }
-//
-// assert eventQueue.size() == 10;
-//
-// // this time we expect all even numbered keys to get evicted, since they are least frequently visited
-// reset(mockAction);
-// expect(mockAction.evict(eq(0))).andReturn(true).once();
-// expect(mockAction.evict(eq(2))).andReturn(true).once();
-// expect(mockAction.evict(eq(4))).andReturn(true).once();
-// expect(mockAction.evict(eq(6))).andReturn(true).once();
-// expect(mockAction.evict(eq(8))).andReturn(true).once();
-// replay(mockAction);
-// a.process(eventQueue);
-// verify(mockAction);
-// }
+ @Override
+ protected boolean timeOrderedQueue() {
+ return false;
+ }
-
protected LFUAlgorithmConfig getNewEvictionAlgorithmConfig() {
return new LFUAlgorithmConfig();
}
Modified: core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lfu/LfuQueueTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lfu/LfuQueueTest.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lfu/LfuQueueTest.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -1,74 +1,78 @@
package org.horizon.eviction.algorithms.lfu;
-import org.horizon.eviction.EntryEvictionData;
import org.horizon.eviction.EvictionQueue;
import org.horizon.eviction.algorithms.BaseQueueTest;
import org.testng.annotations.Test;
+import java.util.HashSet;
import java.util.Iterator;
+import java.util.Set;
@Test(groups = "unit")
public class LfuQueueTest extends BaseQueueTest {
+
+ @Override
+ protected void testContents(EvictionQueue q) {
+ // we should expect the keys in any order here, since they all have an equal visit count
+ Set<Integer> allKeys = intSet(1000);
+
+ for (Object key : q) assert allKeys.remove(key);
+
+ assert allKeys.isEmpty();
+ }
+
+ private Set<Integer> intSet(int count) {
+ Set<Integer> s = new HashSet<Integer>();
+ for (int i = 0; i < count; i++) s.add(i);
+ return s;
+ }
+
public void testOrder() throws Exception {
LFUQueue queue = (LFUQueue) getNewEvictionQueue();
fillQueue(queue, 500);
assert 500 == queue.size();
- queue.reSortEvictionQueue();
+ Set<Integer> allKeys = intSet(500);
+ for (Object key : queue) assert allKeys.remove(key) : "Unexpected key " + key + " encountered!";
+ assert allKeys.isEmpty() : "Not all keys were encountered. Remaining keys " + allKeys;
+ for (int i = 1; i < 500; i += 2) queue.visit(i); // visit all the even keys
- assert 500 == queue.size();
+ System.out.println("Set: " + queue.keys);
+ System.out.println("KeyMap: " + queue.keyMap);
- int k = 0;
- for (EntryEvictionData entry : queue) {
- assert entry.getKey().equals(k);
- if (k % 2 == 0) entry.incrementNumberOfVisits();
- k++;
- }
+ assert (Integer) getFirstEntry(queue) % 2 == 1 : "We don't know which odd key this would be";
- queue.reSortEvictionQueue();
-
- assert getFirstEntryKey(queue).equals(1);
-
// now check the sort order.
- k = 0;
- for (EntryEvictionData entry : queue) {
- if (k < 250)
- assert 0 == entry.getNumberOfVisits();
- else
- assert 1 == entry.getNumberOfVisits();
- k++;
+ for (Object key : queue) {
+ Integer k = (Integer) key;
+ assert queue.keyMap.get(key).visits == k % 2 : "Expecting " + (k % 2) + " visits on key " + key + " but it was " + queue.keyMap.get(key).visits;
}
- k = 0;
- for (Iterator<EntryEvictionData> it = queue.iterator(); it.hasNext() && it.next() != null;) {
+ int k = 0;
+ for (Iterator<Object> it = queue.iterator(); it.hasNext() && it.next() != null;) {
if (k == 250) break;
it.remove();
k++;
}
- queue.doBatchRemove();
-
assert 250 == queue.size();
assert !queue.contains(275);
- for (int i = 0; i < 500; i++) {
- if (i % 2 == 0) {
- EntryEvictionData data = queue.get(i);
- assert 1 == data.getNumberOfVisits();
- if (i > 250) data.incrementNumberOfVisits();
- }
+ for (int i = 0; i < 500; i += 2) {
+ queue.visit(i);
+ LFUQueue.VisitableKey data = queue.keyMap.get(i);
+ assert 1 == data.visits : "Expected visit count to be 1 but it was " + data.visits + " on key " + i;
+ if (i > 250) queue.visit(i); // visit again!
}
- queue.reSortEvictionQueue();
assert 250 == queue.size();
- k = 0;
- for (EntryEvictionData entry : queue) {
- if (k <= 125)
- assert 1 == entry.getNumberOfVisits();
+ for (Object key : queue) {
+ k = (Integer) key;
+ if (k <= 250)
+ assert 1 == queue.keyMap.get(key).visits : "Expected visit count to be 1 but it was " + queue.keyMap.get(key).visits + " on key " + key;
else
- assert 2 == entry.getNumberOfVisits();
- k++;
+ assert 2 == queue.keyMap.get(key).visits : "Expected visit count to be 2 but it was " + queue.keyMap.get(key).visits + " on key " + key;
}
}
@@ -77,7 +81,6 @@
fillQueue(queue, 500);
assert 500 == queue.size();
- assert 500 == queue.evictionList.size();
assert 500 == queue.keyMap.size();
int i = 0;
@@ -86,21 +89,6 @@
i++;
}
assert 250 == queue.size();
-
- for (EntryEvictionData data : queue.removalQueue) {
- int currentIndex = (Integer) data.getKey();
- assert 0 == currentIndex % 2;
-
- assert !queue.contains(currentIndex);
- assert queue.evictionList.contains(data);
- }
-
- assert 500 == queue.evictionList.size();
-
- queue.doBatchRemove();
-
- assert 0 == queue.removalQueue.size();
- assert 250 == queue.evictionList.size();
}
protected EvictionQueue getNewEvictionQueue() {
Modified: core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lru/LruAlgorithmTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lru/LruAlgorithmTest.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lru/LruAlgorithmTest.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -5,56 +5,7 @@
@Test(groups = "unit")
public class LruAlgorithmTest extends BaseAlgorithmTest {
-
protected LRUAlgorithmConfig getNewEvictionAlgorithmConfig() {
return new LRUAlgorithmConfig();
}
-//
-// public void testMinTimeToLive() throws Exception {
-// LRUAlgorithmConfig cfg = getNewEvictionAlgorithmConfig();
-// cfg.setMinTimeToLive(2 * 60 * 60 * 1000); // something enormous - 2 hrs
-// cfg.setMaxEntries(5);
-// EvictionAlgorithm a = createAndInit(cfg);
-// EvictionAction mockAction = EasyMock.createMock(EvictionAction.class);
-// a.setEvictionAction(mockAction);
-// BlockingQueue<EvictionEvent> eventQueue = new LinkedBlockingQueue<EvictionEvent>();
-// for (int i = 0; i < 10; i++) eventQueue.put(new EvictionEvent(i, EvictionEvent.Type.ADD_ENTRY_EVENT));
-//
-// assert eventQueue.size() == 10;
-//
-// // what do we expect to happen on the eviction action class?
-// // nothing at this stage.
-// replay(mockAction);
-// a.process(eventQueue);
-// verify(mockAction);
-//
-// for (EntryEvictionData data : a.getEvictionQueue()) {
-// // change the creation timestamp to before 2 hrs in the past
-// // for all even keys
-// Integer key = (Integer) data.getKey();
-//
-// if (key % 2 == 0) {
-// data.setCreationTimeStamp(1);
-// data.setModifiedTimeStamp(1);
-// EvictionEvent e = new EvictionEvent(key, EvictionEvent.Type.VISIT_ENTRY_EVENT);
-// e.setCreationTimestamp(1);
-// eventQueue.put(e);
-// } else {
-// eventQueue.put(new EvictionEvent(key, EvictionEvent.Type.VISIT_ENTRY_EVENT));
-// }
-// }
-//
-// assert eventQueue.size() == 10;
-//
-// // this time we expect all even numbered keys to get evicted.
-// reset(mockAction);
-// expect(mockAction.evict(eq(0))).andReturn(true).once();
-// expect(mockAction.evict(eq(2))).andReturn(true).once();
-// expect(mockAction.evict(eq(4))).andReturn(true).once();
-// expect(mockAction.evict(eq(6))).andReturn(true).once();
-// expect(mockAction.evict(eq(8))).andReturn(true).once();
-// replay(mockAction);
-// a.process(eventQueue);
-// verify(mockAction);
-// }
}
Modified: core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lru/LruQueueTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lru/LruQueueTest.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/test/java/org/horizon/eviction/algorithms/lru/LruQueueTest.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -1,6 +1,5 @@
package org.horizon.eviction.algorithms.lru;
-import org.horizon.eviction.EntryEvictionData;
import org.horizon.eviction.EvictionQueue;
import org.horizon.eviction.algorithms.BaseQueueTest;
import org.testng.annotations.Test;
@@ -20,78 +19,46 @@
for (int i = 0; i < 500; i++) {
if ((i < 100) || (i >= 300 && i < 400)) {
// visit the entries from 0-99 and the entries from 300 - 399
- reorderByLRU(queue, i);
+ queue.visit(i);
}
}
- // visiting the nodes should have no affect on the maxAgeQueue.
- Iterator<EntryEvictionData> maxAgeIt = queue.iterateMaxAgeQueue();
- int count = 0;
- long lastTs = 0;
- while (maxAgeIt.hasNext()) {
- EntryEvictionData data = maxAgeIt.next();
- assert lastTs <= data.getCreationTimeStamp();
- lastTs = data.getCreationTimeStamp();
- count++;
- }
+ int[] expectedOrder = new int[500];
- Iterator<EntryEvictionData> lruIt = queue.iterateLRUQueue();
- count = 0;
- while (lruIt.hasNext()) {
- EntryEvictionData data = lruIt.next();
- int index = (Integer) data.getKey();
+ // least recently udes first
+ // this is 100 - 299, then 400 - 499
+ int index = 0;
+ for (int i = 100; i < 300; i++) expectedOrder[index++] = i;
+ for (int i = 400; i < 500; i++) expectedOrder[index++] = i;
- // the last 200 in the list should be the visisted LRU ones.
- if (count >= 300 && count < 400)
- assert count - 300 == index;
- else if (count >= 400 && count < 500)
- assert count - 100 == index;
- else if (count < 200)
- assert count + 100 == index;
- else if (count >= 200 && count < 300)
- assert count + 200 == index;
+ // then 0 - 99, and then 300 - 399
+ for (int i = 0; i < 100; i++) expectedOrder[index++] = i;
+ for (int i = 300; i < 400; i++) expectedOrder[index++] = i;
- count++;
+ int i = 0;
+ for (Iterator<Object> it = queue.iterator(); it.hasNext();) {
+ assert it.next().equals(expectedOrder[i++]);
+ it.remove();
}
- Object key = getFirstEntryKey(queue);
- queue.remove(key);
- assert 499 == queue.size();
-
- EntryEvictionData data = queue.iterateLRUQueue().next();
- queue.remove(data.getKey());
- assert 498 == queue.size();
- }
-
- public void testEmptyingInternalCollections() {
- LRUQueue queue = new LRUQueue();
- fillQueue(queue, 1000);
- assert queue.size() == 1000;
- assert queue.maxAgeQueue.size() == 1000;
- assert queue.lruQueue.size() == 1000;
-
- for (Iterator<EntryEvictionData> it = queue.iterator(); it.hasNext() && it.next() != null;) it.remove();
-
assert queue.isEmpty();
- assert queue.maxAgeQueue.isEmpty();
- assert queue.lruQueue.isEmpty();
}
- public void testGetFirstLRUNodeEntry() throws Exception {
+ public void testReordering() throws Exception {
LRUQueue queue = new LRUQueue();
fillQueue(queue, 100);
for (int i = 0; i < 100; i++) {
- // this should move all the even numbered entries to the bottom of the lruQueue.
- // maxAgeQueue should be unaffected.
- if (i % 2 == 0) reorderByLRU(queue, i);
+// this should move all the even numbered entries to the bottom of the lruQueue.
+// maxAgeQueue should be unaffected.
+ if (i % 2 == 0) queue.visit(i);
}
assert 100 == queue.size();
int count = 0;
- for (Iterator<EntryEvictionData> it = queue.iterateLRUQueue(); it.hasNext();) {
- int nodeIndex = (Integer) it.next().getKey();
+ for (Iterator<Object> it = queue.iterator(); it.hasNext();) {
+ int nodeIndex = (Integer) it.next();
if (count < 50) {
// the top 50 should be all odds in the lruQueue/
@@ -105,35 +72,4 @@
}
assert queue.isEmpty();
}
-
- public void testGetFirstMaxAgeNodeEntriy() throws Exception {
- LRUQueue queue = new LRUQueue();
- fillQueue(queue, 100);
-
- for (int i = 0; i < 100; i++) {
- // this should move all the even numbered NodeEntries to the bottom of the lruQueue.
- // maxAgeQueue should be unaffected.
- if (i % 2 == 0) {
- reorderByLRU(queue, i);
- }
- }
-
- int count = 0;
- for (Iterator<EntryEvictionData> it = queue.iterateMaxAgeQueue(); it.hasNext();) {
- int nodeIndex = (Integer) it.next().getKey();
- assert count == nodeIndex;
- it.remove();
- count++;
- }
-
- assert queue.isEmpty();
- }
-
- private void reorderByLRU(LRUQueue queue, Object key) {
- // leave the max age queue alone - it is like a fifo.
-
- // the lru queue is access ordered. meaning the most recently read item is moved to the bottom of the queue.
- // simply calling get against it visits it and will cause LinkedHashMap to move it to the bottom of the queue.
- queue.lruQueue.get(key);
- }
}
Modified: core/branches/flat/src/test/java/org/horizon/eviction/algorithms/mru/MruAlgorithmTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/eviction/algorithms/mru/MruAlgorithmTest.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/test/java/org/horizon/eviction/algorithms/mru/MruAlgorithmTest.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -8,66 +8,9 @@
protected MRUAlgorithmConfig getNewEvictionAlgorithmConfig() {
return new MRUAlgorithmConfig();
}
-//
-// public void testMinTimeToLive() throws Exception {
-// MRUAlgorithmConfig cfg = getNewEvictionAlgorithmConfig();
-// cfg.setMinTimeToLive(2 * 60 * 60 * 1000); // something enormous - 2 hrs
-// cfg.setMaxEntries(5);
-// EvictionAlgorithm a = createAndInit(cfg);
-// EvictionAction mockAction = EasyMock.createMock(EvictionAction.class);
-// a.setEvictionAction(mockAction);
-// BlockingQueue<EvictionEvent> eventQueue = new LinkedBlockingQueue<EvictionEvent>();
-// for (int i = 0; i < 10; i++) eventQueue.put(new EvictionEvent(i, EvictionEvent.Type.ADD_ENTRY_EVENT));
-//
-// assert eventQueue.size() == 10;
-//
-// // what do we expect to happen on the eviction action class?
-// // nothing at this stage.
-// replay(mockAction);
-// a.process(eventQueue);
-// verify(mockAction);
-//
-//
-// // the MRU queue doesn't actually work on timestamps, but instead considers the most recently accessed, and uses a stack.
-// for (EntryEvictionData data : a.getEvictionQueue()) {
-// Integer key = (Integer) data.getKey();
-//
-// if (key % 2 == 1) {
-// // so first touch all of the odd keys.
-// data.setCreationTimeStamp(1);
-// data.setModifiedTimeStamp(1);
-// EvictionEvent e = new EvictionEvent(key, EvictionEvent.Type.VISIT_ENTRY_EVENT);
-// e.setCreationTimestamp(1);
-// eventQueue.put(e);
-// }
-// }
-// // and now the event ones
-// for (EntryEvictionData data : a.getEvictionQueue()) {
-// Integer key = (Integer) data.getKey();
-//
-// if (key % 2 == 0) {
-// // so first touch all of the odd keys.
-// data.setCreationTimeStamp(1);
-// data.setModifiedTimeStamp(1);
-// EvictionEvent e = new EvictionEvent(key, EvictionEvent.Type.VISIT_ENTRY_EVENT);
-// e.setCreationTimestamp(1);
-// eventQueue.put(e);
-// }
-// }
-//
-//
-// // so that the even keys will be the most recently used.
-// assert eventQueue.size() == 10;
-//
-// // this time we expect all even numbered keys to get evicted.
-// reset(mockAction);
-// expect(mockAction.evict(eq(0))).andReturn(true).once();
-// expect(mockAction.evict(eq(2))).andReturn(true).once();
-// expect(mockAction.evict(eq(4))).andReturn(true).once();
-// expect(mockAction.evict(eq(6))).andReturn(true).once();
-// expect(mockAction.evict(eq(8))).andReturn(true).once();
-// replay(mockAction);
-// a.process(eventQueue);
-// verify(mockAction);
-// }
+
+ @Override
+ protected boolean reverseOrder() {
+ return true;
+ }
}
Modified: core/branches/flat/src/test/java/org/horizon/eviction/algorithms/mru/MruQueueTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/eviction/algorithms/mru/MruQueueTest.java 2009-02-06 10:03:13 UTC (rev 7656)
+++ core/branches/flat/src/test/java/org/horizon/eviction/algorithms/mru/MruQueueTest.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -1,7 +1,5 @@
package org.horizon.eviction.algorithms.mru;
-import org.horizon.eviction.EntryEvictionData;
-import org.horizon.eviction.EvictionQueue;
import org.horizon.eviction.algorithms.BaseQueueTest;
import org.testng.annotations.Test;
@@ -9,35 +7,76 @@
@Test(groups = "unit")
public class MruQueueTest extends BaseQueueTest {
- protected EvictionQueue getNewEvictionQueue() {
+ protected MRUQueue getNewEvictionQueue() {
return new MRUQueue();
}
public void testOrder() throws Exception {
- MRUQueue queue = new MRUQueue();
+ MRUQueue queue = getNewEvictionQueue();
+ fillQueue(queue, 500);
+
+ for (int i = 0; i < 500; i++) {
+ if ((i < 100) || (i >= 300 && i < 400)) {
+ // visit the entries from 0-99 and the entries from 300 - 399
+ queue.visit(i);
+ }
+ }
+
+ int[] expectedOrder = new int[500];
+
+ // most recently used first
+ int index = 0;
+ // this is 399 - 300, then 99 - 0
+ for (int i = 399; i > 299; i--) expectedOrder[index++] = i;
+ for (int i = 99; i > -1; i--) expectedOrder[index++] = i;
+
+ // then 499 - 400, then 299 - 100
+ for (int i = 499; i > 399; i--) expectedOrder[index++] = i;
+ for (int i = 299; i > 99; i--) expectedOrder[index++] = i;
+
+ int i = 0;
+ for (Iterator<Object> it = queue.iterator(); it.hasNext();) {
+ assert it.next().equals(expectedOrder[i++]);
+ it.remove();
+ }
+
+ assert queue.isEmpty();
+ }
+
+ public void testReordering() throws Exception {
+ MRUQueue queue = getNewEvictionQueue();
fillQueue(queue, 100);
+
for (int i = 0; i < 100; i++) {
- if (i % 2 == 0) {
- EntryEvictionData data = queue.get(i);
- data.setModifiedTimeStamp(System.currentTimeMillis());
- queue.moveToTopOfStack(i);
- }
+// this should move all the even numbered entries to the bottom of the lruQueue.
+// maxAgeQueue should be unaffected.
+ if (i % 2 == 0) queue.visit(i);
}
+ assert 100 == queue.size();
+
int count = 0;
- for (Iterator<EntryEvictionData> it = queue.iterator(); it.hasNext();) {
- EntryEvictionData data = it.next();
+ for (Iterator<Object> it = queue.iterator(); it.hasNext();) {
+ int nodeIndex = (Integer) it.next();
+
if (count < 50) {
- assert data.getModifiedTimeStamp() > 0;
+ // the top 50 should be all evens in the mruQueue
+ assert nodeIndex % 2 == 0;
} else {
- assert data.getModifiedTimeStamp() == 0;
+ // the bottom fifty should all be odd #'s (and 0)
+ assert nodeIndex % 2 != 0;
}
it.remove();
count++;
}
-
assert queue.isEmpty();
- assert queue.keyMap.isEmpty();
- assert queue.list.isEmpty();
}
+
+ @Override
+ protected int[] expectedKeysForSizingTest() {
+ int[] ints = new int[1000];
+ int index = 0;
+ for (int i = 999; i > -1; i--) ints[index++] = i;
+ return ints;
+ }
}
Added: core/branches/flat/src/test/java/org/horizon/util/BidirectionalLinkedHashMapTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/util/BidirectionalLinkedHashMapTest.java (rev 0)
+++ core/branches/flat/src/test/java/org/horizon/util/BidirectionalLinkedHashMapTest.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -0,0 +1,98 @@
+package org.horizon.util;
+
+import org.testng.annotations.Test;
+
+import java.util.Iterator;
+import java.util.Map;
+
+@Test(groups = "unit")
+public class BidirectionalLinkedHashMapTest {
+ public void testIterators() {
+ BidirectionalLinkedHashMap<Integer, Object> map = new BidirectionalLinkedHashMap<Integer, Object>();
+ initMap(map);
+
+ testOrderBeforeRemoval(map);
+
+ // Remove some stuff and check that the order is still in tact
+ for (int i = 500; i < 600; i++) map.remove(i);
+
+ testOrderAfterRemoval(map);
+
+ // now attemp a visit and test that the visits are NOT recorded
+ map.get(200);
+
+ // order should still be maintained
+ testOrderAfterRemoval(map);
+ }
+
+ public void testAccessOrderIterators() {
+ BidirectionalLinkedHashMap<Integer, Object> map = new BidirectionalLinkedHashMap<Integer, Object>(
+ BidirectionalLinkedHashMap.DEFAULT_INITIAL_CAPACITY,
+ BidirectionalLinkedHashMap.DEFAULT_LOAD_FACTOR,
+ true
+ );
+ initMap(map);
+
+ testOrderBeforeRemoval(map);
+
+ // now attemp a visit and test that the visits are NOT recorded
+ map.get(200);
+
+ // check the forward iterator that everything is in order of entry
+ Iterator<Integer> it = map.keySet().iterator();
+ int index = 0;
+ while (it.hasNext()) {
+ if (index == 200) index = 201;
+ if (index == 1000) index = 200;
+ assert it.next() == index++;
+ }
+
+ // now check the reverse iterator.
+ it = map.keySet().reverseIterator();
+ index = 200; // this should be the first
+ while (it.hasNext()) {
+ assert it.next() == index--;
+ if (index == 199) index = 999;
+ if (index == 200) index = 199;
+ }
+ }
+
+
+ private void initMap(Map<Integer, Object> map) {
+ Object value = new Object();
+ for (int i = 0; i < 1000; i++) map.put(i, value);
+ }
+
+ private void testOrderBeforeRemoval(BidirectionalLinkedHashMap<Integer, Object> map) {
+ // check the forward iterator that everything is in order of entry
+ Iterator<Integer> it = map.keySet().iterator();
+ int index = 0;
+ while (it.hasNext()) assert it.next() == index++;
+
+ assert index == 999 : "Was expecting 999, was " + index;
+
+ // now check the reverse iterator.
+ it = map.keySet().reverseIterator();
+ while (it.hasNext()) assert it.next() == index--;
+
+ assert index == 0 : "Was expecting 0, was " + index;
+ }
+
+
+ private void testOrderAfterRemoval(BidirectionalLinkedHashMap<Integer, Object> map) {
+ Iterator<Integer> it = map.keySet().iterator();
+ int index = 0;
+ while (it.hasNext()) {
+ if (index == 500) index = 600; // 500 - 599 have been removed
+ assert it.next() == index++;
+ }
+
+ // now check the reverse iterator.
+ it = map.keySet().reverseIterator();
+ index = 999;
+ while (it.hasNext()) {
+ if (index == 599) index = 499; // 500 - 599 have been removed
+ assert it.next() == index--;
+ }
+ }
+}
Added: core/branches/flat/src/test/java/org/horizon/util/VisitableLinkedHashSetTest.java
===================================================================
--- core/branches/flat/src/test/java/org/horizon/util/VisitableLinkedHashSetTest.java (rev 0)
+++ core/branches/flat/src/test/java/org/horizon/util/VisitableLinkedHashSetTest.java 2009-02-06 12:27:16 UTC (rev 7657)
@@ -0,0 +1,69 @@
+package org.horizon.util;
+
+import org.testng.annotations.Test;
+
+import java.util.Iterator;
+import java.util.Set;
+
+@Test(groups = "unit")
+public class VisitableLinkedHashSetTest {
+
+ public void testVisitableSet() {
+ VisitableLinkedHashSet<Integer> set = new VisitableLinkedHashSet<Integer>();
+ initSet(set);
+
+ testOrderBeforeRemoval(set);
+
+ // now attemp a visit and test that the visits are NOT recorded
+ set.visit(200);
+
+ // check the forward iterator that everything is in order of entry
+ Iterator<Integer> it = set.iterator();
+ int index = 0;
+ while (it.hasNext()) {
+ if (index == 200) index = 201;
+ if (index == 1000) index = 200;
+ Integer value = it.next();
+ assert value == index++ : "Expecting " + (index - 1) + " but was " + value;
+ }
+
+ // now check the reverse iterator.
+ it = set.reverseIterator();
+ index = 200; // this should be the first
+ while (it.hasNext()) {
+ assert it.next() == index--;
+ if (index == 199) index = 999;
+ if (index == 200) index = 199;
+ }
+
+ for (Iterator i = set.iterator(); i.hasNext();) {
+ i.next();
+ i.remove();
+ }
+
+ assert set.isEmpty();
+ assert set.size() == 0 : "Expecting size to be 0 but was " + set.size();
+ }
+
+
+ private void initSet(Set<Integer> set) {
+ for (int i = 0; i < 1000; i++) set.add(i);
+ }
+
+ private void testOrderBeforeRemoval(VisitableLinkedHashSet<Integer> set) {
+ // check the forward iterator that everything is in order of entry
+ Iterator<Integer> it = set.iterator();
+ int index = 0;
+ while (it.hasNext()) assert it.next() == index++;
+ assert index == 1000 : "Expected 1000, index was " + index;
+ assert set.size() == 1000;
+ // now check the reverse iterator.
+ it = set.reverseIterator();
+ index = 999;
+ while (it.hasNext()) {
+ Integer value = it.next();
+ assert value == index-- : "failed, expecting " + (index + 1) + " but was " + value;
+ }
+ assert index == -1 : "Was " + index + ", instead of -1";
+ }
+}
\ No newline at end of file
15 years, 10 months