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