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