You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2006/10/11 23:28:06 UTC

svn commit: r462959 - in /incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src: main/java/org/apache/cayenne/ main/java/org/apache/cayenne/access/ main/java/org/apache/cayenne/cache/ main/java/org/apache/cayenne/util/ test/java/org/apache/cayenne/cache/

Author: aadamchik
Date: Wed Oct 11 14:28:05 2006
New Revision: 462959

URL: http://svn.apache.org/viewvc?view=rev&rev=462959
Log:
CAY-685 OSQueryCache concurrency improvements
added a method to the QueryCache interface to perform a read-through cache access, thus letting the cache implementor 
to define synchronization policy. By default the MapQueryCache does updates asynchronously, while OSQueryCache
can be configured to do it as "blocking" or "non-blocking"

Modified:
    incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/CayenneContextQueryAction.java
    incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/DataContextQueryAction.java
    incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
    incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/cache/MapQueryCache.java
    incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/cache/OSQueryCache.java
    incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/cache/QueryCache.java
    incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/util/ObjectContextQueryAction.java
    incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/test/java/org/apache/cayenne/cache/MockQueryCache.java

Modified: incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/CayenneContextQueryAction.java
URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/CayenneContextQueryAction.java?view=diff&rev=462959&r1=462958&r2=462959
==============================================================================
--- incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/CayenneContextQueryAction.java (original)
+++ incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/CayenneContextQueryAction.java Wed Oct 11 14:28:05 2006
@@ -21,11 +21,10 @@
 
 import java.util.Collection;
 import java.util.Iterator;
-import java.util.List;
 
+import org.apache.cayenne.cache.CacheObjectFactory;
 import org.apache.cayenne.cache.QueryCache;
 import org.apache.cayenne.query.Query;
-import org.apache.cayenne.query.QueryMetadata;
 import org.apache.cayenne.query.RefreshQuery;
 import org.apache.cayenne.remote.RemoteIncrementalFaultList;
 import org.apache.cayenne.util.ListResponse;
@@ -71,36 +70,20 @@
         return !DONE;
     }
 
