[exo-jcr-commits] exo-jcr SVN: r4588 - 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 13:12:39 EDT 2011


Author: nfilotto
Date: 2011-07-04 13:12:39 -0400 (Mon, 04 Jul 2011)
New Revision: 4588

Added:
   kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/future/
   kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/future/FutureCache.java
   kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/future/FutureExoCache.java
   kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/future/Loader.java
   kernel/trunk/exo.kernel.component.cache/src/test/java/org/exoplatform/services/cache/future/
   kernel/trunk/exo.kernel.component.cache/src/test/java/org/exoplatform/services/cache/future/ConcurrentGetWhenPutTestCase.java
   kernel/trunk/exo.kernel.component.cache/src/test/java/org/exoplatform/services/cache/future/FutureMap.java
   kernel/trunk/exo.kernel.component.cache/src/test/java/org/exoplatform/services/cache/future/GetTestCase.java
   kernel/trunk/exo.kernel.component.cache/src/test/java/org/exoplatform/services/cache/future/StringLoader.java
Modified:
   jcr/trunk/exo.jcr.docs/exo.jcr.docs.developer/en/src/main/docbook/en-US/modules/kernel/cache.xml
Log:
EXOJCR-1419: Move FutureExoCache from GateIn commons to eXo kernel

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-04 12:56:27 UTC (rev 4587)
+++ jcr/trunk/exo.jcr.docs/exo.jcr.docs.developer/en/src/main/docbook/en-US/modules/kernel/cache.xml	2011-07-04 17:12:39 UTC (rev 4588)
@@ -133,7 +133,7 @@
             <entry>replicated</entry>
 
             <entry>Indicates if the cache is replicated. This field is
-            optional. </entry>
+            optional.</entry>
           </row>
 
           <row>
@@ -231,6 +231,36 @@
         the infinite loop described above will still be effective.</para>
       </note>
     </section>
+
+    <section>
+      <title>FutureExoCache</title>
+
+      <para>If the data that you want to store into your eXo Cache instance
+      and/or you would like to prevent multiple concurrent loading of the same
+      data at the same time, you can use
+      <emphasis>org.exoplatform.services.cache.future.FutureExoCache</emphasis>
+      on top of your eXo Cache instance in order to delegate the loading of
+      your data to a loader that will be called only once whatever the total
+      amount of concurrent thread looking for it. See below an example of how
+      the FutureExoCache can be used:</para>
+
+      <programlisting language="java">import org.exoplatform.services.cache.future.Loader;
+import org.exoplatform.services.cache.future.FutureExoCache;
+...
+   // Define first your loader and choose properly your context object in order
+   // to be able to reuse the same loader for different FutureExoCache instances
+   Loader&lt;String, String, String&gt; loader = new Loader&lt;String, String, String&gt;()
+   { 
+      public String retrieve(String context, String key) throws Exception
+      {
+         return "Value loaded thanks to the key = '" + key + "' and the context = '" + context + "'";
+      }
+   };
+   // Create your FutureExoCache from your eXo cache instance and your loader
+   FutureExoCache&lt;String, String, String&gt; myFutureExoCache = new FutureExoCache&lt;String, String, String&gt;(loader, myExoCache);
+   // Get your data from your future cache instance
+   System.out.println(myFutureExoCache.get("my context", "foo"));</programlisting>
+    </section>
   </section>
 
   <section>

