[jbosscache-commits] JBoss Cache SVN: r7662 - in core/branches/flat/src: main/java/org/horizon/commands and 12 other directories.

jbosscache-commits at lists.jboss.org jbosscache-commits at lists.jboss.org
Sun Feb 8 06:20:51 EST 2009


Author: manik.surtani at 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 at 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 at 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 at jboss.org">manik at 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;
-
- at 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
- */
- at 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;
+
+ at 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;
+
+ at 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
+}




More information about the jbosscache-commits mailing list