11d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/* 21d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Copyright (C) 2011 The Guava Authors 31d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 41d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License"); 51d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * you may not use this file except in compliance with the License. 61d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * You may obtain a copy of the License at 71d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 81d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * http://www.apache.org/licenses/LICENSE-2.0 91d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Unless required by applicable law or agreed to in writing, software 111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS, 121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * See the License for the specific language governing permissions and 141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * limitations under the License. 151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpackage com.google.common.cache; 181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport static com.google.common.base.Preconditions.checkArgument; 201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport static com.google.common.base.Preconditions.checkNotNull; 211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport static com.google.common.base.Preconditions.checkState; 221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.base.Function; 241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.cache.CacheLoader.InvalidCacheLoadException; 251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.collect.ImmutableMap; 261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.util.concurrent.ExecutionError; 271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.common.util.concurrent.UncheckedExecutionException; 281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport com.google.gwt.user.client.Timer; 291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.LinkedHashMap; 311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.Map; 321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.concurrent.ConcurrentHashMap; 331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.concurrent.ConcurrentMap; 341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.concurrent.ExecutionException; 351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport java.util.concurrent.TimeUnit; 361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertimport javax.annotation.Nullable; 381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert/** 401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * CacheBuilder emulation. 411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * @author Charles Fry 431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert// TODO(fry): eventually we should emmulate LocalCache instead of CacheBuilder 451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringertpublic class CacheBuilder<K, V> { 461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private static final int UNSET_INT = -1; 471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private static final int DEFAULT_INITIAL_CAPACITY = 16; 481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private static final int DEFAULT_EXPIRATION_NANOS = 0; 491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private int initialCapacity = -1; 511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private int concurrencyLevel = -1; 521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private long expirationMillis = -1; 531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private int maximumSize = -1; 541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert CacheBuilder() {} 561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public static CacheBuilder<Object, Object> newBuilder() { 581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return new CacheBuilder<Object, Object>(); 591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public CacheBuilder<K, V> initialCapacity(int initialCapacity) { 621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert checkState(this.initialCapacity == UNSET_INT, "initial capacity was already set to %s", 631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert this.initialCapacity); 641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert checkArgument(initialCapacity >= 0); 651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert this.initialCapacity = initialCapacity; 661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return this; 671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private int getInitialCapacity() { 701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return (initialCapacity == UNSET_INT) ? DEFAULT_INITIAL_CAPACITY : initialCapacity; 711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public CacheBuilder<K, V> concurrencyLevel(int concurrencyLevel) { 741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert checkState(this.concurrencyLevel == UNSET_INT, "concurrency level was already set to %s", 751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert this.concurrencyLevel); 761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert checkArgument(concurrencyLevel > 0); 771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert // GWT technically only supports concurrencyLevel == 1, but we silently 781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert // ignore other positive values. 791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert this.concurrencyLevel = concurrencyLevel; 801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return this; 811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public CacheBuilder<K, V> expireAfterWrite(long duration, TimeUnit unit) { 841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert checkState(expirationMillis == UNSET_INT, "expireAfterWrite was already set to %s ms", 851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert expirationMillis); 861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert checkArgument(duration >= 0, "duration cannot be negative: %s %s", duration, unit); 871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert this.expirationMillis = unit.toMillis(duration); 881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return this; 891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private long getExpirationMillis() { 921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return (expirationMillis == UNSET_INT) ? DEFAULT_EXPIRATION_NANOS : expirationMillis; 931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public CacheBuilder<K, V> maximumSize(int maximumSize) { 961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (this.maximumSize != -1) { 971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert throw new IllegalStateException("maximum size of " + maximumSize + " was already set"); 981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (maximumSize < 0) { 1001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert throw new IllegalArgumentException("invalid maximum size: " + maximumSize); 1011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert this.maximumSize = maximumSize; 1031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return this; 1041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public <K1 extends K, V1 extends V> Cache<K1, V1> build() { 1071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return new LocalManualCache<K1, V1>(this); 1081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public <K1 extends K, V1 extends V> LoadingCache<K1, V1> build( 1111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert CacheLoader<? super K1, V1> loader) { 1121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return new LocalLoadingCache<K1, V1>(this, loader); 1131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private static class LocalManualCache<K, V> 1161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert extends AbstractCache<K, V> implements Function<K, V> { 1171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert final LocalCache<K, V> localCache; 1181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert LocalManualCache(CacheBuilder<? super K, ? super V> builder) { 1201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert this(builder, null); 1211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert protected LocalManualCache(CacheBuilder<? super K, ? super V> builder, 1241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert CacheLoader<? super K, V> loader) { 1251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert this.localCache = new LocalCache<K, V>(builder, loader); 1261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert // Cache methods 1291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 1311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public V get(K key) throws ExecutionException { 1321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return localCache.getOrLoad(key); 1331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 1361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public V getUnchecked(K key) { 1371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert try { 1381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return get(key); 1391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } catch (ExecutionException e) { 1401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert throw new UncheckedExecutionException(e.getCause()); 1411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 1451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public final V apply(K key) { 1461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return getUnchecked(key); 1471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 1501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Nullable 1511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public V getIfPresent(K key) { 1521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return localCache.get(key); 1531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 1561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public void put(K key, V value) { 1571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert localCache.put(key, value); 1581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 1611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public void invalidate(Object key) { 1621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert checkNotNull(key); 1631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert localCache.remove(key); 1641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 1671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public void invalidateAll() { 1681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert localCache.clear(); 1691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 1721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public long size() { 1731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return localCache.size(); 1741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 1771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public ConcurrentMap<K, V> asMap() { 1781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return localCache; 1791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private static class LocalLoadingCache<K, V> 1831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert extends LocalManualCache<K, V> implements LoadingCache<K, V> { 1841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert LocalLoadingCache(CacheBuilder<? super K, ? super V> builder, 1861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert CacheLoader<? super K, V> loader) { 1871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert super(builder, checkNotNull(loader)); 1881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert // Cache methods 1911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 1931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public ImmutableMap<K, V> getAll(Iterable<? extends K> keys) throws ExecutionException { 1941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert throw new UnsupportedOperationException(); 1951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 1961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 1971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 1981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public void refresh(K key) { 1991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert throw new UnsupportedOperationException(); 2001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert // TODO(fry,user): ConcurrentHashMap never throws a CME when mutating the map during iteration, but 2041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert // this implementation (based on a LHM) does. This will all be replaced soon anyways, so leaving 2051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert // it as is for now. 2061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private static class LocalCache<K, V> extends LinkedHashMap<K, V> 2071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert implements ConcurrentMap<K, V> { 2081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private final CacheLoader<? super K, V> loader; 2091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private final long expirationMillis; 2101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private final int maximumSize; 2111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert LocalCache(CacheBuilder<? super K, ? super V> builder, CacheLoader<? super K, V> loader) { 2131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert super(builder.getInitialCapacity(), 0.75f, (builder.maximumSize != UNSET_INT)); 2141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert this.loader = loader; 2151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert this.expirationMillis = builder.getExpirationMillis(); 2161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert this.maximumSize = builder.maximumSize; 2171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 2201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public V put(K key, V value) { 2211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert V result = super.put(key, value); 2221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (expirationMillis > 0) { 2231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert scheduleRemoval(key, value); 2241d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2251d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return result; 2261d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2271d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2281d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 2291d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert protected boolean removeEldestEntry(Map.Entry<K, V> ignored) { 2301d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return (maximumSize == -1) ? false : size() > maximumSize; 2311d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2321d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2331d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 2341d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public V putIfAbsent(K key, V value) { 2351d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (!containsKey(key)) { 2361d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return put(key, value); 2371d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } else { 2381d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return get(key); 2391d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2401d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2411d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2421d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 2431d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public boolean remove(Object key, Object value) { 2441d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (containsKey(key) && get(key).equals(value)) { 2451d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert remove(key); 2461d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return true; 2471d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2481d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return false; 2491d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2501d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2511d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 2521d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public boolean replace(K key, V oldValue, V newValue) { 2531d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (containsKey(key) && get(key).equals(oldValue)) { 2541d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert put(key, newValue); 2551d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return true; 2561d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2571d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return false; 2581d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2591d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2601d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 2611d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public V replace(K key, V value) { 2621d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return containsKey(key) ? put(key, value) : null; 2631d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2641d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2651d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private void scheduleRemoval(final K key, final V value) { 2661d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /* 2671d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * TODO: Keep weak reference to map, too. Build a priority queue out of the entries themselves 2681d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * instead of creating a task per entry. Then, we could have one recurring task per map (which 2691d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * would clean the entire map and then reschedule itself depending upon when the next 2701d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * expiration comes). We also want to avoid removing an entry prematurely if the entry was set 2711d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * to the same value again. 2721d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 2731d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert Timer timer = new Timer() { 2741d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @Override 2751d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public void run() { 2761d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert remove(key, value); 2771d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2781d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert }; 2791d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert timer.schedule((int) expirationMillis); 2801d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2811d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 2821d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert public V getOrLoad(Object k) throws ExecutionException { 2831d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert // from CustomConcurrentHashMap 2841d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert V result = super.get(k); 2851d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (result == null) { 2861d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert /* 2871d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * This cast isn't safe, but we can rely on the fact that K is almost always passed to 2881d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * Map.get(), and tools like IDEs and Findbugs can catch situations where this isn't the 2891d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * case. 2901d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * 2911d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * The alternative is to add an overloaded method, but the chances of a user calling get() 2921d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * instead of the new API and the risks inherent in adding a new API outweigh this little 2931d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert * hole. 2941d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert */ 2951d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert @SuppressWarnings("unchecked") 2961d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert K key = (K) k; 2971d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert result = compute(key); 2981d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 2991d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return result; 3001d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 3011d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 3021d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert private V compute(K key) throws ExecutionException { 3031d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert V value; 3041d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert try { 3051d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert value = loader.load(key); 3061d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } catch (RuntimeException e) { 3071d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert throw new UncheckedExecutionException(e); 3081d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } catch (Exception e) { 3091d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert throw new ExecutionException(e); 3101d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } catch (Error e) { 3111d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert throw new ExecutionError(e); 3121d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 3131d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 3141d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert if (value == null) { 3151d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert String message = loader + " returned null for key " + key + "."; 3161d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert throw new InvalidCacheLoadException(message); 3171d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 3181d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert put(key, value); 3191d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert return value; 3201d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 3211d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert } 3221d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert 3231d580d0f6ee4f21eb309ba7b509d2c6d671c4044Bjorn Bringert} 324