[infinispan-commits] Infinispan SVN: r423 - in trunk/core/src: main/java/org/infinispan/commands and 10 other directories.

infinispan-commits at lists.jboss.org infinispan-commits at lists.jboss.org
Wed Jun 10 09:46:04 EDT 2009


Author: galder.zamarreno at jboss.com
Date: 2009-06-10 09:46:03 -0400 (Wed, 10 Jun 2009)
New Revision: 423

Added:
   trunk/core/src/main/java/org/infinispan/commands/read/EntrySetCommand.java
   trunk/core/src/main/java/org/infinispan/commands/read/KeySetCommand.java
   trunk/core/src/main/java/org/infinispan/commands/read/ValuesCommand.java
Modified:
   trunk/core/src/main/java/org/infinispan/Cache.java
   trunk/core/src/main/java/org/infinispan/CacheDelegate.java
   trunk/core/src/main/java/org/infinispan/commands/AbstractVisitor.java
   trunk/core/src/main/java/org/infinispan/commands/CommandsFactory.java
   trunk/core/src/main/java/org/infinispan/commands/CommandsFactoryImpl.java
   trunk/core/src/main/java/org/infinispan/commands/Visitor.java
   trunk/core/src/main/java/org/infinispan/container/DataContainer.java
   trunk/core/src/main/java/org/infinispan/container/FIFODataContainer.java
   trunk/core/src/main/java/org/infinispan/container/SimpleDataContainer.java
   trunk/core/src/main/java/org/infinispan/container/SpinLockBasedFIFODataContainer.java
   trunk/core/src/main/java/org/infinispan/container/entries/CacheEntry.java
   trunk/core/src/main/java/org/infinispan/interceptors/MarshalledValueInterceptor.java
   trunk/core/src/main/java/org/infinispan/util/Immutables.java
   trunk/core/src/test/java/org/infinispan/api/CacheAPITest.java
   trunk/core/src/test/java/org/infinispan/container/SimpleDataContainerTest.java
   trunk/core/src/test/java/org/infinispan/distribution/DistSyncFuncTest.java
   trunk/core/src/test/java/org/infinispan/marshall/MarshalledValueTest.java
   trunk/core/src/test/java/org/infinispan/test/TestingUtil.java
Log:
[ISPN-94] (Implement Map.keySet(), Map.values() and Map.entrySet() methods) Done.

Modified: trunk/core/src/main/java/org/infinispan/Cache.java
===================================================================
--- trunk/core/src/main/java/org/infinispan/Cache.java	2009-06-10 12:37:21 UTC (rev 422)
+++ trunk/core/src/main/java/org/infinispan/Cache.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -30,7 +30,9 @@
 import org.infinispan.notifications.Listenable;
 import org.infinispan.util.concurrent.NotifyingFuture;
 
+import java.util.Collection;
 import java.util.Map;
+import java.util.Set;
 import java.util.concurrent.ConcurrentMap;
 import java.util.concurrent.TimeUnit;
 
@@ -90,6 +92,7 @@
  *
  * @author Mircea.Markus at jboss.com
  * @author Manik Surtani
+ * @author Galder Zamarreño
  * @see CacheManager
  * @see DefaultCacheManager
  * @see <a href="http://www.jboss.org/infinispan/docs">Infinispan documentation</a>
@@ -540,4 +543,47 @@
    void compact();
 
    ComponentStatus getStatus();
+
+   /**
+    * Returns a set view of the keys contained in this cache. This set is immutable, so it cannot be modified 
+    * and changes to the cache won't be reflected in the set. When this method is called on a cache configured with 
+    * distribution mode, the set returned only contains the keys locally available in the cache instance. To avoid 
+    * memory issues, there will be not attempt to bring keys from other nodes.
+    * <p/>
+    * This method should only be used for debugging purpouses such as to verify that the cache contains all the keys 
+    * entered. Any other use involving execution of this method on a production system is not recommended.
+    * <p/>
+    * 
+    * @return a set view of the keys contained in this cache.
+    */
+   Set<K> keySet();
+
+   /**
+    * Returns a collection view of the keys contained in this cache. This collection is immutable, so it cannot be modified 
+    * and changes to the cache won't be reflected in the set. When this method is called on a cache configured with 
+    * distribution mode, the collection returned only contains the values locally available in the cache instance. To avoid 
+    * memory issues, there is not attempt to bring values from other nodes.
+    * <p/>
+    * This method should only be used for testing or debugging purpouses such as to verify that the cache contains all the 
+    * values entered. Any other use involving execution of this method on a production system is not recommended.
+    * <p/>
+    * 
+    * @return a collection view of the values contained in this map.
+    */
+   Collection<V> values();
+   
+   /**
+    * Returns a set view of the mappings contained in this cache. This set is immutable, so it cannot be modified 
+    * and changes to the cache won't be reflected in the set. Besides, each element in the returned set is an immutable 
+    * {@link Map.Entry}. When this method is called on a cache configured with distribution mode, the set returned only 
+    * contains the mappings locally available in the cache instance. To avoid memory issues, there will be not attempt 
+    * to bring mappings from other nodes.
+    * <p/>
+    * This method should only be used for debugging purpouses such as to verify that the cache contains all the mappings 
+    * entered. Any other use involving execution of this method on a production system is not recommended.
+    * <p/>
+    * 
+    * @return a set view of the mappings contained in this cache.
+    */
+   Set<Map.Entry<K, V>> entrySet();
 }

Modified: trunk/core/src/main/java/org/infinispan/CacheDelegate.java
===================================================================
--- trunk/core/src/main/java/org/infinispan/CacheDelegate.java	2009-06-10 12:37:21 UTC (rev 422)
+++ trunk/core/src/main/java/org/infinispan/CacheDelegate.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -27,8 +27,11 @@
 import org.infinispan.batch.BatchContainer;
 import org.infinispan.commands.CommandsFactory;
 import org.infinispan.commands.LockControlCommand;
+import org.infinispan.commands.read.EntrySetCommand;
 import org.infinispan.commands.read.GetKeyValueCommand;
+import org.infinispan.commands.read.KeySetCommand;
 import org.infinispan.commands.read.SizeCommand;
+import org.infinispan.commands.read.ValuesCommand;
 import org.infinispan.commands.write.ClearCommand;
 import org.infinispan.commands.write.EvictCommand;
 import org.infinispan.commands.write.PutKeyValueCommand;
@@ -75,6 +78,7 @@
 
 /**
  * @author Mircea.Markus at jboss.com
+ * @author Galder Zamarreño
  * @since 4.0
  */
 @NonVolatile