Added: kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/future/FutureCache.java
===================================================================
--- kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/future/FutureCache.java	                        (rev 0)
+++ kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/future/FutureCache.java	2011-07-04 17:12:39 UTC (rev 4588)
@@ -0,0 +1,148 @@
+/*
+ * 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.future;
+
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
+/**
+ * A future cache that prevents the loading of the same resource twice. This should be used when the resource
+ * to load is very expensive or cannot be concurrently retrieved (like a classloading). 
+ *
+ * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
+ * @version $Revision$
+ * @param <K> the key type parameter
+ * @param <V> the value type parameter
+ * @param <C> the context type parameter
+ */
+public abstract class FutureCache<K, V, C>
+{
+
+   /** . */
+   private final Loader<K, V, C> loader;
+
+   /** . */
+   private final ConcurrentMap<K, FutureTask<V>> futureEntries;
+
+   /** . */
+   private static final Log log = ExoLogger.getLogger("exo.kernel.component.cache.FutureCache");
+
+
+   public FutureCache(Loader<K, V, C> loader)
+   {
+      this.loader = loader;
+      this.futureEntries = new ConcurrentHashMap<K, FutureTask<V>>();
+   }
+
+   protected abstract V get(K key);
+
+   protected abstract void put(K key, V value);
+
+   /**
+    * Perform a cache lookup for the specified key within the specified context.
+    * When the value cannot be loaded (because it does not exist or it failed or anything else that
+    * does not come to my mind), the value null is returned.
+    *
+    * @param context the context in which the resource is accessed
+    * @param key the key identifying the resource
+    * @return the value
+    */
+   public final V get(final C context, final K key)
+   {
+      // First we try a simple cache get
+      V value = get(key);
+
+      // If it does not succeed then we go through a process that will avoid to load
+      // the same resource concurrently
+      if (value == null)
+      {
+         // Create our future
+         FutureTask<V> future = new FutureTask<V>(new Callable<V>()
+         {
+            public V call() throws Exception
+            {
+               // Retrieve the value from the loader
+               V value = loader.retrieve(context, key);
+
+               //
+               if (value != null)
+               {
+                  // Cache it, it is made available to other threads (unless someone removes it)
+                  put(key, value);
+
+                  // Return value
+                  return value;
+               }
+               else
+               {
+                  return null;
+               }
+            }
+         });
+
+         // This boolean means we inserted in the local
+         boolean inserted = true;
+
+         //
+         try
+         {
+            FutureTask<V> phantom = futureEntries.putIfAbsent(key, future);
+
+            // Use the value that could have been inserted by another thread
+            if (phantom != null)
+            {
+               future = phantom;
+               inserted = false;
+            }
+            else
+            {
+               future.run();
+            }
+
+            // Returns the value
+            value = future.get();
+         }
+         catch (ExecutionException e)
+         {
+            log.error("Computing of resource " + key + " threw an exception", e.getCause());
+         }
+         catch (Exception e)
+         {
+            log.error("Retrieval of resource " + key + " threw an exception", e);
+         }
+         finally
+         {
+            // Clean up the per key map but only if our insertion succeeded and with our future
+            if (inserted)
+            {
+               futureEntries.remove(key, future);
+            }
+         }
+      }
+
+      //
+      return value;
+   }
+}

Added: kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/future/FutureExoCache.java
===================================================================
--- kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/future/FutureExoCache.java	                        (rev 0)
+++ kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/future/FutureExoCache.java	2011-07-04 17:12:39 UTC (rev 4588)
@@ -0,0 +1,55 @@
+/*
+ * 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.future;
+
+import org.exoplatform.services.cache.ExoCache;
+
+import java.io.Serializable;
+
+/**
+ * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
+ * @version $Revision$
+ */
+public class FutureExoCache<K extends Serializable, V, C> extends FutureCache<K, V, C>
+{
+
+   /** . */
+   private final ExoCache<K, V> cache;
+
+   public FutureExoCache(Loader<K, V, C> loader, ExoCache<K, V> cache)
+   {
+      super(loader);
+
+      //
+      this.cache = cache;
+   }
+
+   @Override
+   protected V get(K key)
+   {
+      return cache.get(key);
+   }
+
+   @Override
+   protected void put(K key, V entry)
+   {
+      cache.put(key, entry);
+   }
+}
+

Added: kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/future/Loader.java
===================================================================
--- kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/future/Loader.java	                        (rev 0)
+++ kernel/trunk/exo.kernel.component.cache/src/main/java/org/exoplatform/services/cache/future/Loader.java	2011-07-04 17:12:39 UTC (rev 4588)
@@ -0,0 +1,44 @@
+/*
+ * 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.future;
+
+/**
+ * The loader interface is used by the future cache to retrieves the value from the key when it does not exist.
+ *
+ * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
+ * @version $Revision$
+ * @param <K> the key type parameter
+ * @param <V> the value type parameter
+ * @param <C> the context type parameter
+ */
+public interface Loader<K, V, C>
+{
+
+   /**
+    * Retrieves the value from the key within the specified context. If the resource is not found then the value
+    * null must be returned.
+    *
+    * @param context the context
+    * @param key the key
+    * @return the value
+    * @throws Exception any exception that would prevent the value to be loaded
+    */
+   V retrieve(C context, K key) throws Exception;
+
+}

