(no commit message)
authorerik <erik@77661180-640e-0410-b3a8-9f9b13e6d0e0>
Tue, 22 Jun 2010 19:35:52 +0000 (19:35 +0000)
committererik <erik@77661180-640e-0410-b3a8-9f9b13e6d0e0>
Tue, 22 Jun 2010 19:35:52 +0000 (19:35 +0000)
support/general/src/main/java/org/wamblee/cache/Cache.java
support/general/src/main/java/org/wamblee/cache/CachedObject.java
support/general/src/main/java/org/wamblee/cache/EhCache.java
support/general/src/main/java/org/wamblee/cache/package-info.java [new file with mode: 0644]
support/general/src/main/java/org/wamblee/cache/package.html [deleted file]

index 6e0f1b3c97b2564505132d597a27f4f723caeb94..62af5943ef60947ae46b0306844d67d6768557d8 100644 (file)
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- */ 
+ */
 package org.wamblee.cache;
 
 import java.io.Serializable;
 
 /**
- * The <code>Cache</code> interface represents... a cache. In some circumstances
- * it is more optimal to implement caching directly in the code instead of
- * relying on Hibernate caching methods. This interface abstracts from the used
- * cache implementation. Cache implementations must be thread-safe.
+ * The <code>Cache</code> interface represents a cache intended for
+ * application-level caching. In some circumstances it is more optimal to
+ * implement caching directly in the code instead of relying on Hibernate
+ * caching methods. This interface abstracts from the used cache implementation.
+ * Cache implementations must be thread-safe.
  */
 public interface Cache<KeyType extends Serializable, ValueType extends Serializable> {
     /**
@@ -40,7 +41,7 @@ public interface Cache<KeyType extends Serializable, ValueType extends Serializa
      * @param aKey
      *            Key to retrieve.
      * 
-     * @return Key.
+     * @return Value or null if expired..
      */
     ValueType get(KeyType aKey);
 
index 3d200b5ad38d1ba9818c9d4c3d53ebb11f85eaf0..87feaf0e003f957d0f3310071fae9aab47c4cac4 100644 (file)
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
- */ 
+ */
 package org.wamblee.cache;
 
-import org.apache.log4j.Logger;
-
 import java.io.Serializable;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 /**
- * Represents a cached object. The object is either retrieved from the cache if
- * the cache has it, or a call back is invoked to get the object (and put it in
- * the cache).
+ * Represents a cached object identified by the key it has in a certain
+ * {@link Cache}. The object is either retrieved from the cache if the cache has
+ * it, or a call back is invoked to get the object (and put it in the cache).
  * 
  * @author Erik Brakkee
  * 
  */
 public class CachedObject<KeyType extends Serializable, ValueType extends Serializable> {
-    private static final Logger LOGGER = Logger.getLogger(CachedObject.class);
+    private static final Logger LOGGER = Logger.getLogger(CachedObject.class
+        .getName());
 
     /**
      * Cache to use.
@@ -40,6 +41,16 @@ public class CachedObject<KeyType extends Serializable, ValueType extends Serial
      */
     private KeyType objectKey;
 
+    /**
+     * Last known value.
+     */
+    private ValueType value;
+
+    /**
+     * Are we now computing the value or not?
+     */
+    private boolean computing;
+
     /**
      * Computation used to obtain the object if it is not found in the cache.
      */
@@ -67,33 +78,52 @@ public class CachedObject<KeyType extends Serializable, ValueType extends Serial
      * Gets the object. Since the object is cached, different calls to this
      * method may return different objects.
      * 
+     * If the object is expired from the cache it is recomputed using the
+     * callback. In case the callback throws an exception the
+     * last known value is used. In case an exception is thrown, the problem is
+     * also logged. In case a recomputation is already being done by another
+     * thread, the last known value is immediately returned.
+     * 
      * @return Object.
      */
     public ValueType get() {
-        ValueType object = (ValueType) cache.get(objectKey); // the used
-        // cache is
-        // thread safe.
-
-        if (object == null) {
-            // synchronize the computation to make sure that the object is only
-            // computed
-            // once when multiple concurrent threads detect that the entry must
-            // be
-            // recomputed.
+        synchronized (this) {
+            if (computing) {
+                // always return old value while computing.
+                return value;
+            }
+
+            ValueType cachedValue = cache.get(objectKey);
+            if (cachedValue == null) {
+                // expired
+                computing = true;
+            } else {
+                // no change.
+                return value;
+            }
+        }
+        try {
+
+            // we only get here if we are computing
+            // do the computation without holding the lock.
+            LOGGER.fine("Refreshing cache for '" + objectKey + "'");
+            ValueType object = computation.getObject(objectKey);
+            cache.put(objectKey, object);
+
+            synchronized (this) {
+                value = object;
+            }
+        } catch (Exception e) {
+            LOGGER.log(Level.INFO,
+                "Recomputation of cached item failed for key '" + objectKey +
+                    "'", e);
+        } finally {
             synchronized (this) {
-                object = (ValueType) cache.get(objectKey);
-
-                if (object == null) {
-                    // No other thread did a recomputation so we must do this
-                    // now.
-                    LOGGER.debug("Refreshing cache for '" + objectKey + "'");
-                    object = computation.getObject(objectKey);
-                    cache.put(objectKey, object);
-                }
+                computing = false;
             }
         }
 
-        return object;
+        return value;
     }
 
     /**
@@ -122,12 +152,14 @@ public class CachedObject<KeyType extends Serializable, ValueType extends Serial
     public static interface Computation<Key extends Serializable, Value extends Serializable> {
         /**
          * Gets the object. Called when the object is not in the cache.
+         * In case computation fails, an exception should be thrown to ensure that the last 
+         * known value will be used. 
          * 
          * @param aObjectKey
          *            Id of the object in the cache.
          * 
          * @return Object, must be non-null.
          */
-        Value getObject(Key aObjectKey);
+        Value getObject(Key aObjectKey) throws Exception;
     }
 }
index 6c262bf95d4ca049a558d0d5da89243110c63596..9c32dda8b932329271e63b1d8507e3ff5cd733db 100644 (file)
@@ -20,13 +20,13 @@ import net.sf.ehcache.CacheException;
 import net.sf.ehcache.CacheManager;
 import net.sf.ehcache.Element;
 
-import org.apache.log4j.Logger;
 
 import org.wamblee.io.InputResource;
 
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Serializable;
+import java.util.logging.Logger;
 
 /**
  * Cache implemented on top of EhCache.
@@ -36,7 +36,7 @@ import java.io.Serializable;
  */
 public class EhCache<KeyType extends Serializable, ValueType extends Serializable>
     implements org.wamblee.cache.Cache<KeyType, ValueType> {
-    private static final Logger LOGGER = Logger.getLogger(EhCache.class);
+    private static final Logger LOGGER = Logger.getLogger(EhCache.class.getName());
 
     /**
      * EH Cache manager.
@@ -68,7 +68,7 @@ public class EhCache<KeyType extends Serializable, ValueType extends Serializabl
             cache = manager.getCache(aCacheName);
 
             if (cache == null) {
-                LOGGER.warn("Creating cache '" + aCacheName +
+                LOGGER.warning("Creating cache '" + aCacheName +
                     "' because it is not configured");
                 manager.addCache(aCacheName);
                 cache = manager.getCache(aCacheName);
diff --git a/support/general/src/main/java/org/wamblee/cache/package-info.java b/support/general/src/main/java/org/wamblee/cache/package-info.java
new file mode 100644 (file)
index 0000000..540a1c5
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2005-2010 the original author or authors.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */ 
+/**
+ * This package provides an interface for a cache together with several implementations. 
+ * The main interfaces and classes are: 
+ * <ul>
+ *   <li> {@link Cache}: An interface representing an application-level cache with objects in the 
+ *       cache identified by keys. 
+ *   </li>
+ *   <li> {@link CachedObject}: An object representing an object in a cache identified through a 
+ *        key. Upon expiry the cached object invokes a callback to recompute (and cache) the object
+ *        again. 
+ *   </li>
+ *   <li> {@link ComputedValue}: An object caching a computed value with a computation interface 
+ *      determining whether the object is out of date and allowing recomputation of the value. 
+ *   </li> 
+ * </ul>
+ */
+package org.wamblee.cache;
\ No newline at end of file
diff --git a/support/general/src/main/java/org/wamblee/cache/package.html b/support/general/src/main/java/org/wamblee/cache/package.html
deleted file mode 100644 (file)
index 17bde14..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
-<html>
-<head>
-    wamblee.org
-</head>
-<body bgcolor="white">
-
-This package provides an interface for a cache together with several 
-implementations. 
-
-<!-- Put @see and @since tags down here. -->
-
-@since -
-
-</body>
-</html>