@@ -152,7 +156,7 @@
 
    public final int size() {
       SizeCommand command = commandsFactory.buildSizeCommand();
-      return (Integer) invoker.invoke(getInvocationContext(), command);
+      return (Integer) invoker.invoke(icc.createNonTxInvocationContext(), command);
    }
 
    public final boolean isEmpty() {
@@ -188,15 +192,18 @@
    }
 
    public Set<K> keySet() {
-      throw new UnsupportedOperationException("TODO implement me"); // TODO implement me
+      KeySetCommand command = commandsFactory.buildKeySetCommand();
+      return (Set<K>) invoker.invoke(icc.createNonTxInvocationContext(), command);
    }
 
    public Collection<V> values() {
-      throw new UnsupportedOperationException("TODO implement me"); // TODO implement me
+      ValuesCommand command = commandsFactory.buildValuesCommand();
+      return (Collection<V>) invoker.invoke(icc.createNonTxInvocationContext(), command);
    }
 
    public Set<Map.Entry<K, V>> entrySet() {
-      throw new UnsupportedOperationException("TODO implement me"); // TODO implement me
+      EntrySetCommand command = commandsFactory.buildEntrySetCommand();
+      return (Set<Map.Entry<K, V>>) invoker.invoke(icc.createNonTxInvocationContext(), command);
    }
 
    public final void putForExternalRead(K key, V value) {

Modified: trunk/core/src/main/java/org/infinispan/commands/AbstractVisitor.java
===================================================================
--- trunk/core/src/main/java/org/infinispan/commands/AbstractVisitor.java	2009-06-10 12:37:21 UTC (rev 422)
+++ trunk/core/src/main/java/org/infinispan/commands/AbstractVisitor.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -21,8 +21,11 @@
  */
 package org.infinispan.commands;
 
+import org.infinispan.commands.read.EntrySetCommand;
 import org.infinispan.commands.read.GetKeyValueCommand;
+import org.infinispan.commands.read.KeySetCommand;
 import org.infinispan.commands.read.SizeCommand;
+import org.infinispan.commands.read.ValuesCommand;
 import org.infinispan.commands.tx.CommitCommand;
 import org.infinispan.commands.tx.PrepareCommand;
 import org.infinispan.commands.tx.RollbackCommand;
@@ -43,6 +46,7 @@
  *
  * @author Mircea.Markus at jboss.com
  * @author Manik Surtani
+ * @author Galder Zamarreño
  * @since 4.0
  */
 public abstract class AbstractVisitor implements Visitor {
@@ -82,6 +86,18 @@
       return handleDefault(ctx, command);
    }
 
+   public Object visitKeySetCommand(InvocationContext ctx, KeySetCommand command) throws Throwable {
+      return handleDefault(ctx, command);
+   }
+
+   public Object visitValuesCommand(InvocationContext ctx, ValuesCommand command) throws Throwable {
+      return handleDefault(ctx, command);
+   }
+
+   public Object visitEntrySetCommand(InvocationContext ctx, EntrySetCommand command) throws Throwable {
+      return handleDefault(ctx, command);
+   }
+
    // tx commands
 
    public Object visitPrepareCommand(TxInvocationContext ctx, PrepareCommand command) throws Throwable {

Modified: trunk/core/src/main/java/org/infinispan/commands/CommandsFactory.java
===================================================================
--- trunk/core/src/main/java/org/infinispan/commands/CommandsFactory.java	2009-06-10 12:37:21 UTC (rev 422)
+++ trunk/core/src/main/java/org/infinispan/commands/CommandsFactory.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -22,8 +22,11 @@
 package org.infinispan.commands;
 
 import org.infinispan.commands.control.StateTransferControlCommand;
+import org.infinispan.commands.read.EntrySetCommand;
 import org.infinispan.commands.read.GetKeyValueCommand;
+import org.infinispan.commands.read.KeySetCommand;
 import org.infinispan.commands.read.SizeCommand;
+import org.infinispan.commands.read.ValuesCommand;
 import org.infinispan.commands.remote.ClusteredGetCommand;
 import org.infinispan.commands.remote.MultipleRpcCommand;
 import org.infinispan.commands.remote.SingleRpcCommand;
@@ -48,6 +51,7 @@
 
 /**
  * @author Mircea.Markus at jboss.com
+ * @author Galder Zamarreño
  * @since 4.0
  */
 @Scope(Scopes.NAMED_CACHE)
@@ -66,6 +70,12 @@
    SizeCommand buildSizeCommand();
 
    GetKeyValueCommand buildGetKeyValueCommand(Object key);
+   
+   KeySetCommand buildKeySetCommand();
+   
+   ValuesCommand buildValuesCommand();
+   
+   EntrySetCommand buildEntrySetCommand();
 
    PutMapCommand buildPutMapCommand(Map map, long lifespanMillis, long maxIdleTimeMillis);
 

Modified: trunk/core/src/main/java/org/infinispan/commands/CommandsFactoryImpl.java
===================================================================
--- trunk/core/src/main/java/org/infinispan/commands/CommandsFactoryImpl.java	2009-06-10 12:37:21 UTC (rev 422)
+++ trunk/core/src/main/java/org/infinispan/commands/CommandsFactoryImpl.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -23,8 +23,11 @@
 
 import org.infinispan.Cache;
 import org.infinispan.commands.control.StateTransferControlCommand;
+import org.infinispan.commands.read.EntrySetCommand;
 import org.infinispan.commands.read.GetKeyValueCommand;
+import org.infinispan.commands.read.KeySetCommand;
 import org.infinispan.commands.read.SizeCommand;
+import org.infinispan.commands.read.ValuesCommand;
 import org.infinispan.commands.remote.ClusteredGetCommand;
 import org.infinispan.commands.remote.MultipleRpcCommand;
 import org.infinispan.commands.remote.SingleRpcCommand;
@@ -57,6 +60,7 @@
 
 /**
  * @author Mircea.Markus at jboss.com
+ * @author Galder Zamarreño
  * @since 4.0
  */
 public class CommandsFactoryImpl implements CommandsFactory {
@@ -68,6 +72,9 @@
 
    // some stateless commands can be reused so that they aren't constructed again all the time.
    SizeCommand cachedSizeCommand;
+   KeySetCommand cachedKeySetCommand;
+   ValuesCommand cachedValuesCommand;
+   EntrySetCommand cachedEntrySetCommand;
    private InterceptorChain interceptorChain;
    private DistributionManager distributionManager;
    private InvocationContextContainer icc;
@@ -120,7 +127,28 @@
       }
       return cachedSizeCommand;
    }
+   
+   public KeySetCommand buildKeySetCommand() {
+      if (cachedKeySetCommand == null) {
+         cachedKeySetCommand = new KeySetCommand(dataContainer);
+      }
+      return cachedKeySetCommand;
+   }
+   
+   public ValuesCommand buildValuesCommand() {
+      if (cachedValuesCommand == null) {
+         cachedValuesCommand = new ValuesCommand(dataContainer);
+      }
+      return cachedValuesCommand;
+   }
 
+   public EntrySetCommand buildEntrySetCommand() {
+      if (cachedEntrySetCommand == null) {
+         cachedEntrySetCommand = new EntrySetCommand(dataContainer);
+      }
+      return cachedEntrySetCommand;
+   }
+
    public GetKeyValueCommand buildGetKeyValueCommand(Object key) {
       return new GetKeyValueCommand(key, notifier);
    }

Modified: trunk/core/src/main/java/org/infinispan/commands/Visitor.java
===================================================================
--- trunk/core/src/main/java/org/infinispan/commands/Visitor.java	2009-06-10 12:37:21 UTC (rev 422)
+++ trunk/core/src/main/java/org/infinispan/commands/Visitor.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -21,8 +21,11 @@
  */
 package org.infinispan.commands;
 
+import org.infinispan.commands.read.EntrySetCommand;
 import org.infinispan.commands.read.GetKeyValueCommand;
+import org.infinispan.commands.read.KeySetCommand;
 import org.infinispan.commands.read.SizeCommand;
+import org.infinispan.commands.read.ValuesCommand;
 import org.infinispan.commands.tx.CommitCommand;
 import org.infinispan.commands.tx.PrepareCommand;
 import org.infinispan.commands.tx.RollbackCommand;
@@ -38,6 +41,7 @@
 
 /**
  * @author Mircea.Markus at jboss.com
+ * @author Galder Zamarreño
  * @since 4.0
  */
 
@@ -61,6 +65,12 @@
    Object visitSizeCommand(InvocationContext ctx, SizeCommand command) throws Throwable;
 
    Object visitGetKeyValueCommand(InvocationContext ctx, GetKeyValueCommand command) throws Throwable;
+   
+   Object visitKeySetCommand(InvocationContext ctx, KeySetCommand command) throws Throwable;
+   
+   Object visitValuesCommand(InvocationContext ctx, ValuesCommand command) throws Throwable;
+   
+   Object visitEntrySetCommand(InvocationContext ctx, EntrySetCommand command) throws Throwable;
 
    // tx commands
 

Added: trunk/core/src/main/java/org/infinispan/commands/read/EntrySetCommand.java
===================================================================
--- trunk/core/src/main/java/org/infinispan/commands/read/EntrySetCommand.java	                        (rev 0)
+++ trunk/core/src/main/java/org/infinispan/commands/read/EntrySetCommand.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -0,0 +1,71 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, 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.infinispan.commands.read;
+
+import java.util.Set;
+
+import org.infinispan.commands.VisitableCommand;
+import org.infinispan.commands.Visitor;
+import org.infinispan.container.DataContainer;
+import org.infinispan.context.InvocationContext;
+import org.infinispan.util.Immutables;
+
+/**
+ * EntrySetCommand.
+ * 
+ * @author Galder Zamarreño
+ * @since 4.0
+ */
+public class EntrySetCommand implements VisitableCommand {
+   private final DataContainer container;
+
+   public EntrySetCommand(DataContainer container) {
+      this.container = container;
+   }
+   
+   public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable {
+      return visitor.visitEntrySetCommand(ctx, this);
+   }
+
+   public Set perform(InvocationContext ctx) throws Throwable {
+      return Immutables.immutableSetWrap(container.entrySet());
+   }
+
+   public byte getCommandId() {
+      return 0;  // no-op
+   }
+
+   public Object[] getParameters() {
+      return new Object[0];  // no-op
+   }
+
+   public void setParameters(int commandId, Object[] parameters) {
+      // no-op
+   }
+
+   @Override
+   public String toString() {
+      return "EntrySetCommand{" +
+            "set=" + container.entrySet() +
+            '}';   
+   }
+}

Added: trunk/core/src/main/java/org/infinispan/commands/read/KeySetCommand.java
===================================================================
--- trunk/core/src/main/java/org/infinispan/commands/read/KeySetCommand.java	                        (rev 0)
+++ trunk/core/src/main/java/org/infinispan/commands/read/KeySetCommand.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -0,0 +1,72 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, 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.infinispan.commands.read;
+
+import java.util.Set;
+
+import org.infinispan.commands.VisitableCommand;
+import org.infinispan.commands.Visitor;
+import org.infinispan.container.DataContainer;
+import org.infinispan.context.InvocationContext;
+import org.infinispan.util.Immutables;
+
+/**
+ * KeySetCommand.
+ * 
+ * @author Galder Zamarreño
+ * @since 4.0
+ */
+public class KeySetCommand implements VisitableCommand {
+   private final DataContainer container;
+   
+   public KeySetCommand(DataContainer container) {
+      this.container = container;
+   }
+
+   public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable {
+      return visitor.visitKeySetCommand(ctx, this);
+   }
+
+   public Set perform(InvocationContext ctx) throws Throwable {
+      return Immutables.immutableSetWrap(container.keySet());
+   }
+
+   public byte getCommandId() {
+      return 0;  // no-op
+   }
+
+   public Object[] getParameters() {
+      return new Object[0];  // no-op
+   }
+
+   public void setParameters(int commandId, Object[] parameters) {
+       // no-op
+   }
+
+   @Override
+   public String toString() {
+      return "KeySetCommand{" +
+            "set=" + container.keySet() +
+            '}';   
+   }
+   
+}

Added: trunk/core/src/main/java/org/infinispan/commands/read/ValuesCommand.java
===================================================================
--- trunk/core/src/main/java/org/infinispan/commands/read/ValuesCommand.java	                        (rev 0)
+++ trunk/core/src/main/java/org/infinispan/commands/read/ValuesCommand.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -0,0 +1,72 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2009, 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.infinispan.commands.read;
+
+import java.util.Collection;
+
+import org.infinispan.commands.VisitableCommand;
+import org.infinispan.commands.Visitor;
+import org.infinispan.container.DataContainer;
+import org.infinispan.context.InvocationContext;
+import org.infinispan.util.Immutables;
+
+/**
+ * ValuesCommand.
+ * 
+ * @author Galder Zamarreño
+ * @since 4.0
+ */
+public class ValuesCommand implements VisitableCommand {
+   private final DataContainer container;
+   
+   public ValuesCommand(DataContainer container) {
+      this.container = container;
+   }
+
+   public Object acceptVisitor(InvocationContext ctx, Visitor visitor) throws Throwable {
+      return visitor.visitValuesCommand(ctx, this);
+   }
+
+   public Collection perform(InvocationContext ctx) throws Throwable {
+      return Immutables.immutableCollectionWrap(container.values());
+   }
+
+   public byte getCommandId() {
+      return 0;  // no-op
+   }
+
+   public Object[] getParameters() {
+      return new Object[0];  // no-op
+   }
+
+   public void setParameters(int commandId, Object[] parameters) {
+      // no-op
+   }
+   
+   @Override
+   public String toString() {
+      return "ValuesCommand{" +
+            "values=" + container.values() +
+            '}';   
+   }
+
+}

Modified: trunk/core/src/main/java/org/infinispan/container/DataContainer.java
===================================================================
--- trunk/core/src/main/java/org/infinispan/container/DataContainer.java	2009-06-10 12:37:21 UTC (rev 422)
+++ trunk/core/src/main/java/org/infinispan/container/DataContainer.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -25,12 +25,15 @@
 import org.infinispan.factories.scopes.Scope;
 import org.infinispan.factories.scopes.Scopes;
 
+import java.util.Collection;
+import java.util.Map;
 import java.util.Set;
 
 /**
  * The main internal data structure which stores entries
  *
  * @author Manik Surtani (<a href="mailto:manik at jboss.org">manik at jboss.org</a>)
+ * @author Galder Zamarreño
  * @since 4.0
  */
 @Scope(Scopes.NAMED_CACHE)
@@ -83,6 +86,23 @@
    Set<Object> keySet();
 
    /**
+    * @return a set of values contained in the container
+    */
+   Collection<Object> values();
+
+   /**
+    * Returns a mutable set of immutable cache entries exposed as immutable Map.Entry instances. Clients 
+    * of this method such as Cache.entrySet() operation implementors are free to convert the set into an 
+    * immutable set if needed, which is the most common use case. 
+    * 
+    * If a client needs to iterate through a mutable set of mutable cache entries, it should iterate the 
+    * container itself rather than iterating through the return of entrySet().
+    * 
+    * @return a set of immutable cache entries
+    */
+   Set<Map.Entry> entrySet();
+
+   /**
     * Purges entries that have passed their expiry time
     */
    void purgeExpired();

Modified: trunk/core/src/main/java/org/infinispan/container/FIFODataContainer.java
===================================================================
--- trunk/core/src/main/java/org/infinispan/container/FIFODataContainer.java	2009-06-10 12:37:21 UTC (rev 422)
+++ trunk/core/src/main/java/org/infinispan/container/FIFODataContainer.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -2,9 +2,13 @@
 
 import org.infinispan.container.entries.InternalCacheEntry;
 import org.infinispan.container.entries.InternalEntryFactory;
+import org.infinispan.util.Immutables;
 
+import java.util.AbstractCollection;
 import java.util.AbstractSet;
+import java.util.Collection;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 import java.util.concurrent.locks.ReentrantLock;
@@ -25,6 +29,7 @@
  * <p/>
  *
  * @author Manik Surtani
+ * @author Galder Zamarreño
  * @since 4.0
  */
 public class FIFODataContainer implements DataContainer {
@@ -603,7 +608,6 @@
 
 
    protected final class KeySet extends AbstractSet<Object> {
-
       public Iterator<Object> iterator() {
          return new KeyIterator();
       }
@@ -612,7 +616,27 @@
          return FIFODataContainer.this.size();
       }
    }
+   
+   protected final class Values extends AbstractCollection<Object> {
+      public Iterator<Object> iterator() {
+         return new ValueIterator();
+      }
 
+      public int size() {
+         return FIFODataContainer.this.size();
+      }
+   }
+   
+   protected final class EntrySet extends AbstractSet<Map.Entry> {
+      public Iterator<Map.Entry> iterator() {
+         return new ImmutableEntryIterator();
+      }
+
+      public int size() {
+         return FIFODataContainer.this.size();
+      }
+   }
+
    protected abstract class LinkedIterator {
       LinkedEntry current = head;
 
@@ -627,17 +651,29 @@
       }
    }
 
