• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Guava Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 
15 package com.google.common.cache;
16 
17 import static com.google.common.cache.TestingCacheLoaders.bulkLoader;
18 import static com.google.common.cache.TestingCacheLoaders.constantLoader;
19 import static com.google.common.cache.TestingCacheLoaders.errorLoader;
20 import static com.google.common.cache.TestingCacheLoaders.exceptionLoader;
21 import static com.google.common.cache.TestingCacheLoaders.identityLoader;
22 import static com.google.common.cache.TestingRemovalListeners.countingRemovalListener;
23 import static com.google.common.truth.Truth.assertThat;
24 import static java.lang.Thread.currentThread;
25 import static java.util.Arrays.asList;
26 import static java.util.concurrent.TimeUnit.MILLISECONDS;
27 import static org.junit.Assert.assertThrows;
28 
29 import com.google.common.cache.CacheLoader.InvalidCacheLoadException;
30 import com.google.common.cache.TestingCacheLoaders.CountingLoader;
31 import com.google.common.cache.TestingCacheLoaders.IdentityLoader;
32 import com.google.common.cache.TestingRemovalListeners.CountingRemovalListener;
33 import com.google.common.collect.ImmutableList;
34 import com.google.common.collect.ImmutableMap;
35 import com.google.common.collect.Lists;
36 import com.google.common.collect.Maps;
37 import com.google.common.testing.FakeTicker;
38 import com.google.common.testing.TestLogHandler;
39 import com.google.common.util.concurrent.Callables;
40 import com.google.common.util.concurrent.ExecutionError;
41 import com.google.common.util.concurrent.Futures;
42 import com.google.common.util.concurrent.ListenableFuture;
43 import com.google.common.util.concurrent.UncheckedExecutionException;
44 import java.io.IOException;
45 import java.lang.ref.WeakReference;
46 import java.util.List;
47 import java.util.Map;
48 import java.util.Map.Entry;
49 import java.util.concurrent.Callable;
50 import java.util.concurrent.ConcurrentMap;
51 import java.util.concurrent.CountDownLatch;
52 import java.util.concurrent.ExecutionException;
53 import java.util.concurrent.TimeUnit;
54 import java.util.concurrent.atomic.AtomicInteger;
55 import java.util.concurrent.atomic.AtomicReferenceArray;
56 import java.util.logging.LogRecord;
57 import junit.framework.TestCase;
58 
59 /**
60  * Tests relating to cache loading: concurrent loading, exceptions during loading, etc.
61  *
62  * @author mike nonemacher
63  */
64 public class CacheLoadingTest extends TestCase {
65   TestLogHandler logHandler;
66 
67   @Override
setUp()68   public void setUp() throws Exception {
69     super.setUp();
70     logHandler = new TestLogHandler();
71     LocalCache.logger.addHandler(logHandler);
72   }
73 
74   @Override
tearDown()75   public void tearDown() throws Exception {
76     super.tearDown();
77     // TODO(cpovirk): run tests in other thread instead of messing with main thread interrupt status
78     currentThread().interrupted();
79     LocalCache.logger.removeHandler(logHandler);
80   }
81 
popLoggedThrowable()82   private Throwable popLoggedThrowable() {
83     List<LogRecord> logRecords = logHandler.getStoredLogRecords();
84     assertEquals(1, logRecords.size());
85     LogRecord logRecord = logRecords.get(0);
86     logHandler.clear();
87     return logRecord.getThrown();
88   }
89 
checkNothingLogged()90   private void checkNothingLogged() {
91     assertThat(logHandler.getStoredLogRecords()).isEmpty();
92   }
93 
checkLoggedCause(Throwable t)94   private void checkLoggedCause(Throwable t) {
95     assertThat(popLoggedThrowable()).hasCauseThat().isSameInstanceAs(t);
96   }
97 
checkLoggedInvalidLoad()98   private void checkLoggedInvalidLoad() {
99     assertThat(popLoggedThrowable()).isInstanceOf(InvalidCacheLoadException.class);
100   }
101 
testLoad()102   public void testLoad() throws ExecutionException {
103     LoadingCache<Object, Object> cache =
104         CacheBuilder.newBuilder().recordStats().build(identityLoader());
105     CacheStats stats = cache.stats();
106     assertEquals(0, stats.missCount());
107     assertEquals(0, stats.loadSuccessCount());
108     assertEquals(0, stats.loadExceptionCount());
109     assertEquals(0, stats.hitCount());
110 
111     Object key = new Object();
112     assertSame(key, cache.get(key));
113     stats = cache.stats();
114     assertEquals(1, stats.missCount());
115     assertEquals(1, stats.loadSuccessCount());
116     assertEquals(0, stats.loadExceptionCount());
117     assertEquals(0, stats.hitCount());
118 
119     key = new Object();
120     assertSame(key, cache.getUnchecked(key));
121     stats = cache.stats();
122     assertEquals(2, stats.missCount());
123     assertEquals(2, stats.loadSuccessCount());
124     assertEquals(0, stats.loadExceptionCount());
125     assertEquals(0, stats.hitCount());
126 
127     key = new Object();
128     cache.refresh(key);
129     checkNothingLogged();
130     stats = cache.stats();
131     assertEquals(2, stats.missCount());
132     assertEquals(3, stats.loadSuccessCount());
133     assertEquals(0, stats.loadExceptionCount());
134     assertEquals(0, stats.hitCount());
135 
136     assertSame(key, cache.get(key));
137     stats = cache.stats();
138     assertEquals(2, stats.missCount());
139     assertEquals(3, stats.loadSuccessCount());
140     assertEquals(0, stats.loadExceptionCount());
141     assertEquals(1, stats.hitCount());
142 
143     Object value = new Object();
144     // callable is not called
145     assertSame(key, cache.get(key, throwing(new Exception())));
146     stats = cache.stats();
147     assertEquals(2, stats.missCount());
148     assertEquals(3, stats.loadSuccessCount());
149     assertEquals(0, stats.loadExceptionCount());
150     assertEquals(2, stats.hitCount());
151 
152     key = new Object();
153     assertSame(value, cache.get(key, Callables.returning(value)));
154     stats = cache.stats();
155     assertEquals(3, stats.missCount());
156     assertEquals(4, stats.loadSuccessCount());
157     assertEquals(0, stats.loadExceptionCount());
158     assertEquals(2, stats.hitCount());
159   }
160 
testReload()161   public void testReload() throws ExecutionException {
162     final Object one = new Object();
163     final Object two = new Object();
164     CacheLoader<Object, Object> loader =
165         new CacheLoader<Object, Object>() {
166           @Override
167           public Object load(Object key) {
168             return one;
169           }
170 
171           @Override
172           public ListenableFuture<Object> reload(Object key, Object oldValue) {
173             return Futures.immediateFuture(two);
174           }
175         };
176 
177     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
178     Object key = new Object();
179     CacheStats stats = cache.stats();
180     assertEquals(0, stats.missCount());
181     assertEquals(0, stats.loadSuccessCount());
182     assertEquals(0, stats.loadExceptionCount());
183     assertEquals(0, stats.hitCount());
184 
185     assertSame(one, cache.getUnchecked(key));
186     stats = cache.stats();
187     assertEquals(1, stats.missCount());
188     assertEquals(1, stats.loadSuccessCount());
189     assertEquals(0, stats.loadExceptionCount());
190     assertEquals(0, stats.hitCount());
191 
192     cache.refresh(key);
193     checkNothingLogged();
194     stats = cache.stats();
195     assertEquals(1, stats.missCount());
196     assertEquals(2, stats.loadSuccessCount());
197     assertEquals(0, stats.loadExceptionCount());
198     assertEquals(0, stats.hitCount());
199 
200     assertSame(two, cache.getUnchecked(key));
201     stats = cache.stats();
202     assertEquals(1, stats.missCount());
203     assertEquals(2, stats.loadSuccessCount());
204     assertEquals(0, stats.loadExceptionCount());
205     assertEquals(1, stats.hitCount());
206   }
207 
testRefresh()208   public void testRefresh() {
209     final Object one = new Object();
210     final Object two = new Object();
211     FakeTicker ticker = new FakeTicker();
212     CacheLoader<Object, Object> loader =
213         new CacheLoader<Object, Object>() {
214           @Override
215           public Object load(Object key) {
216             return one;
217           }
218 
219           @Override
220           public ListenableFuture<Object> reload(Object key, Object oldValue) {
221             return Futures.immediateFuture(two);
222           }
223         };
224 
225     LoadingCache<Object, Object> cache =
226         CacheBuilder.newBuilder()
227             .recordStats()
228             .ticker(ticker)
229             .refreshAfterWrite(1, MILLISECONDS)
230             .build(loader);
231     Object key = new Object();
232     CacheStats stats = cache.stats();
233     assertEquals(0, stats.missCount());
234     assertEquals(0, stats.loadSuccessCount());
235     assertEquals(0, stats.loadExceptionCount());
236     assertEquals(0, stats.hitCount());
237 
238     assertSame(one, cache.getUnchecked(key));
239     stats = cache.stats();
240     assertEquals(1, stats.missCount());
241     assertEquals(1, stats.loadSuccessCount());
242     assertEquals(0, stats.loadExceptionCount());
243     assertEquals(0, stats.hitCount());
244 
245     ticker.advance(1, MILLISECONDS);
246     assertSame(one, cache.getUnchecked(key));
247     stats = cache.stats();
248     assertEquals(1, stats.missCount());
249     assertEquals(1, stats.loadSuccessCount());
250     assertEquals(0, stats.loadExceptionCount());
251     assertEquals(1, stats.hitCount());
252 
253     ticker.advance(1, MILLISECONDS);
254     assertSame(two, cache.getUnchecked(key));
255     stats = cache.stats();
256     assertEquals(1, stats.missCount());
257     assertEquals(2, stats.loadSuccessCount());
258     assertEquals(0, stats.loadExceptionCount());
259     assertEquals(2, stats.hitCount());
260 
261     ticker.advance(1, MILLISECONDS);
262     assertSame(two, cache.getUnchecked(key));
263     stats = cache.stats();
264     assertEquals(1, stats.missCount());
265     assertEquals(2, stats.loadSuccessCount());
266     assertEquals(0, stats.loadExceptionCount());
267     assertEquals(3, stats.hitCount());
268   }
269 
testRefresh_getIfPresent()270   public void testRefresh_getIfPresent() {
271     final Object one = new Object();
272     final Object two = new Object();
273     FakeTicker ticker = new FakeTicker();
274     CacheLoader<Object, Object> loader =
275         new CacheLoader<Object, Object>() {
276           @Override
277           public Object load(Object key) {
278             return one;
279           }
280 
281           @Override
282           public ListenableFuture<Object> reload(Object key, Object oldValue) {
283             return Futures.immediateFuture(two);
284           }
285         };
286 
287     LoadingCache<Object, Object> cache =
288         CacheBuilder.newBuilder()
289             .recordStats()
290             .ticker(ticker)
291             .refreshAfterWrite(1, MILLISECONDS)
292             .build(loader);
293     Object key = new Object();
294     CacheStats stats = cache.stats();
295     assertEquals(0, stats.missCount());
296     assertEquals(0, stats.loadSuccessCount());
297     assertEquals(0, stats.loadExceptionCount());
298     assertEquals(0, stats.hitCount());
299 
300     assertSame(one, cache.getUnchecked(key));
301     stats = cache.stats();
302     assertEquals(1, stats.missCount());
303     assertEquals(1, stats.loadSuccessCount());
304     assertEquals(0, stats.loadExceptionCount());
305     assertEquals(0, stats.hitCount());
306 
307     ticker.advance(1, MILLISECONDS);
308     assertSame(one, cache.getIfPresent(key));
309     stats = cache.stats();
310     assertEquals(1, stats.missCount());
311     assertEquals(1, stats.loadSuccessCount());
312     assertEquals(0, stats.loadExceptionCount());
313     assertEquals(1, stats.hitCount());
314 
315     ticker.advance(1, MILLISECONDS);
316     assertSame(two, cache.getIfPresent(key));
317     stats = cache.stats();
318     assertEquals(1, stats.missCount());
319     assertEquals(2, stats.loadSuccessCount());
320     assertEquals(0, stats.loadExceptionCount());
321     assertEquals(2, stats.hitCount());
322 
323     ticker.advance(1, MILLISECONDS);
324     assertSame(two, cache.getIfPresent(key));
325     stats = cache.stats();
326     assertEquals(1, stats.missCount());
327     assertEquals(2, stats.loadSuccessCount());
328     assertEquals(0, stats.loadExceptionCount());
329     assertEquals(3, stats.hitCount());
330   }
331 
testBulkLoad_default()332   public void testBulkLoad_default() throws ExecutionException {
333     LoadingCache<Integer, Integer> cache =
334         CacheBuilder.newBuilder()
335             .recordStats()
336             .build(TestingCacheLoaders.<Integer>identityLoader());
337     CacheStats stats = cache.stats();
338     assertEquals(0, stats.missCount());
339     assertEquals(0, stats.loadSuccessCount());
340     assertEquals(0, stats.loadExceptionCount());
341     assertEquals(0, stats.hitCount());
342 
343     assertEquals(ImmutableMap.of(), cache.getAll(ImmutableList.<Integer>of()));
344     assertEquals(0, stats.missCount());
345     assertEquals(0, stats.loadSuccessCount());
346     assertEquals(0, stats.loadExceptionCount());
347     assertEquals(0, stats.hitCount());
348 
349     assertEquals(ImmutableMap.of(1, 1), cache.getAll(asList(1)));
350     stats = cache.stats();
351     assertEquals(1, stats.missCount());
352     assertEquals(1, stats.loadSuccessCount());
353     assertEquals(0, stats.loadExceptionCount());
354     assertEquals(0, stats.hitCount());
355 
356     assertEquals(ImmutableMap.of(1, 1, 2, 2, 3, 3, 4, 4), cache.getAll(asList(1, 2, 3, 4)));
357     stats = cache.stats();
358     assertEquals(4, stats.missCount());
359     assertEquals(4, stats.loadSuccessCount());
360     assertEquals(0, stats.loadExceptionCount());
361     assertEquals(1, stats.hitCount());
362 
363     assertEquals(ImmutableMap.of(2, 2, 3, 3), cache.getAll(asList(2, 3)));
364     stats = cache.stats();
365     assertEquals(4, stats.missCount());
366     assertEquals(4, stats.loadSuccessCount());
367     assertEquals(0, stats.loadExceptionCount());
368     assertEquals(3, stats.hitCount());
369 
370     // duplicate keys are ignored, and don't impact stats
371     assertEquals(ImmutableMap.of(4, 4, 5, 5), cache.getAll(asList(4, 5)));
372     stats = cache.stats();
373     assertEquals(5, stats.missCount());
374     assertEquals(5, stats.loadSuccessCount());
375     assertEquals(0, stats.loadExceptionCount());
376     assertEquals(4, stats.hitCount());
377   }
378 
testBulkLoad_loadAll()379   public void testBulkLoad_loadAll() throws ExecutionException {
380     IdentityLoader<Integer> backingLoader = identityLoader();
381     CacheLoader<Integer, Integer> loader = bulkLoader(backingLoader);
382     LoadingCache<Integer, Integer> cache = CacheBuilder.newBuilder().recordStats().build(loader);
383     CacheStats stats = cache.stats();
384     assertEquals(0, stats.missCount());
385     assertEquals(0, stats.loadSuccessCount());
386     assertEquals(0, stats.loadExceptionCount());
387     assertEquals(0, stats.hitCount());
388 
389     assertEquals(ImmutableMap.of(), cache.getAll(ImmutableList.<Integer>of()));
390     assertEquals(0, stats.missCount());
391     assertEquals(0, stats.loadSuccessCount());
392     assertEquals(0, stats.loadExceptionCount());
393     assertEquals(0, stats.hitCount());
394 
395     assertEquals(ImmutableMap.of(1, 1), cache.getAll(asList(1)));
396     stats = cache.stats();
397     assertEquals(1, stats.missCount());
398     assertEquals(1, stats.loadSuccessCount());
399     assertEquals(0, stats.loadExceptionCount());
400     assertEquals(0, stats.hitCount());
401 
402     assertEquals(ImmutableMap.of(1, 1, 2, 2, 3, 3, 4, 4), cache.getAll(asList(1, 2, 3, 4)));
403     stats = cache.stats();
404     assertEquals(4, stats.missCount());
405     assertEquals(2, stats.loadSuccessCount());
406     assertEquals(0, stats.loadExceptionCount());
407     assertEquals(1, stats.hitCount());
408 
409     assertEquals(ImmutableMap.of(2, 2, 3, 3), cache.getAll(asList(2, 3)));
410     stats = cache.stats();
411     assertEquals(4, stats.missCount());
412     assertEquals(2, stats.loadSuccessCount());
413     assertEquals(0, stats.loadExceptionCount());
414     assertEquals(3, stats.hitCount());
415 
416     // duplicate keys are ignored, and don't impact stats
417     assertEquals(ImmutableMap.of(4, 4, 5, 5), cache.getAll(asList(4, 5)));
418     stats = cache.stats();
419     assertEquals(5, stats.missCount());
420     assertEquals(3, stats.loadSuccessCount());
421     assertEquals(0, stats.loadExceptionCount());
422     assertEquals(4, stats.hitCount());
423   }
424 
testBulkLoad_extra()425   public void testBulkLoad_extra() throws ExecutionException {
426     CacheLoader<Object, Object> loader =
427         new CacheLoader<Object, Object>() {
428           @Override
429           public Object load(Object key) throws Exception {
430             return new Object();
431           }
432 
433           @Override
434           public Map<Object, Object> loadAll(Iterable<?> keys) throws Exception {
435             Map<Object, Object> result = Maps.newHashMap();
436             for (Object key : keys) {
437               Object value = new Object();
438               result.put(key, value);
439               // add extra entries
440               result.put(value, key);
441             }
442             return result;
443           }
444         };
445     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader);
446 
447     Object[] lookupKeys = new Object[] {new Object(), new Object(), new Object()};
448     Map<Object, Object> result = cache.getAll(asList(lookupKeys));
449     assertThat(result.keySet()).containsExactlyElementsIn(asList(lookupKeys));
450     for (Entry<Object, Object> entry : result.entrySet()) {
451       Object key = entry.getKey();
452       Object value = entry.getValue();
453       assertSame(value, result.get(key));
454       assertNull(result.get(value));
455       assertSame(value, cache.asMap().get(key));
456       assertSame(key, cache.asMap().get(value));
457     }
458   }
459 
testBulkLoad_clobber()460   public void testBulkLoad_clobber() throws ExecutionException {
461     final Object extraKey = new Object();
462     final Object extraValue = new Object();
463     CacheLoader<Object, Object> loader =
464         new CacheLoader<Object, Object>() {
465           @Override
466           public Object load(Object key) throws Exception {
467             throw new AssertionError();
468           }
469 
470           @Override
471           public Map<Object, Object> loadAll(Iterable<?> keys) throws Exception {
472             Map<Object, Object> result = Maps.newHashMap();
473             for (Object key : keys) {
474               Object value = new Object();
475               result.put(key, value);
476             }
477             result.put(extraKey, extraValue);
478             return result;
479           }
480         };
481     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader);
482     cache.asMap().put(extraKey, extraKey);
483     assertSame(extraKey, cache.asMap().get(extraKey));
484 
485     Object[] lookupKeys = new Object[] {new Object(), new Object(), new Object()};
486     Map<Object, Object> result = cache.getAll(asList(lookupKeys));
487     assertThat(result.keySet()).containsExactlyElementsIn(asList(lookupKeys));
488     for (Entry<Object, Object> entry : result.entrySet()) {
489       Object key = entry.getKey();
490       Object value = entry.getValue();
491       assertSame(value, result.get(key));
492       assertSame(value, cache.asMap().get(key));
493     }
494     assertNull(result.get(extraKey));
495     assertSame(extraValue, cache.asMap().get(extraKey));
496   }
497 
testBulkLoad_clobberNullValue()498   public void testBulkLoad_clobberNullValue() throws ExecutionException {
499     final Object extraKey = new Object();
500     final Object extraValue = new Object();
501     CacheLoader<Object, Object> loader =
502         new CacheLoader<Object, Object>() {
503           @Override
504           public Object load(Object key) throws Exception {
505             throw new AssertionError();
506           }
507 
508           @Override
509           public Map<Object, Object> loadAll(Iterable<?> keys) throws Exception {
510             Map<Object, Object> result = Maps.newHashMap();
511             for (Object key : keys) {
512               Object value = new Object();
513               result.put(key, value);
514             }
515             result.put(extraKey, extraValue);
516             result.put(extraValue, null);
517             return result;
518           }
519         };
520     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader);
521     cache.asMap().put(extraKey, extraKey);
522     assertSame(extraKey, cache.asMap().get(extraKey));
523 
524     Object[] lookupKeys = new Object[] {new Object(), new Object(), new Object()};
525     assertThrows(InvalidCacheLoadException.class, () -> cache.getAll(asList(lookupKeys)));
526 
527     for (Object key : lookupKeys) {
528       assertTrue(cache.asMap().containsKey(key));
529     }
530     assertSame(extraValue, cache.asMap().get(extraKey));
531     assertFalse(cache.asMap().containsKey(extraValue));
532   }
533 
testBulkLoad_clobberNullKey()534   public void testBulkLoad_clobberNullKey() throws ExecutionException {
535     final Object extraKey = new Object();
536     final Object extraValue = new Object();
537     CacheLoader<Object, Object> loader =
538         new CacheLoader<Object, Object>() {
539           @Override
540           public Object load(Object key) throws Exception {
541             throw new AssertionError();
542           }
543 
544           @Override
545           public Map<Object, Object> loadAll(Iterable<?> keys) throws Exception {
546             Map<Object, Object> result = Maps.newHashMap();
547             for (Object key : keys) {
548               Object value = new Object();
549               result.put(key, value);
550             }
551             result.put(extraKey, extraValue);
552             result.put(null, extraKey);
553             return result;
554           }
555         };
556     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader);
557     cache.asMap().put(extraKey, extraKey);
558     assertSame(extraKey, cache.asMap().get(extraKey));
559 
560     Object[] lookupKeys = new Object[] {new Object(), new Object(), new Object()};
561     assertThrows(InvalidCacheLoadException.class, () -> cache.getAll(asList(lookupKeys)));
562 
563     for (Object key : lookupKeys) {
564       assertTrue(cache.asMap().containsKey(key));
565     }
566     assertSame(extraValue, cache.asMap().get(extraKey));
567     assertFalse(cache.asMap().containsValue(extraKey));
568   }
569 
testBulkLoad_partial()570   public void testBulkLoad_partial() throws ExecutionException {
571     final Object extraKey = new Object();
572     final Object extraValue = new Object();
573     CacheLoader<Object, Object> loader =
574         new CacheLoader<Object, Object>() {
575           @Override
576           public Object load(Object key) throws Exception {
577             throw new AssertionError();
578           }
579 
580           @Override
581           public Map<Object, Object> loadAll(Iterable<?> keys) throws Exception {
582             Map<Object, Object> result = Maps.newHashMap();
583             // ignore request keys
584             result.put(extraKey, extraValue);
585             return result;
586           }
587         };
588     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().build(loader);
589 
590     Object[] lookupKeys = new Object[] {new Object(), new Object(), new Object()};
591     assertThrows(InvalidCacheLoadException.class, () -> cache.getAll(asList(lookupKeys)));
592     assertSame(extraValue, cache.asMap().get(extraKey));
593   }
594 
testLoadNull()595   public void testLoadNull() throws ExecutionException {
596     LoadingCache<Object, Object> cache =
597         CacheBuilder.newBuilder().recordStats().build(constantLoader(null));
598     CacheStats stats = cache.stats();
599     assertEquals(0, stats.missCount());
600     assertEquals(0, stats.loadSuccessCount());
601     assertEquals(0, stats.loadExceptionCount());
602     assertEquals(0, stats.hitCount());
603 
604     assertThrows(InvalidCacheLoadException.class, () -> cache.get(new Object()));
605     stats = cache.stats();
606     assertEquals(1, stats.missCount());
607     assertEquals(0, stats.loadSuccessCount());
608     assertEquals(1, stats.loadExceptionCount());
609     assertEquals(0, stats.hitCount());
610 
611     assertThrows(InvalidCacheLoadException.class, () -> cache.getUnchecked(new Object()));
612     stats = cache.stats();
613     assertEquals(2, stats.missCount());
614     assertEquals(0, stats.loadSuccessCount());
615     assertEquals(2, stats.loadExceptionCount());
616     assertEquals(0, stats.hitCount());
617 
618     cache.refresh(new Object());
619     checkLoggedInvalidLoad();
620     stats = cache.stats();
621     assertEquals(2, stats.missCount());
622     assertEquals(0, stats.loadSuccessCount());
623     assertEquals(3, stats.loadExceptionCount());
624     assertEquals(0, stats.hitCount());
625 
626     assertThrows(
627         InvalidCacheLoadException.class, () -> cache.get(new Object(), Callables.returning(null)));
628     stats = cache.stats();
629     assertEquals(3, stats.missCount());
630     assertEquals(0, stats.loadSuccessCount());
631     assertEquals(4, stats.loadExceptionCount());
632     assertEquals(0, stats.hitCount());
633 
634     assertThrows(InvalidCacheLoadException.class, () -> cache.getAll(asList(new Object())));
635     stats = cache.stats();
636     assertEquals(4, stats.missCount());
637     assertEquals(0, stats.loadSuccessCount());
638     assertEquals(5, stats.loadExceptionCount());
639     assertEquals(0, stats.hitCount());
640   }
641 
testReloadNull()642   public void testReloadNull() throws ExecutionException {
643     final Object one = new Object();
644     CacheLoader<Object, Object> loader =
645         new CacheLoader<Object, Object>() {
646           @Override
647           public Object load(Object key) {
648             return one;
649           }
650 
651           @Override
652           public ListenableFuture<Object> reload(Object key, Object oldValue) {
653             return null;
654           }
655         };
656 
657     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
658     Object key = new Object();
659     CacheStats stats = cache.stats();
660     assertEquals(0, stats.missCount());
661     assertEquals(0, stats.loadSuccessCount());
662     assertEquals(0, stats.loadExceptionCount());
663     assertEquals(0, stats.hitCount());
664 
665     assertSame(one, cache.getUnchecked(key));
666     stats = cache.stats();
667     assertEquals(1, stats.missCount());
668     assertEquals(1, stats.loadSuccessCount());
669     assertEquals(0, stats.loadExceptionCount());
670     assertEquals(0, stats.hitCount());
671 
672     cache.refresh(key);
673     checkLoggedInvalidLoad();
674     stats = cache.stats();
675     assertEquals(1, stats.missCount());
676     assertEquals(1, stats.loadSuccessCount());
677     assertEquals(1, stats.loadExceptionCount());
678     assertEquals(0, stats.hitCount());
679 
680     assertSame(one, cache.getUnchecked(key));
681     stats = cache.stats();
682     assertEquals(1, stats.missCount());
683     assertEquals(1, stats.loadSuccessCount());
684     assertEquals(1, stats.loadExceptionCount());
685     assertEquals(1, stats.hitCount());
686   }
687 
testReloadNullFuture()688   public void testReloadNullFuture() throws ExecutionException {
689     final Object one = new Object();
690     CacheLoader<Object, Object> loader =
691         new CacheLoader<Object, Object>() {
692           @Override
693           public Object load(Object key) {
694             return one;
695           }
696 
697           @Override
698           public ListenableFuture<Object> reload(Object key, Object oldValue) {
699             return Futures.immediateFuture(null);
700           }
701         };
702 
703     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
704     Object key = new Object();
705     CacheStats stats = cache.stats();
706     assertEquals(0, stats.missCount());
707     assertEquals(0, stats.loadSuccessCount());
708     assertEquals(0, stats.loadExceptionCount());
709     assertEquals(0, stats.hitCount());
710 
711     assertSame(one, cache.getUnchecked(key));
712     stats = cache.stats();
713     assertEquals(1, stats.missCount());
714     assertEquals(1, stats.loadSuccessCount());
715     assertEquals(0, stats.loadExceptionCount());
716     assertEquals(0, stats.hitCount());
717 
718     cache.refresh(key);
719     checkLoggedInvalidLoad();
720     stats = cache.stats();
721     assertEquals(1, stats.missCount());
722     assertEquals(1, stats.loadSuccessCount());
723     assertEquals(1, stats.loadExceptionCount());
724     assertEquals(0, stats.hitCount());
725 
726     assertSame(one, cache.getUnchecked(key));
727     stats = cache.stats();
728     assertEquals(1, stats.missCount());
729     assertEquals(1, stats.loadSuccessCount());
730     assertEquals(1, stats.loadExceptionCount());
731     assertEquals(1, stats.hitCount());
732   }
733 
testRefreshNull()734   public void testRefreshNull() {
735     final Object one = new Object();
736     FakeTicker ticker = new FakeTicker();
737     CacheLoader<Object, Object> loader =
738         new CacheLoader<Object, Object>() {
739           @Override
740           public Object load(Object key) {
741             return one;
742           }
743 
744           @Override
745           public ListenableFuture<Object> reload(Object key, Object oldValue) {
746             return Futures.immediateFuture(null);
747           }
748         };
749 
750     LoadingCache<Object, Object> cache =
751         CacheBuilder.newBuilder()
752             .recordStats()
753             .ticker(ticker)
754             .refreshAfterWrite(1, MILLISECONDS)
755             .build(loader);
756     Object key = new Object();
757     CacheStats stats = cache.stats();
758     assertEquals(0, stats.missCount());
759     assertEquals(0, stats.loadSuccessCount());
760     assertEquals(0, stats.loadExceptionCount());
761     assertEquals(0, stats.hitCount());
762 
763     assertSame(one, cache.getUnchecked(key));
764     stats = cache.stats();
765     assertEquals(1, stats.missCount());
766     assertEquals(1, stats.loadSuccessCount());
767     assertEquals(0, stats.loadExceptionCount());
768     assertEquals(0, stats.hitCount());
769 
770     ticker.advance(1, MILLISECONDS);
771     assertSame(one, cache.getUnchecked(key));
772     stats = cache.stats();
773     assertEquals(1, stats.missCount());
774     assertEquals(1, stats.loadSuccessCount());
775     assertEquals(0, stats.loadExceptionCount());
776     assertEquals(1, stats.hitCount());
777 
778     ticker.advance(1, MILLISECONDS);
779     assertSame(one, cache.getUnchecked(key));
780     // refreshed
781     stats = cache.stats();
782     assertEquals(1, stats.missCount());
783     assertEquals(1, stats.loadSuccessCount());
784     assertEquals(1, stats.loadExceptionCount());
785     assertEquals(2, stats.hitCount());
786 
787     ticker.advance(1, MILLISECONDS);
788     assertSame(one, cache.getUnchecked(key));
789     stats = cache.stats();
790     assertEquals(1, stats.missCount());
791     assertEquals(1, stats.loadSuccessCount());
792     assertEquals(2, stats.loadExceptionCount());
793     assertEquals(3, stats.hitCount());
794   }
795 
testBulkLoadNull()796   public void testBulkLoadNull() throws ExecutionException {
797     LoadingCache<Object, Object> cache =
798         CacheBuilder.newBuilder().recordStats().build(bulkLoader(constantLoader(null)));
799     CacheStats stats = cache.stats();
800     assertEquals(0, stats.missCount());
801     assertEquals(0, stats.loadSuccessCount());
802     assertEquals(0, stats.loadExceptionCount());
803     assertEquals(0, stats.hitCount());
804 
805     assertThrows(InvalidCacheLoadException.class, () -> cache.getAll(asList(new Object())));
806     stats = cache.stats();
807     assertEquals(1, stats.missCount());
808     assertEquals(0, stats.loadSuccessCount());
809     assertEquals(1, stats.loadExceptionCount());
810     assertEquals(0, stats.hitCount());
811   }
812 
testBulkLoadNullMap()813   public void testBulkLoadNullMap() throws ExecutionException {
814     LoadingCache<Object, Object> cache =
815         CacheBuilder.newBuilder()
816             .recordStats()
817             .build(
818                 new CacheLoader<Object, Object>() {
819                   @Override
820                   public Object load(Object key) {
821                     throw new AssertionError();
822                   }
823 
824                   @Override
825                   public Map<Object, Object> loadAll(Iterable<?> keys) {
826                     return null;
827                   }
828                 });
829 
830     CacheStats stats = cache.stats();
831     assertEquals(0, stats.missCount());
832     assertEquals(0, stats.loadSuccessCount());
833     assertEquals(0, stats.loadExceptionCount());
834     assertEquals(0, stats.hitCount());
835 
836     assertThrows(InvalidCacheLoadException.class, () -> cache.getAll(asList(new Object())));
837     stats = cache.stats();
838     assertEquals(1, stats.missCount());
839     assertEquals(0, stats.loadSuccessCount());
840     assertEquals(1, stats.loadExceptionCount());
841     assertEquals(0, stats.hitCount());
842   }
843 
testLoadError()844   public void testLoadError() throws ExecutionException {
845     Error e = new Error();
846     CacheLoader<Object, Object> loader = errorLoader(e);
847     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
848     CacheStats stats = cache.stats();
849     assertEquals(0, stats.missCount());
850     assertEquals(0, stats.loadSuccessCount());
851     assertEquals(0, stats.loadExceptionCount());
852     assertEquals(0, stats.hitCount());
853 
854     ExecutionError expected = assertThrows(ExecutionError.class, () -> cache.get(new Object()));
855     assertThat(expected).hasCauseThat().isSameInstanceAs(e);
856     stats = cache.stats();
857     assertEquals(1, stats.missCount());
858     assertEquals(0, stats.loadSuccessCount());
859     assertEquals(1, stats.loadExceptionCount());
860     assertEquals(0, stats.hitCount());
861 
862     expected = assertThrows(ExecutionError.class, () -> cache.getUnchecked(new Object()));
863     assertThat(expected).hasCauseThat().isSameInstanceAs(e);
864     stats = cache.stats();
865     assertEquals(2, stats.missCount());
866     assertEquals(0, stats.loadSuccessCount());
867     assertEquals(2, stats.loadExceptionCount());
868     assertEquals(0, stats.hitCount());
869 
870     cache.refresh(new Object());
871     checkLoggedCause(e);
872     stats = cache.stats();
873     assertEquals(2, stats.missCount());
874     assertEquals(0, stats.loadSuccessCount());
875     assertEquals(3, stats.loadExceptionCount());
876     assertEquals(0, stats.hitCount());
877 
878     final Error callableError = new Error();
879     expected =
880         assertThrows(
881             ExecutionError.class,
882             () ->
883                 cache.get(
884                     new Object(),
885                     new Callable<Object>() {
886                       @Override
887                       public Object call() {
888                         throw callableError;
889                       }
890                     }));
891     assertThat(expected).hasCauseThat().isSameInstanceAs(callableError);
892     stats = cache.stats();
893     assertEquals(3, stats.missCount());
894     assertEquals(0, stats.loadSuccessCount());
895     assertEquals(4, stats.loadExceptionCount());
896     assertEquals(0, stats.hitCount());
897 
898     expected = assertThrows(ExecutionError.class, () -> cache.getAll(asList(new Object())));
899     assertThat(expected).hasCauseThat().isSameInstanceAs(e);
900     stats = cache.stats();
901     assertEquals(4, stats.missCount());
902     assertEquals(0, stats.loadSuccessCount());
903     assertEquals(5, stats.loadExceptionCount());
904     assertEquals(0, stats.hitCount());
905   }
906 
testReloadError()907   public void testReloadError() throws ExecutionException {
908     final Object one = new Object();
909     final Error e = new Error();
910     CacheLoader<Object, Object> loader =
911         new CacheLoader<Object, Object>() {
912           @Override
913           public Object load(Object key) {
914             return one;
915           }
916 
917           @Override
918           public ListenableFuture<Object> reload(Object key, Object oldValue) {
919             throw e;
920           }
921         };
922 
923     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
924     Object key = new Object();
925     CacheStats stats = cache.stats();
926     assertEquals(0, stats.missCount());
927     assertEquals(0, stats.loadSuccessCount());
928     assertEquals(0, stats.loadExceptionCount());
929     assertEquals(0, stats.hitCount());
930 
931     assertSame(one, cache.getUnchecked(key));
932     stats = cache.stats();
933     assertEquals(1, stats.missCount());
934     assertEquals(1, stats.loadSuccessCount());
935     assertEquals(0, stats.loadExceptionCount());
936     assertEquals(0, stats.hitCount());
937 
938     cache.refresh(key);
939     checkLoggedCause(e);
940     stats = cache.stats();
941     assertEquals(1, stats.missCount());
942     assertEquals(1, stats.loadSuccessCount());
943     assertEquals(1, stats.loadExceptionCount());
944     assertEquals(0, stats.hitCount());
945 
946     assertSame(one, cache.getUnchecked(key));
947     stats = cache.stats();
948     assertEquals(1, stats.missCount());
949     assertEquals(1, stats.loadSuccessCount());
950     assertEquals(1, stats.loadExceptionCount());
951     assertEquals(1, stats.hitCount());
952   }
953 
testReloadFutureError()954   public void testReloadFutureError() throws ExecutionException {
955     final Object one = new Object();
956     final Error e = new Error();
957     CacheLoader<Object, Object> loader =
958         new CacheLoader<Object, Object>() {
959           @Override
960           public Object load(Object key) {
961             return one;
962           }
963 
964           @Override
965           public ListenableFuture<Object> reload(Object key, Object oldValue) {
966             return Futures.immediateFailedFuture(e);
967           }
968         };
969 
970     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
971     Object key = new Object();
972     CacheStats stats = cache.stats();
973     assertEquals(0, stats.missCount());
974     assertEquals(0, stats.loadSuccessCount());
975     assertEquals(0, stats.loadExceptionCount());
976     assertEquals(0, stats.hitCount());
977 
978     assertSame(one, cache.getUnchecked(key));
979     stats = cache.stats();
980     assertEquals(1, stats.missCount());
981     assertEquals(1, stats.loadSuccessCount());
982     assertEquals(0, stats.loadExceptionCount());
983     assertEquals(0, stats.hitCount());
984 
985     cache.refresh(key);
986     checkLoggedCause(e);
987     stats = cache.stats();
988     assertEquals(1, stats.missCount());
989     assertEquals(1, stats.loadSuccessCount());
990     assertEquals(1, stats.loadExceptionCount());
991     assertEquals(0, stats.hitCount());
992 
993     assertSame(one, cache.getUnchecked(key));
994     stats = cache.stats();
995     assertEquals(1, stats.missCount());
996     assertEquals(1, stats.loadSuccessCount());
997     assertEquals(1, stats.loadExceptionCount());
998     assertEquals(1, stats.hitCount());
999   }
1000 
testRefreshError()1001   public void testRefreshError() {
1002     final Object one = new Object();
1003     final Error e = new Error();
1004     FakeTicker ticker = new FakeTicker();
1005     CacheLoader<Object, Object> loader =
1006         new CacheLoader<Object, Object>() {
1007           @Override
1008           public Object load(Object key) {
1009             return one;
1010           }
1011 
1012           @Override
1013           public ListenableFuture<Object> reload(Object key, Object oldValue) {
1014             return Futures.immediateFailedFuture(e);
1015           }
1016         };
1017 
1018     LoadingCache<Object, Object> cache =
1019         CacheBuilder.newBuilder()
1020             .recordStats()
1021             .ticker(ticker)
1022             .refreshAfterWrite(1, MILLISECONDS)
1023             .build(loader);
1024     Object key = new Object();
1025     CacheStats stats = cache.stats();
1026     assertEquals(0, stats.missCount());
1027     assertEquals(0, stats.loadSuccessCount());
1028     assertEquals(0, stats.loadExceptionCount());
1029     assertEquals(0, stats.hitCount());
1030 
1031     assertSame(one, cache.getUnchecked(key));
1032     stats = cache.stats();
1033     assertEquals(1, stats.missCount());
1034     assertEquals(1, stats.loadSuccessCount());
1035     assertEquals(0, stats.loadExceptionCount());
1036     assertEquals(0, stats.hitCount());
1037 
1038     ticker.advance(1, MILLISECONDS);
1039     assertSame(one, cache.getUnchecked(key));
1040     stats = cache.stats();
1041     assertEquals(1, stats.missCount());
1042     assertEquals(1, stats.loadSuccessCount());
1043     assertEquals(0, stats.loadExceptionCount());
1044     assertEquals(1, stats.hitCount());
1045 
1046     ticker.advance(1, MILLISECONDS);
1047     assertSame(one, cache.getUnchecked(key));
1048     // refreshed
1049     stats = cache.stats();
1050     assertEquals(1, stats.missCount());
1051     assertEquals(1, stats.loadSuccessCount());
1052     assertEquals(1, stats.loadExceptionCount());
1053     assertEquals(2, stats.hitCount());
1054 
1055     ticker.advance(1, MILLISECONDS);
1056     assertSame(one, cache.getUnchecked(key));
1057     stats = cache.stats();
1058     assertEquals(1, stats.missCount());
1059     assertEquals(1, stats.loadSuccessCount());
1060     assertEquals(2, stats.loadExceptionCount());
1061     assertEquals(3, stats.hitCount());
1062   }
1063 
testBulkLoadError()1064   public void testBulkLoadError() throws ExecutionException {
1065     Error e = new Error();
1066     CacheLoader<Object, Object> loader = errorLoader(e);
1067     LoadingCache<Object, Object> cache =
1068         CacheBuilder.newBuilder().recordStats().build(bulkLoader(loader));
1069     CacheStats stats = cache.stats();
1070     assertEquals(0, stats.missCount());
1071     assertEquals(0, stats.loadSuccessCount());
1072     assertEquals(0, stats.loadExceptionCount());
1073     assertEquals(0, stats.hitCount());
1074 
1075     ExecutionError expected =
1076         assertThrows(ExecutionError.class, () -> cache.getAll(asList(new Object())));
1077     assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1078     stats = cache.stats();
1079     assertEquals(1, stats.missCount());
1080     assertEquals(0, stats.loadSuccessCount());
1081     assertEquals(1, stats.loadExceptionCount());
1082     assertEquals(0, stats.hitCount());
1083   }
1084 
testLoadCheckedException()1085   public void testLoadCheckedException() {
1086     Exception e = new Exception();
1087     CacheLoader<Object, Object> loader = exceptionLoader(e);
1088     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
1089     CacheStats stats = cache.stats();
1090     assertEquals(0, stats.missCount());
1091     assertEquals(0, stats.loadSuccessCount());
1092     assertEquals(0, stats.loadExceptionCount());
1093     assertEquals(0, stats.hitCount());
1094 
1095     Exception expected = assertThrows(ExecutionException.class, () -> cache.get(new Object()));
1096     assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1097     stats = cache.stats();
1098     assertEquals(1, stats.missCount());
1099     assertEquals(0, stats.loadSuccessCount());
1100     assertEquals(1, stats.loadExceptionCount());
1101     assertEquals(0, stats.hitCount());
1102 
1103     expected =
1104         assertThrows(UncheckedExecutionException.class, () -> cache.getUnchecked(new Object()));
1105     assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1106     stats = cache.stats();
1107     assertEquals(2, stats.missCount());
1108     assertEquals(0, stats.loadSuccessCount());
1109     assertEquals(2, stats.loadExceptionCount());
1110     assertEquals(0, stats.hitCount());
1111 
1112     cache.refresh(new Object());
1113     checkLoggedCause(e);
1114     stats = cache.stats();
1115     assertEquals(2, stats.missCount());
1116     assertEquals(0, stats.loadSuccessCount());
1117     assertEquals(3, stats.loadExceptionCount());
1118     assertEquals(0, stats.hitCount());
1119 
1120     Exception callableException = new Exception();
1121     expected =
1122         assertThrows(
1123             ExecutionException.class, () -> cache.get(new Object(), throwing(callableException)));
1124     assertThat(expected).hasCauseThat().isSameInstanceAs(callableException);
1125     stats = cache.stats();
1126     assertEquals(3, stats.missCount());
1127     assertEquals(0, stats.loadSuccessCount());
1128     assertEquals(4, stats.loadExceptionCount());
1129     assertEquals(0, stats.hitCount());
1130 
1131     expected = assertThrows(ExecutionException.class, () -> cache.getAll(asList(new Object())));
1132     assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1133     stats = cache.stats();
1134     assertEquals(4, stats.missCount());
1135     assertEquals(0, stats.loadSuccessCount());
1136     assertEquals(5, stats.loadExceptionCount());
1137     assertEquals(0, stats.hitCount());
1138   }
1139 
testLoadInterruptedException()1140   public void testLoadInterruptedException() {
1141     Exception e = new InterruptedException();
1142     CacheLoader<Object, Object> loader = exceptionLoader(e);
1143     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
1144     CacheStats stats = cache.stats();
1145     assertEquals(0, stats.missCount());
1146     assertEquals(0, stats.loadSuccessCount());
1147     assertEquals(0, stats.loadExceptionCount());
1148     assertEquals(0, stats.hitCount());
1149 
1150     // Sanity check:
1151     assertFalse(currentThread().interrupted());
1152 
1153     Exception expected = assertThrows(ExecutionException.class, () -> cache.get(new Object()));
1154     assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1155     assertTrue(currentThread().interrupted());
1156     stats = cache.stats();
1157     assertEquals(1, stats.missCount());
1158     assertEquals(0, stats.loadSuccessCount());
1159     assertEquals(1, stats.loadExceptionCount());
1160     assertEquals(0, stats.hitCount());
1161 
1162     expected =
1163         assertThrows(UncheckedExecutionException.class, () -> cache.getUnchecked(new Object()));
1164     assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1165     assertTrue(currentThread().interrupted());
1166     stats = cache.stats();
1167     assertEquals(2, stats.missCount());
1168     assertEquals(0, stats.loadSuccessCount());
1169     assertEquals(2, stats.loadExceptionCount());
1170     assertEquals(0, stats.hitCount());
1171 
1172     cache.refresh(new Object());
1173     assertTrue(currentThread().interrupted());
1174     checkLoggedCause(e);
1175     stats = cache.stats();
1176     assertEquals(2, stats.missCount());
1177     assertEquals(0, stats.loadSuccessCount());
1178     assertEquals(3, stats.loadExceptionCount());
1179     assertEquals(0, stats.hitCount());
1180 
1181     Exception callableException = new InterruptedException();
1182     expected =
1183         assertThrows(
1184             ExecutionException.class, () -> cache.get(new Object(), throwing(callableException)));
1185     assertThat(expected).hasCauseThat().isSameInstanceAs(callableException);
1186     assertTrue(currentThread().interrupted());
1187     stats = cache.stats();
1188     assertEquals(3, stats.missCount());
1189     assertEquals(0, stats.loadSuccessCount());
1190     assertEquals(4, stats.loadExceptionCount());
1191     assertEquals(0, stats.hitCount());
1192 
1193     expected = assertThrows(ExecutionException.class, () -> cache.getAll(asList(new Object())));
1194     assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1195     assertTrue(currentThread().interrupted());
1196     stats = cache.stats();
1197     assertEquals(4, stats.missCount());
1198     assertEquals(0, stats.loadSuccessCount());
1199     assertEquals(5, stats.loadExceptionCount());
1200     assertEquals(0, stats.hitCount());
1201   }
1202 
testReloadCheckedException()1203   public void testReloadCheckedException() {
1204     final Object one = new Object();
1205     final Exception e = new Exception();
1206     CacheLoader<Object, Object> loader =
1207         new CacheLoader<Object, Object>() {
1208           @Override
1209           public Object load(Object key) {
1210             return one;
1211           }
1212 
1213           @Override
1214           public ListenableFuture<Object> reload(Object key, Object oldValue) throws Exception {
1215             throw e;
1216           }
1217         };
1218 
1219     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
1220     Object key = new Object();
1221     CacheStats stats = cache.stats();
1222     assertEquals(0, stats.missCount());
1223     assertEquals(0, stats.loadSuccessCount());
1224     assertEquals(0, stats.loadExceptionCount());
1225     assertEquals(0, stats.hitCount());
1226 
1227     assertSame(one, cache.getUnchecked(key));
1228     stats = cache.stats();
1229     assertEquals(1, stats.missCount());
1230     assertEquals(1, stats.loadSuccessCount());
1231     assertEquals(0, stats.loadExceptionCount());
1232     assertEquals(0, stats.hitCount());
1233 
1234     cache.refresh(key);
1235     checkLoggedCause(e);
1236     stats = cache.stats();
1237     assertEquals(1, stats.missCount());
1238     assertEquals(1, stats.loadSuccessCount());
1239     assertEquals(1, stats.loadExceptionCount());
1240     assertEquals(0, stats.hitCount());
1241 
1242     assertSame(one, cache.getUnchecked(key));
1243     stats = cache.stats();
1244     assertEquals(1, stats.missCount());
1245     assertEquals(1, stats.loadSuccessCount());
1246     assertEquals(1, stats.loadExceptionCount());
1247     assertEquals(1, stats.hitCount());
1248   }
1249 
testReloadFutureCheckedException()1250   public void testReloadFutureCheckedException() {
1251     final Object one = new Object();
1252     final Exception e = new Exception();
1253     CacheLoader<Object, Object> loader =
1254         new CacheLoader<Object, Object>() {
1255           @Override
1256           public Object load(Object key) {
1257             return one;
1258           }
1259 
1260           @Override
1261           public ListenableFuture<Object> reload(Object key, Object oldValue) {
1262             return Futures.immediateFailedFuture(e);
1263           }
1264         };
1265 
1266     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
1267     Object key = new Object();
1268     CacheStats stats = cache.stats();
1269     assertEquals(0, stats.missCount());
1270     assertEquals(0, stats.loadSuccessCount());
1271     assertEquals(0, stats.loadExceptionCount());
1272     assertEquals(0, stats.hitCount());
1273 
1274     assertSame(one, cache.getUnchecked(key));
1275     stats = cache.stats();
1276     assertEquals(1, stats.missCount());
1277     assertEquals(1, stats.loadSuccessCount());
1278     assertEquals(0, stats.loadExceptionCount());
1279     assertEquals(0, stats.hitCount());
1280 
1281     cache.refresh(key);
1282     checkLoggedCause(e);
1283     stats = cache.stats();
1284     assertEquals(1, stats.missCount());
1285     assertEquals(1, stats.loadSuccessCount());
1286     assertEquals(1, stats.loadExceptionCount());
1287     assertEquals(0, stats.hitCount());
1288 
1289     assertSame(one, cache.getUnchecked(key));
1290     stats = cache.stats();
1291     assertEquals(1, stats.missCount());
1292     assertEquals(1, stats.loadSuccessCount());
1293     assertEquals(1, stats.loadExceptionCount());
1294     assertEquals(1, stats.hitCount());
1295   }
1296 
testRefreshCheckedException()1297   public void testRefreshCheckedException() {
1298     final Object one = new Object();
1299     final Exception e = new Exception();
1300     FakeTicker ticker = new FakeTicker();
1301     CacheLoader<Object, Object> loader =
1302         new CacheLoader<Object, Object>() {
1303           @Override
1304           public Object load(Object key) {
1305             return one;
1306           }
1307 
1308           @Override
1309           public ListenableFuture<Object> reload(Object key, Object oldValue) {
1310             return Futures.immediateFailedFuture(e);
1311           }
1312         };
1313 
1314     LoadingCache<Object, Object> cache =
1315         CacheBuilder.newBuilder()
1316             .recordStats()
1317             .ticker(ticker)
1318             .refreshAfterWrite(1, MILLISECONDS)
1319             .build(loader);
1320     Object key = new Object();
1321     CacheStats stats = cache.stats();
1322     assertEquals(0, stats.missCount());
1323     assertEquals(0, stats.loadSuccessCount());
1324     assertEquals(0, stats.loadExceptionCount());
1325     assertEquals(0, stats.hitCount());
1326 
1327     assertSame(one, cache.getUnchecked(key));
1328     stats = cache.stats();
1329     assertEquals(1, stats.missCount());
1330     assertEquals(1, stats.loadSuccessCount());
1331     assertEquals(0, stats.loadExceptionCount());
1332     assertEquals(0, stats.hitCount());
1333 
1334     ticker.advance(1, MILLISECONDS);
1335     assertSame(one, cache.getUnchecked(key));
1336     stats = cache.stats();
1337     assertEquals(1, stats.missCount());
1338     assertEquals(1, stats.loadSuccessCount());
1339     assertEquals(0, stats.loadExceptionCount());
1340     assertEquals(1, stats.hitCount());
1341 
1342     ticker.advance(1, MILLISECONDS);
1343     assertSame(one, cache.getUnchecked(key));
1344     // refreshed
1345     stats = cache.stats();
1346     assertEquals(1, stats.missCount());
1347     assertEquals(1, stats.loadSuccessCount());
1348     assertEquals(1, stats.loadExceptionCount());
1349     assertEquals(2, stats.hitCount());
1350 
1351     ticker.advance(1, MILLISECONDS);
1352     assertSame(one, cache.getUnchecked(key));
1353     stats = cache.stats();
1354     assertEquals(1, stats.missCount());
1355     assertEquals(1, stats.loadSuccessCount());
1356     assertEquals(2, stats.loadExceptionCount());
1357     assertEquals(3, stats.hitCount());
1358   }
1359 
testBulkLoadCheckedException()1360   public void testBulkLoadCheckedException() {
1361     Exception e = new Exception();
1362     CacheLoader<Object, Object> loader = exceptionLoader(e);
1363     LoadingCache<Object, Object> cache =
1364         CacheBuilder.newBuilder().recordStats().build(bulkLoader(loader));
1365     CacheStats stats = cache.stats();
1366     assertEquals(0, stats.missCount());
1367     assertEquals(0, stats.loadSuccessCount());
1368     assertEquals(0, stats.loadExceptionCount());
1369     assertEquals(0, stats.hitCount());
1370 
1371     ExecutionException expected =
1372         assertThrows(ExecutionException.class, () -> cache.getAll(asList(new Object())));
1373     assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1374     stats = cache.stats();
1375     assertEquals(1, stats.missCount());
1376     assertEquals(0, stats.loadSuccessCount());
1377     assertEquals(1, stats.loadExceptionCount());
1378     assertEquals(0, stats.hitCount());
1379   }
1380 
testBulkLoadInterruptedException()1381   public void testBulkLoadInterruptedException() {
1382     Exception e = new InterruptedException();
1383     CacheLoader<Object, Object> loader = exceptionLoader(e);
1384     LoadingCache<Object, Object> cache =
1385         CacheBuilder.newBuilder().recordStats().build(bulkLoader(loader));
1386     CacheStats stats = cache.stats();
1387     assertEquals(0, stats.missCount());
1388     assertEquals(0, stats.loadSuccessCount());
1389     assertEquals(0, stats.loadExceptionCount());
1390     assertEquals(0, stats.hitCount());
1391 
1392     ExecutionException expected =
1393         assertThrows(ExecutionException.class, () -> cache.getAll(asList(new Object())));
1394     assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1395     assertTrue(currentThread().interrupted());
1396     stats = cache.stats();
1397     assertEquals(1, stats.missCount());
1398     assertEquals(0, stats.loadSuccessCount());
1399     assertEquals(1, stats.loadExceptionCount());
1400     assertEquals(0, stats.hitCount());
1401   }
1402 
testLoadUncheckedException()1403   public void testLoadUncheckedException() throws ExecutionException {
1404     Exception e = new RuntimeException();
1405     CacheLoader<Object, Object> loader = exceptionLoader(e);
1406     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
1407     CacheStats stats = cache.stats();
1408     assertEquals(0, stats.missCount());
1409     assertEquals(0, stats.loadSuccessCount());
1410     assertEquals(0, stats.loadExceptionCount());
1411     assertEquals(0, stats.hitCount());
1412 
1413     UncheckedExecutionException expected =
1414         assertThrows(UncheckedExecutionException.class, () -> cache.get(new Object()));
1415     assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1416     stats = cache.stats();
1417     assertEquals(1, stats.missCount());
1418     assertEquals(0, stats.loadSuccessCount());
1419     assertEquals(1, stats.loadExceptionCount());
1420     assertEquals(0, stats.hitCount());
1421 
1422     expected =
1423         assertThrows(UncheckedExecutionException.class, () -> cache.getUnchecked(new Object()));
1424     assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1425     stats = cache.stats();
1426     assertEquals(2, stats.missCount());
1427     assertEquals(0, stats.loadSuccessCount());
1428     assertEquals(2, stats.loadExceptionCount());
1429     assertEquals(0, stats.hitCount());
1430 
1431     cache.refresh(new Object());
1432     checkLoggedCause(e);
1433     stats = cache.stats();
1434     assertEquals(2, stats.missCount());
1435     assertEquals(0, stats.loadSuccessCount());
1436     assertEquals(3, stats.loadExceptionCount());
1437     assertEquals(0, stats.hitCount());
1438 
1439     Exception callableException = new RuntimeException();
1440     expected =
1441         assertThrows(
1442             UncheckedExecutionException.class,
1443             () -> cache.get(new Object(), throwing(callableException)));
1444     assertThat(expected).hasCauseThat().isSameInstanceAs(callableException);
1445     stats = cache.stats();
1446     assertEquals(3, stats.missCount());
1447     assertEquals(0, stats.loadSuccessCount());
1448     assertEquals(4, stats.loadExceptionCount());
1449     assertEquals(0, stats.hitCount());
1450 
1451     expected =
1452         assertThrows(UncheckedExecutionException.class, () -> cache.getAll(asList(new Object())));
1453     assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1454     stats = cache.stats();
1455     assertEquals(4, stats.missCount());
1456     assertEquals(0, stats.loadSuccessCount());
1457     assertEquals(5, stats.loadExceptionCount());
1458     assertEquals(0, stats.hitCount());
1459   }
1460 
testReloadUncheckedException()1461   public void testReloadUncheckedException() throws ExecutionException {
1462     final Object one = new Object();
1463     final Exception e = new RuntimeException();
1464     CacheLoader<Object, Object> loader =
1465         new CacheLoader<Object, Object>() {
1466           @Override
1467           public Object load(Object key) {
1468             return one;
1469           }
1470 
1471           @Override
1472           public ListenableFuture<Object> reload(Object key, Object oldValue) throws Exception {
1473             throw e;
1474           }
1475         };
1476 
1477     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
1478     Object key = new Object();
1479     CacheStats stats = cache.stats();
1480     assertEquals(0, stats.missCount());
1481     assertEquals(0, stats.loadSuccessCount());
1482     assertEquals(0, stats.loadExceptionCount());
1483     assertEquals(0, stats.hitCount());
1484 
1485     assertSame(one, cache.getUnchecked(key));
1486     stats = cache.stats();
1487     assertEquals(1, stats.missCount());
1488     assertEquals(1, stats.loadSuccessCount());
1489     assertEquals(0, stats.loadExceptionCount());
1490     assertEquals(0, stats.hitCount());
1491 
1492     cache.refresh(key);
1493     checkLoggedCause(e);
1494     stats = cache.stats();
1495     assertEquals(1, stats.missCount());
1496     assertEquals(1, stats.loadSuccessCount());
1497     assertEquals(1, stats.loadExceptionCount());
1498     assertEquals(0, stats.hitCount());
1499 
1500     assertSame(one, cache.getUnchecked(key));
1501     stats = cache.stats();
1502     assertEquals(1, stats.missCount());
1503     assertEquals(1, stats.loadSuccessCount());
1504     assertEquals(1, stats.loadExceptionCount());
1505     assertEquals(1, stats.hitCount());
1506   }
1507 
testReloadFutureUncheckedException()1508   public void testReloadFutureUncheckedException() throws ExecutionException {
1509     final Object one = new Object();
1510     final Exception e = new RuntimeException();
1511     CacheLoader<Object, Object> loader =
1512         new CacheLoader<Object, Object>() {
1513           @Override
1514           public Object load(Object key) {
1515             return one;
1516           }
1517 
1518           @Override
1519           public ListenableFuture<Object> reload(Object key, Object oldValue) {
1520             return Futures.immediateFailedFuture(e);
1521           }
1522         };
1523 
1524     LoadingCache<Object, Object> cache = CacheBuilder.newBuilder().recordStats().build(loader);
1525     Object key = new Object();
1526     CacheStats stats = cache.stats();
1527     assertEquals(0, stats.missCount());
1528     assertEquals(0, stats.loadSuccessCount());
1529     assertEquals(0, stats.loadExceptionCount());
1530     assertEquals(0, stats.hitCount());
1531 
1532     assertSame(one, cache.getUnchecked(key));
1533     stats = cache.stats();
1534     assertEquals(1, stats.missCount());
1535     assertEquals(1, stats.loadSuccessCount());
1536     assertEquals(0, stats.loadExceptionCount());
1537     assertEquals(0, stats.hitCount());
1538 
1539     cache.refresh(key);
1540     checkLoggedCause(e);
1541     stats = cache.stats();
1542     assertEquals(1, stats.missCount());
1543     assertEquals(1, stats.loadSuccessCount());
1544     assertEquals(1, stats.loadExceptionCount());
1545     assertEquals(0, stats.hitCount());
1546 
1547     assertSame(one, cache.getUnchecked(key));
1548     stats = cache.stats();
1549     assertEquals(1, stats.missCount());
1550     assertEquals(1, stats.loadSuccessCount());
1551     assertEquals(1, stats.loadExceptionCount());
1552     assertEquals(1, stats.hitCount());
1553   }
1554 
testRefreshUncheckedException()1555   public void testRefreshUncheckedException() {
1556     final Object one = new Object();
1557     final Exception e = new RuntimeException();
1558     FakeTicker ticker = new FakeTicker();
1559     CacheLoader<Object, Object> loader =
1560         new CacheLoader<Object, Object>() {
1561           @Override
1562           public Object load(Object key) {
1563             return one;
1564           }
1565 
1566           @Override
1567           public ListenableFuture<Object> reload(Object key, Object oldValue) {
1568             return Futures.immediateFailedFuture(e);
1569           }
1570         };
1571 
1572     LoadingCache<Object, Object> cache =
1573         CacheBuilder.newBuilder()
1574             .recordStats()
1575             .ticker(ticker)
1576             .refreshAfterWrite(1, MILLISECONDS)
1577             .build(loader);
1578     Object key = new Object();
1579     CacheStats stats = cache.stats();
1580     assertEquals(0, stats.missCount());
1581     assertEquals(0, stats.loadSuccessCount());
1582     assertEquals(0, stats.loadExceptionCount());
1583     assertEquals(0, stats.hitCount());
1584 
1585     assertSame(one, cache.getUnchecked(key));
1586     stats = cache.stats();
1587     assertEquals(1, stats.missCount());
1588     assertEquals(1, stats.loadSuccessCount());
1589     assertEquals(0, stats.loadExceptionCount());
1590     assertEquals(0, stats.hitCount());
1591 
1592     ticker.advance(1, MILLISECONDS);
1593     assertSame(one, cache.getUnchecked(key));
1594     stats = cache.stats();
1595     assertEquals(1, stats.missCount());
1596     assertEquals(1, stats.loadSuccessCount());
1597     assertEquals(0, stats.loadExceptionCount());
1598     assertEquals(1, stats.hitCount());
1599 
1600     ticker.advance(1, MILLISECONDS);
1601     assertSame(one, cache.getUnchecked(key));
1602     // refreshed
1603     stats = cache.stats();
1604     assertEquals(1, stats.missCount());
1605     assertEquals(1, stats.loadSuccessCount());
1606     assertEquals(1, stats.loadExceptionCount());
1607     assertEquals(2, stats.hitCount());
1608 
1609     ticker.advance(1, MILLISECONDS);
1610     assertSame(one, cache.getUnchecked(key));
1611     stats = cache.stats();
1612     assertEquals(1, stats.missCount());
1613     assertEquals(1, stats.loadSuccessCount());
1614     assertEquals(2, stats.loadExceptionCount());
1615     assertEquals(3, stats.hitCount());
1616   }
1617 
testBulkLoadUncheckedException()1618   public void testBulkLoadUncheckedException() throws ExecutionException {
1619     Exception e = new RuntimeException();
1620     CacheLoader<Object, Object> loader = exceptionLoader(e);
1621     LoadingCache<Object, Object> cache =
1622         CacheBuilder.newBuilder().recordStats().build(bulkLoader(loader));
1623     CacheStats stats = cache.stats();
1624     assertEquals(0, stats.missCount());
1625     assertEquals(0, stats.loadSuccessCount());
1626     assertEquals(0, stats.loadExceptionCount());
1627     assertEquals(0, stats.hitCount());
1628 
1629     UncheckedExecutionException expected =
1630         assertThrows(UncheckedExecutionException.class, () -> cache.getAll(asList(new Object())));
1631     assertThat(expected).hasCauseThat().isSameInstanceAs(e);
1632     stats = cache.stats();
1633     assertEquals(1, stats.missCount());
1634     assertEquals(0, stats.loadSuccessCount());
1635     assertEquals(1, stats.loadExceptionCount());
1636     assertEquals(0, stats.hitCount());
1637   }
1638 
testReloadAfterFailure()1639   public void testReloadAfterFailure() throws ExecutionException {
1640     final AtomicInteger count = new AtomicInteger();
1641     final Exception e = new IllegalStateException("exception to trigger failure on first load()");
1642     CacheLoader<Integer, String> failOnceFunction =
1643         new CacheLoader<Integer, String>() {
1644 
1645           @Override
1646           public String load(Integer key) throws Exception {
1647             if (count.getAndIncrement() == 0) {
1648               throw e;
1649             }
1650             return key.toString();
1651           }
1652         };
1653     CountingRemovalListener<Integer, String> removalListener = countingRemovalListener();
1654     LoadingCache<Integer, String> cache =
1655         CacheBuilder.newBuilder().removalListener(removalListener).build(failOnceFunction);
1656 
1657     UncheckedExecutionException ue =
1658         assertThrows(UncheckedExecutionException.class, () -> cache.getUnchecked(1));
1659     assertThat(ue).hasCauseThat().isSameInstanceAs(e);
1660 
1661     assertEquals("1", cache.getUnchecked(1));
1662     assertEquals(0, removalListener.getCount());
1663 
1664     count.set(0);
1665     cache.refresh(2);
1666     checkLoggedCause(e);
1667 
1668     assertEquals("2", cache.getUnchecked(2));
1669     assertEquals(0, removalListener.getCount());
1670   }
1671 
1672 
1673   @AndroidIncompatible // Depends on GC behavior
testReloadAfterValueReclamation()1674   public void testReloadAfterValueReclamation() throws InterruptedException, ExecutionException {
1675     CountingLoader countingLoader = new CountingLoader();
1676     LoadingCache<Object, Object> cache =
1677         CacheBuilder.newBuilder().weakValues().build(countingLoader);
1678     ConcurrentMap<Object, Object> map = cache.asMap();
1679 
1680     int iterations = 10;
1681     WeakReference<Object> ref = new WeakReference<>(null);
1682     int expectedComputations = 0;
1683     for (int i = 0; i < iterations; i++) {
1684       // The entry should get garbage collected and recomputed.
1685       Object oldValue = ref.get();
1686       if (oldValue == null) {
1687         expectedComputations++;
1688       }
1689       ref = new WeakReference<>(cache.getUnchecked(1));
1690       oldValue = null;
1691       Thread.sleep(i);
1692       System.gc();
1693     }
1694     assertEquals(expectedComputations, countingLoader.getCount());
1695 
1696     for (int i = 0; i < iterations; i++) {
1697       // The entry should get garbage collected and recomputed.
1698       Object oldValue = ref.get();
1699       if (oldValue == null) {
1700         expectedComputations++;
1701       }
1702       cache.refresh(1);
1703       checkNothingLogged();
1704       ref = new WeakReference<>(map.get(1));
1705       oldValue = null;
1706       Thread.sleep(i);
1707       System.gc();
1708     }
1709     assertEquals(expectedComputations, countingLoader.getCount());
1710   }
1711 
testReloadAfterSimulatedValueReclamation()1712   public void testReloadAfterSimulatedValueReclamation() throws ExecutionException {
1713     CountingLoader countingLoader = new CountingLoader();
1714     LoadingCache<Object, Object> cache =
1715         CacheBuilder.newBuilder().concurrencyLevel(1).weakValues().build(countingLoader);
1716 
1717     Object key = new Object();
1718     assertNotNull(cache.getUnchecked(key));
1719 
1720     CacheTesting.simulateValueReclamation(cache, key);
1721 
1722     // this blocks if computation can't deal with partially-collected values
1723     assertNotNull(cache.getUnchecked(key));
1724     assertEquals(1, cache.size());
1725     assertEquals(2, countingLoader.getCount());
1726 
1727     CacheTesting.simulateValueReclamation(cache, key);
1728     cache.refresh(key);
1729     checkNothingLogged();
1730     assertEquals(1, cache.size());
1731     assertEquals(3, countingLoader.getCount());
1732   }
1733 
testReloadAfterSimulatedKeyReclamation()1734   public void testReloadAfterSimulatedKeyReclamation() throws ExecutionException {
1735     CountingLoader countingLoader = new CountingLoader();
1736     LoadingCache<Object, Object> cache =
1737         CacheBuilder.newBuilder().concurrencyLevel(1).weakKeys().build(countingLoader);
1738 
1739     Object key = new Object();
1740     assertNotNull(cache.getUnchecked(key));
1741     assertEquals(1, cache.size());
1742 
1743     CacheTesting.simulateKeyReclamation(cache, key);
1744 
1745     // this blocks if computation can't deal with partially-collected values
1746     assertNotNull(cache.getUnchecked(key));
1747     assertEquals(2, countingLoader.getCount());
1748 
1749     CacheTesting.simulateKeyReclamation(cache, key);
1750     cache.refresh(key);
1751     checkNothingLogged();
1752     assertEquals(3, countingLoader.getCount());
1753   }
1754 
1755   /**
1756    * Make sure LoadingCache correctly wraps ExecutionExceptions and UncheckedExecutionExceptions.
1757    */
testLoadingExceptionWithCause()1758   public void testLoadingExceptionWithCause() {
1759     final Exception cause = new Exception();
1760     final UncheckedExecutionException uee = new UncheckedExecutionException(cause);
1761     final ExecutionException ee = new ExecutionException(cause);
1762 
1763     LoadingCache<Object, Object> cacheUnchecked =
1764         CacheBuilder.newBuilder().build(exceptionLoader(uee));
1765     LoadingCache<Object, Object> cacheChecked =
1766         CacheBuilder.newBuilder().build(exceptionLoader(ee));
1767 
1768     try {
1769       cacheUnchecked.get(new Object());
1770       fail();
1771     } catch (ExecutionException e) {
1772       fail();
1773     } catch (UncheckedExecutionException caughtEe) {
1774       assertThat(caughtEe).hasCauseThat().isSameInstanceAs(uee);
1775     }
1776 
1777     UncheckedExecutionException caughtUee =
1778         assertThrows(
1779             UncheckedExecutionException.class, () -> cacheUnchecked.getUnchecked(new Object()));
1780     assertThat(caughtUee).hasCauseThat().isSameInstanceAs(uee);
1781 
1782     cacheUnchecked.refresh(new Object());
1783     checkLoggedCause(uee);
1784 
1785     try {
1786       cacheUnchecked.getAll(asList(new Object()));
1787       fail();
1788     } catch (ExecutionException e) {
1789       fail();
1790     } catch (UncheckedExecutionException caughtEe) {
1791       assertThat(caughtEe).hasCauseThat().isSameInstanceAs(uee);
1792     }
1793 
1794     ExecutionException caughtEe =
1795         assertThrows(ExecutionException.class, () -> cacheChecked.get(new Object()));
1796     assertThat(caughtEe).hasCauseThat().isSameInstanceAs(ee);
1797 
1798     caughtUee =
1799         assertThrows(
1800             UncheckedExecutionException.class, () -> cacheChecked.getUnchecked(new Object()));
1801     assertThat(caughtUee).hasCauseThat().isSameInstanceAs(ee);
1802 
1803     cacheChecked.refresh(new Object());
1804     checkLoggedCause(ee);
1805 
1806     caughtEe =
1807         assertThrows(ExecutionException.class, () -> cacheChecked.getAll(asList(new Object())));
1808     assertThat(caughtEe).hasCauseThat().isSameInstanceAs(ee);
1809   }
1810 
testBulkLoadingExceptionWithCause()1811   public void testBulkLoadingExceptionWithCause() {
1812     final Exception cause = new Exception();
1813     final UncheckedExecutionException uee = new UncheckedExecutionException(cause);
1814     final ExecutionException ee = new ExecutionException(cause);
1815 
1816     LoadingCache<Object, Object> cacheUnchecked =
1817         CacheBuilder.newBuilder().build(bulkLoader(exceptionLoader(uee)));
1818     LoadingCache<Object, Object> cacheChecked =
1819         CacheBuilder.newBuilder().build(bulkLoader(exceptionLoader(ee)));
1820 
1821     try {
1822       cacheUnchecked.getAll(asList(new Object()));
1823       fail();
1824     } catch (ExecutionException e) {
1825       fail();
1826     } catch (UncheckedExecutionException caughtEe) {
1827       assertThat(caughtEe).hasCauseThat().isSameInstanceAs(uee);
1828     }
1829 
1830     ExecutionException caughtEe =
1831         assertThrows(ExecutionException.class, () -> cacheChecked.getAll(asList(new Object())));
1832     assertThat(caughtEe).hasCauseThat().isSameInstanceAs(ee);
1833   }
1834 
1835   @AndroidIncompatible // Bug? expected:<1> but was:<2>
testConcurrentLoading()1836   public void testConcurrentLoading() throws InterruptedException {
1837     testConcurrentLoading(CacheBuilder.newBuilder());
1838   }
1839 
testConcurrentLoading(CacheBuilder<Object, Object> builder)1840   private static void testConcurrentLoading(CacheBuilder<Object, Object> builder)
1841       throws InterruptedException {
1842     testConcurrentLoadingDefault(builder);
1843     testConcurrentLoadingNull(builder);
1844     testConcurrentLoadingUncheckedException(builder);
1845     testConcurrentLoadingCheckedException(builder);
1846   }
1847 
1848   @AndroidIncompatible // Bug? expected:<1> but was:<2>
testConcurrentExpirationLoading()1849   public void testConcurrentExpirationLoading() throws InterruptedException {
1850     testConcurrentLoading(CacheBuilder.newBuilder().expireAfterWrite(10, TimeUnit.SECONDS));
1851   }
1852 
1853   /**
1854    * On a successful concurrent computation, only one thread does the work, but all the threads get
1855    * the same result.
1856    */
testConcurrentLoadingDefault(CacheBuilder<Object, Object> builder)1857   private static void testConcurrentLoadingDefault(CacheBuilder<Object, Object> builder)
1858       throws InterruptedException {
1859 
1860     int count = 10;
1861     final AtomicInteger callCount = new AtomicInteger();
1862     final CountDownLatch startSignal = new CountDownLatch(count + 1);
1863     final Object result = new Object();
1864 
1865     LoadingCache<String, Object> cache =
1866         builder.build(
1867             new CacheLoader<String, Object>() {
1868               @Override
1869               public Object load(String key) throws InterruptedException {
1870                 callCount.incrementAndGet();
1871                 startSignal.await();
1872                 return result;
1873               }
1874             });
1875 
1876     List<Object> resultArray = doConcurrentGet(cache, "bar", count, startSignal);
1877 
1878     assertEquals(1, callCount.get());
1879     for (int i = 0; i < count; i++) {
1880       assertSame("result(" + i + ") didn't match expected", result, resultArray.get(i));
1881     }
1882   }
1883 
1884   /**
1885    * On a concurrent computation that returns null, all threads should get an
1886    * InvalidCacheLoadException, with the loader only called once. The result should not be cached (a
1887    * later request should call the loader again).
1888    */
testConcurrentLoadingNull(CacheBuilder<Object, Object> builder)1889   private static void testConcurrentLoadingNull(CacheBuilder<Object, Object> builder)
1890       throws InterruptedException {
1891 
1892     int count = 10;
1893     final AtomicInteger callCount = new AtomicInteger();
1894     final CountDownLatch startSignal = new CountDownLatch(count + 1);
1895 
1896     LoadingCache<String, String> cache =
1897         builder.build(
1898             new CacheLoader<String, String>() {
1899               @Override
1900               public String load(String key) throws InterruptedException {
1901                 callCount.incrementAndGet();
1902                 startSignal.await();
1903                 return null;
1904               }
1905             });
1906 
1907     List<Object> result = doConcurrentGet(cache, "bar", count, startSignal);
1908 
1909     assertEquals(1, callCount.get());
1910     for (int i = 0; i < count; i++) {
1911       assertThat(result.get(i)).isInstanceOf(InvalidCacheLoadException.class);
1912     }
1913 
1914     // subsequent calls should call the loader again, not get the old exception
1915     try {
1916       cache.getUnchecked("bar");
1917       fail();
1918     } catch (InvalidCacheLoadException expected) {
1919     }
1920     assertEquals(2, callCount.get());
1921   }
1922 
1923   /**
1924    * On a concurrent computation that throws an unchecked exception, all threads should get the
1925    * (wrapped) exception, with the loader called only once. The result should not be cached (a later
1926    * request should call the loader again).
1927    */
testConcurrentLoadingUncheckedException(CacheBuilder<Object, Object> builder)1928   private static void testConcurrentLoadingUncheckedException(CacheBuilder<Object, Object> builder)
1929       throws InterruptedException {
1930 
1931     int count = 10;
1932     final AtomicInteger callCount = new AtomicInteger();
1933     final CountDownLatch startSignal = new CountDownLatch(count + 1);
1934     final RuntimeException e = new RuntimeException();
1935 
1936     LoadingCache<String, String> cache =
1937         builder.build(
1938             new CacheLoader<String, String>() {
1939               @Override
1940               public String load(String key) throws InterruptedException {
1941                 callCount.incrementAndGet();
1942                 startSignal.await();
1943                 throw e;
1944               }
1945             });
1946 
1947     List<Object> result = doConcurrentGet(cache, "bar", count, startSignal);
1948 
1949     assertEquals(1, callCount.get());
1950     for (int i = 0; i < count; i++) {
1951       // doConcurrentGet alternates between calling getUnchecked and calling get, but an unchecked
1952       // exception thrown by the loader is always wrapped as an UncheckedExecutionException.
1953       assertThat(result.get(i)).isInstanceOf(UncheckedExecutionException.class);
1954       assertThat(((UncheckedExecutionException) result.get(i))).hasCauseThat().isSameInstanceAs(e);
1955     }
1956 
1957     // subsequent calls should call the loader again, not get the old exception
1958     try {
1959       cache.getUnchecked("bar");
1960       fail();
1961     } catch (UncheckedExecutionException expected) {
1962     }
1963     assertEquals(2, callCount.get());
1964   }
1965 
1966   /**
1967    * On a concurrent computation that throws a checked exception, all threads should get the
1968    * (wrapped) exception, with the loader called only once. The result should not be cached (a later
1969    * request should call the loader again).
1970    */
testConcurrentLoadingCheckedException(CacheBuilder<Object, Object> builder)1971   private static void testConcurrentLoadingCheckedException(CacheBuilder<Object, Object> builder)
1972       throws InterruptedException {
1973 
1974     int count = 10;
1975     final AtomicInteger callCount = new AtomicInteger();
1976     final CountDownLatch startSignal = new CountDownLatch(count + 1);
1977     final IOException e = new IOException();
1978 
1979     LoadingCache<String, String> cache =
1980         builder.build(
1981             new CacheLoader<String, String>() {
1982               @Override
1983               public String load(String key) throws IOException, InterruptedException {
1984                 callCount.incrementAndGet();
1985                 startSignal.await();
1986                 throw e;
1987               }
1988             });
1989 
1990     List<Object> result = doConcurrentGet(cache, "bar", count, startSignal);
1991 
1992     assertEquals(1, callCount.get());
1993     for (int i = 0; i < count; i++) {
1994       // doConcurrentGet alternates between calling getUnchecked and calling get. If we call get(),
1995       // we should get an ExecutionException; if we call getUnchecked(), we should get an
1996       // UncheckedExecutionException.
1997       int mod = i % 3;
1998       if (mod == 0 || mod == 2) {
1999         assertThat(result.get(i)).isInstanceOf(ExecutionException.class);
2000         assertThat((ExecutionException) result.get(i)).hasCauseThat().isSameInstanceAs(e);
2001       } else {
2002         assertThat(result.get(i)).isInstanceOf(UncheckedExecutionException.class);
2003         assertThat((UncheckedExecutionException) result.get(i)).hasCauseThat().isSameInstanceAs(e);
2004       }
2005     }
2006 
2007     // subsequent calls should call the loader again, not get the old exception
2008     try {
2009       cache.getUnchecked("bar");
2010       fail();
2011     } catch (UncheckedExecutionException expected) {
2012     }
2013     assertEquals(2, callCount.get());
2014   }
2015 
2016   /**
2017    * Test-helper method that performs {@code nThreads} concurrent calls to {@code cache.get(key)} or
2018    * {@code cache.getUnchecked(key)}, and returns a List containing each of the results. The result
2019    * for any given call to {@code cache.get} or {@code cache.getUnchecked} is the value returned, or
2020    * the exception thrown.
2021    *
2022    * <p>As we iterate from {@code 0} to {@code nThreads}, threads with an even index will call
2023    * {@code getUnchecked}, and threads with an odd index will call {@code get}. If the cache throws
2024    * exceptions, this difference may be visible in the returned List.
2025    */
doConcurrentGet( final LoadingCache<K, ?> cache, final K key, int nThreads, final CountDownLatch gettersStartedSignal)2026   private static <K> List<Object> doConcurrentGet(
2027       final LoadingCache<K, ?> cache,
2028       final K key,
2029       int nThreads,
2030       final CountDownLatch gettersStartedSignal)
2031       throws InterruptedException {
2032 
2033     final AtomicReferenceArray<Object> result = new AtomicReferenceArray<>(nThreads);
2034     final CountDownLatch gettersComplete = new CountDownLatch(nThreads);
2035     for (int i = 0; i < nThreads; i++) {
2036       final int index = i;
2037       Thread thread =
2038           new Thread(
2039               new Runnable() {
2040                 @Override
2041                 public void run() {
2042                   gettersStartedSignal.countDown();
2043                   Object value = null;
2044                   try {
2045                     int mod = index % 3;
2046                     if (mod == 0) {
2047                       value = cache.get(key);
2048                     } else if (mod == 1) {
2049                       value = cache.getUnchecked(key);
2050                     } else {
2051                       cache.refresh(key);
2052                       value = cache.get(key);
2053                     }
2054                     result.set(index, value);
2055                   } catch (Throwable t) {
2056                     result.set(index, t);
2057                   }
2058                   gettersComplete.countDown();
2059                 }
2060               });
2061       thread.start();
2062       // we want to wait until each thread is WAITING - one thread waiting inside CacheLoader.load
2063       // (in startSignal.await()), and the others waiting for that thread's result.
2064       while (thread.isAlive() && thread.getState() != Thread.State.WAITING) {
2065         Thread.yield();
2066       }
2067     }
2068     gettersStartedSignal.countDown();
2069     gettersComplete.await();
2070 
2071     List<Object> resultList = Lists.newArrayListWithExpectedSize(nThreads);
2072     for (int i = 0; i < nThreads; i++) {
2073       resultList.add(result.get(i));
2074     }
2075     return resultList;
2076   }
2077 
testAsMapDuringLoading()2078   public void testAsMapDuringLoading() throws InterruptedException, ExecutionException {
2079     final CountDownLatch getStartedSignal = new CountDownLatch(2);
2080     final CountDownLatch letGetFinishSignal = new CountDownLatch(1);
2081     final CountDownLatch getFinishedSignal = new CountDownLatch(2);
2082     final String getKey = "get";
2083     final String refreshKey = "refresh";
2084     final String suffix = "Suffix";
2085 
2086     CacheLoader<String, String> computeFunction =
2087         new CacheLoader<String, String>() {
2088           @Override
2089           public String load(String key) throws InterruptedException {
2090             getStartedSignal.countDown();
2091             letGetFinishSignal.await();
2092             return key + suffix;
2093           }
2094         };
2095 
2096     final LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(computeFunction);
2097     ConcurrentMap<String, String> map = cache.asMap();
2098     map.put(refreshKey, refreshKey);
2099     assertEquals(1, map.size());
2100     assertFalse(map.containsKey(getKey));
2101     assertSame(refreshKey, map.get(refreshKey));
2102 
2103     new Thread() {
2104       @Override
2105       public void run() {
2106         cache.getUnchecked(getKey);
2107         getFinishedSignal.countDown();
2108       }
2109     }.start();
2110     new Thread() {
2111       @Override
2112       public void run() {
2113         cache.refresh(refreshKey);
2114         getFinishedSignal.countDown();
2115       }
2116     }.start();
2117 
2118     getStartedSignal.await();
2119 
2120     // computation is in progress; asMap shouldn't have changed
2121     assertEquals(1, map.size());
2122     assertFalse(map.containsKey(getKey));
2123     assertSame(refreshKey, map.get(refreshKey));
2124 
2125     // let computation complete
2126     letGetFinishSignal.countDown();
2127     getFinishedSignal.await();
2128     checkNothingLogged();
2129 
2130     // asMap view should have been updated
2131     assertEquals(2, cache.size());
2132     assertEquals(getKey + suffix, map.get(getKey));
2133     assertEquals(refreshKey + suffix, map.get(refreshKey));
2134   }
2135 
testInvalidateDuringLoading()2136   public void testInvalidateDuringLoading() throws InterruptedException, ExecutionException {
2137     // computation starts; invalidate() is called on the key being computed, computation finishes
2138     final CountDownLatch computationStarted = new CountDownLatch(2);
2139     final CountDownLatch letGetFinishSignal = new CountDownLatch(1);
2140     final CountDownLatch getFinishedSignal = new CountDownLatch(2);
2141     final String getKey = "get";
2142     final String refreshKey = "refresh";
2143     final String suffix = "Suffix";
2144 
2145     CacheLoader<String, String> computeFunction =
2146         new CacheLoader<String, String>() {
2147           @Override
2148           public String load(String key) throws InterruptedException {
2149             computationStarted.countDown();
2150             letGetFinishSignal.await();
2151             return key + suffix;
2152           }
2153         };
2154 
2155     final LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(computeFunction);
2156     ConcurrentMap<String, String> map = cache.asMap();
2157     map.put(refreshKey, refreshKey);
2158 
2159     new Thread() {
2160       @Override
2161       public void run() {
2162         cache.getUnchecked(getKey);
2163         getFinishedSignal.countDown();
2164       }
2165     }.start();
2166     new Thread() {
2167       @Override
2168       public void run() {
2169         cache.refresh(refreshKey);
2170         getFinishedSignal.countDown();
2171       }
2172     }.start();
2173 
2174     computationStarted.await();
2175     cache.invalidate(getKey);
2176     cache.invalidate(refreshKey);
2177     assertFalse(map.containsKey(getKey));
2178     assertFalse(map.containsKey(refreshKey));
2179 
2180     // let computation complete
2181     letGetFinishSignal.countDown();
2182     getFinishedSignal.await();
2183     checkNothingLogged();
2184 
2185     // results should be visible
2186     assertEquals(2, cache.size());
2187     assertEquals(getKey + suffix, map.get(getKey));
2188     assertEquals(refreshKey + suffix, map.get(refreshKey));
2189     assertEquals(2, cache.size());
2190   }
2191 
testInvalidateAndReloadDuringLoading()2192   public void testInvalidateAndReloadDuringLoading()
2193       throws InterruptedException, ExecutionException {
2194     // computation starts; clear() is called, computation finishes
2195     final CountDownLatch computationStarted = new CountDownLatch(2);
2196     final CountDownLatch letGetFinishSignal = new CountDownLatch(1);
2197     final CountDownLatch getFinishedSignal = new CountDownLatch(4);
2198     final String getKey = "get";
2199     final String refreshKey = "refresh";
2200     final String suffix = "Suffix";
2201 
2202     CacheLoader<String, String> computeFunction =
2203         new CacheLoader<String, String>() {
2204           @Override
2205           public String load(String key) throws InterruptedException {
2206             computationStarted.countDown();
2207             letGetFinishSignal.await();
2208             return key + suffix;
2209           }
2210         };
2211 
2212     final LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(computeFunction);
2213     ConcurrentMap<String, String> map = cache.asMap();
2214     map.put(refreshKey, refreshKey);
2215 
2216     new Thread() {
2217       @Override
2218       public void run() {
2219         cache.getUnchecked(getKey);
2220         getFinishedSignal.countDown();
2221       }
2222     }.start();
2223     new Thread() {
2224       @Override
2225       public void run() {
2226         cache.refresh(refreshKey);
2227         getFinishedSignal.countDown();
2228       }
2229     }.start();
2230 
2231     computationStarted.await();
2232     cache.invalidate(getKey);
2233     cache.invalidate(refreshKey);
2234     assertFalse(map.containsKey(getKey));
2235     assertFalse(map.containsKey(refreshKey));
2236 
2237     // start new computations
2238     new Thread() {
2239       @Override
2240       public void run() {
2241         cache.getUnchecked(getKey);
2242         getFinishedSignal.countDown();
2243       }
2244     }.start();
2245     new Thread() {
2246       @Override
2247       public void run() {
2248         cache.refresh(refreshKey);
2249         getFinishedSignal.countDown();
2250       }
2251     }.start();
2252 
2253     // let computation complete
2254     letGetFinishSignal.countDown();
2255     getFinishedSignal.await();
2256     checkNothingLogged();
2257 
2258     // results should be visible
2259     assertEquals(2, cache.size());
2260     assertEquals(getKey + suffix, map.get(getKey));
2261     assertEquals(refreshKey + suffix, map.get(refreshKey));
2262   }
2263 
testExpandDuringLoading()2264   public void testExpandDuringLoading() throws InterruptedException {
2265     final int count = 3;
2266     final AtomicInteger callCount = new AtomicInteger();
2267     // tells the computing thread when to start computing
2268     final CountDownLatch computeSignal = new CountDownLatch(1);
2269     // tells the main thread when computation is pending
2270     final CountDownLatch secondSignal = new CountDownLatch(1);
2271     // tells the main thread when the second get has started
2272     final CountDownLatch thirdSignal = new CountDownLatch(1);
2273     // tells the main thread when the third get has started
2274     final CountDownLatch fourthSignal = new CountDownLatch(1);
2275     // tells the test when all gets have returned
2276     final CountDownLatch doneSignal = new CountDownLatch(count);
2277 
2278     CacheLoader<String, String> computeFunction =
2279         new CacheLoader<String, String>() {
2280           @Override
2281           public String load(String key) throws InterruptedException {
2282             callCount.incrementAndGet();
2283             secondSignal.countDown();
2284             computeSignal.await();
2285             return key + "foo";
2286           }
2287         };
2288 
2289     final LoadingCache<String, String> cache =
2290         CacheBuilder.newBuilder().weakKeys().build(computeFunction);
2291 
2292     final AtomicReferenceArray<String> result = new AtomicReferenceArray<>(count);
2293 
2294     final String key = "bar";
2295 
2296     // start computing thread
2297     new Thread() {
2298       @Override
2299       public void run() {
2300         result.set(0, cache.getUnchecked(key));
2301         doneSignal.countDown();
2302       }
2303     }.start();
2304 
2305     // wait for computation to start
2306     secondSignal.await();
2307 
2308     // start waiting thread
2309     new Thread() {
2310       @Override
2311       public void run() {
2312         thirdSignal.countDown();
2313         result.set(1, cache.getUnchecked(key));
2314         doneSignal.countDown();
2315       }
2316     }.start();
2317 
2318     // give the second get a chance to run; it is okay for this to be racy
2319     // as the end result should be the same either way
2320     thirdSignal.await();
2321     Thread.yield();
2322 
2323     // Expand!
2324     CacheTesting.forceExpandSegment(cache, key);
2325 
2326     // start another waiting thread
2327     new Thread() {
2328       @Override
2329       public void run() {
2330         fourthSignal.countDown();
2331         result.set(2, cache.getUnchecked(key));
2332         doneSignal.countDown();
2333       }
2334     }.start();
2335 
2336     // give the third get a chance to run; it is okay for this to be racy
2337     // as the end result should be the same either way
2338     fourthSignal.await();
2339     Thread.yield();
2340 
2341     // let computation finish
2342     computeSignal.countDown();
2343     doneSignal.await();
2344 
2345     assertTrue(callCount.get() == 1);
2346     assertEquals("barfoo", result.get(0));
2347     assertEquals("barfoo", result.get(1));
2348     assertEquals("barfoo", result.get(2));
2349     assertEquals("barfoo", cache.getUnchecked(key));
2350   }
2351 
2352   // Test ignored because it is extremely flaky in CI builds
2353   public void
ignoreTestExpandDuringRefresh()2354       ignoreTestExpandDuringRefresh()
2355       throws InterruptedException, ExecutionException {
2356     final AtomicInteger callCount = new AtomicInteger();
2357     // tells the computing thread when to start computing
2358     final CountDownLatch computeSignal = new CountDownLatch(1);
2359     // tells the main thread when computation is pending
2360     final CountDownLatch secondSignal = new CountDownLatch(1);
2361     // tells the main thread when the second get has started
2362     final CountDownLatch thirdSignal = new CountDownLatch(1);
2363     // tells the main thread when the third get has started
2364     final CountDownLatch fourthSignal = new CountDownLatch(1);
2365     // tells the test when all gets have returned
2366     final CountDownLatch doneSignal = new CountDownLatch(3);
2367     final String suffix = "Suffix";
2368 
2369     CacheLoader<String, String> computeFunction =
2370         new CacheLoader<String, String>() {
2371           @Override
2372           public String load(String key) throws InterruptedException {
2373             callCount.incrementAndGet();
2374             secondSignal.countDown();
2375             computeSignal.await();
2376             return key + suffix;
2377           }
2378         };
2379 
2380     final AtomicReferenceArray<String> result = new AtomicReferenceArray<>(2);
2381 
2382     final LoadingCache<String, String> cache = CacheBuilder.newBuilder().build(computeFunction);
2383     final String key = "bar";
2384     cache.asMap().put(key, key);
2385 
2386     // start computing thread
2387     new Thread() {
2388       @Override
2389       public void run() {
2390         cache.refresh(key);
2391         doneSignal.countDown();
2392       }
2393     }.start();
2394 
2395     // wait for computation to start
2396     secondSignal.await();
2397     checkNothingLogged();
2398 
2399     // start waiting thread
2400     new Thread() {
2401       @Override
2402       public void run() {
2403         thirdSignal.countDown();
2404         result.set(0, cache.getUnchecked(key));
2405         doneSignal.countDown();
2406       }
2407     }.start();
2408 
2409     // give the second get a chance to run; it is okay for this to be racy
2410     // as the end result should be the same either way
2411     thirdSignal.await();
2412     Thread.yield();
2413 
2414     // Expand!
2415     CacheTesting.forceExpandSegment(cache, key);
2416 
2417     // start another waiting thread
2418     new Thread() {
2419       @Override
2420       public void run() {
2421         fourthSignal.countDown();
2422         result.set(1, cache.getUnchecked(key));
2423         doneSignal.countDown();
2424       }
2425     }.start();
2426 
2427     // give the third get a chance to run; it is okay for this to be racy
2428     // as the end result should be the same either way
2429     fourthSignal.await();
2430     Thread.yield();
2431 
2432     // let computation finish
2433     computeSignal.countDown();
2434     doneSignal.await();
2435 
2436     assertTrue(callCount.get() == 1);
2437     assertEquals(key, result.get(0));
2438     assertEquals(key, result.get(1));
2439     assertEquals(key + suffix, cache.getUnchecked(key));
2440   }
2441 
throwing(final Exception exception)2442   static <T> Callable<T> throwing(final Exception exception) {
2443     return new Callable<T>() {
2444       @Override
2445       public T call() throws Exception {
2446         throw exception;
2447       }
2448     };
2449   }
2450 }
2451