Added: kernel/trunk/exo.kernel.component.cache/src/test/java/org/exoplatform/services/cache/future/ConcurrentGetWhenPutTestCase.java
===================================================================
--- kernel/trunk/exo.kernel.component.cache/src/test/java/org/exoplatform/services/cache/future/ConcurrentGetWhenPutTestCase.java	                        (rev 0)
+++ kernel/trunk/exo.kernel.component.cache/src/test/java/org/exoplatform/services/cache/future/ConcurrentGetWhenPutTestCase.java	2011-07-04 17:12:39 UTC (rev 4588)
@@ -0,0 +1,177 @@
+/*
+ * 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.future;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+/**
+ * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
+ * @version $Revision$
+ */
+public class ConcurrentGetWhenPutTestCase extends TestCase
+{
+
+   /** . */
+   private AssertionFailedError failure;
+
+   /** . */
+   private List<String> events = Collections.synchronizedList(new LinkedList<String>());
+
+   FutureCache<String, String, Callable<String>> futureCache = new FutureCache<String, String, Callable<String>>(new StringLoader()) {
+
+
+      @Override
+      protected String get(String key)
+      {
+         if (key == key1)
+         {
+            if (Thread.currentThread() != thread1)
+            {
+               failure = new AssertionFailedError();
+            }
+            events.add("get/key1");
+         }
+         else if (key == key2)
+         {
+            if (Thread.currentThread() != thread2)
+            {
+               failure = new AssertionFailedError();
+            }
+            events.add("get/key2");
+         }
+         else
+         {
+            failure = new AssertionFailedError();
+         }
+         return null;
+      }
+
+      @Override
+      protected void put(String key, String value)
+      {
+         if (key == key1)
+         {
+            if (Thread.currentThread() == thread1)
+            {
+               events.add("begin_put/key1/" + value);
+
+               //
+               thread2.start();
+
+               //
+               while (thread2.getState() != Thread.State.WAITING)
+               {
+                  // Wait until thread 2 is blocked
+               }
+
+               //
+               events.add("end_put/key1");
+            }
+            else
+            {
+               failure = new AssertionFailedError();
+            }
+         }
+         else
+         {
+            failure = new AssertionFailedError();
+         }
+      }
+   };
+
+   /** . */
+   private final String key1 = new String("foo");
+
+   /** . */
+   private final String key2 = new String("foo");
+
+   Thread thread1 = new Thread()
+   {
+      @Override
+      public void run()
+      {
+         String v = futureCache.get(new Callable<String>()
+         {
+            public String call() throws Exception
+            {
+               events.add("call/key1");
+               return "foo_value_1";
+            }
+         }, key1);
+         events.add("retrieved/key1/" + v);
+      }
+   };
+
+   Thread thread2 = new Thread()
+   {
+      @Override
+      public void run()
+      {
+         String v = futureCache.get(new Callable<String>()
+         {
+            public String call() throws Exception
+            {
+               failure = new AssertionFailedError();
+               return "foo_value_2";
+            }
+         }, key2);
+         events.add("retrieved/key2/" + v);
+      }
+   };
+
+   public void testMain() throws Exception
+   {
+      thread1.start();
+
+      //
+      thread1.join();
+      thread2.join();
+
+      //
+      if (failure != null)
+      {
+         throw failure;
+      }
+
+      //
+      List<String> expectedEvents = Arrays.asList(
+         "get/key1",
+         "call/key1",
+         "begin_put/key1/foo_value_1",
+         "get/key2",
+         "end_put/key1"
+      );
+
+      //
+      assertEquals(expectedEvents, events.subList(0, expectedEvents.size()));
+
+      //
+      Set<String> expectedEndEvents = new HashSet<String>(Arrays.asList("retrieved/key1/foo_value_1", "retrieved/key2/foo_value_1"));
+      assertEquals(expectedEndEvents, new HashSet<String>(events.subList(expectedEvents.size(), events.size())));
+   }
+}
\ No newline at end of file

