1 /**
2 * Copyright 2003-2007 Greg Luck
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 package net.sf.ehcache.constructs.blocking;
18
19
20 import net.sf.ehcache.CacheException;
21 import net.sf.ehcache.Ehcache;
22 import net.sf.ehcache.Element;
23 import net.sf.ehcache.constructs.concurrent.Mutex;
24 import org.apache.commons.logging.Log;
25 import org.apache.commons.logging.LogFactory;
26
27
28 /**
29 * A {@link net.sf.ehcache.Cache} backed cache that creates entries on demand.
30 * <p/>
31 * Clients of the cache simply call it without needing knowledge of whether
32 * the entry exists in the cache, or whether it needs updating before use.
33 * <p/>
34 * <p/>
35 * Thread safety depends on the factory being used. The UpdatingCacheEntryFactory should be made
36 * thread safe. In addition users of returned values should not modify their contents.
37 *
38 * @author Greg Luck
39 * @version $Id: UpdatingSelfPopulatingCache.java 512 2007-07-10 09:18:45Z gregluck $
40 */
41 public class UpdatingSelfPopulatingCache extends SelfPopulatingCache {
42 private static final Log LOG = LogFactory.getLog(UpdatingSelfPopulatingCache.class.getName());
43
44 /**
45 * Creates a SelfPopulatingCache.
46 */
47 public UpdatingSelfPopulatingCache(Ehcache cache, final UpdatingCacheEntryFactory factory)
48 throws CacheException {
49 super(cache, factory);
50 }
51
52 /**
53 * Looks up an object.
54 * <p/>
55 * If null, it creates it. If not null, it updates it. For performance this method should only be
56 * used with {@link UpdatingCacheEntryFactory}'s
57 * <p/>
58 * It is expected that
59 * gets, which update as part of the get, might take considerable time. Access to the cache cannot be blocked
60 * while that is happening. This method is therefore not synchronized. Mutexes are used for thread safety based on key
61 *
62 * @param key
63 * @return a value
64 * @throws net.sf.ehcache.CacheException
65 */
66 public Element get(final Object key) throws LockTimeoutException {
67 String oldThreadName = Thread.currentThread().getName();
68 setThreadName("get", key);
69
70 try {
71
72 Ehcache backingCache = getCache();
73 Element element = backingCache.get(key);
74
75 if (element == null) {
76 element = super.get(key);
77 } else {
78 Mutex lock = getLockForKey(key);
79 try {
80 lock.acquire();
81 update(key);
82 } finally {
83 lock.release();
84 }
85 }
86 return element;
87 } catch (final Throwable throwable) {
88 // Could not fetch - Ditch the entry from the cache and rethrow
89 setThreadName("put", key);
90 put(new Element(key, null));
91 throw new LockTimeoutException("Could not fetch object for cache entry \"" + key + "\".", throwable);
92 } finally {
93 Thread.currentThread().setName(oldThreadName);
94 }
95 }
96
97 /**
98 * Element can never be null. Add a null guard just in case.
99 * @param key
100 */
101 protected void update(final Object key) {
102 try {
103 Ehcache backingCache = getCache();
104 final Element element = backingCache.getQuiet(key);
105
106 if (element == null) {
107 if (LOG.isTraceEnabled()) {
108 LOG.trace(getName() + ": entry with key " + key + " has been removed - skipping it");
109 }
110 return;
111 }
112
113 refreshElement(element, backingCache);
114 } catch (final Exception e) {
115 // Collect the exception and keep going.
116 // Throw the exception once all the entries have been refreshed
117 // If the refresh fails, keep the old element. It will simply become staler.
118 LOG.warn(getName() + "Could not refresh element " + key, e);
119 }
120 }
121
122 /**
123 * This method should not be used. Because elements are always updated before they are
124 * returned, it makes no sense to refresh this cache.
125 */
126 public void refresh() throws CacheException {
127 throw new CacheException("UpdatingSelfPopulatingCache objects should not be refreshed.");
128 }
129
130 }