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