1 /***
2 * Copyright 2003-2010 Terracotta, Inc.
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.concurrent.Sync;
24 import net.sf.ehcache.concurrent.LockType;
25
26
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30
31 /***
32 * A {@link net.sf.ehcache.Cache} backed cache that creates entries on demand.
33 * <p/>
34 * Clients of the cache simply call it without needing knowledge of whether
35 * the entry exists in the cache, or whether it needs updating before use.
36 * <p/>
37 * <p/>
38 * Thread safety depends on the factory being used. The UpdatingCacheEntryFactory should be made
39 * thread safe. In addition users of returned values should not modify their contents.
40 *
41 * @author Greg Luck
42 * @version $Id: UpdatingSelfPopulatingCache.html 13146 2011-08-01 17:12:39Z oletizi $
43 */
44 public class UpdatingSelfPopulatingCache extends SelfPopulatingCache {
45
46 private static final Logger LOG = LoggerFactory.getLogger(UpdatingSelfPopulatingCache.class.getName());
47
48 /***
49 * Creates a SelfPopulatingCache.
50 */
51 public UpdatingSelfPopulatingCache(Ehcache cache, final UpdatingCacheEntryFactory factory)
52 throws CacheException {
53 super(cache, factory);
54 }
55
56 /***
57 * Looks up an object.
58 * <p/>
59 * If null, it creates it. If not null, it updates it. For performance this method should only be
60 * used with {@link UpdatingCacheEntryFactory}'s
61 * <p/>
62 * It is expected that
63 * gets, which update as part of the get, might take considerable time. Access to the cache cannot be blocked
64 * while that is happening. This method is therefore not synchronized. Sync's are used for thread safety based on key
65 *
66 * @param key
67 * @return a value
68 * @throws net.sf.ehcache.CacheException
69 */
70 public Element get(final Object key) throws LockTimeoutException {
71
72 try {
73
74 Ehcache backingCache = getCache();
75 Element element = backingCache.get(key);
76
77 if (element == null) {
78 element = super.get(key);
79 } else {
80 Sync lock = getLockForKey(key);
81 try {
82 lock.lock(LockType.WRITE);
83 update(key);
84 } finally {
85 lock.unlock(LockType.WRITE);
86 }
87 }
88 return element;
89 } catch (final Throwable throwable) {
90
91 put(new Element(key, null));
92 throw new LockTimeoutException("Could not update object for cache entry with key \"" + key + "\".", throwable);
93 }
94 }
95
96 /***
97 * Element can never be null. Add a null guard just in case.
98 * @param key
99 */
100 protected void update(final Object key) {
101 try {
102 Ehcache backingCache = getCache();
103 final Element element = backingCache.getQuiet(key);
104
105 if (element == null) {
106 if (LOG.isDebugEnabled()) {
107 LOG.debug(getName() + ": entry with key " + key + " has been removed - skipping it");
108 }
109 return;
110 }
111
112 refreshElement(element, backingCache);
113 } catch (final Exception e) {
114
115
116
117 LOG.warn(getName() + "Could not refresh element " + key, e);
118 }
119 }
120
121 /***
122 * This method should not be used. Because elements are always updated before they are
123 * returned, it makes no sense to refresh this cache.
124 */
125 public void refresh() throws CacheException {
126 throw new CacheException("UpdatingSelfPopulatingCache objects should not be refreshed.");
127 }
128
129 }