    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.util.concurrent.UncheckedExecutionException;
    021
    022    import java.util.concurrent.ConcurrentMap;
    023    import java.util.concurrent.ExecutionException;
    024    import java.util.concurrent.atomic.AtomicLong;
    025
    026    /**
    027     * This class provides a skeletal implementation of the {@code Cache} interface to minimize the
    028     * effort required to implement this interface.
    029     *
    030     * <p>To implement a cache, the programmer needs only to extend this class and provide an
    031     * implementation for the {@code get} method. This implementation throws an
    032     * {@link UnsupportedOperationException} on calls to {@link #size}, {@link #invalidate},
    033     * {@link #invalidateAll}, {@link #stats}, and {@link #asMap}. The methods
    034     * {@link #getUnchecked} and {@link #apply} are implemented in terms of {@link #get}. The method
    035     * {@link #cleanUp} is a no-op.
    036     *
    037     * @author Charles Fry
    038     * @since 10.0
    039     */
    040    @Beta
    041    public abstract class AbstractCache<K, V> implements Cache<K, V> {
    042
    043      /** Constructor for use by subclasses. */
    044      protected AbstractCache() {}
    045
    046      @Override
    047      public V getUnchecked(K key) {
    048        try {
    049          return get(key);
    050        } catch (ExecutionException e) {
    051          throw new UncheckedExecutionException(e.getCause());
    052        }
    053      }
    054
    055      @Override
    056      public final V apply(K key) {
    057        return getUnchecked(key);
    058      }
    059
    060      @Override
    061      public void cleanUp() {}
    062
    063      @Override
    064      public long size() {
    065        throw new UnsupportedOperationException();
    066      }
    067
    068      @Override
    069      public void invalidate(Object key) {
    070        throw new UnsupportedOperationException();
    071      }
    072
    073      @Override
    074      public void invalidateAll() {
    075        throw new UnsupportedOperationException();
    076      }
    077
    078      @Override
    079      public CacheStats stats() {
    080        throw new UnsupportedOperationException();
    081      }
    082
    083      @Override
    084      public ConcurrentMap<K, V> asMap() {
    085        throw new UnsupportedOperationException();
    086      }
    087
    088      /**
    089       * Accumulates statistics during the operation of a {@link Cache} for presentation by {@link
    090       * Cache#stats}. This is solely intended for consumption by {@code Cache} implementors.
    091       *
    092       * @since 10.0
    093       */
    094      @Beta
    095      public interface StatsCounter {
    096        /**
    097         * Records a single hit. This should be called when a cache request returns a cached value.
    098         */
    099        public void recordHit();
    100
    101        /**
    102         * Records the successful load of a new entry. This should be called when a cache request
    103         * causes an entry to be loaded, and the loading completes succesfully. In contrast to
    104         * {@link #recordConcurrentMiss}, this method should only be called by the loading thread.
    105         *
    106         * @param loadTime the number of nanoseconds the cache spent computing or retrieving the new
    107         *     value
    108         */
    109        public void recordLoadSuccess(long loadTime);
    110
    111        /**
    112         * Records the failed load of a new entry. This should be called when a cache request causes
    113         * an entry to be loaded, but an exception is thrown while loading the entry. In contrast to
    114         * {@link #recordConcurrentMiss}, this method should only be called by the loading thread.
    115         *
    116         * @param loadTime the number of nanoseconds the cache spent computing or retrieving the new
    117         *     value prior to an exception being thrown
    118         */
    119        public void recordLoadException(long loadTime);
    120
    121        /**
    122         * Records a single concurrent miss. This should be called when a cache request returns a
    123         * value which was loaded by a different thread. In contrast to {@link #recordLoadSuccess}
    124         * and {@link #recordLoadException}, this method should never be called by the loading
    125         * thread. Multiple concurrent calls to {@link Cache} lookup methods with the same key on an
    126         * absent value should result in a single call to either {@code recordLoadSuccess} or
    127         * {@code recordLoadException} and multiple calls to this method, despite all being served by
    128         * the results of a single load operation.
    129         */
    130        public void recordConcurrentMiss();
    131
    132        /**
    133         * Records the eviction of an entry from the cache. This should only been called when an entry
    134         * is evicted due to the cache's eviction strategy, and not as a result of manual {@linkplain
    135         * Cache#invalidate invalidations}.
    136         */
    137        public void recordEviction();
    138
    139        /**
    140         * Returns a snapshot of this counter's values. Note that this may be an inconsistent view, as
    141         * it may be interleaved with update operations.
    142         */
    143        public CacheStats snapshot();
    144      }
    145
    146      /**
    147       * A thread-safe {@link StatsCounter} implementation for use by {@link Cache} implementors.
    148       *
    149       * @since 10.0
    150       */
    151      @Beta
    152      public static class SimpleStatsCounter implements StatsCounter {
    153        private final AtomicLong hitCount = new AtomicLong();
    154        private final AtomicLong missCount = new AtomicLong();
    155        private final AtomicLong loadSuccessCount = new AtomicLong();
    156        private final AtomicLong loadExceptionCount = new AtomicLong();
    157        private final AtomicLong totalLoadTime = new AtomicLong();
    158        private final AtomicLong evictionCount = new AtomicLong();
    159
    160        @Override
    161        public void recordHit() {
    162          hitCount.incrementAndGet();
    163        }
    164
    165        @Override
    166        public void recordLoadSuccess(long loadTime) {
    167          missCount.incrementAndGet();
    168          loadSuccessCount.incrementAndGet();
    169          totalLoadTime.addAndGet(loadTime);
    170        }
    171
    172        @Override
    173        public void recordLoadException(long loadTime) {
    174          missCount.incrementAndGet();
    175          loadExceptionCount.incrementAndGet();
    176          totalLoadTime.addAndGet(loadTime);
    177        }
    178
    179        @Override
    180        public void recordConcurrentMiss() {
    181          missCount.incrementAndGet();
    182        }
    183
    184        @Override
    185        public void recordEviction() {
    186          evictionCount.incrementAndGet();
    187        }
    188
    189        @Override
    190        public CacheStats snapshot() {
    191          return new CacheStats(
    192              hitCount.get(),
    193              missCount.get(),
    194              loadSuccessCount.get(),
    195              loadExceptionCount.get(),
    196              totalLoadTime.get(),
    197              evictionCount.get());
    198        }
    199
    200        /**
    201         * Increments all counters by the values in {@code other}.
    202         */
    203        public void incrementBy(StatsCounter other) {
    204          CacheStats otherStats = other.snapshot();
    205          hitCount.addAndGet(otherStats.hitCount());
    206          missCount.addAndGet(otherStats.missCount());
    207          loadSuccessCount.addAndGet(otherStats.loadSuccessCount());
    208          loadExceptionCount.addAndGet(otherStats.loadExceptionCount());
    209          totalLoadTime.addAndGet(otherStats.totalLoadTime());
    210          evictionCount.addAndGet(otherStats.evictionCount());
    211        }
    212      }
    213    }