@Nullable
Map<K, V> loadAll(Set<? extends K> keys, CacheLoader<? super K, V> loader)
throws ExecutionException {
checkNotNull(loader);
checkNotNull(keys);
Stopwatch stopwatch = new Stopwatch().start();
Map<K, V> result;
boolean success = false;
try {
@SuppressWarnings("unchecked") // safe since all keys extend K
Map<K, V> map = (Map<K, V>) loader.loadAll(keys);
result = map;
success = true;
} catch (UnsupportedLoadingOperationException e) {
success = true;
throw e;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new ExecutionException(e);
} catch (RuntimeException e) {
throw new UncheckedExecutionException(e);
} catch (Exception e) {
throw new ExecutionException(e);
} catch (Error e) {
throw new ExecutionError(e);
} finally {
if (!success) {
globalStatsCounter.recordLoadException(stopwatch.elapsed(NANOSECONDS));
}
}
if (result == null) {
globalStatsCounter.recordLoadException(stopwatch.elapsed(NANOSECONDS));
throw new InvalidCacheLoadException(loader + " returned null map from loadAll");
}
stopwatch.stop();
// TODO(fry): batch by segment
boolean nullsPresent = false;
for (Map.Entry<K, V> entry : result.entrySet()) {
K key = entry.getKey();
V value = entry.getValue();
if (key == null || value == null) {
// delay failure until non-null entries are stored
nullsPresent = true;
} else {
put(key, value);
}
}
if (nullsPresent) {
globalStatsCounter.recordLoadException(stopwatch.elapsed(NANOSECONDS));
throw new InvalidCacheLoadException(loader + " returned null keys or values from loadAll");
}
// TODO(fry): record count of loaded entries
globalStatsCounter.recordLoadSuccess(stopwatch.elapsed(NANOSECONDS));
return result;
}