View Javadoc
1   /*
2    * Copyright (C) 2011 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5    * in compliance with the License. You may obtain a copy of the License at
6    *
7    * http://www.apache.org/licenses/LICENSE-2.0
8    *
9    * Unless required by applicable law or agreed to in writing, software distributed under the License
10   * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11   * or implied. See the License for the specific language governing permissions and limitations under
12   * the License.
13   */
14  
15  package com.google.common.cache;
16  
17  import com.google.common.annotations.GwtCompatible;
18  import com.google.common.collect.ImmutableMap;
19  import com.google.common.collect.Maps;
20  import java.util.Map;
21  import java.util.concurrent.Callable;
22  import java.util.concurrent.ConcurrentMap;
23  import java.util.concurrent.ExecutionException;
24  
25  /**
26   * This class provides a skeletal implementation of the {@code Cache} interface to minimize the
27   * effort required to implement this interface.
28   *
29   * <p>To implement a cache, the programmer needs only to extend this class and provide an
30   * implementation for the {@link #put} and {@link #getIfPresent} methods. {@link #getAllPresent} is
31   * implemented in terms of {@link #getIfPresent}; {@link #putAll} is implemented in terms of
32   * {@link #put}, {@link #invalidateAll(Iterable)} is implemented in terms of {@link #invalidate}.
33   * The method {@link #cleanUp} is a no-op. All other methods throw an
34   * {@link UnsupportedOperationException}.
35   *
36   * @author Charles Fry
37   * @since 10.0
38   */
39  @GwtCompatible
40  public abstract class AbstractCache<K, V> implements Cache<K, V> {
41  
42    /** Constructor for use by subclasses. */
43    protected AbstractCache() {}
44  
45    /**
46     * @since 11.0
47     */
48    @Override
49    public V get(K key, Callable<? extends V> valueLoader) throws ExecutionException {
50      throw new UnsupportedOperationException();
51    }
52  
53    /**
54     * {@inheritDoc}
55     *
56     * <p>This implementation of {@code getAllPresent} lacks any insight into the internal cache data
57     * structure, and is thus forced to return the query keys instead of the cached keys. This is only
58     * possible with an unsafe cast which requires {@code keys} to actually be of type {@code K}.
59     *
60     * @since 11.0
61     */
62    @Override
63    public ImmutableMap<K, V> getAllPresent(Iterable<?> keys) {
64      Map<K, V> result = Maps.newLinkedHashMap();
65      for (Object key : keys) {
66        if (!result.containsKey(key)) {
67          @SuppressWarnings("unchecked")
68          K castKey = (K) key;
69          V value = getIfPresent(key);
70          if (value != null) {
71            result.put(castKey, value);
72          }
73        }
74      }
75      return ImmutableMap.copyOf(result);
76    }
77  
78    /**
79     * @since 11.0
80     */
81    @Override
82    public void put(K key, V value) {
83      throw new UnsupportedOperationException();
84    }
85  
86    /**
87     * @since 12.0
88     */
89    @Override
90    public void putAll(Map<? extends K, ? extends V> m) {
91      for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
92        put(entry.getKey(), entry.getValue());
93      }
94    }
95  
96    @Override
97    public void cleanUp() {}
98  
99    @Override
100   public long size() {
101     throw new UnsupportedOperationException();
102   }
103 
104   @Override
105   public void invalidate(Object key) {
106     throw new UnsupportedOperationException();
107   }
108 
109   /**
110    * @since 11.0
111    */
112   @Override
113   public void invalidateAll(Iterable<?> keys) {
114     for (Object key : keys) {
115       invalidate(key);
116     }
117   }
118 
119   @Override
120   public void invalidateAll() {
121     throw new UnsupportedOperationException();
122   }
123 
124   @Override
125   public CacheStats stats() {
126     throw new UnsupportedOperationException();
127   }
128 
129   @Override
130   public ConcurrentMap<K, V> asMap() {
131     throw new UnsupportedOperationException();
132   }
133 
134   /**
135    * Accumulates statistics during the operation of a {@link Cache} for presentation by
136    * {@link Cache#stats}. This is solely intended for consumption by {@code Cache} implementors.
137    *
138    * @since 10.0
139    */
140   public interface StatsCounter {
141     /**
142      * Records cache hits. This should be called when a cache request returns a cached value.
143      *
144      * @param count the number of hits to record
145      * @since 11.0
146      */
147     void recordHits(int count);
148 
149     /**
150      * Records cache misses. This should be called when a cache request returns a value that was not
151      * found in the cache. This method should be called by the loading thread, as well as by threads
152      * blocking on the load. Multiple concurrent calls to {@link Cache} lookup methods with the same
153      * key on an absent value should result in a single call to either {@code recordLoadSuccess} or
154      * {@code recordLoadException} and multiple calls to this method, despite all being served by
155      * the results of a single load operation.
156      *
157      * @param count the number of misses to record
158      * @since 11.0
159      */
160     void recordMisses(int count);
161 
162     /**
163      * Records the successful load of a new entry. This should be called when a cache request causes
164      * an entry to be loaded, and the loading completes successfully. In contrast to
165      * {@link #recordMisses}, this method should only be called by the loading thread.
166      *
167      * @param loadTime the number of nanoseconds the cache spent computing or retrieving the new
168      *     value
169      */
170     void recordLoadSuccess(long loadTime);
171 
172     /**
173      * Records the failed load of a new entry. This should be called when a cache request causes an
174      * entry to be loaded, but an exception is thrown while loading the entry. In contrast to
175      * {@link #recordMisses}, this method should only be called by the loading thread.
176      *
177      * @param loadTime the number of nanoseconds the cache spent computing or retrieving the new
178      *     value prior to an exception being thrown
179      */
180     void recordLoadException(long loadTime);
181 
182     /**
183      * Records the eviction of an entry from the cache. This should only been called when an entry
184      * is evicted due to the cache's eviction strategy, and not as a result of manual
185      * {@linkplain Cache#invalidate invalidations}.
186      */
187     void recordEviction();
188 
189     /**
190      * Returns a snapshot of this counter's values. Note that this may be an inconsistent view, as
191      * it may be interleaved with update operations.
192      */
193     CacheStats snapshot();
194   }
195 
196   /**
197    * A thread-safe {@link StatsCounter} implementation for use by {@link Cache} implementors.
198    *
199    * @since 10.0
200    */
201   public static final class SimpleStatsCounter implements StatsCounter {
202     private final LongAddable hitCount = LongAddables.create();
203     private final LongAddable missCount = LongAddables.create();
204     private final LongAddable loadSuccessCount = LongAddables.create();
205     private final LongAddable loadExceptionCount = LongAddables.create();
206     private final LongAddable totalLoadTime = LongAddables.create();
207     private final LongAddable evictionCount = LongAddables.create();
208 
209     /**
210      * Constructs an instance with all counts initialized to zero.
211      */
212     public SimpleStatsCounter() {}
213 
214     /**
215      * @since 11.0
216      */
217     @Override
218     public void recordHits(int count) {
219       hitCount.add(count);
220     }
221 
222     /**
223      * @since 11.0
224      */
225     @Override
226     public void recordMisses(int count) {
227       missCount.add(count);
228     }
229 
230     @Override
231     public void recordLoadSuccess(long loadTime) {
232       loadSuccessCount.increment();
233       totalLoadTime.add(loadTime);
234     }
235 
236     @Override
237     public void recordLoadException(long loadTime) {
238       loadExceptionCount.increment();
239       totalLoadTime.add(loadTime);
240     }
241 
242     @Override
243     public void recordEviction() {
244       evictionCount.increment();
245     }
246 
247     @Override
248     public CacheStats snapshot() {
249       return new CacheStats(
250           hitCount.sum(),
251           missCount.sum(),
252           loadSuccessCount.sum(),
253           loadExceptionCount.sum(),
254           totalLoadTime.sum(),
255           evictionCount.sum());
256     }
257 
258     /**
259      * Increments all counters by the values in {@code other}.
260      */
261     public void incrementBy(StatsCounter other) {
262       CacheStats otherStats = other.snapshot();
263       hitCount.add(otherStats.hitCount());
264       missCount.add(otherStats.missCount());
265       loadSuccessCount.add(otherStats.loadSuccessCount());
266       loadExceptionCount.add(otherStats.loadExceptionCount());
267       totalLoadTime.add(otherStats.totalLoadTime());
268       evictionCount.add(otherStats.evictionCount());
269     }
270   }
271 }