View Javadoc

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              // Could not fetch - Ditch the entry from the cache and rethrow
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             // Collect the exception and keep going.
115             // Throw the exception once all the entries have been refreshed
116             // If the refresh fails, keep the old element. It will simply become staler.
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 }