/*
* Copyright 2015 Ben Manes. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.github.benmanes.caffeine.guava;
import static java.util.Objects.requireNonNull;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import javax.annotation.Nonnull;
import com.github.benmanes.caffeine.cache.CacheLoader;
import com.google.common.base.Throwables;
import com.google.common.cache.CacheLoader.InvalidCacheLoadException;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.ImmutableMap;
import com.google.common.util.concurrent.ExecutionError;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.UncheckedExecutionException;
/**
* A Caffeine-backed loading cache through a Guava facade.
*
* @author ben.manes@gmail.com (Ben Manes)
*/
@SuppressWarnings("deprecation")
final class CaffeinatedGuavaLoadingCache<K, V> extends CaffeinatedGuavaCache<K, V>
implements LoadingCache<K, V> {
static final ThreadLocal<Boolean> nullBulkLoad = ThreadLocal.withInitial(() -> Boolean.FALSE);
static final long serialVersionUID = 1L;
final com.github.benmanes.caffeine.cache.LoadingCache<K, V> cache;
CaffeinatedGuavaLoadingCache(com.github.benmanes.caffeine.cache.LoadingCache<K, V> cache) {
super(cache);
this.cache = cache;
}
@Override
@SuppressWarnings("PMD.PreserveStackTrace")
public V get(K key) throws ExecutionException {
requireNonNull(key);
try {
return cache.get(key);
} catch (InvalidCacheLoadException e) {
throw e;
} catch (CacheLoaderException e) {
throw new ExecutionException(e.getCause());
} catch (RuntimeException e) {
throw new UncheckedExecutionException(e);
} catch (Error e) {
throw new ExecutionError(e);
}
}
@Override
@SuppressWarnings({"PMD.PreserveStackTrace", "PMD.AvoidCatchingNPE"})
public V getUnchecked(K key) {
try {
return cache.get(key);
} catch (NullPointerException | InvalidCacheLoadException e) {
throw e;
} catch (CacheLoaderException e) {
throw new UncheckedExecutionException(e.getCause());
} catch (Exception e) {
throw new UncheckedExecutionException(e);
} catch (Error e) {
throw new ExecutionError(e);
}
}
@Override
@SuppressWarnings({"PMD.PreserveStackTrace", "PMD.AvoidCatchingNPE"})
public ImmutableMap<K, V> getAll(Iterable<? extends K> keys) throws ExecutionException {
try {
Map<K, V> result = cache.getAll(keys);
if (nullBulkLoad.get()) {
nullBulkLoad.set(false);
throw new InvalidCacheLoadException("null key or value");
}
for (K key : keys) {
if (!result.containsKey(key)) {
throw new InvalidCacheLoadException("loadAll failed to return a value for " + key);
}
}
return ImmutableMap.copyOf(result);
} catch (NullPointerException | InvalidCacheLoadException e) {
throw e;
} catch (CacheLoaderException e) {
throw new ExecutionException(e.getCause());
} catch (Exception e) {
throw new UncheckedExecutionException(e);
} catch (Error e) {
throw new ExecutionError(e);
}
}
@Override
public V apply(@Nonnull K key) {
return cache.get(key);
}
@Override
public void refresh(K key) {
cache.refresh(key);
}
static class SingleLoader<K, V> implements CacheLoader<K, V>, Serializable {
private static final long serialVersionUID = 1L;
final com.google.common.cache.CacheLoader<K, V> cacheLoader;
SingleLoader(com.google.common.cache.CacheLoader<K, V> cacheLoader) {
this.cacheLoader = requireNonNull(cacheLoader);
}
@Override
public V load(K key) {
try {
V value = cacheLoader.load(key);
if (value == null) {
throw new InvalidCacheLoadException("null value");
}
return value;
} catch (RuntimeException | Error e) {
throw e;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new CacheLoaderException(e);
} catch (Exception e) {
throw new CacheLoaderException(e);
}
}
@Override
public V reload(K key, V oldValue) {
try {
V value = Futures.getUnchecked(cacheLoader.reload(key, oldValue));
if (value == null) {
throw new InvalidCacheLoadException("null value");
}
return value;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new CacheLoaderException(e);
} catch (Exception e) {
Throwables.throwIfUnchecked(e);
throw new RuntimeException(e);
}
}
}
static final class BulkLoader<K, V> extends SingleLoader<K, V> {
private static final long serialVersionUID = 1L;
BulkLoader(com.google.common.cache.CacheLoader<K, V> cacheLoader) {
super(cacheLoader);
}
@Override
public Map<K, V> loadAll(Iterable<? extends K> keys) {
try {
Map<K, V> loaded = cacheLoader.loadAll(keys);
if (loaded == null) {
throw new InvalidCacheLoadException("null map");
}
Map<K, V> result = new HashMap<>(loaded.size());
loaded.forEach((key, value) -> {
if ((key == null) || (value == null)) {
nullBulkLoad.set(true);
} else {
result.put(key, value);
}
});
return result;
} catch (RuntimeException | Error e) {
throw e;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new CacheLoaderException(e);
} catch (Exception e) {
throw new CacheLoaderException(e);
}
}
}
}