• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Guava Authors
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 com.google.common.cache;
18 
19 import com.google.common.annotations.Beta;
20 import com.google.common.annotations.GwtCompatible;
21 import com.google.common.collect.ImmutableMap;
22 import com.google.common.collect.Maps;
23 import com.google.common.util.concurrent.UncheckedExecutionException;
24 
25 import java.util.Map;
26 import java.util.concurrent.Callable;
27 import java.util.concurrent.ConcurrentMap;
28 import java.util.concurrent.ExecutionException;
29 import java.util.concurrent.atomic.AtomicLong;
30 
31 /**
32  * This class provides a skeletal implementation of the {@code Cache} interface to minimize the
33  * effort required to implement this interface.
34  *
35  * <p>To implement a cache, the programmer needs only to extend this class and provide an
36  * implementation for the {@link #getIfPresent} method. {@link #getAllPresent} is implemented in
37  * terms of {@code getIfPresent}; {@link #invalidateAll(Iterable)} is implemented in terms of
38  * {@link #invalidate}. The method {@link #cleanUp} is a no-op. All other methods throw an
39  * {@link UnsupportedOperationException}.
40  *
41  * @author Charles Fry
42  * @since 10.0
43  */
44 @Beta
45 @GwtCompatible
46 public abstract class AbstractCache<K, V> implements Cache<K, V> {
47 
48   /** Constructor for use by subclasses. */
AbstractCache()49   protected AbstractCache() {}
50 
51   /**
52    * @since 11.0
53    */
54   @Override
get(K key, Callable<? extends V> valueLoader)55   public V get(K key, Callable<? extends V> valueLoader) throws ExecutionException {
56     throw new UnsupportedOperationException();
57   }
58 
59   /**
60    * @since 11.0
61    */
62   @Override
getAllPresent(Iterable<? extends K> keys)63   public ImmutableMap<K, V> getAllPresent(Iterable<? extends K> keys) {
64     Map<K, V> result = Maps.newLinkedHashMap();
65     for (K key : keys) {
66       if (!result.containsKey(key)) {
67         result.put(key, getIfPresent(key));
68       }
69     }
70     return ImmutableMap.copyOf(result);
71   }
72 
73   /**
74    * @since 11.0
75    */
76   @Override
put(K key, V value)77   public void put(K key, V value) {
78     throw new UnsupportedOperationException();
79   }
80 
81   @Override
cleanUp()82   public void cleanUp() {}
83 
84   @Override
size()85   public long size() {
86     throw new UnsupportedOperationException();
87   }
88 
89   @Override
invalidate(Object key)90   public void invalidate(Object key) {
91     throw new UnsupportedOperationException();
92   }
93 
94   /**
95    * @since 11.0
96    */
97   @Override
invalidateAll(Iterable<?> keys)98   public void invalidateAll(Iterable<?> keys) {
99     for (Object key : keys) {
100       invalidate(key);
101     }
102   }
103 
104   @Override
invalidateAll()105   public void invalidateAll() {
106     throw new UnsupportedOperationException();
107   }
108 
109   @Override
stats()110   public CacheStats stats() {
111     throw new UnsupportedOperationException();
112   }
113 
114   @Override
asMap()115   public ConcurrentMap<K, V> asMap() {
116     throw new UnsupportedOperationException();
117   }
118 
119   @Deprecated
120   @Override
getUnchecked(K key)121   public V getUnchecked(K key) {
122     try {
123       return get(key);
124     } catch (ExecutionException e) {
125       throw new UncheckedExecutionException(e.getCause());
126     }
127   }
128 
129   @Deprecated
130   @Override
apply(K key)131   public V apply(K key) {
132     return getUnchecked(key);
133   }
134 
135   /**
136    * Accumulates statistics during the operation of a {@link Cache} for presentation by {@link
137    * Cache#stats}. This is solely intended for consumption by {@code Cache} implementors.
138    *
139    * @since 10.0
140    */
141   @Beta
142   public interface StatsCounter {
143     /**
144      * Records cache hits. This should be called when a cache request returns a cached value.
145      *
146      * @param count the number of hits to record
147      * @since 11.0
148      */
recordHits(int count)149     public void recordHits(int count);
150 
151     /**
152      * Records cache misses. This should be called when a cache request returns a value that was
153      * not found in the cache. This method should be called by the loading thread, as well as by
154      * threads blocking on the load. Multiple concurrent calls to {@link Cache} lookup methods with
155      * the same key on an absent value should result in a single call to either
156      * {@code recordLoadSuccess} or {@code recordLoadException} and multiple calls to this method,
157      * despite all being served by the results of a single load operation.
158      *
159      * @param count the number of misses to record
160      * @since 11.0
161      */
recordMisses(int count)162     public void recordMisses(int count);
163 
164     /**
165      * Records the successful load of a new entry. This should be called when a cache request
166      * causes an entry to be loaded, and the loading completes successfully. In contrast to
167      * {@link #recordConcurrentMiss}, this method should only be called by the loading thread.
168      *
169      * @param loadTime the number of nanoseconds the cache spent computing or retrieving the new
170      *     value
171      */
recordLoadSuccess(long loadTime)172     public void recordLoadSuccess(long loadTime);
173 
174     /**
175      * Records the failed load of a new entry. This should be called when a cache request causes
176      * an entry to be loaded, but an exception is thrown while loading the entry. In contrast to
177      * {@link #recordConcurrentMiss}, this method should only be called by the loading thread.
178      *
179      * @param loadTime the number of nanoseconds the cache spent computing or retrieving the new
180      *     value prior to an exception being thrown
181      */
recordLoadException(long loadTime)182     public void recordLoadException(long loadTime);
183 
184     /**
185      * Records the eviction of an entry from the cache. This should only been called when an entry
186      * is evicted due to the cache's eviction strategy, and not as a result of manual {@linkplain
187      * Cache#invalidate invalidations}.
188      */
recordEviction()189     public void recordEviction();
190 
191     /**
192      * Returns a snapshot of this counter's values. Note that this may be an inconsistent view, as
193      * it may be interleaved with update operations.
194      */
snapshot()195     public CacheStats snapshot();
196   }
197 
198   /**
199    * A thread-safe {@link StatsCounter} implementation for use by {@link Cache} implementors.
200    *
201    * @since 10.0
202    */
203   @Beta
204   public static class SimpleStatsCounter implements StatsCounter {
205     private final AtomicLong hitCount = new AtomicLong();
206     private final AtomicLong missCount = new AtomicLong();
207     private final AtomicLong loadSuccessCount = new AtomicLong();
208     private final AtomicLong loadExceptionCount = new AtomicLong();
209     private final AtomicLong totalLoadTime = new AtomicLong();
210     private final AtomicLong evictionCount = new AtomicLong();
211 
212     /**
213      * @since 11.0
214      */
215     @Override
recordHits(int count)216     public void recordHits(int count) {
217       hitCount.addAndGet(count);
218     }
219 
220     /**
221      * @since 11.0
222      */
223     @Override
recordMisses(int count)224     public void recordMisses(int count) {
225       missCount.addAndGet(count);
226     }
227 
228     @Override
recordLoadSuccess(long loadTime)229     public void recordLoadSuccess(long loadTime) {
230       loadSuccessCount.incrementAndGet();
231       totalLoadTime.addAndGet(loadTime);
232     }
233 
234     @Override
recordLoadException(long loadTime)235     public void recordLoadException(long loadTime) {
236       loadExceptionCount.incrementAndGet();
237       totalLoadTime.addAndGet(loadTime);
238     }
239 
240     @Override
recordEviction()241     public void recordEviction() {
242       evictionCount.incrementAndGet();
243     }
244 
245     @Override
snapshot()246     public CacheStats snapshot() {
247       return new CacheStats(
248           hitCount.get(),
249           missCount.get(),
250           loadSuccessCount.get(),
251           loadExceptionCount.get(),
252           totalLoadTime.get(),
253           evictionCount.get());
254     }
255 
256     /**
257      * Increments all counters by the values in {@code other}.
258      */
incrementBy(StatsCounter other)259     public void incrementBy(StatsCounter other) {
260       CacheStats otherStats = other.snapshot();
261       hitCount.addAndGet(otherStats.hitCount());
262       missCount.addAndGet(otherStats.missCount());
263       loadSuccessCount.addAndGet(otherStats.loadSuccessCount());
264       loadExceptionCount.addAndGet(otherStats.loadExceptionCount());
265       totalLoadTime.addAndGet(otherStats.totalLoadTime());
266       evictionCount.addAndGet(otherStats.evictionCount());
267     }
268   }
269 }
270