[exo-jcr-commits] exo-jcr SVN: r4587 - kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache and 3 other directories.

do-not-reply at jboss.org do-not-reply at jboss.org
Mon Jul 4 08:56:27 EDT 2011


Author: nfilotto
Date: 2011-07-04 08:56:27 -0400 (Mon, 04 Jul 2011)
New Revision: 4587

Added:
   kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/impl/InvalidationExoCache.java
Modified:
   jcr/trunk/exo.jcr.docs/exo.jcr.docs.developer/en/src/main/docbook/en-US/modules/kernel/cache.xml
   kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/ExoCacheConfig.java
   kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/impl/CacheServiceImpl.java
   kernel/trunk/exo.kernel.component.ext.cache.impl.infinispan.v4/src/main/java/org/exoplatform/services/cache/impl/infinispan/AbstractExoCache.java
   kernel/trunk/exo.kernel.component.ext.cache.impl.jboss.v3/src/test/java/org/exoplatform/services/cache/impl/jboss/TestAbstractExoCache.java
Log:
EXOJCR-1418: Support non-serializable values in distributed eXo caches
The attribute "avoidValueReplication" has been added into ExoCacheConfig to allow to enable the invalidation mode by configuration if needed

Modified: jcr/trunk/exo.jcr.docs/exo.jcr.docs.developer/en/src/main/docbook/en-US/modules/kernel/cache.xml
===================================================================
--- jcr/trunk/exo.jcr.docs/exo.jcr.docs.developer/en/src/main/docbook/en-US/modules/kernel/cache.xml	2011-07-01 14:27:05 UTC (rev 4586)
+++ jcr/trunk/exo.jcr.docs/exo.jcr.docs.developer/en/src/main/docbook/en-US/modules/kernel/cache.xml	2011-07-04 12:56:27 UTC (rev 4587)
@@ -20,7 +20,7 @@
     create new <envar>ExoCache</envar> instances. See the below example of
     <envar>org.exoplatform.services.cache.CacheService</envar>
     definition:</para>
-    
+
     <programlisting language="xml">  &lt;component&gt;
     &lt;key&gt;org.exoplatform.services.cache.CacheService&lt;/key&gt;
     &lt;jmx-name&gt;cache:type=CacheService&lt;/jmx-name&gt;
@@ -40,16 +40,17 @@
     &lt;/init-params&gt;
   &lt;/component&gt;</programlisting>
 
-      <note>
-        <para>The <envar>ExoCacheConfig</envar> which name is
-        <envar>default</envar>, will be the default configuration of all the
-        <envar>ExoCache</envar> instances that don't have dedicated
-        configuration.</para>
-      </note>
-      <para> See the below example about how to define a new
+    <note>
+      <para>The <envar>ExoCacheConfig</envar> which name is
+      <envar>default</envar>, will be the default configuration of all the
+      <envar>ExoCache</envar> instances that don't have dedicated
+      configuration.</para>
+    </note>
+
+    <para>See the below example about how to define a new
     <envar>ExoCacheConfig</envar> thanks to a
     <emphasis>external-component-plugin</emphasis>:</para>
-    
+
     <programlisting language="xml">  &lt;external-component-plugins&gt;
     &lt;target-component&gt;org.exoplatform.services.cache.CacheService&lt;/target-component&gt;
     &lt;component-plugin&gt;
@@ -125,14 +126,14 @@
             <entry>distributed</entry>
 
             <entry>Indicates if the cache is distributed. This field is
-            optional. This field is used for backward compatibility.</entry>
+            optional. This field is deprecated.</entry>
           </row>
 
           <row>
             <entry>replicated</entry>
 
             <entry>Indicates if the cache is replicated. This field is
-            optional. This field is deprecated.</entry>
+            optional. </entry>
           </row>
 
           <row>
@@ -141,11 +142,95 @@
             <entry>Indicates if the log is enabled. This field is optional.
             This field is used for backward compatibility.</entry>
           </row>
+
+          <row>
+            <entry>avoidValueReplication</entry>
+
+            <entry>Indicates whether the values of the cache should be
+            replicated or not in case of a replicated cache. This field is
+            optional. By default it is disabled. Find more details about this
+            field in the next section.</entry>
+          </row>
         </tbody>
       </tgroup>
     </table>
+  </section>
 
-    <para></para>
+  <section>
+    <title>Advanced concepts</title>
+
+    <section>
+      <title>Invalidation</title>
+
+      <para>In case, you have big values or non serializable values and you
+      need a replicated cache to at list invalidate the data when it is
+      needed, you can use the invalidation mode that will work on top of any
+      replicated cache implementations. This is possible thanks to the class
+      <emphasis>InvalidationExoCache</emphasis> which is actually a decorator
+      whose idea is to replicate the the hash code of the value in order to
+      know if it is needed or not to invalidate the local data, if the new
+      hash code of the value is the same as the old value, we assume that it
+      is the same value so we don't invalidate the old value. This is required
+      to avoid the following infinite loop that we will face with invalidation
+      mode proposed out of the box by JBoss Cache for example:</para>
+
+      <orderedlist>
+        <listitem>
+          <para>Cluster node #1 puts (key1, value1) into the cache</para>
+        </listitem>
+
+        <listitem>
+          <para>On cluster node #2 key1 is invalidated by put call in node
+          #1</para>
+        </listitem>
+
+        <listitem>
+          <para>Node #2 re-loads key1 and puts (key1, value1) into the
+          cache</para>
+        </listitem>
+
+        <listitem>
+          <para>On cluster node #1 key1 is invalidated, so we get back to step
+          #1</para>
+        </listitem>
+      </orderedlist>
+
+      <para>In the use case above, thanks to the
+      <emphasis>InvalidationExoCache</emphasis> since the value loaded at step
+      #3 has the same hash code as the value loaded as step #1, the step #4
+      won't invalidate the data on the cluster node #1.</para>
+
+      <para>It exists 2 ways to use the invalidation mode which are the
+      following:</para>
+
+      <orderedlist>
+        <listitem>
+          <para>By configuration: For this you simply need to set the
+          parameter <emphasis>avoidValueReplication</emphasis> to
+          <emphasis>true</emphasis> in your eXo cache configuration, this will
+          indicate the CacheService to wrap your eXo cache instance into an
+          <emphasis>InvalidationExoCache</emphasis>.</para>
+        </listitem>
+
+        <listitem>
+          <para>Programmatically; You can wrap your eXo cache instance into an
+          <emphasis>org.exoplatform.services.cache.impl.InvalidationExoCache</emphasis>
+          yourself using the public constructors that are available. Please
+          note that if you use <emphasis>CacheListeners</emphasis> add them to
+          the InvalidationExoCache instance instead of the nested eXo Cache
+          because the nested eXo Cache will contain only hash codes so the
+          related listeners will get hash codes instead of the real
+          values.</para>
+        </listitem>
+      </orderedlist>
+
+      <note>
+        <para>The invalidation will be efficient if and only if the hash code
+        method is properly implemented, in other words 2 value objects
+        representing the same data will return the same hash code otherwise
+        the infinite loop described above will still be effective.</para>
+      </note>
+    </section>
   </section>
 
   <section>
@@ -158,7 +243,7 @@
 
     <para>You just need to implement your own <envar>ExoCacheFactory</envar>
     and register it in an eXo container, as described below:</para>