Added: kernel/trunk/exo.kernel.component.cache/src/test/java/org/exoplatform/services/cache/future/FutureMap.java
===================================================================
--- kernel/trunk/exo.kernel.component.cache/src/test/java/org/exoplatform/services/cache/future/FutureMap.java	                        (rev 0)
+++ kernel/trunk/exo.kernel.component.cache/src/test/java/org/exoplatform/services/cache/future/FutureMap.java	2011-07-04 17:12:39 UTC (rev 4588)
@@ -0,0 +1,54 @@
+/*
+ * 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.future;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
+ * @version $Revision$
+ */
+public class FutureMap<C> extends FutureCache<String, String, C>
+{
+
+   /** . */
+   final Map<String, String> data;
+
+   public FutureMap(Loader<String, String, C> loader)
+   {
+      super(loader);
+
+      //
+      this.data = Collections.synchronizedMap(new HashMap<String, String>());
+   }
+
+   @Override
+   protected String get(String key)
+   {
+      return data.get(key);
+   }
+
+   @Override
+   protected void put(String key, String value)
+   {
+      data.put(key, value);
+   }
+}

Added: kernel/trunk/exo.kernel.component.cache/src/test/java/org/exoplatform/services/cache/future/GetTestCase.java
===================================================================
--- kernel/trunk/exo.kernel.component.cache/src/test/java/org/exoplatform/services/cache/future/GetTestCase.java	                        (rev 0)
+++ kernel/trunk/exo.kernel.component.cache/src/test/java/org/exoplatform/services/cache/future/GetTestCase.java	2011-07-04 17:12:39 UTC (rev 4588)
@@ -0,0 +1,70 @@
+/*
+ * 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.future;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import java.util.concurrent.Callable;
+
+/**
+ * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
+ * @version $Revision$
+ */
+public class GetTestCase extends TestCase
+{
+   public void testGet()
+   {
+      FutureMap<Callable<String>> futureCache = new FutureMap<Callable<String>>(new StringLoader());
+      Assert.assertEquals("foo_value", futureCache.get(new Callable<String>()
+      {
+         public String call() throws Exception
+         {
+            return "foo_value";
+         }
+      }, "foo"));
+      Assert.assertEquals("foo_value", futureCache.data.get("foo"));
+   }
+
+   public void testNullValue()
+   {
+      FutureMap<Callable<String>> futureCache = new FutureMap<Callable<String>>(new StringLoader());
+      Assert.assertEquals(null, futureCache.get(new Callable<String>()
+      {
+         public String call() throws Exception
+         {
+            return null;
+         }
+      }, "foo"));
+      Assert.assertFalse(futureCache.data.containsKey("foo"));
+   }
+
+   public void testThrowException()
+   {
+      FutureMap<Callable<String>> futureCache = new FutureMap<Callable<String>>(new StringLoader());
+      Assert.assertEquals(null, futureCache.get(new Callable<String>()
+      {
+         public String call() throws Exception
+         {
+            throw new Exception("DON'T FREAK OUT");
+         }
+      }, "foo"));
+      Assert.assertFalse(futureCache.data.containsKey("foo"));
+   }
+}

Added: kernel/trunk/exo.kernel.component.cache/src/test/java/org/exoplatform/services/cache/future/StringLoader.java
===================================================================
--- kernel/trunk/exo.kernel.component.cache/src/test/java/org/exoplatform/services/cache/future/StringLoader.java	                        (rev 0)
+++ kernel/trunk/exo.kernel.component.cache/src/test/java/org/exoplatform/services/cache/future/StringLoader.java	2011-07-04 17:12:39 UTC (rev 4588)
@@ -0,0 +1,33 @@
+/*
+ * 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.future;
+
+import java.util.concurrent.Callable;
+
+/**
+ * @author <a href="mailto:julien.viet at exoplatform.com">Julien Viet</a>
+ * @version $Revision$
+ */
+public class StringLoader implements Loader<String, String, Callable<String>>
+{
+   public String retrieve(Callable<String> context, String key) throws Exception
+   {
+      return context.call();
+   }
+}



More information about the exo-jcr-commits mailing list