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 [EMAIL PROTECTED] 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 [EMAIL PROTECTED] 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 [EMAIL PROTECTED] #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
- * [EMAIL PROTECTED] org.apache.cayenne.DataChannel#onQuery(ObjectContext, 
Query)} logic on
- * behalf of an ObjectContext.
+ * [EMAIL PROTECTED] 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;
     }
 


Reply via email to