-   protected final class ValueIterator extends LinkedIterator implements Iterator<InternalCacheEntry> {
+   protected final class EntryIterator extends LinkedIterator implements Iterator<InternalCacheEntry> {
       public InternalCacheEntry next() {
          return current.e;
       }
    }
+   
+   protected final class ImmutableEntryIterator extends LinkedIterator implements Iterator<Map.Entry> {
+      public Map.Entry next() {
+         return Immutables.immutableEntry(current.e);
+      }
+   }
 
    protected final class KeyIterator extends LinkedIterator implements Iterator<Object> {
       public Object next() {
          return current.e.getKey();
       }
    }
+   
+   protected final class ValueIterator extends LinkedIterator implements Iterator<Object> {
+      public Object next() {
+         return current.e.getValue();
+      }
+   }
 
 
    // ----------- PUBLIC API ---------------
@@ -760,6 +796,14 @@
       return keySet;
    }
 
+   public Collection<Object> values() {
+      return new Values();
+   }
+   
+   public Set<Map.Entry> entrySet() {
+      return new EntrySet();
+   }
+
    public void purgeExpired() {
       for (InternalCacheEntry ice : this) {
          if (ice.isExpired()) remove(ice.getKey());
@@ -767,6 +811,6 @@
    }
 
    public Iterator<InternalCacheEntry> iterator() {
-      return new ValueIterator();
+      return new EntryIterator();
    }
 }

