View Javadoc

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 }