-    
+
     <programlisting language="java">package org.exoplatform.services.cache;
 ...
 public interface ExoCacheFactory {
@@ -178,7 +263,7 @@
     can simply register your factory by adding a file
     <emphasis>conf/portal/configuration.xml</emphasis> with a content of the
     following type:</para>
-    
+
     <programlisting language="xml">&lt;configuration&gt;
   &lt;component&gt;
     &lt;key&gt;org.exoplatform.services.cache.ExoCacheFactory&lt;/key&gt;
@@ -188,14 +273,14 @@
 &lt;/configuration&gt;
 </programlisting>
 
-<note>
-        <para>Since kernel 2.3.0-CR1, if the configuration is not a sub class
-        of <envar>ExoCacheConfig</envar> and the implementation given in the
-        configuration is the full qualified name of an existing implementation
-        of eXo Cache, we will assume that the user expects to have an instance
-        of this eXo Cache type so we won't use the configured cache
-        factory.</para>
-      </note>
+    <note>
+      <para>Since kernel 2.3.0-CR1, if the configuration is not a sub class of
+      <envar>ExoCacheConfig</envar> and the implementation given in the
+      configuration is the full qualified name of an existing implementation
+      of eXo Cache, we will assume that the user expects to have an instance
+      of this eXo Cache type so we won't use the configured cache
+      factory.</para>
+    </note>
   </section>
 
   <section>
@@ -210,7 +295,7 @@
       wish as you can do with any components.</para>
 
       <para>The default configuration of the factory is:</para>
-      
+
       <programlisting language="xml">&lt;configuration&gt;  
   &lt;component&gt;
     &lt;key&gt;org.exoplatform.services.cache.ExoCacheFactory&lt;/key&gt;
@@ -278,7 +363,7 @@
       <para>If for a given reason, you need to use a specific configuration
       for a cache, you can register one thanks to an "<emphasis>external
       plugin</emphasis>", see an example below:</para>
-      
+
       <programlisting language="xml">&lt;configuration&gt;
   ...
   &lt;external-component-plugins&gt;
@@ -323,9 +408,8 @@
         <title>Understanding a cache creator</title>
 
         <para>The factory for jboss cache, delegates the cache creation to
-        <envar>ExoCacheCreator</envar> that is defined as
-        below:</para>
-        
+        <envar>ExoCacheCreator</envar> that is defined as below:</para>
+
         <programlisting language="java">package org.exoplatform.services.cache.impl.jboss;
 ...
 public interface ExoCacheCreator {
@@ -387,9 +471,8 @@
         <title>Registering a cache creator</title>
 
         <para>You can register any cache creator that you want thanks to an
-        <emphasis>"external plugin"</emphasis>, see an example
-        below:</para>
-        
+        <emphasis>"external plugin"</emphasis>, see an example below:</para>
+
         <programlisting language="xml">  &lt;external-component-plugins&gt;
     &lt;target-component&gt;org.exoplatform.services.cache.ExoCacheFactory&lt;/target-component&gt;
     &lt;component-plugin&gt;
@@ -483,7 +566,6 @@
  &lt;object type="org.exoplatform.services.cache.impl.jboss.fifo.FIFOExoCacheCreator"&gt;&lt;/object&gt;
 &lt;/object-param&gt;
 ...</programlisting>
-
         </section>
 
         <section>
@@ -496,7 +578,6 @@
   &lt;object type="org.exoplatform.services.cache.impl.jboss.mru.MRUExoCacheCreator"&gt;&lt;/object&gt;
 &lt;/object-param&gt;
 ...</programlisting>
-
         </section>
 
         <section>
@@ -707,56 +788,54 @@
       &lt;/object-param&gt; 
 ...</programlisting>
 
-<table>
-                <title>Fields description</title>
+        <table>
+          <title>Fields description</title>
 
-                <tgroup cols="2">
-                  <tbody>
-                    <row>
-                      <entry>maxNodes</entry>
+          <tgroup cols="2">
+            <tbody>
+              <row>
+                <entry>maxNodes</entry>
 
-                      <entry>This is the maximum number of nodes allowed in
-                      this region. 0 denotes immediate expiry, -1 denotes no
-                      limit.</entry>
-                    </row>
+                <entry>This is the maximum number of nodes allowed in this
+                region. 0 denotes immediate expiry, -1 denotes no
+                limit.</entry>
+              </row>
 
-                    <row>
-                      <entry>minTimeToLive</entry>
+              <row>
+                <entry>minTimeToLive</entry>
 
-                      <entry>The minimum amount of time (in milliseconds) that
-                      a node must be allowed to live after being accessed
-                      before it is allowed to be considered for eviction. 0
-                      denotes that this feature is disabled, which is the
-                      default value.</entry>
-                    </row>
+                <entry>The minimum amount of time (in milliseconds) that a
+                node must be allowed to live after being accessed before it is
+                allowed to be considered for eviction. 0 denotes that this
+                feature is disabled, which is the default value.</entry>
+              </row>
 
-                    <row>
-                      <entry>maxAge</entry>
+              <row>
+                <entry>maxAge</entry>
 
-                      <entry>Lifespan of a node (in milliseconds) regardless
-                      of idle time before the node is swept away. 0 denotes
-                      immediate expiry, -1 denotes no limit.</entry>
-                    </row>
+                <entry>Lifespan of a node (in milliseconds) regardless of idle
+                time before the node is swept away. 0 denotes immediate
+                expiry, -1 denotes no limit.</entry>
+              </row>
 
-                    <row>
-                      <entry>timeToLive</entry>
+              <row>
+                <entry>timeToLive</entry>
 
-                      <entry>The amount of time that a node is not written to
-                      or read (in milliseconds) before the node is swept away.
-                      0 denotes immediate expiry, -1 denotes no limit.</entry>
-                    </row>
-                  </tbody>
-                </tgroup>
-              </table>
-             
-          <itemizedlist>
+                <entry>The amount of time that a node is not written to or
+                read (in milliseconds) before the node is swept away. 0
+                denotes immediate expiry, -1 denotes no limit.</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </table>
 
+        <itemizedlist>
           <listitem>
             <para>Old configuration</para>
           </listitem>
-          </itemizedlist>
+        </itemizedlist>
 
-          <programlisting language="xml">...
+        <programlisting language="xml">...
       &lt;object-param&gt;
         &lt;name&gt;lru-with-old-config&lt;/name&gt;
         &lt;description&gt;The lru cache configuration&lt;/description&gt;
@@ -769,38 +848,36 @@
       &lt;/object-param&gt; 
 ...</programlisting>
 
-            <table>
-              <title>Fields description</title>
+        <table>
+          <title>Fields description</title>
 
-              <tgroup cols="2">
-                <tbody>
-                  <row>
-                    <entry>maxSize</entry>
+          <tgroup cols="2">
+            <tbody>
+              <row>
+                <entry>maxSize</entry>
 
-                    <entry>This is the maximum number of nodes allowed in this
-                    region. 0 denotes immediate expiry, -1 denotes no
-                    limit.</entry>
-                  </row>
+                <entry>This is the maximum number of nodes allowed in this
+                region. 0 denotes immediate expiry, -1 denotes no
+                limit.</entry>
+              </row>
 
-                  <row>
-                    <entry>liveTime</entry>
+              <row>
+                <entry>liveTime</entry>
 
-                    <entry>The minimum amount of time (in seconds) that a node
-                    must be allowed to live after being accessed before it is
-                    allowed to be considered for eviction. 0 denotes that this
-                    feature is disabled, which is the default value.</entry>
-                  </row>
-                </tbody>
-              </tgroup>
-            </table>
+                <entry>The minimum amount of time (in seconds) that a node
+                must be allowed to live after being accessed before it is
+                allowed to be considered for eviction. 0 denotes that this
+                feature is disabled, which is the default value.</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </table>
 
-            <note>
-                <para>For the fields <emphasis>maxAge</emphasis> and
-                <emphasis>timeToLive</emphasis> needed by JBoss cache, we will
-                use the default values provided by the creator.</para>
-            </note>
-         
-       
+        <note>
+          <para>For the fields <emphasis>maxAge</emphasis> and
+          <emphasis>timeToLive</emphasis> needed by JBoss cache, we will use
+          the default values provided by the creator.</para>
+        </note>
       </section>
 
       <section>
@@ -811,8 +888,8 @@
             <para>New configuration</para>
           </listitem>
         </itemizedlist>
-            
-            <programlisting language="xml">...
+
+        <programlisting language="xml">...
        &lt;object-param&gt;
         &lt;name&gt;fifo&lt;/name&gt;
         &lt;description&gt;The fifo cache configuration&lt;/description&gt;
@@ -824,38 +901,38 @@
       &lt;/object-param&gt;
 ...</programlisting>
 
-            <table>
-              <title>Fields description</title>
+        <table>
+          <title>Fields description</title>
 
-              <tgroup cols="2">
-                <tbody>
-                  <row>
-                    <entry>maxNodes</entry>
+          <tgroup cols="2">
+            <tbody>
+              <row>
+                <entry>maxNodes</entry>
 
-                    <entry>This is the maximum number of nodes allowed in this
-                    region. 0 denotes immediate expiry, -1 denotes no
-                    limit.</entry>
-                  </row>
+                <entry>This is the maximum number of nodes allowed in this
+                region. 0 denotes immediate expiry, -1 denotes no
+                limit.</entry>
+              </row>
 
-                  <row>
-                    <entry>minTimeToLive</entry>
+              <row>
+                <entry>minTimeToLive</entry>
 
-                    <entry>The minimum amount of time (in milliseconds) that a
-                    node must be allowed to live after being accessed before
-                    it is allowed to be considered for eviction. 0 denotes
-                    that this feature is disabled, which is the default
-                    value.</entry>
-                  </row>
-                </tbody>
-              </tgroup>
-            </table>
-            <itemizedlist>
-              <listitem>
-                <para>Old configuration</para>
-              </listitem>
-            </itemizedlist>
-            
-            <programlisting language="xml">...
+                <entry>The minimum amount of time (in milliseconds) that a
+                node must be allowed to live after being accessed before it is
+                allowed to be considered for eviction. 0 denotes that this
+                feature is disabled, which is the default value.</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </table>
+
+        <itemizedlist>
+          <listitem>
+            <para>Old configuration</para>
+          </listitem>
+        </itemizedlist>
+
+        <programlisting language="xml">...
        &lt;object-param&gt;
         &lt;name&gt;fifo-with-old-config&lt;/name&gt;
         &lt;description&gt;The fifo cache configuration&lt;/description&gt;
@@ -868,30 +945,30 @@
       &lt;/object-param&gt;
 ...</programlisting>
 
-            <table>
-              <title>Fields description</title>
+        <table>
+          <title>Fields description</title>
 
-              <tgroup cols="2">
-                <tbody>
-                  <row>
-                    <entry>maxSize</entry>
+          <tgroup cols="2">
+            <tbody>
+              <row>
+                <entry>maxSize</entry>
 
-                    <entry>This is the maximum number of nodes allowed in this
-                    region. 0 denotes immediate expiry, -1 denotes no
-                    limit.</entry>
-                  </row>
+                <entry>This is the maximum number of nodes allowed in this
+                region. 0 denotes immediate expiry, -1 denotes no
+                limit.</entry>
+              </row>
 
-                  <row>
-                    <entry>liveTime</entry>
+              <row>
+                <entry>liveTime</entry>
 
-                    <entry>The minimum amount of time (in seconds) that a node
-                    must be allowed to live after being accessed before it is
-                    allowed to be considered for eviction. 0 denotes that this
-                    feature is disabled, which is the default value.</entry>
-                  </row>
-                </tbody>
-              </tgroup>
-            </table>
+                <entry>The minimum amount of time (in seconds) that a node
+                must be allowed to live after being accessed before it is
+                allowed to be considered for eviction. 0 denotes that this
+                feature is disabled, which is the default value.</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </table>
       </section>
 
       <section>
@@ -902,8 +979,8 @@
             <para>New configuration</para>
           </listitem>
         </itemizedlist>
-            
-            <programlisting language="xml">...
+
+        <programlisting language="xml">...
        &lt;object-param&gt;
         &lt;name&gt;mru&lt;/name&gt;
         &lt;description&gt;The mru cache configuration&lt;/description&gt;
@@ -915,39 +992,38 @@
       &lt;/object-param&gt; 
 ...</programlisting>
 
-            <table>
-              <title>Fields description</title>
+        <table>
+          <title>Fields description</title>
 
-              <tgroup cols="2">
-                <tbody>
-                  <row>
-                    <entry>maxNodes</entry>
+          <tgroup cols="2">
+            <tbody>
+              <row>
+                <entry>maxNodes</entry>
 
-                    <entry>This is the maximum number of nodes allowed in this
-                    region. 0 denotes immediate expiry, -1 denotes no
-                    limit.</entry>
-                  </row>
+                <entry>This is the maximum number of nodes allowed in this
+                region. 0 denotes immediate expiry, -1 denotes no
+                limit.</entry>
+              </row>
 
-                  <row>
-                    <entry>minTimeToLive</entry>
+              <row>
+                <entry>minTimeToLive</entry>
 
-                    <entry>The minimum amount of time (in milliseconds) that a
-                    node must be allowed to live after being accessed before
-                    it is allowed to be considered for eviction. 0 denotes
-                    that this feature is disabled, which is the default
-                    value.</entry>
-                  </row>
-                </tbody>
-              </tgroup>
-            </table>
-          
+                <entry>The minimum amount of time (in milliseconds) that a
+                node must be allowed to live after being accessed before it is
+                allowed to be considered for eviction. 0 denotes that this
+                feature is disabled, which is the default value.</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </table>
+
         <itemizedlist>
           <listitem>
             <para>Old configuration</para>
           </listitem>
         </itemizedlist>
-            
-            <programlisting language="xml">...
+
+        <programlisting language="xml">...
       &lt;object-param&gt;
         &lt;name&gt;mru-with-old-config&lt;/name&gt;
         &lt;description&gt;The mru cache configuration&lt;/description&gt;
@@ -960,30 +1036,30 @@
       &lt;/object-param&gt; 
 ...</programlisting>
 
-            <table>
-              <title>Fields description</title>
+        <table>
+          <title>Fields description</title>
 
-              <tgroup cols="2">
-                <tbody>
-                  <row>
-                    <entry>maxSize</entry>
+          <tgroup cols="2">
+            <tbody>
+              <row>
+                <entry>maxSize</entry>
 
-                    <entry>This is the maximum number of nodes allowed in this
-                    region. 0 denotes immediate expiry, -1 denotes no
-                    limit.</entry>
-                  </row>
+                <entry>This is the maximum number of nodes allowed in this
+                region. 0 denotes immediate expiry, -1 denotes no
+                limit.</entry>
+              </row>
 
-                  <row>
-                    <entry>liveTime</entry>
+              <row>
+                <entry>liveTime</entry>
 
-                    <entry>The minimum amount of time (in seconds) that a node
-                    must be allowed to live after being accessed before it is
-                    allowed to be considered for eviction. 0 denotes that this
-                    feature is disabled, which is the default value.</entry>
-                  </row>
-                </tbody>
-              </tgroup>
-            </table>
+                <entry>The minimum amount of time (in seconds) that a node
+                must be allowed to live after being accessed before it is
+                allowed to be considered for eviction. 0 denotes that this
+                feature is disabled, which is the default value.</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </table>
       </section>
 
       <section>
@@ -994,8 +1070,8 @@
             <para>New configuration</para>
           </listitem>
         </itemizedlist>
-            
-            <programlisting language="xml">...
+
+        <programlisting language="xml">...
        &lt;object-param&gt;
         &lt;name&gt;lfu&lt;/name&gt;
         &lt;description&gt;The lfu cache configuration&lt;/description&gt;
@@ -1008,49 +1084,49 @@
       &lt;/object-param&gt; 
 ...</programlisting>
 
-            <table>
-              <title>Fields description</title>
+        <table>
+          <title>Fields description</title>
 
-              <tgroup cols="2">
-                <tbody>
-                  <row>
-                    <entry>maxNodes</entry>
+          <tgroup cols="2">
+            <tbody>
+              <row>
+                <entry>maxNodes</entry>
 
-                    <entry>This is the maximum number of nodes allowed in this
-                    region. 0 denotes immediate expiry, -1 denotes no
-                    limit.</entry>
-                  </row>
+                <entry>This is the maximum number of nodes allowed in this
+                region. 0 denotes immediate expiry, -1 denotes no
+                limit.</entry>
+              </row>
 
-                  <row>
-                    <entry>minNodes</entry>
+              <row>
+                <entry>minNodes</entry>
 
-                    <entry>This is the minimum number of nodes allowed in this
-                    region. This value determines what the eviction queue
-                    should prune down to per pass. e.g. If minNodes is 10 and
-                    the cache grows to 100 nodes, the cache is pruned down to
-                    the 10 most frequently used nodes when the eviction timer
-                    makes a pass through the eviction algorithm.</entry>
-                  </row>
+                <entry>This is the minimum number of nodes allowed in this
+                region. This value determines what the eviction queue should
+                prune down to per pass. e.g. If minNodes is 10 and the cache
+                grows to 100 nodes, the cache is pruned down to the 10 most
+                frequently used nodes when the eviction timer makes a pass
+                through the eviction algorithm.</entry>
+              </row>
 
-                  <row>
-                    <entry>minTimeToLive</entry>
+              <row>
+                <entry>minTimeToLive</entry>
 
-                    <entry>The minimum amount of time (in milliseconds) that a
-                    node must be allowed to live after being accessed before
-                    it is allowed to be considered for eviction. 0 denotes
-                    that this feature is disabled, which is the default
-                    value.</entry>
-                  </row>
-                </tbody>
-              </tgroup>
-            </table>
-          <itemizedlist>          
-            <listitem>
-              <para>Old configuration</para>
-            </listitem>
-          </itemizedlist>
-          
-            <programlisting language="xml">...
+                <entry>The minimum amount of time (in milliseconds) that a
+                node must be allowed to live after being accessed before it is
+                allowed to be considered for eviction. 0 denotes that this
+                feature is disabled, which is the default value.</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </table>
+
+        <itemizedlist>
+          <listitem>
+            <para>Old configuration</para>
+          </listitem>
+        </itemizedlist>
+
+        <programlisting language="xml">...
       &lt;object-param&gt;
         &lt;name&gt;lfu-with-old-config&lt;/name&gt;
         &lt;description&gt;The lfu cache configuration&lt;/description&gt;
@@ -1063,37 +1139,36 @@
       &lt;/object-param&gt; 
 ...</programlisting>
 
-            <table>
-              <title>Fields description</title>
+        <table>
+          <title>Fields description</title>
 
-              <tgroup cols="2">
-                <tbody>
-                  <row>
-                    <entry>maxSize</entry>
+          <tgroup cols="2">
+            <tbody>
+              <row>
+                <entry>maxSize</entry>
 
-                    <entry>This is the maximum number of nodes allowed in this
-                    region. 0 denotes immediate expiry, -1 denotes no
-                    limit.</entry>
-                  </row>
+                <entry>This is the maximum number of nodes allowed in this
+                region. 0 denotes immediate expiry, -1 denotes no
+                limit.</entry>
+              </row>
 
-                  <row>
-                    <entry>liveTime</entry>
+              <row>
+                <entry>liveTime</entry>
 
-                    <entry>The minimum amount of time (in milliseconds) that a
-                    node must be allowed to live after being accessed before
-                    it is allowed to be considered for eviction. 0 denotes
-                    that this feature is disabled, which is the default
-                    value.</entry>
-                  </row>
-                </tbody>
-              </tgroup>
-            </table>
+                <entry>The minimum amount of time (in milliseconds) that a
+                node must be allowed to live after being accessed before it is
+                allowed to be considered for eviction. 0 denotes that this
+                feature is disabled, which is the default value.</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </table>
 
-            <para><note>
-                <para>For the fields <emphasis>minNodes</emphasis> and
-                <emphasis>timeToLive</emphasis> needed by JBoss cache, we will
-                use the default values provided by the creator.</para>
-              </note></para>
+        <para><note>
+            <para>For the fields <emphasis>minNodes</emphasis> and
+            <emphasis>timeToLive</emphasis> needed by JBoss cache, we will use
+            the default values provided by the creator.</para>
+          </note></para>
       </section>
 
       <section>
@@ -1104,8 +1179,8 @@
             <para>New configuration</para>
           </listitem>
         </itemizedlist>
-            
-            <programlisting language="xml">...
+
+        <programlisting language="xml">...
        &lt;object-param&gt;
         &lt;name&gt;ea&lt;/name&gt;
         &lt;description&gt;The ea cache configuration&lt;/description&gt;
@@ -1118,46 +1193,45 @@
       &lt;/object-param&gt; 
 ...</programlisting>
 
-            <table>
-              <title>Fields description</title>
+        <table>
+          <title>Fields description</title>
 
-              <tgroup cols="2">
-                <tbody>
-                  <row>
-                    <entry>maxNodes</entry>
+          <tgroup cols="2">
+            <tbody>
+              <row>
+                <entry>maxNodes</entry>
 
-                    <entry>This is the maximum number of nodes allowed in this
-                    region. 0 denotes immediate expiry, -1 denotes no
-                    limit.</entry>
-                  </row>
+                <entry>This is the maximum number of nodes allowed in this
+                region. 0 denotes immediate expiry, -1 denotes no
+                limit.</entry>
+              </row>
 
-                  <row>
-                    <entry>minTimeToLive</entry>
+              <row>
+                <entry>minTimeToLive</entry>
 
-                    <entry>The minimum amount of time (in milliseconds) that a
-                    node must be allowed to live after being accessed before
-                    it is allowed to be considered for eviction. 0 denotes
-                    that this feature is disabled, which is the default
-                    value.</entry>
-                  </row>
+                <entry>The minimum amount of time (in milliseconds) that a
+                node must be allowed to live after being accessed before it is
+                allowed to be considered for eviction. 0 denotes that this
+                feature is disabled, which is the default value.</entry>
+              </row>
 
-                  <row>
-                    <entry>expirationTimeout</entry>
+              <row>
+                <entry>expirationTimeout</entry>
 
-                    <entry>This is the timeout after which the cache entry
-                    must be evicted.</entry>
-                  </row>
-                </tbody>
-              </tgroup>
-            </table>
-          
+                <entry>This is the timeout after which the cache entry must be
+                evicted.</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </table>
+
         <itemizedlist>
           <listitem>
             <para>Old configuration</para>
           </listitem>
         </itemizedlist>
-        
-            <programlisting language="xml">...
+
+        <programlisting language="xml">...
       &lt;object-param&gt;
         &lt;name&gt;ea-with-old-config&lt;/name&gt;
         &lt;description&gt;The ea cache configuration&lt;/description&gt;
@@ -1170,38 +1244,36 @@
       &lt;/object-param&gt; 
 ...</programlisting>
 
-            <table>
-              <title>Fields description</title>
+        <table>
+          <title>Fields description</title>
 
-              <tgroup cols="2">
-                <tbody>
-                  <row>
-                    <entry>maxSize</entry>
+          <tgroup cols="2">
+            <tbody>
+              <row>
+                <entry>maxSize</entry>
 
-                    <entry>This is the maximum number of nodes allowed in this
-                    region. 0 denotes immediate expiry, -1 denotes no
-                    limit.</entry>
-                  </row>
+                <entry>This is the maximum number of nodes allowed in this
+                region. 0 denotes immediate expiry, -1 denotes no
+                limit.</entry>
+              </row>
 
-                  <row>
-                    <entry>liveTime</entry>
+              <row>
+                <entry>liveTime</entry>
 
-                    <entry>The minimum amount of time (in milliseconds) that a
-                    node must be allowed to live after being accessed before
-                    it is allowed to be considered for eviction. 0 denotes
-                    that this feature is disabled, which is the default
-                    value.</entry>
-                  </row>
-                </tbody>
-              </tgroup>
-            </table>
+                <entry>The minimum amount of time (in milliseconds) that a
+                node must be allowed to live after being accessed before it is
+                allowed to be considered for eviction. 0 denotes that this
+                feature is disabled, which is the default value.</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </table>
 
-            <para><note>
-                <para>For the fields <emphasis>expirationTimeout</emphasis>
-                needed by JBoss cache, we will use the default values provided
-                by the creator.</para>
-              </note></para>
-         
+        <para><note>
+            <para>For the fields <emphasis>expirationTimeout</emphasis> needed
+            by JBoss cache, we will use the default values provided by the
+            creator.</para>
+          </note></para>
       </section>
     </section>
   </section>
@@ -1218,7 +1290,7 @@
       if you wish as you can do with any components.</para>
 
       <para>The default configuration of the factory is:</para>
-      
+
       <programlisting language="xml">&lt;configuration&gt;  
   &lt;component&gt;
     &lt;key&gt;org.exoplatform.services.cache.ExoCacheFactory&lt;/key&gt;
@@ -1241,12 +1313,13 @@
 
       <para>The default configuration template aims to be the skeleton from
       which we will create any type of infinispan cache instance, thus it must
-      be very generic.</para> 
+      be very generic.</para>
+
       <note>
-          <para>All the cache instances that will rely on this cache
-          configuration will share the same
-          <envar>EmbeddedCacheManager.</envar></para>
-        </note>
+        <para>All the cache instances that will rely on this cache
+        configuration will share the same
+        <envar>EmbeddedCacheManager.</envar></para>
+      </note>
     </section>
 
     <section>
@@ -1255,7 +1328,7 @@
       <para>If for a given reason, you need to use a specific configuration
       for a cache, you can register one thanks to an "<emphasis>external
       plugin</emphasis>", see an example below:</para>
-      
+
       <programlisting language="xml">&lt;configuration&gt;
   ...
   &lt;external-component-plugins&gt;
@@ -1306,9 +1379,8 @@
         <title>Understanding a cache creator</title>
 
         <para>The factory for infinispan, delegates the cache creation to
-        <envar>ExoCacheCreator</envar> that is defined as
-        below:</para>
-        
+        <envar>ExoCacheCreator</envar> that is defined as below:</para>
+
         <programlisting language="java">package org.exoplatform.services.cache.impl.infinispan;
 ...
 public interface ExoCacheCreator {
@@ -1374,9 +1446,8 @@
         <title>Register a cache creator</title>
 
         <para>You can register any cache creator you want thanks to an
-        <emphasis>"external plugin"</emphasis>, see an example
-        below:</para>
-        
+        <emphasis>"external plugin"</emphasis>, see an example below:</para>
+
         <programlisting language="xml">  &lt;external-component-plugins&gt;
     &lt;target-component&gt;org.exoplatform.services.cache.ExoCacheFactory&lt;/target-component&gt;
     &lt;component-plugin&gt;
@@ -1535,8 +1606,8 @@
             <para>New configuration</para>
           </listitem>
         </itemizedlist>
-            
-            <programlisting language="xml">...
+
+        <programlisting language="xml">...
        &lt;object-param&gt;
         &lt;name&gt;myCache&lt;/name&gt;
         &lt;description&gt;My cache configuration&lt;/description&gt;
@@ -1550,67 +1621,65 @@
         &lt;/object&gt;
       &lt;/object-param&gt; 
 ...</programlisting>
-              <table>
-                <title>Fields description</title>
 
-                <tgroup cols="2">
-                  <tbody>
-                    <row>
-                      <entry>strategy</entry>
+        <table>
+          <title>Fields description</title>
 
-                      <entry>The name of the strategy to use such as
-                      'UNORDERED', 'FIFO', 'LRU', 'LIRS' and 'NONE' (to
-                      disable eviction).</entry>
-                    </row>
+          <tgroup cols="2">
+            <tbody>
+              <row>
+                <entry>strategy</entry>
 
-                    <row>
-                      <entry>maxEntries</entry>
+                <entry>The name of the strategy to use such as 'UNORDERED',
+                'FIFO', 'LRU', 'LIRS' and 'NONE' (to disable
+                eviction).</entry>
+              </row>
 
-                      <entry>Maximum number of entries in a cache instance. If
-                      selected value is not a power of two the actual value
-                      will default to the least power of two larger than
-                      selected value. -1 means no limit which is also the
-                      default value.</entry>
-                    </row>
+              <row>
+                <entry>maxEntries</entry>
 
-                    <row>
-                      <entry>lifespan</entry>
+                <entry>Maximum number of entries in a cache instance. If
+                selected value is not a power of two the actual value will
+                default to the least power of two larger than selected value.
+                -1 means no limit which is also the default value.</entry>
+              </row>
 
-                      <entry>Maximum lifespan of a cache entry, after which
-                      the entry is expired cluster-wide, in milliseconds. -1
-                      means the entries never expire which is also the default
-                      value.</entry>
-                    </row>
+              <row>
+                <entry>lifespan</entry>
 
-                    <row>
-                      <entry>maxIdle</entry>
+                <entry>Maximum lifespan of a cache entry, after which the
+                entry is expired cluster-wide, in milliseconds. -1 means the
+                entries never expire which is also the default value.</entry>
+              </row>
 
-                      <entry>Maximum idle time a cache entry will be
-                      maintained in the cache, in milliseconds. If the idle
-                      time is exceeded, the entry will be expired
-                      cluster-wide. -1 means the entries never expire which is
-                      also the default value.</entry>
-                    </row>
+              <row>
+                <entry>maxIdle</entry>
 
-                    <row>
-                      <entry>wakeUpInterval</entry>
+                <entry>Maximum idle time a cache entry will be maintained in
+                the cache, in milliseconds. If the idle time is exceeded, the
+                entry will be expired cluster-wide. -1 means the entries never
+                expire which is also the default value.</entry>
+              </row>
 
-                      <entry>Interval between subsequent eviction runs, in
-                      milliseconds. If you wish to disable the periodic
-                      eviction process altogether, set wakeupInterval to -1.
-                      The default value is 5000.</entry>
-                    </row>
-                  </tbody>
-                </tgroup>
-              </table>
-          
+              <row>
+                <entry>wakeUpInterval</entry>
+
+                <entry>Interval between subsequent eviction runs, in
+                milliseconds. If you wish to disable the periodic eviction
+                process altogether, set wakeupInterval to -1. The default
+                value is 5000.</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </table>
+
         <itemizedlist>
           <listitem>
             <para>Old configuration</para>
           </listitem>
         </itemizedlist>
 
-            <programlisting language="xml">...
+        <programlisting language="xml">...
       &lt;object-param&gt;
         &lt;name&gt;myCache&lt;/name&gt;
         &lt;description&gt;My cache configuration&lt;/description&gt;
@@ -1622,48 +1691,45 @@
       &lt;/object-param&gt; 
 ...</programlisting>
 
-            <table>
-              <title>Fields description</title>
+        <table>
+          <title>Fields description</title>
 
-              <tgroup cols="2">
-                <tbody>
-                  <row>
-                    <entry>maxSize</entry>
+          <tgroup cols="2">
+            <tbody>
+              <row>
+                <entry>maxSize</entry>
 
-                    <entry>Maximum number of entries in a cache instance. If
-                    selected value is not a power of two the actual value will
-                    default to the least power of two larger than selected
-                    value. -1 means no limit which is also the default
-                    value.</entry>
-                  </row>
+                <entry>Maximum number of entries in a cache instance. If
+                selected value is not a power of two the actual value will
+                default to the least power of two larger than selected value.
+                -1 means no limit which is also the default value.</entry>
+              </row>
 
-                  <row>
-                    <entry>liveTime</entry>
+              <row>
+                <entry>liveTime</entry>
 
-                    <entry>Maximum lifespan of a cache entry, after which the
-                    entry is expired cluster-wide, in milliseconds. -1 means
-                    the entries never expire which is also the default
-                    value.</entry>
-                  </row>
+                <entry>Maximum lifespan of a cache entry, after which the
+                entry is expired cluster-wide, in milliseconds. -1 means the
+                entries never expire which is also the default value.</entry>
+              </row>
 
-                  <row>
-                    <entry>implementation</entry>
+              <row>
+                <entry>implementation</entry>
 
-                    <entry>The name of the implementation to use the expected
-                    value is one of the eviction strategies defined in the
-                    field <emphasis>implementations</emphasis> of the generic
-                    cache creator.</entry>
-                  </row>
-                </tbody>
-              </tgroup>
-            </table>
+                <entry>The name of the implementation to use the expected
+                value is one of the eviction strategies defined in the field
+                <emphasis>implementations</emphasis> of the generic cache
+                creator.</entry>
+              </row>
+            </tbody>
+          </tgroup>
+        </table>
 
-            <para><note>
-                <para>For the fields <emphasis>maxIdle</emphasis> and
-                <emphasis>wakeUpInterval</emphasis> needed by infinispan, we
-                will use the default values provided by the creator.</para>
-              </note></para>
-             
+        <para><note>
+            <para>For the fields <emphasis>maxIdle</emphasis> and
+            <emphasis>wakeUpInterval</emphasis> needed by infinispan, we will
+            use the default values provided by the creator.</para>
+          </note></para>
       </section>
     </section>
   </section>

Modified: kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/ExoCacheConfig.java
===================================================================
--- kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/ExoCacheConfig.java	2011-07-01 14:27:05 UTC (rev 4586)
+++ kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/ExoCacheConfig.java	2011-07-04 12:56:27 UTC (rev 4587)
@@ -68,6 +68,11 @@
     */
    private boolean logEnabled;
 
+   /**
+    * Indicates whether or not the replication of the values should be avoided
+    */
+   public boolean avoidValueReplication;
+
    public String getName()
    {
       return name;
@@ -147,8 +152,24 @@
    {
       this.logEnabled = enableLogging;
    }
+ 
+   /**
+    * @return the avoidValueReplication
+    */
+   public boolean avoidValueReplication()
+   {
+      return avoidValueReplication;
+   }
 
    /**
+    * @param avoidValueReplication the avoidValueReplication to set
+    */
+   public void setAvoidValueReplication(boolean avoidValueReplication)
+   {
+      this.avoidValueReplication = avoidValueReplication;
+   }
+
+   /**
     * @see java.lang.Object#clone()
     */
    @Override

Modified: kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/impl/CacheServiceImpl.java
===================================================================
--- kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/impl/CacheServiceImpl.java	2011-07-01 14:27:05 UTC (rev 4586)
+++ kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/impl/CacheServiceImpl.java	2011-07-04 12:56:27 UTC (rev 4587)
@@ -154,6 +154,7 @@
       return null;
    }
 
+   @SuppressWarnings({"rawtypes", "unchecked"})
    private ExoCache<? extends Serializable, ?> createCacheInstance(String region) throws Exception
    {
       ExoCacheConfig config = configs_.get(region);
@@ -198,7 +199,9 @@
       {
          managed.registerCache(simple);
       }
-      return simple;
+      // If the flag avoid value replication is enabled we wrap the eXo cache instance
+      // into an InvalidationExoCache to enable the invalidation
+      return safeConfig.avoidValueReplication() ? new InvalidationExoCache(simple) : simple;
    }
 
    public Collection<ExoCache<? extends Serializable, ?>> getAllCacheInstances()

Added: kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/impl/InvalidationExoCache.java
===================================================================
--- kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/impl/InvalidationExoCache.java	                        (rev 0)
+++ kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/impl/InvalidationExoCache.java	2011-07-04 12:56:27 UTC (rev 4587)
@@ -0,0 +1,560 @@
+/*
+ * Copyright (C) 2011 eXo Platform SAS.
+ *
+ * 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.exoplatform.services.cache.impl;
+
+import org.exoplatform.services.cache.CacheListener;
+import org.exoplatform.services.cache.CacheListenerContext;
+import org.exoplatform.services.cache.CachedObjectSelector;
+import org.exoplatform.services.cache.ExoCache;
+import org.exoplatform.services.cache.ObjectCacheInfo;
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
+
+import java.io.Externalizable;
+import java.io.IOException;
+import java.io.ObjectInput;
+import java.io.ObjectOutput;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * This eXo cache type is a decorator allowing ExoCache instances that have
+ * big values or non serializable values to be replicated thanks to an invalidation
+ * mechanism. To prevent infinite loop described below, we replicate the hash code of
+ * the value such that if the hash code is the same, we don't invalidate the value locally
+ * <ul>
+ * <li>Cluster node #1 puts (key1, value1) into the cache</li>
+ * <li>On cluster node #2 key1 is invalidated by the put call in node #1</li>
+ * <li>Node #2 re-loads key1 and puts (key1, value1) into the cache</li>
+ * <li>On cluster node #1 key1 is invalidated, so we get back to step #1</li>
+ * </ul>
+ * 
+ * @author <a href="mailto:nfilotto at exoplatform.com">Nicolas Filotto</a>
+ * @version $Id$
+ *
+ */
+public class InvalidationExoCache<K extends Serializable, V> implements ExoCache<K, V>, CacheListener<K, InvalidationExoCache.HashCode<V>>
+{
+   /**
+    * Logger.
+    */
+   private static final Log LOG = ExoLogger.getLogger("exo.kernel.component.cache.InvalidationExoCache");
+
+   /**
+    * The eXo cache instance that we would like to replicate using the invalidation
+    * mechanism
+    */
+   private final ExoCache<K, HashCode<V>> delegate;
+   
+   /**
+    * The listeners of the cache
+    */
+   private final CopyOnWriteArrayList<CacheListener<? super K, ? super V>> listeners;
+   
+   /**
+    * The local cache that contains the real values
+    */
+   private final ConcurrentMap<K, V> localCache;
+   
+   /**
+    * @param delegate the underneath eXo cache instance, we assume that the eXo cache
+    * implementation behind is fully functional.
+    */
+   public InvalidationExoCache(ExoCache<K, V> delegate)
+   {
+      this(delegate, 16);
+   }
+   
+   /**
+    * @param delegate the underneath eXo cache instance, we assume that the eXo cache
+    * implementation behind is fully functional.
+    * @concurrencyLevel the estimated number of concurrently
+    * updating threads. The implementation performs internal sizing
+    * to try to accommodate this many threads.  
+    */
+   @SuppressWarnings("unchecked")
+   public InvalidationExoCache(ExoCache<K, V> delegate, int concurrencyLevel)
+   {
+      this.delegate = (ExoCache<K, HashCode<V>>)delegate;
+      // We listen to the cache in order to get a callbacks in case of internal puts for example
+      this.delegate.addCacheListener(this);
+      this.listeners = new CopyOnWriteArrayList<CacheListener<? super K, ? super V>>();
+      this.localCache = new ConcurrentHashMap<K, V>(concurrencyLevel, 0.75f, concurrencyLevel);      
+   }
+   
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#getName()
+    */
+   public String getName()
+   {
+      return delegate.getName();
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#setName(java.lang.String)
+    */
+   public void setName(String name)
+   {
+      delegate.setName(name);
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#getLabel()
+    */
+   public String getLabel()
+   {
+      return delegate.getLabel();
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#setLabel(java.lang.String)
+    */
+   public void setLabel(String s)
+   {
+      delegate.setLabel(s);
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#get(java.io.Serializable)
+    */
+   public V get(Serializable name)
+   {
+      HashCode<V> result = delegate.get(name);
+      return result == null ? null : localCache.get(name);
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#remove(java.io.Serializable)
+    */
+   public V remove(Serializable key) throws NullPointerException
+   {
+      V value = localCache.get(key);
+      delegate.remove(key);
+      return value;
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#put(java.io.Serializable, java.lang.Object)
+    */
+   public void put(K key, V value) throws NullPointerException
+   {
+      delegate.put(key, new HashCode<V>(value));
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#putMap(java.util.Map)
+    */
+   public void putMap(Map<? extends K, ? extends V> objs) throws NullPointerException, IllegalArgumentException
+   {
+      if (objs == null)
+      {
+         throw new NullPointerException("No null map accepted");
+      }
+      Map<K, HashCode<V>> map = new LinkedHashMap<K, HashCode<V>>();
+      for (Entry<? extends K, ? extends V> entry : objs.entrySet())
+      {
+         if (entry.getKey() == null)
+         {
+            throw new IllegalArgumentException("No null cache key accepted");
+         }
+         else if (entry.getValue() == null)
+         {
+            throw new IllegalArgumentException("No null cache value accepted");            
+         }
+         map.put(entry.getKey(), new HashCode<V>(entry.getValue()));
+      }
+      delegate.putMap(map);
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#clearCache()
+    */
+   public void clearCache()
+   {
+      delegate.clearCache();
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#select(org.exoplatform.services.cache.CachedObjectSelector)
+    */
+   public void select(CachedObjectSelector<? super K, ? super V> selector) throws Exception
+   {
+      if (selector == null)
+      {
+         throw new IllegalArgumentException("No null selector");
+      }
+      for (Entry<K, V> entry : localCache.entrySet())
+      {
+         final K key = entry.getKey();
+         final V value = entry.getValue();
+         ObjectCacheInfo<V> info = new ObjectCacheInfo<V>()
+         {
+            public V get()
+            {
+               return value;
+            }
+
+            public long getExpireTime()
+            {
+               // Cannot know: The expire time is managed by JBoss Cache itself
+               return -1;
+            }
+         };
+         if (selector.select(key, info))
+         {
+            selector.onSelect(this, key, info);
+         }
+      }
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#getCacheSize()
+    */
+   public int getCacheSize()
+   {
+      return localCache.size();
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#getMaxSize()
+    */
+   public int getMaxSize()
+   {
+      return delegate.getMaxSize();
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#setMaxSize(int)
+    */
+   public void setMaxSize(int max)
+   {
+      delegate.setMaxSize(max);
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#getLiveTime()
+    */
+   public long getLiveTime()
+   {
+      return delegate.getLiveTime();
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#setLiveTime(long)
+    */
+   public void setLiveTime(long period)
+   {
+      delegate.setLiveTime(period);
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#getCacheHit()
+    */
+   public int getCacheHit()
+   {
+      return delegate.getCacheHit();
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#getCacheMiss()
+    */
+   public int getCacheMiss()
+   {
+      return delegate.getCacheMiss();
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#getCachedObjects()
+    */
+   public List<? extends V> getCachedObjects()
+   {
+      return new ArrayList<V>(localCache.values());
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#removeCachedObjects()
+    */
+   public List<? extends V> removeCachedObjects()
+   {
+      final List<? extends V> list = getCachedObjects();
+      clearCache();
+      return list;
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#addCacheListener(org.exoplatform.services.cache.CacheListener)
+    */
+   public void addCacheListener(CacheListener<? super K, ? super V> listener) throws NullPointerException
+   {
+      if (listener == null)
+      {
+         throw new NullPointerException();
+      }
+      listeners.add(listener);
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#isLogEnabled()
+    */
+   public boolean isLogEnabled()
+   {
+      return delegate.isLogEnabled();
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.ExoCache#setLogEnabled(boolean)
+    */
+   public void setLogEnabled(boolean b)
+   {
+      delegate.setLogEnabled(b);
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.CacheListener#onExpire(org.exoplatform.services.cache.CacheListenerContext, java.io.Serializable, java.lang.Object)
+    */
+   public void onExpire(CacheListenerContext context, K key, HashCode<V> obj) throws Exception
+   {
+      V value = localCache.remove(key);
+      if (listeners.isEmpty())
+      {
+         return;
+      }
+      for (CacheListener<? super K, ? super V> listener : listeners)
+      {
+         try
+         {
+            listener.onExpire(context, key, value);
+         }
+         catch (Exception e)
+         {
+            if (LOG.isWarnEnabled())
+               LOG.warn("Cannot execute the CacheListener properly", e);
+         }
+      }
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.CacheListener#onRemove(org.exoplatform.services.cache.CacheListenerContext, java.io.Serializable, java.lang.Object)
+    */
+   public void onRemove(CacheListenerContext context, K key, HashCode<V> obj) throws Exception
+   {
+      V value = localCache.remove(key);
+      if (listeners.isEmpty())
+      {
+         return;
+      }
+      for (CacheListener<? super K, ? super V> listener : listeners)
+      {
+         try
+         {
+            listener.onRemove(context, key, value);
+         }
+         catch (Exception e)
+         {
+            if (LOG.isWarnEnabled())
+               LOG.warn("Cannot execute the CacheListener properly", e);
+         }
+      }
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.CacheListener#onPut(org.exoplatform.services.cache.CacheListenerContext, java.io.Serializable, java.lang.Object)
+    */
+   public void onPut(CacheListenerContext context, K key, HashCode<V> obj) throws Exception
+   {
+      V value = obj.getValue();
+      if (value != null)
+      {
+         // we assume that it is a local put since the value is inside the HashCode object
+         localCache.put(key, value);
+      }
+      else
+      {
+         // we assume that it is a remote put since the value is not inside the HashCode object
+         V currentValue = localCache.get(key);
+         if (currentValue != null && obj != null && currentValue.hashCode() == obj.hashCode())
+         {
+            // We assume that it is the same value so we don't change the value in the cache
+            value = currentValue;
+         }
+         else
+         {
+            // A new value has been added to the cache so we invalidate the local one
+            value = null;
+            localCache.remove(key);
+         }
+      }
+      if (listeners.isEmpty())
+      {
+         return;
+      }
+      for (CacheListener<? super K, ? super V> listener : listeners)
+         try
+         {
+            listener.onPut(context, key, value);
+         }
+         catch (Exception e)
+         {
+            if (LOG.isWarnEnabled())
+               LOG.warn("Cannot execute the CacheListener properly", e);
+         }
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.CacheListener#onGet(org.exoplatform.services.cache.CacheListenerContext, java.io.Serializable, java.lang.Object)
+    */
+   public void onGet(CacheListenerContext context, K key, HashCode<V> obj) throws Exception
+   {
+      if (listeners.isEmpty())
+      {
+         return;
+      }
+      V value = obj == null ? null : localCache.get(key);
+      for (CacheListener<? super K, ? super V> listener : listeners)
+         try
+         {
+            listener.onGet(context, key, value);
+         }
+         catch (Exception e)
+         {
+            if (LOG.isWarnEnabled())
+               LOG.warn("Cannot execute the CacheListener properly", e);
+         }
+   }
+
+   /**
+    * @see org.exoplatform.services.cache.CacheListener#onClearCache(org.exoplatform.services.cache.CacheListenerContext)
+    */
+   public void onClearCache(CacheListenerContext context) throws Exception
+   {
+      localCache.clear();
+      if (listeners.isEmpty())
+      {
+         return;
+      }
+      for (CacheListener<? super K, ? super V> listener : listeners)
+      {
+         try
+         {
+            listener.onClearCache(context);
+         }
+         catch (Exception e)
+         {
+            if (LOG.isWarnEnabled())
+               LOG.warn("Cannot execute the CacheListener properly", e);
+         }
+      }      
+   }
+      
+   /**
+    * We use this class to propagate the hash code of the value efficiently over the network
+    */
+   public static class HashCode<V> implements Externalizable
+   {
+      /**
+       * The hash code of the value
+       */
+      private int hashCode;
+      
+      /**
+       * The corresponding value
+       */
+      private V value;
+      
+      public HashCode() {}
+      
+      public HashCode(V value)
+      {
+         this.hashCode = value.hashCode();
+         this.value = value;
+      }
+      
+      /**
+       * @return the value
+       */
+      public V getValue()
+      {
+         return value;
+      }
+
+      /**
+       * @see java.io.Externalizable#writeExternal(java.io.ObjectOutput)
+       */
+      public void writeExternal(ObjectOutput out) throws IOException
+      {
+         out.writeInt(hashCode);
+      }
+
+      /**
+       * @see java.io.Externalizable#readExternal(java.io.ObjectInput)
+       */
+      public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
+      {
+         this.hashCode = in.readInt();
+      }
+
+      /**
+       * @see java.lang.Object#hashCode()
+       */
+      @Override
+      public int hashCode()
+      {
+         return hashCode;
+      }
+
+      /**
+       * @see java.lang.Object#equals(java.lang.Object)
+       */
+      @Override
+      public boolean equals(Object obj)
+      {
+         if (this == obj)
+            return true;
+         if (obj == null)
+            return false;
+         if (getClass() != obj.getClass())
+            return false;
+         @SuppressWarnings("rawtypes")
+         HashCode other = (HashCode)obj;
+         if (hashCode != other.hashCode)
+            return false;
+         if (value != null && other.value != null)
+         {
+            return value.equals(other.value);
+         }
+         return true;
+      }
+
+      /**
+       * @see java.lang.Object#toString()
+       */
+      @Override
+      public String toString()
+      {
+         return "HashCode [hashCode=" + hashCode + ", value=" + value + "]";
+      }
+   }   
+}

Modified: kernel/trunk/exo.kernel.component.ext.cache.impl.infinispan.v4/src/main/java/org/exoplatform/services/cache/impl/infinispan/AbstractExoCache.java
===================================================================
--- kernel/trunk/exo.kernel.component.ext.cache.impl.infinispan.v4/src/main/java/org/exoplatform/services/cache/impl/infinispan/AbstractExoCache.java	2011-07-01 14:27:05 UTC (rev 4586)
+++ kernel/trunk/exo.kernel.component.ext.cache.impl.infinispan.v4/src/main/java/org/exoplatform/services/cache/impl/infinispan/AbstractExoCache.java	2011-07-04 12:56:27 UTC (rev 4587)
@@ -31,8 +31,10 @@
 import org.infinispan.Cache;
 import org.infinispan.context.Flag;
 import org.infinispan.notifications.Listener;
+import org.infinispan.notifications.cachelistener.annotation.CacheEntryEvicted;
 import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified;
 import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved;
+import org.infinispan.notifications.cachelistener.event.CacheEntryEvictedEvent;
 import org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent;
 import org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent;
 
@@ -472,18 +474,21 @@
    @Listener
    public class CacheEventListener
    {
-// Infinispan triggers a CacheEntryEvictedEvent only at explicit eviction which is
-// not what we want here. So it will be considered as non supported      
-//      @CacheEntryEvicted
-//      public void cacheEntryEvicted(CacheEntryEvictedEvent evt)
-//      {
-//         if (evt.isPre())
-//         {
-//            final K key = (K)evt.getKey();
-//            final V value = cache.withFlags(Flag.SKIP_LOCKING).get(key);
-//            onExpire(key, value);
-//         }
-//      }
+      /**
+       * Warning Infinispan triggers a <code>CacheEntryEvictedEvent</code> only at explicit eviction
+       * that is done lazily which is not exactly what we expect, we still use it to be 
+       * able to use it with <code>avoidValueReplication</code> set to <code>true</code>.
+       */
+      @CacheEntryEvicted
+      public void cacheEntryEvicted(CacheEntryEvictedEvent evt)
+      {
+         if (evt.isPre())
+         {
+            final K key = (K)evt.getKey();
+            final V value = cache.withFlags(Flag.SKIP_LOCKING).get(key);
+            onExpire(key, value);
+         }
+      }
 
       @CacheEntryRemoved
       public void cacheEntryRemoved(CacheEntryRemovedEvent evt)

Modified: kernel/trunk/exo.kernel.component.ext.cache.impl.jboss.v3/src/test/java/org/exoplatform/services/cache/impl/jboss/TestAbstractExoCache.java
===================================================================
--- kernel/trunk/exo.kernel.component.ext.cache.impl.jboss.v3/src/test/java/org/exoplatform/services/cache/impl/jboss/TestAbstractExoCache.java	2011-07-01 14:27:05 UTC (rev 4586)
+++ kernel/trunk/exo.kernel.component.ext.cache.impl.jboss.v3/src/test/java/org/exoplatform/services/cache/impl/jboss/TestAbstractExoCache.java	2011-07-04 12:56:27 UTC (rev 4587)
@@ -31,6 +31,7 @@
 import org.exoplatform.services.cache.ExoCacheFactory;
 import org.exoplatform.services.cache.ExoCacheInitException;
 import org.exoplatform.services.cache.ObjectCacheInfo;
+import org.exoplatform.services.cache.impl.InvalidationExoCache;
 import org.exoplatform.services.cache.impl.jboss.lru.LRUExoCacheCreator;
 import org.exoplatform.test.BasicTestCase;
 
@@ -41,8 +42,8 @@
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Set;
-import java.util.Map.Entry;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicInteger;
 
@@ -270,17 +271,17 @@
       config2.setLiveTime(1);
       config2.setImplementation("LRU");
       config2.setDistributed(true);
-      AbstractExoCache<Serializable, Object> cache1 =
-         (AbstractExoCache<Serializable, Object>)getExoCacheFactoryInstance().createCache(config);
-      MyCacheListener listener1 = new MyCacheListener();
+      AbstractExoCache<Serializable, String> cache1 =
+         (AbstractExoCache<Serializable, String>)getExoCacheFactoryInstance().createCache(config);
+      MyCacheListener<String> listener1 = new MyCacheListener<String>();
       cache1.addCacheListener(listener1);
-      AbstractExoCache<Serializable, Object> cache2 =
-         (AbstractExoCache<Serializable, Object>)getExoCacheFactoryInstance().createCache(config);
-      MyCacheListener listener2 = new MyCacheListener();
+      AbstractExoCache<Serializable, String> cache2 =
+         (AbstractExoCache<Serializable, String>)getExoCacheFactoryInstance().createCache(config);
+      MyCacheListener<String> listener2 = new MyCacheListener<String>();
       cache2.addCacheListener(listener2);
-      AbstractExoCache<Serializable, Object> cache3 =
-         (AbstractExoCache<Serializable, Object>)getExoCacheFactoryInstance().createCache(config2);
-      MyCacheListener listener3 = new MyCacheListener();
+      AbstractExoCache<Serializable, String> cache3 =
+         (AbstractExoCache<Serializable, String>)getExoCacheFactoryInstance().createCache(config2);
+      MyCacheListener<String> listener3 = new MyCacheListener<String>();
       cache3.addCacheListener(listener3);
       try
       {
@@ -358,7 +359,7 @@
          assertEquals(1, listener1.clearCache);
          assertEquals(0, listener2.clearCache);
          assertEquals(0, listener3.clearCache);
-         Map<Serializable, Object> values = new HashMap<Serializable, Object>();
+         Map<Serializable, String> values = new HashMap<Serializable, String>();
          values.put(new MyKey("a"), "a");
          values.put(new MyKey("b"), "b");
          cache1.putMap(values);
@@ -379,22 +380,22 @@
          assertEquals(1, listener1.clearCache);
          assertEquals(0, listener2.clearCache);
          assertEquals(0, listener3.clearCache);
-         values = new HashMap<Serializable, Object>()
+         values = new HashMap<Serializable, String>()
          {
             private static final long serialVersionUID = 1L;
 
-            public Set<Entry<Serializable, Object>> entrySet()
+            public Set<Entry<Serializable, String>> entrySet()
             {
-               Set<Entry<Serializable, Object>> set = new LinkedHashSet<Entry<Serializable, Object>>(super.entrySet());
-               set.add(new Entry<Serializable, Object>()
+               Set<Entry<Serializable, String>> set = new LinkedHashSet<Entry<Serializable, String>>(super.entrySet());
+               set.add(new Entry<Serializable, String>()
                {
 
-                  public Object setValue(Object paramV)
+                  public String setValue(String paramV)
                   {
                      return null;
                   }
 
-                  public Object getValue()
+                  public String getValue()
                   {
                      throw new RuntimeException("An exception");
                   }
@@ -456,6 +457,247 @@
       }
    }
 
+   @SuppressWarnings("unchecked")
+   public void testDistributedCacheWithNSValues() throws Exception
+   {
+      System.out
+         .println("WARNING: For Linux distributions the following JVM parameter must be set to true, java.net.preferIPv4Stack = "
+            + System.getProperty("java.net.preferIPv4Stack"));
+      ExoCacheConfig config = new ExoCacheConfig();
+      config.setName("MyCacheDistributedWithNSValues");
+      config.setMaxSize(5);
+      config.setLiveTime(1);
+      config.setImplementation("LRU");
+      config.setDistributed(true);
+      config.setAvoidValueReplication(true);
+      ExoCacheConfig config2 = new ExoCacheConfig();
+      config2.setName("MyCacheDistributedWithNSValues2");
+      config2.setMaxSize(5);
+      config2.setLiveTime(1);
+      config2.setImplementation("LRU");
+      config2.setDistributed(true);
+      config2.setAvoidValueReplication(true);
+      AbstractExoCache<Serializable, MyNonSerializableValue> acache1 =
+         (AbstractExoCache<Serializable, MyNonSerializableValue>)getExoCacheFactoryInstance().createCache(config);
+      MyCacheListener<MyNonSerializableValue> listener1 = new MyCacheListener<MyNonSerializableValue>();
+      ExoCache<Serializable, MyNonSerializableValue> cache1 = new InvalidationExoCache<Serializable, MyNonSerializableValue>(acache1);
+      cache1.addCacheListener(listener1);
+      AbstractExoCache<Serializable, MyNonSerializableValue> acache2 =
+         (AbstractExoCache<Serializable, MyNonSerializableValue>)getExoCacheFactoryInstance().createCache(config);
+      MyCacheListener<MyNonSerializableValue> listener2 = new MyCacheListener<MyNonSerializableValue>();
+      ExoCache<Serializable, MyNonSerializableValue> cache2 = new InvalidationExoCache<Serializable, MyNonSerializableValue>(acache2);
+      cache2.addCacheListener(listener2);
+      AbstractExoCache<Serializable, MyNonSerializableValue> acache3 =
+         (AbstractExoCache<Serializable, MyNonSerializableValue>)getExoCacheFactoryInstance().createCache(config2);
+      MyCacheListener<MyNonSerializableValue> listener3 = new MyCacheListener<MyNonSerializableValue>();
+      ExoCache<Serializable, MyNonSerializableValue> cache3 = new InvalidationExoCache<Serializable, MyNonSerializableValue>(acache3);
+      cache3.addCacheListener(listener3);
+      try
+      {
+         cache1.put(new MyKey("a"), new MyNonSerializableValue("b"));
+         assertEquals(1, cache1.getCacheSize());
+         assertNull(cache2.get(new MyKey("a")));
+         assertEquals(0, cache2.getCacheSize());
+         assertEquals(0, cache3.getCacheSize());
+         assertEquals(1, listener1.put);
+         assertEquals(1, listener2.put);
+         assertEquals(0, listener3.put);
+         assertEquals(0, listener1.get);
+         assertEquals(1, listener2.get);
+         assertEquals(0, listener3.get);
+         cache2.put(new MyKey("b"), new MyNonSerializableValue("c"));
+         assertEquals(1, cache1.getCacheSize());
+         assertEquals(1, cache2.getCacheSize());
+         assertNull(cache1.get(new MyKey("b")));
+         assertEquals(0, cache3.getCacheSize());
+         assertEquals(2, listener1.put);
+         assertEquals(2, listener2.put);
+         assertEquals(0, listener3.put);
+         assertEquals(1, listener1.get);
+         assertEquals(1, listener2.get);
+         assertEquals(0, listener3.get);
+         cache3.put(new MyKey("c"), new MyNonSerializableValue("d"));
+         assertEquals(1, cache1.getCacheSize());
+         assertEquals(1, cache2.getCacheSize());
+         assertEquals(1, cache3.getCacheSize());
+         assertEquals(new MyNonSerializableValue("d"), cache3.get(new MyKey("c")));
+         assertEquals(2, listener1.put);
+         assertEquals(2, listener2.put);
+         assertEquals(1, listener3.put);
+         assertEquals(1, listener1.get);
+         assertEquals(1, listener2.get);
+         assertEquals(1, listener3.get);
+         cache2.put(new MyKey("a"), new MyNonSerializableValue("a"));
+         assertEquals(0, cache1.getCacheSize());
+         assertEquals(2, cache2.getCacheSize());
+         assertNull(cache1.get(new MyKey("a")));
+         assertEquals(3, listener1.put);
+         assertEquals(3, listener2.put);
+         assertEquals(1, listener3.put);
+         assertEquals(2, listener1.get);
+         assertEquals(1, listener2.get);
+         assertEquals(1, listener3.get);
+         cache2.remove(new MyKey("a"));
+         assertEquals(0, cache1.getCacheSize());
+         assertEquals(1, cache2.getCacheSize());
+         assertEquals(3, listener1.put);
+         assertEquals(3, listener2.put);
+         assertEquals(1, listener3.put);
+         assertEquals(2, listener1.get);
+         assertEquals(1, listener2.get);
+         assertEquals(1, listener3.get);
+         assertEquals(1, listener1.remove);
+         assertEquals(1, listener2.remove);
+         assertEquals(0, listener3.remove);
+         cache1.put(new MyKey("c"), new MyNonSerializableValue("c"));
+         cache1.clearCache();
+         assertEquals(0, cache1.getCacheSize());
+         assertNull(cache1.get(new MyKey("b")));
+         assertEquals(new MyNonSerializableValue("c"), cache2.get(new MyKey("b")));
+         assertNull(cache2.get(new MyKey("c")));
+         assertEquals(1, cache2.getCacheSize());
+         assertEquals(4, listener1.put);
+         assertEquals(4, listener2.put);
+         assertEquals(1, listener3.put);
+         assertEquals(3, listener1.get);
+         assertEquals(3, listener2.get);
+         assertEquals(1, listener3.get);
+         assertEquals(1, listener1.remove);
+         assertEquals(1, listener2.remove);
+         assertEquals(0, listener3.remove);
+         assertEquals(1, listener1.clearCache);
+         assertEquals(0, listener2.clearCache);
+         assertEquals(0, listener3.clearCache);
+         Map<Serializable, MyNonSerializableValue> values = new HashMap<Serializable, MyNonSerializableValue>();
+         values.put(new MyKey("a"), new MyNonSerializableValue("a"));
+         values.put(new MyKey("b"), new MyNonSerializableValue("b"));
+         cache1.putMap(values);
+         assertEquals(2, cache1.getCacheSize());
+         Thread.sleep(40);
+         assertNull(cache2.get(new MyKey("a")));
+         assertNull(cache2.get(new MyKey("b")));
+         assertEquals(0, cache2.getCacheSize());
+         assertEquals(6, listener1.put);
+         assertEquals(6, listener2.put);
+         assertEquals(1, listener3.put);
+         assertEquals(3, listener1.get);
+         assertEquals(5, listener2.get);
+         assertEquals(1, listener3.get);
+         assertEquals(1, listener1.remove);
+         assertEquals(1, listener2.remove);
+         assertEquals(0, listener3.remove);
+         assertEquals(1, listener1.clearCache);
+         assertEquals(0, listener2.clearCache);
+         assertEquals(0, listener3.clearCache);
+         values = new HashMap<Serializable, MyNonSerializableValue>()
+         {
+            private static final long serialVersionUID = 1L;
+
+            public Set<Entry<Serializable, MyNonSerializableValue>> entrySet()
+            {
+               Set<Entry<Serializable, MyNonSerializableValue>> set = new LinkedHashSet<Entry<Serializable, MyNonSerializableValue>>(super.entrySet());
+               set.add(new Entry<Serializable, MyNonSerializableValue>()
+               {
+
+                  public MyNonSerializableValue setValue(MyNonSerializableValue paramV)
+                  {
+                     return null;
+                  }
+
+                  public MyNonSerializableValue getValue()
+                  {
+                     throw new RuntimeException("An exception");
+                  }
+
+                  public Serializable getKey()
+                  {
+                     return "c";
+                  }
+               });
+               return set;
+            }
+         };
+         values.put(new MyKey("e"), new MyNonSerializableValue("e"));
+         values.put(new MyKey("d"), new MyNonSerializableValue("d"));
+         try
+         {
+            cache1.putMap(values);
+         }
+         catch (Exception e)
+         {
+            // ignore me
+         }
+         assertEquals(2, cache1.getCacheSize());
+         assertEquals(0, cache2.getCacheSize());
+         assertEquals(1, cache3.getCacheSize());
+         assertEquals(6, listener1.put);
+         assertEquals(6, listener2.put);
+         assertEquals(1, listener3.put);
+         assertEquals(3, listener1.get);
+         assertEquals(5, listener2.get);
+         assertEquals(1, listener3.get);
+         assertEquals(1, listener1.remove);
+         assertEquals(1, listener2.remove);
+         assertEquals(0, listener3.remove);
+         assertEquals(1, listener1.clearCache);
+         assertEquals(0, listener2.clearCache);
+         assertEquals(0, listener3.clearCache);
+         assertEquals(0, listener1.expire);
+         assertEquals(0, listener2.expire);
+         assertEquals(0, listener3.expire);
+         Thread.sleep(1600);
+         assertEquals(0, cache1.getCacheSize());
+         assertEquals(0, cache2.getCacheSize());
+         assertEquals(0, cache3.getCacheSize());
+         assertEquals(6, listener1.put);
+         assertEquals(6, listener2.put);
+         assertEquals(1, listener3.put);
+         assertEquals(3, listener1.get);
+         assertEquals(5, listener2.get);
+         assertEquals(1, listener3.get);
+         assertEquals(1, listener1.remove);
+         assertEquals(1, listener2.remove);
+         assertEquals(0, listener3.remove);
+         assertEquals(1, listener1.clearCache);
+         assertEquals(0, listener2.clearCache);
+         assertEquals(0, listener3.clearCache);
+         assertEquals(2, listener1.expire);
+         assertEquals(3, listener2.expire);
+         assertEquals(1, listener3.expire);
+         cache1.put(new MyKey("a"), new MyNonSerializableValue("b"));
+         assertNotNull(cache1.get(new MyKey("a")));
+         assertNull(cache2.get(new MyKey("a")));
+         cache2.put(new MyKey("a"), new MyNonSerializableValue("c"));
+         assertNotNull(cache2.get(new MyKey("a")));
+         assertNull(cache1.get(new MyKey("a")));
+         cache1.put(new MyKey("a"), new MyNonSerializableValue("c"));
+         assertEquals(new MyNonSerializableValue("c"), cache2.get(new MyKey("a")));
+         assertEquals(new MyNonSerializableValue("c"), cache1.get(new MyKey("a")));
+         assertEquals(9, listener1.put);
+         assertEquals(9, listener2.put);
+         assertEquals(1, listener3.put);
+         assertEquals(6, listener1.get);
+         assertEquals(8, listener2.get);
+         assertEquals(1, listener3.get);
+         assertEquals(1, listener1.remove);
+         assertEquals(1, listener2.remove);
+         assertEquals(0, listener3.remove);
+         assertEquals(1, listener1.clearCache);
+         assertEquals(0, listener2.clearCache);
+         assertEquals(0, listener3.clearCache);
+         assertEquals(2, listener1.expire);
+         assertEquals(3, listener2.expire);
+         assertEquals(1, listener3.expire);
+         
+      }
+      finally
+      {
+         acache1.cache.stop();
+         acache2.cache.stop();
+         acache3.cache.stop();
+      }
+   }
+
    public void testMultiThreading() throws Exception
    {
       long time = System.currentTimeMillis();
@@ -645,7 +887,7 @@
       System.out.println("Total Time = " + (System.currentTimeMillis() - time));
    }
 
-   public static class MyCacheListener implements CacheListener<Serializable, Object>
+   public static class MyCacheListener<T> implements CacheListener<Serializable, T>
    {
 
       public int clearCache;
@@ -658,64 +900,67 @@
 
       public int remove;
 
-      public void onClearCache(ExoCache<Serializable, Object> cache) throws Exception
+      public void onClearCache(CacheListenerContext context) throws Exception
       {
          clearCache++;
       }
 
-      public void onExpire(ExoCache<Serializable, Object> cache, Serializable key, Object obj) throws Exception
+      public void onExpire(CacheListenerContext context, Serializable key, T obj) throws Exception
       {
          expire++;
       }
 
-      public void onGet(ExoCache<Serializable, Object> cache, Serializable key, Object obj) throws Exception
+      public void onGet(CacheListenerContext context, Serializable key, T obj) throws Exception
       {
          get++;
       }
 
-      public void onPut(ExoCache<Serializable, Object> cache, Serializable key, Object obj) throws Exception
+      public void onPut(CacheListenerContext context, Serializable key, T obj) throws Exception
       {
          put++;
       }
 
-      public void onRemove(ExoCache<Serializable, Object> cache, Serializable key, Object obj) throws Exception
+      public void onRemove(CacheListenerContext context, Serializable key, T obj) throws Exception
       {
          remove++;
       }
+   }
 
-      public void onClearCache(CacheListenerContext context) throws Exception
-      {
-         clearCache++;
-      }
+   public static class MyKey implements Serializable
+   {
+      private static final long serialVersionUID = 1L;
 
-      public void onExpire(CacheListenerContext context, Serializable key, Object obj) throws Exception
+      public String value;
+
+      public MyKey(String value)
       {
-         expire++;
+         this.value = value;
       }
 
-      public void onGet(CacheListenerContext context, Serializable key, Object obj) throws Exception
+      @Override
+      public boolean equals(Object paramObject)
       {
-         get++;
+         return paramObject instanceof MyKey && ((MyKey)paramObject).value.endsWith(value);
       }
 
-      public void onPut(CacheListenerContext context, Serializable key, Object obj) throws Exception
+      @Override
+      public int hashCode()
       {
-         put++;
+         return value.hashCode();
       }
 
-      public void onRemove(CacheListenerContext context, Serializable key, Object obj) throws Exception
+      @Override
+      public String toString()
       {
-         remove++;
+         return value;
       }
    }
-
-   public static class MyKey implements Serializable
+   
+   public static class MyNonSerializableValue
    {
-      private static final long serialVersionUID = 1L;
-
       public String value;
 
-      public MyKey(String value)
+      public MyNonSerializableValue(String value)
       {
          this.value = value;
       }
@@ -723,7 +968,7 @@
       @Override
       public boolean equals(Object paramObject)
       {
-         return paramObject instanceof MyKey && ((MyKey)paramObject).value.endsWith(value);
+         return paramObject instanceof MyNonSerializableValue && ((MyNonSerializableValue)paramObject).value.endsWith(value);
       }
 
       @Override



More information about the exo-jcr-commits mailing list