Modified: trunk/core/src/main/java/org/infinispan/container/SimpleDataContainer.java
===================================================================
--- trunk/core/src/main/java/org/infinispan/container/SimpleDataContainer.java	2009-06-10 12:37:21 UTC (rev 422)
+++ trunk/core/src/main/java/org/infinispan/container/SimpleDataContainer.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -4,10 +4,14 @@
 import org.infinispan.container.entries.InternalCacheEntry;
 import org.infinispan.container.entries.InternalEntryFactory;
 import org.infinispan.factories.annotations.Stop;
+import org.infinispan.util.Immutables;
 
+import java.util.AbstractCollection;
 import java.util.AbstractSet;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
@@ -22,6 +26,7 @@
  * This implementation offers O(1) performance for all operations.
  *
  * @author Manik Surtani
+ * @author Galder Zamarreño
  * @since 4.0
  */
 @ThreadSafe
@@ -126,7 +131,15 @@
    public Set<Object> keySet() {
       return new KeySet();
    }
+   
+   public Collection<Object> values() {
+      return new Values();
+   }
 
+   public Set<Map.Entry> entrySet() {
+      return new EntrySet();
+   }
+
    public void purgeExpired() {
       for (Iterator<InternalCacheEntry> entries = mortalEntries.values().iterator(); entries.hasNext();) {
          InternalCacheEntry e = entries.next();
@@ -198,13 +211,24 @@
          throw new UnsupportedOperationException();
       }
    }
+   
+   private class EntrySet extends AbstractSet<Map.Entry> {
+      public Iterator<Map.Entry> iterator() {
+         return new ImmutableEntryIterator(immortalEntries.values().iterator(), mortalEntries.values().iterator());
+      }
 
-   private class EntryIterator implements Iterator<InternalCacheEntry> {
+      @Override
+      public int size() {
+         return immortalEntries.size() + mortalEntries.size();
+      }
+   }
+
+   private class MortalInmortalIterator {
       Iterator<Iterator<InternalCacheEntry>> metaIterator;
       Iterator<InternalCacheEntry> currentIterator;
       InternalCacheEntry next;
 
-      private EntryIterator(Iterator<InternalCacheEntry> immortalIterator, Iterator<InternalCacheEntry> mortalIterator) {
+      private MortalInmortalIterator(Iterator<InternalCacheEntry> immortalIterator, Iterator<InternalCacheEntry> mortalIterator) {
          metaIterator = Arrays.asList(immortalIterator, mortalIterator).iterator();
          if (metaIterator.hasNext()) currentIterator = metaIterator.next();
       }
@@ -218,13 +242,51 @@
          return hasNext;
       }
 
+      public void remove() {
+         throw new UnsupportedOperationException();
+      }
+   }
+   
+   private class EntryIterator extends MortalInmortalIterator implements Iterator<InternalCacheEntry> {
+      private EntryIterator(Iterator<InternalCacheEntry> immortalIterator, Iterator<InternalCacheEntry> mortalIterator) {
+         super(immortalIterator, mortalIterator);
+      }
+      
       @SuppressWarnings("unchecked")
       public InternalCacheEntry next() {
          return currentIterator.next();
       }
+   }
+   
+   private class ImmutableEntryIterator extends MortalInmortalIterator implements Iterator<Map.Entry> {
+      private ImmutableEntryIterator(Iterator<InternalCacheEntry> immortalIterator, Iterator<InternalCacheEntry> mortalIterator) {
+         super(immortalIterator, mortalIterator);
+      }
+      
+      public Map.Entry next() {
+         return Immutables.immutableEntry(currentIterator.next());
+      }
+   }
+   
+   private class Values extends AbstractCollection<Object> {
+      @Override
+      public Iterator<Object> iterator() {         
+         return new ValueIterator(immortalEntries.values().iterator(), mortalEntries.values().iterator());
+      }
 
-      public void remove() {
-         throw new UnsupportedOperationException();
+      @Override
+      public int size() {
+         return immortalEntries.size() + mortalEntries.size();
+      }      
+   }
+   
+   private class ValueIterator extends MortalInmortalIterator implements Iterator<Object> {
+      private ValueIterator(Iterator<InternalCacheEntry> immortalIterator, Iterator<InternalCacheEntry> mortalIterator) {
+         super(immortalIterator, mortalIterator);
       }
+      
+      public Object next() {
+         return currentIterator.next().getValue();
+      }
    }
 }

Modified: trunk/core/src/main/java/org/infinispan/container/SpinLockBasedFIFODataContainer.java
===================================================================
--- trunk/core/src/main/java/org/infinispan/container/SpinLockBasedFIFODataContainer.java	2009-06-10 12:37:21 UTC (rev 422)
+++ trunk/core/src/main/java/org/infinispan/container/SpinLockBasedFIFODataContainer.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -1,11 +1,16 @@
 package org.infinispan.container;
 
 import net.jcip.annotations.ThreadSafe;
+
 import org.infinispan.container.entries.InternalCacheEntry;
 import org.infinispan.container.entries.InternalEntryFactory;
+import org.infinispan.util.Immutables;
 
+import java.util.AbstractCollection;
 import java.util.AbstractSet;
+import java.util.Collection;
 import java.util.Iterator;
+import java.util.Map;
 import java.util.Set;
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.locks.ReentrantLock;
@@ -27,6 +32,7 @@
  * and due to the nature of these spin locks, they should only be held for a minimal amount of time.
  *
  * @author Manik Surtani
+ * @author Galder Zamarreño
  * @since 4.0
  */
 @ThreadSafe
@@ -231,7 +237,15 @@
       if (keySet == null) keySet = new KeySet();
       return keySet;
    }
+   
+   public Set<Map.Entry> entrySet() {
+      return new EntrySet();
+   }
 
+   public Collection<Object> values() {
+      return new Values();
+   }
+
    public void purgeExpired() {
       for (InternalCacheEntry ice : this) {
          if (ice.isExpired()) remove(ice.getKey());
@@ -239,7 +253,7 @@
    }
 
    public Iterator<InternalCacheEntry> iterator() {
-      return new ValueIterator();
+      return new EntryIterator();
    }
 
    // --------------- Internals
@@ -526,7 +540,6 @@
    }
 
    protected final class KeySet extends AbstractSet<Object> {
-
       public Iterator<Object> iterator() {
          return new KeyIterator();
       }
@@ -535,7 +548,27 @@
          return SpinLockBasedFIFODataContainer.this.size();
       }
    }
+   
+   protected final class Values extends AbstractCollection<Object> {
+      public Iterator<Object> iterator() {
+         return new ValueIterator();
+      }
+      
+      public int size() {
+         return SpinLockBasedFIFODataContainer.this.size();
+      }      
+   }
+   
+   protected final class EntrySet extends AbstractSet<Map.Entry> {
+      public Iterator<Map.Entry> iterator() {
+         return new ImmutableEntryIterator();
+      }
 
+      public int size() {
+         return SpinLockBasedFIFODataContainer.this.size();
+      }
+   }
+
    protected abstract class LinkedIterator {
       Aux nextAux = dummyEntry.next;
 
@@ -548,7 +581,7 @@
       }
    }
 