-    private boolean interceptLocalCache() {
-
-        if (metadata.getCacheKey() == null) {
-            return !DONE;
-        }
-
-        boolean cache = QueryMetadata.LOCAL_CACHE.equals(metadata.getCachePolicy());
-        boolean cacheOrCacheRefresh = cache
-                || QueryMetadata.LOCAL_CACHE_REFRESH.equals(metadata.getCachePolicy());
-
-        if (!cacheOrCacheRefresh) {
-            return !DONE;
-        }
+    protected QueryCache getQueryCache() {
+        return ((CayenneContext) actingContext).getQueryCache();
+    }
 
-        QueryCache queryCache = ((CayenneContext) actingContext).getQueryCache();
-        if (cache) {
+    protected CacheObjectFactory getCacheObjectFactory() {
+        return new CacheObjectFactory() {
 
-            List cachedResults = queryCache.get(metadata);
-            if (cachedResults != null) {
-                response = new ListResponse(cachedResults);
-                return DONE;
+            public Object createObject() {
+                if (interceptPaginatedQuery() != DONE) {
+                    runQuery();
+                }
+                return response.firstList();
             }
-        }
-
-        if (interceptPaginatedQuery() != DONE) {
-            runQuery();
-        }
-
-        queryCache.put(metadata, response.firstList());
-        return DONE;
+        };
     }
 
     private boolean interceptRefreshQuery() {

Modified: incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/DataContextQueryAction.java
URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/DataContextQueryAction.java?view=diff&rev=462959&r1=462958&r2=462959
==============================================================================
--- incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/DataContextQueryAction.java (original)
+++ incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/DataContextQueryAction.java Wed Oct 11 14:28:05 2006
@@ -21,7 +21,6 @@
 
 import java.util.Collection;
 import java.util.Iterator;
-import java.util.List;
 import java.util.Map;
 
 import org.apache.cayenne.DataObject;
@@ -32,7 +31,6 @@
 import org.apache.cayenne.cache.QueryCache;
 import org.apache.cayenne.query.ObjectIdQuery;
 import org.apache.cayenne.query.Query;
-import org.apache.cayenne.query.QueryMetadata;
 import org.apache.cayenne.query.RefreshQuery;
 import org.apache.cayenne.util.ListResponse;
 import org.apache.cayenne.util.ObjectContextQueryAction;
@@ -107,37 +105,8 @@
         return !DONE;
     }
 
-    /*
-     * Wraps execution in local cache checks.
-     */
-    private boolean interceptLocalCache() {
-
-        if (metadata.getCacheKey() == null) {
-            return !DONE;
-        }
-
-        boolean cache = QueryMetadata.LOCAL_CACHE.equals(metadata.getCachePolicy());
-        boolean cacheOrCacheRefresh = cache
-                || QueryMetadata.LOCAL_CACHE_REFRESH.equals(metadata.getCachePolicy());
-
-        if (!cacheOrCacheRefresh) {
-            return !DONE;
-        }
-
-        QueryCache queryCache = ((DataContext) actingContext).getQueryCache();
-
-        if (cache) {
-
-            List cachedResults = queryCache.get(metadata);
-            if (cachedResults != null) {
-                response = new ListResponse(cachedResults);
-                return DONE;
-            }
-        }
-
-        runQuery();
-        queryCache.put(metadata, response.firstList());
-        return DONE;
+    protected QueryCache getQueryCache() {
+        return ((DataContext) actingContext).getQueryCache();
     }
 
     private boolean interceptRefreshQuery() {

Modified: incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java
URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java?view=diff&rev=462959&r1=462958&r2=462959
==============================================================================
--- incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java (original)
+++ incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/access/DataDomainQueryAction.java Wed Oct 11 14:28:05 2006
@@ -34,6 +34,7 @@
 import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.Persistent;
 import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.cache.CacheObjectFactory;
 import org.apache.cayenne.cache.QueryCache;
 import org.apache.cayenne.map.DataMap;
 import org.apache.cayenne.map.DbRelationship;
@@ -327,37 +328,51 @@
         }
 
         QueryCache queryCache = domain.getQueryCache();
+        CacheObjectFactory factory = getCacheObjectFactory();
 
         if (cache) {
-            List cachedRows = queryCache.get(metadata);
+            List cachedResults = queryCache.get(metadata, factory);
 
-            if (cachedRows != null) {
-                // decorate result immutable list to avoid messing up the cache
-                this.response = new ListResponse(Collections.unmodifiableList(cachedRows));
-
-                if (cachedRows instanceof ListWithPrefetches) {
-                    this.prefetchResultsByPath = ((ListWithPrefetches) cachedRows)
-                            .getPrefetchResultsByPath();
-                }
+            // response may already be initialized by the factory above ... it is null if
+            // there was a preexisting cache entry
+            if (response == null) {
+                response = new ListResponse(cachedResults);
+            }
 
-                return DONE;
+            if (cachedResults instanceof ListWithPrefetches) {
+                this.prefetchResultsByPath = ((ListWithPrefetches) cachedResults)
+                        .getPrefetchResultsByPath();
             }
         }
+        else {
+            // on cache-refresh request, fetch without blocking and fill the cache
+            queryCache.put(metadata, (List) factory.createObject());
+        }
+
+        return DONE;
+    }
+    
+    private CacheObjectFactory getCacheObjectFactory() {
+        return new CacheObjectFactory() {
 
-        runQueryInTransaction();
+            public Object createObject() {
+                runQueryInTransaction();
 
-        List list = response.firstList();
-        if (list != null) {
+                List list = response.firstList();
+                if (list != null) {
 
-            // include prefetches in the cached result
-            if (prefetchResultsByPath != null) {
-                list = new ListWithPrefetches(list, prefetchResultsByPath);
-            }
+                    // make an immutable list to make sure callers don't mess it up
+                    list = Collections.unmodifiableList(list);
 
-            queryCache.put(metadata, list);
-        }
+                    // include prefetches in the cached result
+                    if (prefetchResultsByPath != null) {
+                        list = new ListWithPrefetches(list, prefetchResultsByPath);
+                    }
+                }
 
-        return DONE;
+                return list;
+            }
+        };
     }
 
     /*

Modified: incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/cache/MapQueryCache.java
URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/cache/MapQueryCache.java?view=diff&rev=462959&r1=462958&r2=462959
==============================================================================
--- incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/cache/MapQueryCache.java (original)
+++ incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/cache/MapQueryCache.java Wed Oct 11 14:28:05 2006
@@ -23,6 +23,7 @@
 import java.util.List;
 import java.util.Map;
 
+import org.apache.cayenne.CayenneRuntimeException;
 import org.apache.cayenne.query.QueryMetadata;
 import org.apache.commons.collections.map.LRUMap;
 
@@ -59,6 +60,36 @@
         }
 
         return (entry != null) ? entry.list : null;
+    }
+
+    /**
+     * Returns a non-null cached value. If it is not present in the cache, it is obtained
+     * by calling {@link CacheObjectFactory#createObject()} without blocking the cache. As
+     * a result there is a potential of multiple threads to be updating cache in parallel -
+     * this wouldn't lead to corruption of the cache, but can be suboptimal.
+     */
+    public List get(QueryMetadata metadata, CacheObjectFactory factory) {
+        List result = get(metadata);
+        if (result == null) {
+            Object newObject = factory.createObject();
+
+            if (!(newObject instanceof List)) {
+                if (newObject == null) {
+                    throw new CayenneRuntimeException("Null on cache rebuilding: "
+                            + metadata.getCacheKey());
+                }
+                else {
+                    throw new CayenneRuntimeException(
+                            "Invalid query result, expected List, got "
+                                    + newObject.getClass().getName());
+                }
+            }
+
+            result = (List) newObject;
+            put(metadata, result);
+        }
+
+        return result;
     }
 
     public void put(QueryMetadata metadata, List results) {

Modified: incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/cache/OSQueryCache.java
URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/cache/OSQueryCache.java?view=diff&rev=462959&r1=462958&r2=462959
==============================================================================
--- incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/cache/OSQueryCache.java (original)
+++ incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/cache/OSQueryCache.java Wed Oct 11 14:28:05 2006
@@ -42,31 +42,31 @@
  * look like this:
  * 
  * <pre>
- *                       # OSCache configuration file
- *                      
- *                       # OSCache standard configuration per
- *                       #     http://www.opensymphony.com/oscache/wiki/Configuration.html
- *                       # ---------------------------------------------------------------
- *                      
- *                       #cache.memory=true
- *                       cache.capacity=5000
- *                       cache.algorithm=com.opensymphony.oscache.base.algorithm.LRUCache
- *                      
- *                      
- *                       # Cayenne specific properties
- *                       # ---------------------------------------------------------------
- *                      
- *                       # Default refresh period in seconds:
- *                       cayenne.default.refresh = 60
- *                      
- *                       # Default expiry specified as cron expressions per
- *                       #    http://www.opensymphony.com/oscache/wiki/Cron%20Expressions.html
- *                       # expire entries every hour on the 10's minute
- *                       cayenne.default.cron = 10 * * * *
- *                      
- *                       # Same parameters can be overriden per query
- *                       cayenne.group.xyz.refresh = 120
- *                       cayenne.group.xyz.cron = 10 1 * * *
+ * # OSCache configuration file
+ *                        
+ * # OSCache standard configuration per
+ * #     http://www.opensymphony.com/oscache/wiki/Configuration.html
+ * # ---------------------------------------------------------------
+ *                        
+ * #cache.memory=true
+ * #cache.blocking=false
+ * cache.capacity=5000
+ * cache.algorithm=com.opensymphony.oscache.base.algorithm.LRUCache
+ *                        
+ * # Cayenne specific properties
+ * # ---------------------------------------------------------------
+ *                        
+ * # Default refresh period in seconds:
+ * cayenne.default.refresh = 60
+ *                        
+ * # Default expiry specified as cron expressions per
+ * #    http://www.opensymphony.com/oscache/wiki/Cron%20Expressions.html
+ * # expire entries every hour on the 10's minute
+ * cayenne.default.cron = 10 * * * *
+ *                        
+ * # Same parameters can be overriden per query
+ * cayenne.group.xyz.refresh = 120
+ * cayenne.group.xyz.cron = 10 1 * * *
  * </pre>
  * 
  * Further extension of OSQueryCache is possible by using OSCache listener API.
@@ -99,7 +99,7 @@
     public OSQueryCache(GeneralCacheAdministrator cache, Properties properties) {
         init(cache, properties);
     }
-    
+
     /**
      * Returns a collection of group names that have been configured explicitly via
      * properties.
@@ -109,7 +109,7 @@
                 ? Collections.unmodifiableCollection(refreshSpecifications.keySet())
                 : Collections.EMPTY_SET;
     }
-    
+
     public String getCronExpression(String groupName) {
 
         RefreshSpecification spec = null;
@@ -124,7 +124,7 @@
 
         return spec.cronExpression;
     }
-    
+
     public int getRrefreshPeriod(String groupName) {
 
         RefreshSpecification spec = null;
@@ -245,19 +245,34 @@
             return null;
         }
 
-        RefreshSpecification refresh = null;
+        RefreshSpecification refresh = getRefreshSpecification(metadata);
 
-        if (refreshSpecifications != null) {
-            String[] groups = metadata.getCacheGroups();
-            if (groups != null && groups.length > 0) {
-                refresh = (RefreshSpecification) refreshSpecifications.get(groups[0]);
-            }
+        try {
+            return (List) osCache.getFromCache(
+                    key,
+                    refresh.refreshPeriod,
+                    refresh.cronExpression);
+        }
+        catch (NeedsRefreshException e) {
+            osCache.cancelUpdate(key);
+            return null;
         }
+    }
 
-        if (refresh == null) {
-            refresh = defaultRefreshSpecification;
+    /**
+     * Returns a non-null cached value. If it is not present in the cache, it is obtained
+     * by calling {@link CacheObjectFactory#createObject()}. Whether the cache provider
+     * will block on the entry update or not is controlled by "cache.blocking"
+     * configuration property and is "false" by default.
+     */
+    public List get(QueryMetadata metadata, CacheObjectFactory factory) {
+        String key = metadata.getCacheKey();
+        if (key == null) {
+            return null;
         }
 
+        RefreshSpecification refresh = getRefreshSpecification(metadata);
+
         try {
             return (List) osCache.getFromCache(
                     key,
@@ -265,9 +280,38 @@
                     refresh.cronExpression);
         }
         catch (NeedsRefreshException e) {
-            osCache.cancelUpdate(key);
-            return null;
+            boolean updated = false;
+            try {
+                Object result = factory.createObject();
+                osCache.putInCache(key, result);
+                updated = true;
+                return (List) result;
+            }
+            finally {
+                if (!updated) {
+                    // It is essential that cancelUpdate is called if the
+                    // cached content could not be rebuilt
+                    osCache.cancelUpdate(key);
+                }
+            }
         }
+    }
+    
+    /**
+     * Returns non-null RefreshSpecification for the QueryMetadata.
+     */
+    RefreshSpecification getRefreshSpecification(QueryMetadata metadata) {
+
+        RefreshSpecification refresh = null;
+
+        if (refreshSpecifications != null) {
+            String[] groups = metadata.getCacheGroups();
+            if (groups != null && groups.length > 0) {
+                refresh = (RefreshSpecification) refreshSpecifications.get(groups[0]);
+            }
+        }
+
+        return refresh != null ? refresh : defaultRefreshSpecification;
     }
 
     public void put(QueryMetadata metadata, List results) {

Modified: incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/cache/QueryCache.java
URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/cache/QueryCache.java?view=diff&rev=462959&r1=462958&r2=462959
==============================================================================
--- incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/cache/QueryCache.java (original)
+++ incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/cache/QueryCache.java Wed Oct 11 14:28:05 2006
@@ -30,7 +30,22 @@
  */
 public interface QueryCache {
 
+    /**
+     * Returns a cached query result for the given QueryMetadata or null if the result is
+     * not cached or is expired.
+     */
     List get(QueryMetadata metadata);
+
+    /**
+     * Returns a cached query result for the given QueryMetadata. If the result is not
+     * cached or is expired, cache will use provided factory to rebuild the value and
+     * store it in the cache. A corollary is that this method never returns null.
+     * <p/>Compared to {@link #get(QueryMetadata)}, this method allows the cache to do
+     * appropriate synchronization when refreshing the entry, preventing multiple threads
+     * from running the same query when a missing entry is requested by multiple threads
+     * simultaneously.
+     */
+    List get(QueryMetadata metadata, CacheObjectFactory factory);
 
     void put(QueryMetadata metadata, List results);
 

Modified: incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/util/ObjectContextQueryAction.java
URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/util/ObjectContextQueryAction.java?view=diff&rev=462959&r1=462958&r2=462959
==============================================================================
--- incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/util/ObjectContextQueryAction.java (original)
+++ incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/main/java/org/apache/cayenne/util/ObjectContextQueryAction.java Wed Oct 11 14:28:05 2006
@@ -17,7 +17,6 @@
  *  under the License.
  ****************************************************************/
 
-
 package org.apache.cayenne.util;
 
 import java.util.ArrayList;
@@ -28,6 +27,8 @@
 import org.apache.cayenne.ObjectId;
 import org.apache.cayenne.Persistent;
 import org.apache.cayenne.QueryResponse;
+import org.apache.cayenne.cache.CacheObjectFactory;
+import org.apache.cayenne.cache.QueryCache;
 import org.apache.cayenne.property.ArcProperty;
 import org.apache.cayenne.property.ClassDescriptor;
 import org.apache.cayenne.query.ObjectIdQuery;
@@ -37,8 +38,8 @@
 
 /**
  * A helper class that implements
- * {@link org.apache.cayenne.DataChannel#onQuery(ObjectContext, Query)} logic on
- * behalf of an ObjectContext.
+ * {@link org.apache.cayenne.DataChannel#onQuery(ObjectContext, Query)} logic on behalf of
+ * an ObjectContext.
  * <p>
  * <i>Intended for internal use only.</i>
  * </p>
@@ -46,7 +47,7 @@
  * @since 1.2
  * @author Andrus Adamchik
  */
-public class ObjectContextQueryAction {
+public abstract class ObjectContextQueryAction {
 
     protected static final boolean DONE = true;
 
@@ -76,7 +77,9 @@
 
         if (interceptOIDQuery() != DONE) {
             if (interceptRelationshipQuery() != DONE) {
-                runQuery();
+                if (interceptLocalCache() != DONE) {
+                    runQuery();
+                }
             }
         }
 
@@ -208,6 +211,61 @@
         }
 
         return !DONE;
+    }
+
+    /**
+     * @since 3.0
+     */
+    protected boolean interceptLocalCache() {
+
+        if (metadata.getCacheKey() == null) {
+            return !DONE;
+        }
+
+        boolean cache = QueryMetadata.LOCAL_CACHE.equals(metadata.getCachePolicy());
+        boolean cacheOrCacheRefresh = cache
+                || QueryMetadata.LOCAL_CACHE_REFRESH.equals(metadata.getCachePolicy());
+
+        if (!cacheOrCacheRefresh) {
+            return !DONE;
+        }
+
+        QueryCache queryCache = getQueryCache();
+        CacheObjectFactory factory = getCacheObjectFactory();
+
+        if (cache) {
+            List cachedResults = queryCache.get(metadata, factory);
+
+            // response may already be initialized by the factory above ... it is null if
+            // there was a preexisting cache entry
+            if (response == null) {
+                response = new ListResponse(cachedResults);
+            }
+        }
+        else {
+            // on cache-refresh request, fetch without blocking and fill the cache
+            queryCache.put(metadata, (List) factory.createObject());
+        }
+
+        return DONE;
+    }
+
+    /**
+     * @since 3.0
+     */
+    protected abstract QueryCache getQueryCache();
+
+    /**
+     * @since 3.0
+     */
+    protected CacheObjectFactory getCacheObjectFactory() {
+        return new CacheObjectFactory() {
+
+            public Object createObject() {
+                runQuery();
+                return response.firstList();
+            }
+        };
     }
 
     /**

Modified: incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/test/java/org/apache/cayenne/cache/MockQueryCache.java
URL: http://svn.apache.org/viewvc/incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/test/java/org/apache/cayenne/cache/MockQueryCache.java?view=diff&rev=462959&r1=462958&r2=462959
==============================================================================
--- incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/test/java/org/apache/cayenne/cache/MockQueryCache.java (original)
+++ incubator/cayenne/main/trunk/core/cayenne-jdk1.4/src/test/java/org/apache/cayenne/cache/MockQueryCache.java Wed Oct 11 14:28:05 2006
@@ -22,13 +22,16 @@
 
 import org.apache.cayenne.query.QueryMetadata;
 
-
 public class MockQueryCache implements QueryCache {
 
     public void clear() {
     }
 
     public List get(QueryMetadata metadata) {
+        return null;
+    }
+
+    public List get(QueryMetadata metadata, CacheObjectFactory factory) {
         return null;
     }