@Test public void shouldFetchOnlyOnceInBackgroundHalfWayThroughExpirationWhenMultipleThreadsTryToGetKeys() throws Exception { final KeysDownloader instance = newInstanceForTests(); httpResponseBuilder = new HttpResponseBuilder().setContent("keys1").setCacheControlWithMaxAgeInSeconds(3L); assertEquals("keys1", instance.download()); httpResponseBuilder = new HttpResponseBuilder().setContent("keys2"); currentTimeInMillis += 1500L; List<FutureTask<String>> tasks = new ArrayList<>(); for (int i = 0; i < 10; i++) { tasks.add( new FutureTask<String>( new Callable<String>() { @Override public String call() throws Exception { return instance.download(); } })); } httpTransportGetCount.set(0); backgroundFetchStartedCount.set(0); delayHttpResponseLatch = new CountDownLatch(1); for (FutureTask<String> task : tasks) { executor.execute(task); } for (FutureTask<String> task : tasks) { assertEquals("keys1", task.get(5, TimeUnit.SECONDS)); } delayHttpResponseLatch.countDown(); waitForLatch(backgroundFetchFinishedLatch); assertEquals(1, backgroundFetchStartedCount.get()); assertEquals(1, httpTransportGetCount.get()); }
public String download() throws IOException { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { if (shouldProactivelyRefreshDataInBackground()) { refreshInBackground(); } return cachedData; } } synchronized (fetchDataLock) { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { return cachedData; } } return fetchAndCacheData(); } }
KeysDownloader { public String download() throws IOException { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { if (shouldProactivelyRefreshDataInBackground()) { refreshInBackground(); } return cachedData; } } synchronized (fetchDataLock) { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { return cachedData; } } return fetchAndCacheData(); } } }
KeysDownloader { public String download() throws IOException { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { if (shouldProactivelyRefreshDataInBackground()) { refreshInBackground(); } return cachedData; } } synchronized (fetchDataLock) { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { return cachedData; } } return fetchAndCacheData(); } } KeysDownloader(Executor backgroundExecutor, HttpTransport httpTransport, String url); }
KeysDownloader { public String download() throws IOException { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { if (shouldProactivelyRefreshDataInBackground()) { refreshInBackground(); } return cachedData; } } synchronized (fetchDataLock) { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { return cachedData; } } return fetchAndCacheData(); } } KeysDownloader(Executor backgroundExecutor, HttpTransport httpTransport, String url); String download(); HttpTransport getHttpTransport(); String getUrl(); void refreshInBackground(); }
KeysDownloader { public String download() throws IOException { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { if (shouldProactivelyRefreshDataInBackground()) { refreshInBackground(); } return cachedData; } } synchronized (fetchDataLock) { synchronized (instanceStateLock) { if (hasNonExpiredDataCached()) { return cachedData; } } return fetchAndCacheData(); } } KeysDownloader(Executor backgroundExecutor, HttpTransport httpTransport, String url); String download(); HttpTransport getHttpTransport(); String getUrl(); void refreshInBackground();  }