001 /*
002 * Copyright (C) 2011 The Guava Authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017 package com.google.common.cache;
018
019 import com.google.common.annotations.Beta;
020 import com.google.common.annotations.GwtCompatible;
021 import com.google.common.collect.ImmutableMap;
022 import com.google.common.collect.Maps;
023
024 import java.util.Map;
025 import java.util.concurrent.Callable;
026 import java.util.concurrent.ConcurrentMap;
027 import java.util.concurrent.ExecutionException;
028 import java.util.concurrent.atomic.AtomicLong;
029
030 /**
031 * This class provides a skeletal implementation of the {@code Cache} interface to minimize the
032 * effort required to implement this interface.
033 *
034 * <p>To implement a cache, the programmer needs only to extend this class and provide an
035 * implementation for the {@link #put} and {@link #getIfPresent} methods. {@link #getAllPresent} is
036 * implemented in terms of {@link #getIfPresent}; {@link #putAll} is implemented in terms of
037 * {@link #put}, {@link #invalidateAll(Iterable)} is implemented in terms of {@link #invalidate}.
038 * The method {@link #cleanUp} is a no-op. All other methods throw an
039 * {@link UnsupportedOperationException}.
040 *
041 * @author Charles Fry
042 * @since 10.0
043 */
044 @Beta
045 @GwtCompatible
046 public abstract class AbstractCache<K, V> implements Cache<K, V> {
047
048 /** Constructor for use by subclasses. */
049 protected AbstractCache() {}
050
051 /**
052 * @since 11.0
053 */
054 @Override
055 public V get(K key, Callable<? extends V> valueLoader) throws ExecutionException {
056 throw new UnsupportedOperationException();
057 }
058
059 /**
060 * This implementation of {@code getAllPresent} lacks any insight into the internal cache data
061 * structure, and is thus forced to return the query keys instead of the cached keys. This is only
062 * possible with an unsafe cast which requires {@code keys} to actually be of type {@code K}.
063 *
064 * {@inheritDoc}
065 *
066 * @since 11.0
067 */
068 @Override
069 public ImmutableMap<K, V> getAllPresent(Iterable<?> keys) {
070 Map<K, V> result = Maps.newLinkedHashMap();
071 for (Object key : keys) {
072 if (!result.containsKey(key)) {
073 @SuppressWarnings("unchecked")
074 K castKey = (K) key;
075 result.put(castKey, getIfPresent(key));
076 }
077 }
078 return ImmutableMap.copyOf(result);
079 }
080
081 /**
082 * @since 11.0
083 */
084 @Override
085 public void put(K key, V value) {
086 throw new UnsupportedOperationException();
087 }
088
089 /**
090 * @since 12.0
091 */
092 @Override
093 public void putAll(Map<? extends K, ? extends V> m) {
094 for (Map.Entry<? extends K, ? extends V> entry : m.entrySet()) {
095 put(entry.getKey(), entry.getValue());
096 }
097 }
098
099 @Override
100 public void cleanUp() {}
101
102 @Override
103 public long size() {
104 throw new UnsupportedOperationException();
105 }
106
107 @Override
108 public void invalidate(Object key) {
109 throw new UnsupportedOperationException();
110 }
111
112 /**
113 * @since 11.0
114 */
115 @Override
116 public void invalidateAll(Iterable<?> keys) {
117 for (Object key : keys) {
118 invalidate(key);
119 }
120 }
121
122 @Override
123 public void invalidateAll() {
124 throw new UnsupportedOperationException();
125 }
126
127 @Override
128 public CacheStats stats() {
129 throw new UnsupportedOperationException();
130 }
131
132 @Override
133 public ConcurrentMap<K, V> asMap() {
134 throw new UnsupportedOperationException();
135 }
136
137 /**
138 * Accumulates statistics during the operation of a {@link Cache} for presentation by {@link
139 * Cache#stats}. This is solely intended for consumption by {@code Cache} implementors.
140 *
141 * @since 10.0
142 */
143 @Beta
144 public interface StatsCounter {
145 /**
146 * Records cache hits. This should be called when a cache request returns a cached value.
147 *
148 * @param count the number of hits to record
149 * @since 11.0
150 */
151 public void recordHits(int count);
152
153 /**
154 * Records cache misses. This should be called when a cache request returns a value that was
155 * not found in the cache. This method should be called by the loading thread, as well as by
156 * threads blocking on the load. Multiple concurrent calls to {@link Cache} lookup methods with
157 * the same key on an absent value should result in a single call to either
158 * {@code recordLoadSuccess} or {@code recordLoadException} and multiple calls to this method,
159 * despite all being served by the results of a single load operation.
160 *
161 * @param count the number of misses to record
162 * @since 11.0
163 */
164 public void recordMisses(int count);
165
166 /**
167 * Records the successful load of a new entry. This should be called when a cache request
168 * causes an entry to be loaded, and the loading completes successfully. In contrast to
169 * {@link #recordMisses}, this method should only be called by the loading thread.
170 *
171 * @param loadTime the number of nanoseconds the cache spent computing or retrieving the new
172 * value
173 */
174 public void recordLoadSuccess(long loadTime);
175
176 /**
177 * Records the failed load of a new entry. This should be called when a cache request causes
178 * an entry to be loaded, but an exception is thrown while loading the entry. In contrast to
179 * {@link #recordMisses}, this method should only be called by the loading thread.
180 *
181 * @param loadTime the number of nanoseconds the cache spent computing or retrieving the new
182 * value prior to an exception being thrown
183 */
184 public void recordLoadException(long loadTime);
185
186 /**
187 * Records the eviction of an entry from the cache. This should only been called when an entry
188 * is evicted due to the cache's eviction strategy, and not as a result of manual {@linkplain
189 * Cache#invalidate invalidations}.
190 */
191 public void recordEviction();
192
193 /**
194 * Returns a snapshot of this counter's values. Note that this may be an inconsistent view, as
195 * it may be interleaved with update operations.
196 */
197 public CacheStats snapshot();
198 }
199
200 /**
201 * A thread-safe {@link StatsCounter} implementation for use by {@link Cache} implementors.
202 *
203 * @since 10.0
204 */
205 @Beta
206 public static class SimpleStatsCounter implements StatsCounter {
207 private final AtomicLong hitCount = new AtomicLong();
208 private final AtomicLong missCount = new AtomicLong();
209 private final AtomicLong loadSuccessCount = new AtomicLong();
210 private final AtomicLong loadExceptionCount = new AtomicLong();
211 private final AtomicLong totalLoadTime = new AtomicLong();
212 private final AtomicLong evictionCount = new AtomicLong();
213
214 /**
215 * @since 11.0
216 */
217 @Override
218 public void recordHits(int count) {
219 hitCount.addAndGet(count);
220 }
221
222 /**
223 * @since 11.0
224 */
225 @Override
226 public void recordMisses(int count) {
227 missCount.addAndGet(count);
228 }
229
230 @Override
231 public void recordLoadSuccess(long loadTime) {
232 loadSuccessCount.incrementAndGet();
233 totalLoadTime.addAndGet(loadTime);
234 }
235
236 @Override
237 public void recordLoadException(long loadTime) {
238 loadExceptionCount.incrementAndGet();
239 totalLoadTime.addAndGet(loadTime);
240 }
241
242 @Override
243 public void recordEviction() {
244 evictionCount.incrementAndGet();
245 }
246
247 @Override
248 public CacheStats snapshot() {
249 return new CacheStats(
250 hitCount.get(),
251 missCount.get(),
252 loadSuccessCount.get(),
253 loadExceptionCount.get(),
254 totalLoadTime.get(),
255 evictionCount.get());
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.addAndGet(otherStats.hitCount());
264 missCount.addAndGet(otherStats.missCount());
265 loadSuccessCount.addAndGet(otherStats.loadSuccessCount());
266 loadExceptionCount.addAndGet(otherStats.loadExceptionCount());
267 totalLoadTime.addAndGet(otherStats.totalLoadTime());
268 evictionCount.addAndGet(otherStats.evictionCount());
269 }
270 }
271 }