KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > cache > OSQueryCache


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19 package org.apache.cayenne.cache;
20
21 import java.util.Collection JavaDoc;
22 import java.util.Collections JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.Iterator JavaDoc;
25 import java.util.List JavaDoc;
26 import java.util.Map JavaDoc;
27 import java.util.Properties JavaDoc;
28
29 import org.apache.cayenne.CayenneRuntimeException;
30 import org.apache.cayenne.query.QueryMetadata;
31
32 import com.opensymphony.oscache.base.CacheEntry;
33 import com.opensymphony.oscache.base.NeedsRefreshException;
34 import com.opensymphony.oscache.general.GeneralCacheAdministrator;
35
36 /**
37  * A {@link QueryCache} implementation based on OpenSymphony OSCache. Query cache
38  * parameters are initialized from "/oscache.properties" file per <a
39  * HREF="http://www.opensymphony.com/oscache/wiki/Configuration.html">OSCache</a>
40  * documentation. In addition to the standard OSCache parameters, Cayenne provdier allows
41  * to setup global cache expiration parameters, and parameters matching the main query
42  * cache group (i.e. the cache groups specified first). A sample oscache.properties may
43  * look like this:
44  *
45  * <pre>
46  * # OSCache configuration file
47  *
48  * # OSCache standard configuration per
49  * # http://www.opensymphony.com/oscache/wiki/Configuration.html
50  * # ---------------------------------------------------------------
51  *
52  * #cache.memory=true
53  * #cache.blocking=false
54  * cache.capacity=5000
55  * cache.algorithm=com.opensymphony.oscache.base.algorithm.LRUCache
56  *
57  * # Cayenne specific properties
58  * # ---------------------------------------------------------------
59  *
60  * # Default refresh period in seconds:
61  * cayenne.default.refresh = 60
62  *
63  * # Default expiry specified as cron expressions per
64  * # http://www.opensymphony.com/oscache/wiki/Cron%20Expressions.html
65  * # expire entries every hour on the 10's minute
66  * cayenne.default.cron = 10 * * * *
67  *
68  * # Same parameters can be overriden per query
69  * cayenne.group.xyz.refresh = 120
70  * cayenne.group.xyz.cron = 10 1 * * *
71  * </pre>
72  *
73  * Further extension of OSQueryCache is possible by using OSCache listener API.
74  *
75  * @since 3.0
76  * @author Andrus Adamchik
77  */