-   protected final class ValueIterator extends LinkedIterator implements Iterator<InternalCacheEntry> {
+   protected final class EntryIterator extends LinkedIterator implements Iterator<InternalCacheEntry> {
       public InternalCacheEntry next() {
          LinkedEntry le = nextAux.next;
          if (le == dummyEntry) return null;
@@ -557,6 +590,15 @@
       }
    }
 
+   protected final class ImmutableEntryIterator extends LinkedIterator implements Iterator<Map.Entry> {
+      public Map.Entry next() {
+         LinkedEntry le = nextAux.next;
+         if (le == dummyEntry) return null;
+         nextAux = le.next;
+         return Immutables.immutableEntry(le.entry);
+      }
+   }
+   
    protected final class KeyIterator extends LinkedIterator implements Iterator<Object> {
       public Object next() {
          LinkedEntry le = nextAux.next;
@@ -566,6 +608,15 @@
       }
    }
 
+   protected final class ValueIterator extends LinkedIterator implements Iterator<Object> {
+      public Object next() {
+         LinkedEntry le = nextAux.next;
+         if (le == dummyEntry) return null;
+         nextAux = le.next;
+         return le.entry.getValue();
+      }
+   }
+   
    protected static abstract class SpinLock {
       final AtomicBoolean l = new AtomicBoolean(false);
 

Modified: trunk/core/src/main/java/org/infinispan/container/entries/CacheEntry.java
===================================================================
--- trunk/core/src/main/java/org/infinispan/container/entries/CacheEntry.java	2009-06-10 12:37:21 UTC (rev 422)
+++ trunk/core/src/main/java/org/infinispan/container/entries/CacheEntry.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -1,14 +1,17 @@
 package org.infinispan.container.entries;
 
+import java.util.Map;
+
 import org.infinispan.container.DataContainer;
 
 /**
  * An entry that is stored in the data container
  *
  * @author Manik Surtani
+ * @author Galder Zamarreño
  * @since 4.0
  */
-public interface CacheEntry {
+public interface CacheEntry extends Map.Entry<Object, Object> {
 
    /**
     * Tests whether the entry represents a null value, typically used for repeatable read.

Modified: trunk/core/src/main/java/org/infinispan/interceptors/MarshalledValueInterceptor.java
===================================================================
--- trunk/core/src/main/java/org/infinispan/interceptors/MarshalledValueInterceptor.java	2009-06-10 12:37:21 UTC (rev 422)
+++ trunk/core/src/main/java/org/infinispan/interceptors/MarshalledValueInterceptor.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -21,18 +21,25 @@
  */
 package org.infinispan.interceptors;
 
+import org.infinispan.commands.read.EntrySetCommand;
 import org.infinispan.commands.read.GetKeyValueCommand;
+import org.infinispan.commands.read.KeySetCommand;
+import org.infinispan.commands.read.ValuesCommand;
 import org.infinispan.commands.write.PutKeyValueCommand;
 import org.infinispan.commands.write.PutMapCommand;
 import org.infinispan.commands.write.RemoveCommand;
+import org.infinispan.container.entries.InternalEntryFactory;
 import org.infinispan.context.InvocationContext;
 import org.infinispan.factories.annotations.Inject;
 import org.infinispan.interceptors.base.CommandInterceptor;
 import org.infinispan.marshall.MarshalledValue;
 import org.infinispan.marshall.Marshaller;
+import org.infinispan.util.Immutables;
 
 import java.io.IOException;
 import java.io.NotSerializableException;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -112,7 +119,52 @@
       compact(mv);
       return processRetVal(retVal);
    }
+   
+   @Override
+   public Object visitKeySetCommand(InvocationContext ctx, KeySetCommand command) throws Throwable {
+      Set keys = (Set) invokeNextInterceptor(ctx, command);
+      Set copy = new HashSet(keys.size());
+      for (Object key : keys) {
+         if (key instanceof MarshalledValue) {
+            key = ((MarshalledValue) key).get();
+         } 
+         copy.add(key);
+      }
+      return Immutables.immutableSetWrap(copy);
+   }
 
+   @Override
+   public Object visitValuesCommand(InvocationContext ctx, ValuesCommand command) throws Throwable {
+      Collection values = (Collection) invokeNextInterceptor(ctx, command);
+      Collection copy = new ArrayList();  
+      for (Object value : values) {
+         if (value instanceof MarshalledValue) {
+            value = ((MarshalledValue) value).get();
+         }
+         copy.add(value);
+      }
+      return Immutables.immutableCollectionWrap(copy);
+   }
+   
+   @Override
+   public Object visitEntrySetCommand(InvocationContext ctx, EntrySetCommand command) throws Throwable {
+      Set<Map.Entry> entries = (Set<Map.Entry>) invokeNextInterceptor(ctx, command);
+      Set<Map.Entry> copy = new HashSet<Map.Entry>(entries.size());
+      for (Map.Entry entry : entries) {
+         Object key = entry.getKey();
+         Object value = entry.getValue();
+         if (key instanceof MarshalledValue) {
+            key = ((MarshalledValue) key).get();
+         }
+         if (value instanceof MarshalledValue) {
+            value = ((MarshalledValue) value).get();
+         }
+         Map.Entry newEntry = Immutables.immutableEntry(InternalEntryFactory.create(key, value, -1));
+         copy.add(newEntry);
+      }
+      return Immutables.immutableSetWrap(copy);
+   }
+
    private Object compactAndProcessRetVal(Set<MarshalledValue> marshalledValues, Object retVal)
          throws IOException, ClassNotFoundException {
       if (trace) log.trace("Compacting MarshalledValues created");

Modified: trunk/core/src/main/java/org/infinispan/util/Immutables.java
===================================================================
--- trunk/core/src/main/java/org/infinispan/util/Immutables.java	2009-06-10 12:37:21 UTC (rev 422)
+++ trunk/core/src/main/java/org/infinispan/util/Immutables.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -37,6 +37,7 @@
  * Factory for generating immutable type wrappers.
  *
  * @author Jason T. Greene
+ * @author Galder Zamarreño
  * @since 4.0
  */
 public class Immutables {
@@ -172,6 +173,16 @@
 
       return new ImmutableCollectionWrapper<T>(copy);
    }
+   
+   /**
+    * Wraps a collection with an immutable collection. There is no copying involved.
+    *
+    * @param collection the collection to wrap
+    * @return an immutable collection wrapper that delegates to the original collection
+    */
+   public static <T> Collection<T> immutableCollectionWrap(Collection<? extends T> collection) {
+      return new ImmutableCollectionWrapper<T>(collection);
+   }
 
    @SuppressWarnings("unchecked")
    private static <T> T attemptCopyConstructor(T source, Class<? super T> clazz) {
@@ -194,6 +205,16 @@
 
       return new ImmutableReversibleOrderedSetWrapper<T>(copy);
    }
+   
+   /**
+    * Wraps a {@link Map.Entry}} with an immutable {@link Map.Entry}}. There is no copying involved.
+    *
+    * @param entry the mapping to wrap.
+    * @return an immutable {@link Map.Entry}} wrapper that delegates to the original mapping.
+    */
+   public static Map.Entry immutableEntry(Map.Entry entry) {
+      return new ImmutableEntry(entry);
+   }
 
 
    public interface Immutable {
@@ -321,7 +342,7 @@
       }
    }
 
-   static class ImmutableEntry<K, V> implements Entry<K, V> {
+   static class ImmutableEntry<K, V> implements Entry<K, V>, Immutable {
       private K key;
       private V value;
       private int hash;

Modified: trunk/core/src/test/java/org/infinispan/api/CacheAPITest.java
===================================================================
--- trunk/core/src/test/java/org/infinispan/api/CacheAPITest.java	2009-06-10 12:37:21 UTC (rev 422)
+++ trunk/core/src/test/java/org/infinispan/api/CacheAPITest.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -8,13 +8,17 @@
 import org.infinispan.test.fwk.TestCacheManagerFactory;
 import org.infinispan.transaction.lookup.DummyTransactionManagerLookup;
 import org.infinispan.transaction.tm.DummyTransactionManager;
+import org.infinispan.util.ObjectDuplicator;
 import org.infinispan.util.concurrent.IsolationLevel;
-import static org.testng.AssertJUnit.assertEquals;
-import static org.testng.AssertJUnit.assertNull;
 import org.testng.annotations.Test;
 
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 /**
  * Tests the {@link org.infinispan.Cache} public API at a high level
@@ -43,8 +47,8 @@
     */
    public void testConfiguration() {
       Configuration c = cache.getConfiguration();
-      assertEquals(Configuration.CacheMode.LOCAL, c.getCacheMode());
-      assertEquals(DummyTransactionManagerLookup.class.getName(), c.getTransactionManagerLookupClass());
+      assert Configuration.CacheMode.LOCAL.equals(c.getCacheMode());
+      assert DummyTransactionManagerLookup.class.getName().equals(c.getTransactionManagerLookupClass());
 
       // note that certain values should be immutable.  E.g., CacheMode cannot be changed on the fly.
       try {
@@ -68,19 +72,31 @@
       Map<String, String> data = new HashMap<String, String>();
       data.put(key, value);
 
-      assertNull(cache.get(key));
+      assert cache.get(key) == null;
+      assert cache.keySet().isEmpty();
+      assert cache.values().isEmpty();
+      assert cache.entrySet().isEmpty();
 
       cache.put(key, value);
 
-      assertEquals(value, cache.get(key));
+      assert value.equals(cache.get(key));
+      assert 1 == cache.keySet().size() && 1 == cache.values().size();
+      assert cache.keySet().contains(key);
+      assert cache.values().contains(value);
 
       cache.remove(key);
 
-      assertNull(cache.get(key));
+      assert cache.get(key) == null;
+      assert cache.keySet().isEmpty();
+      assert cache.values().isEmpty();
+      assert cache.entrySet().isEmpty();
 
       cache.putAll(data);
 
-      assertEquals(value, cache.get(key));
+      assert value.equals(cache.get(key));
+      assert 1 == cache.keySet().size() && 1 == cache.values().size(); 
+      assert cache.keySet().contains(key);
+      assert cache.values().contains(value);
    }
 
    /**
@@ -88,100 +104,163 @@
     */
    public void testEvict() {
       String key1 = "keyOne", key2 = "keyTwo", value = "value";
+      int size = 0;
 
       cache.put(key1, value);
       cache.put(key2, value);
 
       assert cache.containsKey(key1);
       assert cache.containsKey(key2);
-      assert cache.size() == 2;
+      size = 2;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
+      assert cache.keySet().contains(key1);
+      assert cache.keySet().contains(key2);
+      assert cache.values().contains(value);
 
       // evict two
       cache.evict(key2);
 
       assert cache.containsKey(key1);
       assert !cache.containsKey(key2);
-      assert cache.size() == 1;
+      size = 1;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
+      assert cache.keySet().contains(key1);
+      assert !cache.keySet().contains(key2);
+      assert cache.values().contains(value);
 
       cache.evict(key1);
 
       assert !cache.containsKey(key1);
       assert !cache.containsKey(key2);
       assert cache.size() == 0;
+      assert !cache.keySet().contains(key1);
+      assert !cache.keySet().contains(key2);
+      assert cache.keySet().isEmpty();
+      assert !cache.values().contains(value);
+      assert cache.values().isEmpty();
+      assert cache.entrySet().isEmpty();
    }
 
    public void testStopClearsData() throws Exception {
       String key = "key", value = "value";
+      int size = 0;
       cache.put(key, value);
       assert cache.get(key).equals(value);
-      assert 1 == cache.size();
+      size = 1;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
+      assert cache.keySet().contains(key);
+      assert cache.values().contains(value);
       cache.stop();
 
       cache.start();
 
       assert !cache.containsKey(key);
       assert cache.isEmpty();
+      assert !cache.keySet().contains(key);
+      assert cache.keySet().isEmpty();
+      assert !cache.values().contains(value);
+      assert cache.values().isEmpty();
+      assert cache.entrySet().isEmpty();
    }
 
    public void testRollbackAfterPut() throws Exception {
-      String key = "key", value = "value";
+      String key = "key", value = "value", key2 = "keyTwo", value2="value2";
+      int size = 0;
       cache.put(key, value);
       assert cache.get(key).equals(value);
-      assert 1 == cache.size();
+      size = 1;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
+      assert cache.keySet().contains(key);
+      assert cache.values().contains(value);
 
       DummyTransactionManager.getInstance().begin();
-      cache.put("key2", "value2");
-      assert cache.get("key2").equals("value2");
+      cache.put(key2, value2);
+      assert cache.get(key2).equals(value2);
+      assert !cache.keySet().contains(key2);
+      size = 1;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
+      assert !cache.values().contains(value2);
       DummyTransactionManager.getInstance().rollback();
 
       assert cache.get(key).equals(value);
-      assert 1 == cache.size();
+      size = 1;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
+      assert cache.keySet().contains(key);
+      assert cache.values().contains(value);
    }
 
    public void testRollbackAfterOverwrite() throws Exception {
-      String key = "key", value = "value";
+      String key = "key", value = "value", value2= "value2";
+      int size = 0;      
       cache.put(key, value);
       assert cache.get(key).equals(value);
-      assert 1 == cache.size();
+      size = 1;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
+      assert cache.keySet().contains(key);
+      assert cache.values().contains(value);
 
       DummyTransactionManager.getInstance().begin();
-      cache.put(key, "value2");
-      assert cache.get(key).equals("value2");
-      assert 1 == cache.size();
+      cache.put(key, value2);
+      assert cache.get(key).equals(value2);
+      size = 1;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
+      assert cache.keySet().contains(key);
+      assert !cache.values().contains(value2);
       DummyTransactionManager.getInstance().rollback();
 
       assert cache.get(key).equals(value);
-      assert 1 == cache.size();
+      size = 1;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
+      assert cache.keySet().contains(key);
+      assert cache.values().contains(value);
    }
 
    public void testRollbackAfterRemove() throws Exception {
       String key = "key", value = "value";
+      int size = 0;
       cache.put(key, value);
       assert cache.get(key).equals(value);
-      assert 1 == cache.size();
+      size = 1;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
+      assert cache.keySet().contains(key);
+      assert cache.values().contains(value);
 
       DummyTransactionManager.getInstance().begin();
       cache.remove(key);
       assert cache.get(key) == null;
+      size = 1;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
       DummyTransactionManager.getInstance().rollback();
 
       assert cache.get(key).equals(value);
-      assert 1 == cache.size();
+      size = 1;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
+      assert cache.keySet().contains(key);
+      assert cache.values().contains(value);
    }
 
    public void testRollbackAfterClear() throws Exception {
       String key = "key", value = "value";
+      int size = 0;
       cache.put(key, value);
       assert cache.get(key).equals(value);
-      assert 1 == cache.size();
+      size = 1;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
+      assert cache.keySet().contains(key);
+      assert cache.values().contains(value);
 
       DummyTransactionManager.getInstance().begin();
       cache.clear();
       assert cache.get(key) == null;
+      size = 1;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
       DummyTransactionManager.getInstance().rollback();
 
       assert cache.get(key).equals(value);
-      assert 1 == cache.size();
+      size = 1;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
+      assert cache.keySet().contains(key);
+      assert cache.values().contains(value);
    }
 
    public void testConcurrentMapMethods() {
@@ -209,21 +288,31 @@
 
    public void testSizeAndContents() throws Exception {
       String key = "key", value = "value";
+      int size = 0;
 
       assert cache.isEmpty();
-      assert cache.size() == 0;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
       assert !cache.containsKey(key);
+      assert !cache.keySet().contains(key);
+      assert !cache.values().contains(value);
 
       cache.put(key, value);
-      assert cache.size() == 1;
+      size = 1;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
       assert cache.containsKey(key);
       assert !cache.isEmpty();
+      assert cache.containsKey(key);
+      assert cache.keySet().contains(key);
+      assert cache.values().contains(value);
 
       assert cache.remove(key).equals(value);
 
       assert cache.isEmpty();
-      assert cache.size() == 0;
+      size = 0;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
       assert !cache.containsKey(key);
+      assert !cache.keySet().contains(key);
+      assert !cache.values().contains(value);      
 
       Map<String, String> m = new HashMap<String, String>();
       m.put("1", "one");
@@ -234,7 +323,8 @@
       assert cache.get("1").equals("one");
       assert cache.get("2").equals("two");
       assert cache.get("3").equals("three");
-      assert cache.size() == 3;
+      size = 3;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
 
       m = new HashMap<String, String>();
       m.put("1", "newvalue");
@@ -246,6 +336,108 @@
       assert cache.get("2").equals("two");
       assert cache.get("3").equals("three");
       assert cache.get("4").equals("four");
-      assert cache.size() == 4;
+      size = 4;
+      assert size == cache.size() && size == cache.keySet().size() && size == cache.values().size() && size == cache.entrySet().size();
    }
+   
+   public void testKeyValueEntryCollections() {
+      String key1 = "1", value1 = "one", key2 = "2", value2 = "two", key3 = "3", value3 = "three";
+      Map<String, String> m = new HashMap<String, String>();
+      m.put(key1, value1);
+      m.put(key2, value2);
+      m.put(key3, value3);
+      cache.putAll(m);
+      assert 3 == cache.size() && 3 == cache.keySet().size() && 3 == cache.values().size() && 3 == cache.entrySet().size();
+
+      Set expKeys = new HashSet();
+      expKeys.add(key1);
+      expKeys.add(key2);
+      expKeys.add(key3);
+      
+      Set expValues = new HashSet();
+      expValues.add(value1);
+      expValues.add(value2);
+      expValues.add(value3);
+      
+      Set expKeyEntries = ObjectDuplicator.duplicateSet(expKeys);
+      Set expValueEntries = ObjectDuplicator.duplicateSet(expValues);
+
+      Set<String> keys = cache.keySet();
+      for (String key : keys) assert expKeys.remove(key);
+      assert expKeys.isEmpty() : "Did not see keys " + expKeys + " in iterator!";
+      
+      Collection<String> values = cache.values();
+      for (String value : values) assert expValues.remove(value);
+      assert expValues.isEmpty() : "Did not see keys " + expValues + " in iterator!";
+      
+      Set<Map.Entry> entries = cache.entrySet();
+      for (Map.Entry entry : entries) {
+         assert expKeyEntries.remove(entry.getKey());
+         assert expValueEntries.remove(entry.getValue());
+      }
+      assert expKeyEntries.isEmpty() : "Did not see keys " + expKeyEntries + " in iterator!";
+      assert expValueEntries.isEmpty() : "Did not see keys " + expValueEntries + " in iterator!";
+   }
+   
+   public void testImmutabilityOfKeyValueEntryCollections() {
+      final String key1 = "1", value1 = "one", key2 = "2", value2 = "two", key3 = "3", value3 = "three";
+      Map<String, String> m = new HashMap<String, String>();
+      m.put(key1, value1);
+      m.put(key2, value2);
+      m.put(key3, value3);
+      cache.putAll(m);
+      
+      Set<String> keys = cache.keySet();
+      Collection<String> values = cache.values();
+      Set<Map.Entry> entries = cache.entrySet();
+      Collection[] collections = new Collection[]{keys, values, entries};
+      Object newObj = new Object();
+      List newObjCol = new ArrayList();
+      newObjCol.add(newObj);
+      for (Collection col : collections) {
+         try {
+            col.add(newObj);
+            assert false : "Should have thrown a UnsupportedOperationException";
+         } catch (UnsupportedOperationException uoe) {
+         }
+         try {
+            col.addAll(newObjCol);
+            assert false : "Should have thrown a UnsupportedOperationException";
+         } catch (UnsupportedOperationException uoe) {
+         }
+         
+         try {
+            col.clear();
+            assert false : "Should have thrown a UnsupportedOperationException";
+         } catch (UnsupportedOperationException uoe) {
+         }
+         
+         try {
+            col.remove(key1);
+            assert false : "Should have thrown a UnsupportedOperationException";
+         } catch (UnsupportedOperationException uoe) {
+         }
+         
+         try {
+            col.removeAll(newObjCol);
+            assert false : "Should have thrown a UnsupportedOperationException";
+         } catch (UnsupportedOperationException uoe) {
+         }
+         
+         try {
+            col.retainAll(newObjCol);
+            assert false : "Should have thrown a UnsupportedOperationException";
+         } catch (UnsupportedOperationException uoe) {
+         }
+      }
+      
+      for (Map.Entry entry : entries) {
+         try {
+            entry.setValue(newObj);
+            assert false : "Should have thrown a UnsupportedOperationException";
+         } catch (UnsupportedOperationException uoe) {
+         }
+      }
+   }
+   
 }

Modified: trunk/core/src/test/java/org/infinispan/container/SimpleDataContainerTest.java
===================================================================
--- trunk/core/src/test/java/org/infinispan/container/SimpleDataContainerTest.java	2009-06-10 12:37:21 UTC (rev 422)
+++ trunk/core/src/test/java/org/infinispan/container/SimpleDataContainerTest.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -5,11 +5,13 @@
 import org.infinispan.container.entries.MortalCacheEntry;
 import org.infinispan.container.entries.TransientCacheEntry;
 import org.infinispan.container.entries.TransientMortalCacheEntry;
+import org.infinispan.util.Immutables;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import java.util.HashSet;
+import java.util.Map;
 import java.util.Set;
 
 @Test(groups = "unit", testName = "container.SimpleDataContainerTest")
@@ -137,7 +139,7 @@
       assert expected.isEmpty() : "Did not see keys " + expected + " in iterator!";
    }
 
-   public void testEntrySet() {
+   public void testContainerIteration() {
       dc.put("k1", "v", 6000000, -1);
       dc.put("k2", "v", -1, -1);
       dc.put("k3", "v", -1, 6000000);
@@ -155,4 +157,55 @@
 
       assert expected.isEmpty() : "Did not see keys " + expected + " in iterator!";
    }
+   
+   public void testKeys() {
+      dc.put("k1", "v1", 6000000, -1);
+      dc.put("k2", "v2", -1, -1);
+      dc.put("k3", "v3", -1, 6000000);
+      dc.put("k4", "v4", 6000000, 6000000);
+
+      Set expected = new HashSet();
+      expected.add("k1");
+      expected.add("k2");
+      expected.add("k3");
+      expected.add("k4");
+
+      for (Object o : dc.keySet()) assert expected.remove(o);
+
+      assert expected.isEmpty() : "Did not see keys " + expected + " in iterator!";
+   }
+
+   public void testValues() {
+      dc.put("k1", "v1", 6000000, -1);
+      dc.put("k2", "v2", -1, -1);
+      dc.put("k3", "v3", -1, 6000000);
+      dc.put("k4", "v4", 6000000, 6000000);
+
+      Set expected = new HashSet();
+      expected.add("v1");
+      expected.add("v2");
+      expected.add("v3");
+      expected.add("v4");
+
+      for (Object o : dc.values()) assert expected.remove(o);
+
+      assert expected.isEmpty() : "Did not see keys " + expected + " in iterator!";
+   }
+
+   public void testEntrySet() {
+      dc.put("k1", "v1", 6000000, -1);
+      dc.put("k2", "v2", -1, -1);
+      dc.put("k3", "v3", -1, 6000000);
+      dc.put("k4", "v4", 6000000, 6000000);
+
+      Set expected = new HashSet();
+      expected.add(Immutables.immutableEntry(dc.get("k1")));
+      expected.add(Immutables.immutableEntry(dc.get("k2")));
+      expected.add(Immutables.immutableEntry(dc.get("k3")));
+      expected.add(Immutables.immutableEntry(dc.get("k4")));
+
+      for (Map.Entry o : dc.entrySet()) assert expected.remove(o);
+
+      assert expected.isEmpty() : "Did not see keys " + expected + " in iterator!";
+   }
 }

Modified: trunk/core/src/test/java/org/infinispan/distribution/DistSyncFuncTest.java
===================================================================
--- trunk/core/src/test/java/org/infinispan/distribution/DistSyncFuncTest.java	2009-06-10 12:37:21 UTC (rev 422)
+++ trunk/core/src/test/java/org/infinispan/distribution/DistSyncFuncTest.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -1,10 +1,16 @@
 package org.infinispan.distribution;
 
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
 import org.infinispan.Cache;
 import org.infinispan.commands.write.ClearCommand;
 import org.infinispan.commands.write.PutKeyValueCommand;
 import org.infinispan.commands.write.RemoveCommand;
 import org.infinispan.commands.write.ReplaceCommand;
+import org.infinispan.test.TestingUtil;
+import org.infinispan.util.ObjectDuplicator;
 import org.testng.annotations.Test;
 
 @Test(groups = "functional", testName = "distribution.DistSyncFuncTest")
@@ -154,4 +160,39 @@
 
       for (Cache<Object, String> c : caches) assert c.isEmpty();
    }
+   
+   public void testKeyValueEntryCollections() {
+      c1.put("1", "one");
+      asyncWait("1", PutKeyValueCommand.class, getNonOwnersExcludingSelf("1", addressOf(c1)));
+      c2.put("2", "two");
+      asyncWait("2", PutKeyValueCommand.class, getNonOwnersExcludingSelf("2", addressOf(c2)));
+      c3.put("3", "three");
+      asyncWait("3", PutKeyValueCommand.class, getNonOwnersExcludingSelf("3", addressOf(c3)));
+      c4.put("4", "four");
+      asyncWait("4", PutKeyValueCommand.class, getNonOwnersExcludingSelf("4", addressOf(c4)));
+      
+      for (Cache c : caches) {
+         Set expKeys = TestingUtil.getInternalKeys(c);
+         Collection expValues = TestingUtil.getInternalValues(c);
+         
+         Set expKeyEntries = ObjectDuplicator.duplicateSet(expKeys);
+         Collection expValueEntries = ObjectDuplicator.duplicateCollection(expValues);
+         
+         Set keys = c.keySet();
+         for (Object key : keys) assert expKeys.remove(key);
+         assert expKeys.isEmpty() : "Did not see keys " + expKeys + " in iterator!";
+         
+         Collection values = c.values();
+         for (Object value : values) assert expValues.remove(value);
+         assert expValues.isEmpty() : "Did not see keys " + expValues + " in iterator!";
+         
+         Set<Map.Entry> entries = c.entrySet();
+         for (Map.Entry entry : entries) {
+            assert expKeyEntries.remove(entry.getKey());
+            assert expValueEntries.remove(entry.getValue());
+         }
+         assert expKeyEntries.isEmpty() : "Did not see keys " + expKeyEntries + " in iterator!";
+         assert expValueEntries.isEmpty() : "Did not see keys " + expValueEntries + " in iterator!";
+      }      
+   }
 }

Modified: trunk/core/src/test/java/org/infinispan/marshall/MarshalledValueTest.java
===================================================================
--- trunk/core/src/test/java/org/infinispan/marshall/MarshalledValueTest.java	2009-06-10 12:37:21 UTC (rev 422)
+++ trunk/core/src/test/java/org/infinispan/marshall/MarshalledValueTest.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -18,6 +18,7 @@
 import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent;
 import org.infinispan.test.MultipleCacheManagersTest;
 import org.infinispan.test.TestingUtil;
+import org.infinispan.util.ObjectDuplicator;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -28,7 +29,13 @@
 import java.io.IOException;
 import java.io.ObjectInput;
 import java.io.ObjectOutput;
+import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 /**
  * Tests implicit marshalled values
@@ -196,6 +203,92 @@
       assertSerialized(mv);
       assertSerializationCounts(2, 1);
    }
+   
+   public void testKeySetValuesEntrySetCollectionReferences() {
+      Pojo key1 = new Pojo(), value1 = new Pojo(), key2 = new Pojo(), value2 = new Pojo();
+      String key3 = "3", value3 = "three"; 
+      cache1.put(key1, value1);
+      cache1.put(key2, value2);
+      cache1.put(key3, value3);
+      
+      Set expKeys = new HashSet();
+      expKeys.add(key1);
+      expKeys.add(key2);
+      expKeys.add(key3);
+      
+      Set expValues = new HashSet();
+      expValues.add(value1);
+      expValues.add(value2);
+      expValues.add(value3);
+      
+      Set expKeyEntries = ObjectDuplicator.duplicateSet(expKeys);
+      Set expValueEntries = ObjectDuplicator.duplicateSet(expValues);
+      
+      Set keys = cache2.keySet();
+      for (Object key : keys) assert expKeys.remove(key);
+      assert expKeys.isEmpty() : "Did not see keys " + expKeys + " in iterator!";
+      
+      Collection values = cache2.values();
+      for (Object key : values) assert expValues.remove(key);
+      assert expValues.isEmpty() : "Did not see keys " + expValues + " in iterator!";
+      
+      Set<Map.Entry> entries = cache2.entrySet();
+      for (Map.Entry entry : entries) {
+         assert expKeyEntries.remove(entry.getKey());
+         assert expValueEntries.remove(entry.getValue());
+      }
+      assert expKeyEntries.isEmpty() : "Did not see keys " + expKeyEntries + " in iterator!";
+      assert expValueEntries.isEmpty() : "Did not see keys " + expValueEntries + " in iterator!";
+      
+      Collection[] collections = new Collection[]{keys, values, entries};
+      Object newObj = new Object();
+      List newObjCol = new ArrayList();
+      newObjCol.add(newObj);
+      for (Collection col : collections) {
+         try {
+            col.add(newObj);
+            assert false : "Should have thrown a UnsupportedOperationException";
+         } catch (UnsupportedOperationException uoe) {
+         }
+         try {
+            col.addAll(newObjCol);
+            assert false : "Should have thrown a UnsupportedOperationException";
+         } catch (UnsupportedOperationException uoe) {
+         }
+         
+         try {
+            col.clear();
+            assert false : "Should have thrown a UnsupportedOperationException";
+         } catch (UnsupportedOperationException uoe) {
+         }
+         
+         try {
+            col.remove(key1);
+            assert false : "Should have thrown a UnsupportedOperationException";
+         } catch (UnsupportedOperationException uoe) {
+         }
+         
+         try {
+            col.removeAll(newObjCol);
+            assert false : "Should have thrown a UnsupportedOperationException";
+         } catch (UnsupportedOperationException uoe) {
+         }
+         
+         try {
+            col.retainAll(newObjCol);
+            assert false : "Should have thrown a UnsupportedOperationException";
+         } catch (UnsupportedOperationException uoe) {
+         }
+      }
+      
+      for (Map.Entry entry : entries) {
+         try {
+            entry.setValue(newObj);
+            assert false : "Should have thrown a UnsupportedOperationException";
+         } catch (UnsupportedOperationException uoe) {
+         }
+      }
+   }
 
    public void testEqualsAndHashCode() throws Exception {
       Pojo pojo = new Pojo();

Modified: trunk/core/src/test/java/org/infinispan/test/TestingUtil.java
===================================================================
--- trunk/core/src/test/java/org/infinispan/test/TestingUtil.java	2009-06-10 12:37:21 UTC (rev 422)
+++ trunk/core/src/test/java/org/infinispan/test/TestingUtil.java	2009-06-10 13:46:03 UTC (rev 423)
@@ -30,11 +30,14 @@
 import javax.transaction.TransactionManager;
 import java.io.File;
 import java.lang.reflect.Field;
+import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Random;
+import java.util.Set;
 
 public class TestingUtil {
    private static Random random = new Random();
@@ -634,4 +637,22 @@
       builder.append("]");
       return builder.toString();
    }
+   
+   public static Set getInternalKeys(Cache cache) {
+      DataContainer dataContainer = TestingUtil.extractComponent(cache, DataContainer.class);
+      Set keys = new HashSet();
+      for (CacheEntry entry : dataContainer) {
+         keys.add(entry.getKey());
+      }
+      return keys;
+   }
+   
+   public static Collection getInternalValues(Cache cache) {
+      DataContainer dataContainer = TestingUtil.extractComponent(cache, DataContainer.class);
+      Collection values = new ArrayList();
+      for (CacheEntry entry : dataContainer) {
+         values.add(entry.getValue());
+      }
+      return values;
+   }
 }




More information about the infinispan-commits mailing list