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