78 public class OSQueryCache implements QueryCache {
79
80     public static final int DEFAULT_REFRESH_PERIOD = CacheEntry.INDEFINITE_EXPIRY;
81
82     static String JavaDoc DEFAULT_REFRESH_KEY = "cayenne.default.refresh";
83     static String JavaDoc DEFAULT_CRON_KEY = "cayenne.default.cron";
84
85     static String JavaDoc GROUP_PREFIX = "cayenne.group.";
86     static String JavaDoc REFRESH_SUFFIX = ".refresh";
87     static String JavaDoc CRON_SUFFIX = ".cron";
88
89     protected GeneralCacheAdministrator osCache;
90
91     RefreshSpecification defaultRefreshSpecification;
92     Map JavaDoc refreshSpecifications;
93     Properties JavaDoc properties;
94
95     public OSQueryCache() {
96         OSCacheAdministrator admin = new OSCacheAdministrator();
97         init(admin, admin.getProperties());
98     }
99
100     public OSQueryCache(GeneralCacheAdministrator cache, Properties JavaDoc properties) {
101         init(cache, properties);
102     }
103
104     /**
105      * Returns a collection of group names that have been configured explicitly via
106      * properties.
107      */

108     public Collection JavaDoc getGroupNames() {
109         return refreshSpecifications != null
110                 ? Collections.unmodifiableCollection(refreshSpecifications.keySet())
111                 : Collections.EMPTY_SET;
112     }
113
114     public String JavaDoc getCronExpression(String JavaDoc groupName) {
115
116         RefreshSpecification spec = null;
117
118         if (refreshSpecifications != null) {
119             spec = (RefreshSpecification) refreshSpecifications.get(groupName);
120         }
121
122         if (spec == null) {
123             spec = defaultRefreshSpecification;
124         }
125
126         return spec.cronExpression;
127     }
128
129     public int getRrefreshPeriod(String JavaDoc groupName) {
130
131         RefreshSpecification spec = null;
132
133         if (refreshSpecifications != null) {
134             spec = (RefreshSpecification) refreshSpecifications.get(groupName);
135         }
136
137         if (spec == null) {
138             spec = defaultRefreshSpecification;
139         }
140
141         return spec.refreshPeriod;
142     }
143
144     /**
145      * Returns the underlying OSCache manager object.
146      */

147     public GeneralCacheAdministrator getOsCache() {
148         return osCache;
149     }
150
151     /**
152      * Returns configuration properties. Usually this is the contents of
153      * "oscache.properties" file.
154      */

155     public Properties JavaDoc getProperties() {
156         return properties;
157     }
158
159     void init(GeneralCacheAdministrator cache, Properties JavaDoc properties) {
160         this.properties = properties;
161         this.osCache = cache;
162         this.defaultRefreshSpecification = new RefreshSpecification();
163
164         // load defaults and per-query settings
165
if (properties != null) {
166
167             // first extract defaults...
168
String JavaDoc defaultRefresh = properties.getProperty(DEFAULT_REFRESH_KEY);
169             if (defaultRefresh != null) {
170                 defaultRefreshSpecification.setRefreshPeriod(defaultRefresh);
171             }
172
173             String JavaDoc defaultCron = properties.getProperty(DEFAULT_CRON_KEY);
174             if (defaultCron != null) {
175                 defaultRefreshSpecification.cronExpression = defaultCron;
176             }
177
178             // now check for per-query settings
179
Iterator JavaDoc it = properties.entrySet().iterator();
180             while (it.hasNext()) {
181
182                 Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
183
184                 if (entry.getKey() == null || entry.getValue() == null) {
185                     continue;
186                 }
187
188                 String JavaDoc key = entry.getKey().toString();
189                 if (key.startsWith(GROUP_PREFIX)) {
190
191                     if (key.endsWith(REFRESH_SUFFIX)) {
192                         String JavaDoc name = key.substring(GROUP_PREFIX.length(), key.length()
193                                 - REFRESH_SUFFIX.length());
194
195                         initRefreshPolicy(name, entry.getValue());
196                     }
197                     else if (key.endsWith(CRON_SUFFIX)) {
198                         String JavaDoc name = key.substring(GROUP_PREFIX.length(), key.length()
199                                 - CRON_SUFFIX.length());
200
201                         initCronPolicy(name, entry.getValue());
202                     }
203                 }
204             }
205         }
206     }
207
208     /**
209      * Called internally for each group that is configured with cron policy in the
210      * properties. Exposed mainly for the benefit of subclasses. When overriding, call
211      * 'super'.
212      */

213     protected void initCronPolicy(String JavaDoc groupName, Object JavaDoc value) {
214         nonNullSpec(groupName).cronExpression = value != null ? value.toString() : null;
215     }
216
217     /**
218      * Called internally for each group that is configured with refresh policy in the
219      * properties. Exposed mainly for the benefit of subclasses. When overriding, call
220      * 'super'.
221      */

222     protected void initRefreshPolicy(String JavaDoc groupName, Object JavaDoc value) {
223         nonNullSpec(groupName).setRefreshPeriod(value);
224     }
225
226     private RefreshSpecification nonNullSpec(String JavaDoc name) {
227         if (refreshSpecifications == null) {
228             refreshSpecifications = new HashMap JavaDoc();
229         }
230
231         RefreshSpecification spec = (RefreshSpecification) refreshSpecifications
232                 .get(name);
233         if (spec == null) {
234             spec = new RefreshSpecification();
235             spec.cronExpression = defaultRefreshSpecification.cronExpression;
236             spec.refreshPeriod = defaultRefreshSpecification.refreshPeriod;
237             refreshSpecifications.put(name, spec);
238         }
239
240         return spec;
241     }
242
243     public List JavaDoc get(QueryMetadata metadata) {
244         String JavaDoc key = metadata.getCacheKey();
245         if (key == null) {
246             return null;
247         }
248
249         RefreshSpecification refresh = getRefreshSpecification(metadata);
250
251         try {
252             return (List JavaDoc) osCache.getFromCache(
253                     key,
254                     refresh.refreshPeriod,
255                     refresh.cronExpression);
256         }
257         catch (NeedsRefreshException e) {
258             osCache.cancelUpdate(key);
259             return null;
260         }
261     }
262
263     /**
264      * Returns a non-null cached value. If it is not present in the cache, it is obtained
265      * by calling {@link QueryCacheEntryFactory#createObject()}. Whether the cache provider
266      * will block on the entry update or not is controlled by "cache.blocking"
267      * configuration property and is "false" by default.
268      */

269     public List JavaDoc get(QueryMetadata metadata, QueryCacheEntryFactory factory) {
270         String JavaDoc key = metadata.getCacheKey();
271         if (key == null) {
272             return null;
273         }
274
275         RefreshSpecification refresh = getRefreshSpecification(metadata);
276
277         try {
278             return (List JavaDoc) osCache.getFromCache(
279                     key,
280                     refresh.refreshPeriod,
281                     refresh.cronExpression);
282         }
283         catch (NeedsRefreshException e) {
284             boolean updated = false;
285             try {
286                 Object JavaDoc result = factory.createObject();
287
288                 if (!(result instanceof List JavaDoc)) {
289                     if (result == null) {
290                         throw new CayenneRuntimeException("Null on cache rebuilding: "
291                                 + metadata.getCacheKey());
292                     }
293                     else {
294                         throw new CayenneRuntimeException(
295                                 "Invalid query result, expected List, got "
296                                         + result.getClass().getName());
297                     }
298                 }
299
300                 List JavaDoc list = (List JavaDoc) result;
301
302                 put(metadata, list);
303                 updated = true;
304                 return list;
305             }
306             finally {
307                 if (!updated) {
308                     // It is essential that cancelUpdate is called if the
309
// cached content could not be rebuilt
310
osCache.cancelUpdate(key);
311                 }
312             }
313         }
314     }
315     
316     /**
317      * Returns non-null RefreshSpecification for the QueryMetadata.
318      */

319     RefreshSpecification getRefreshSpecification(QueryMetadata metadata) {
320
321         RefreshSpecification refresh = null;
322
323         if (refreshSpecifications != null) {
324             String JavaDoc[] groups = metadata.getCacheGroups();
325             if (groups != null && groups.length > 0) {
326                 refresh = (RefreshSpecification) refreshSpecifications.get(groups[0]);
327             }
328         }
329
330         return refresh != null ? refresh : defaultRefreshSpecification;
331     }
332
333     public void put(QueryMetadata metadata, List JavaDoc results) {
334         String JavaDoc key = metadata.getCacheKey();
335         if (key != null) {
336             osCache.putInCache(key, results, metadata.getCacheGroups());
337         }
338     }
339
340     public void remove(String JavaDoc key) {
341         if (key != null) {
342             osCache.removeEntry(key);
343         }
344     }
345
346     public void removeGroup(String JavaDoc groupKey) {
347         if (groupKey != null) {
348             osCache.flushGroup(groupKey);
349         }
350     }
351
352     public void clear() {
353         osCache.flushAll();
354     }
355
356     public int size() {
357         return osCache.getCache().getSize();
358     }
359
360     public int capacity() {
361         return osCache.getCache().getCapacity();
362     }
363
364     final static class RefreshSpecification {
365
366         int refreshPeriod;
367         String JavaDoc cronExpression;
368
369         RefreshSpecification() {
370             this.refreshPeriod = DEFAULT_REFRESH_PERIOD;
371         }
372
373         RefreshSpecification(int refrehsPeriod, String JavaDoc cronExpression) {
374             this.refreshPeriod = refrehsPeriod;
375             this.cronExpression = cronExpression;
376         }
377
378         void setRefreshPeriod(Object JavaDoc value) {
379             try {
380                 refreshPeriod = Integer.parseInt(value.toString());
381             }
382             catch (NumberFormatException JavaDoc e) {
383                 // ignore...
384
}
385         }
386     }
387
388     final class OSCacheAdministrator extends GeneralCacheAdministrator {
389
390         OSCacheAdministrator() {
391         }
392
393         OSCacheAdministrator(Properties JavaDoc properties) {
394             super(properties);
395         }
396
397         Properties JavaDoc getProperties() {
398             return super.config.getProperties();
399         }
400     }
401 }
402
Popular Tags