@Test void cacheConcurrentFetches() throws Exception { final AtomicInteger numOfCacheMisses = new AtomicInteger(0); final ResourceLoader resourceLoader = Mockito.mock(ResourceLoader.class); final Resource resource = Mockito.mock(Resource.class); final ReentrantLock lockBackingMock = new ReentrantLock(); final CountDownLatch lockAcquisitionsAttempted = new CountDownLatch(2); final CloseableLock resourceLock = Mockito.mock(CloseableLock.class); Mockito.doAnswer(invocation -> { lockAcquisitionsAttempted.countDown(); lockBackingMock.lock(); return null; }).when(resourceLock).lock(); Mockito.doAnswer(invocation -> { lockBackingMock.unlock(); return null; }).when(resourceLock).close(); final FileLockFactory fileLockFactory = Mockito.mock(FileLockFactory.class); Mockito.when( fileLockFactory.getLock(Mockito.any()) ).thenReturn(resourceLock); final ResourceLoader resourceLoader2 = Mockito.mock(ResourceLoader.class); final Resource resource2 = Mockito.mock(Resource.class); Mockito.when( resourceLoader.getResource(Mockito.anyString()) ).thenReturn(resource); Mockito.when( resource.getInputStream() ).thenAnswer( (Answer<InputStream>) invocation -> { numOfCacheMisses.incrementAndGet(); return simulateDownloadWithWait(); } ); Mockito.when(resource.exists()).thenReturn(true); final FetchingCacheServiceImpl cache1 = new FetchingCacheServiceImpl( resourceLoader, cacheArguments, fileLockFactory, cleanUpTaskExecutor ); Mockito.when( resourceLoader2.getResource(Mockito.anyString()) ).thenReturn(resource2); Mockito.when( resource2.getInputStream() ).thenAnswer( (Answer<InputStream>) invocation -> { numOfCacheMisses.incrementAndGet(); return simulateDownloadWithWait(); } ); Mockito.when(resource2.exists()).thenReturn(true); final FetchingCacheServiceImpl cache2 = new FetchingCacheServiceImpl( resourceLoader2, cacheArguments, fileLockFactory, cleanUpTaskExecutor ); downloadCompleted.set(false); numOfCacheMisses.set(0); final CountDownLatch allFetchesDone = new CountDownLatch(2); executorService.submit(() -> { try { cache1.get(uri, targetFile); } catch (Exception e) { e.printStackTrace(); } finally { allFetchesDone.countDown(); } }); executorService.submit(() -> { try { cache2.get(uri, targetFile); } catch (Exception e) { e.printStackTrace(); } finally { allFetchesDone.countDown(); } }); lockAcquisitionsAttempted.await(); downloadCompleted.set(true); simulateDownloadLock.lock(); try { downloadComplete.signal(); } finally { simulateDownloadLock.unlock(); } allFetchesDone.await(); Assertions.assertThat(numOfCacheMisses.get()).isEqualTo(1); }
@Override public void get(final URI sourceFileUri, final File destinationFile) throws DownloadException, IOException { lookupOrDownload(sourceFileUri, destinationFile); }
FetchingCacheServiceImpl implements FetchingCacheService { @Override public void get(final URI sourceFileUri, final File destinationFile) throws DownloadException, IOException { lookupOrDownload(sourceFileUri, destinationFile); } }
FetchingCacheServiceImpl implements FetchingCacheService { @Override public void get(final URI sourceFileUri, final File destinationFile) throws DownloadException, IOException { lookupOrDownload(sourceFileUri, destinationFile); } FetchingCacheServiceImpl(
        final ResourceLoader resourceLoader,
        final ArgumentDelegates.CacheArguments cacheArguments,
        final FileLockFactory fileLockFactory,
        final TaskExecutor cleanUpTaskExecutor
    ); }
FetchingCacheServiceImpl implements FetchingCacheService { @Override public void get(final URI sourceFileUri, final File destinationFile) throws DownloadException, IOException { lookupOrDownload(sourceFileUri, destinationFile); } FetchingCacheServiceImpl(
        final ResourceLoader resourceLoader,
        final ArgumentDelegates.CacheArguments cacheArguments,
        final FileLockFactory fileLockFactory,
        final TaskExecutor cleanUpTaskExecutor
    ); @Override void get(final URI sourceFileUri, final File destinationFile); @Override void get(final Set<Pair<URI, File>> sourceDestinationPairs); }
FetchingCacheServiceImpl implements FetchingCacheService { @Override public void get(final URI sourceFileUri, final File destinationFile) throws DownloadException, IOException { lookupOrDownload(sourceFileUri, destinationFile); } FetchingCacheServiceImpl(
        final ResourceLoader resourceLoader,
        final ArgumentDelegates.CacheArguments cacheArguments,
        final FileLockFactory fileLockFactory,
        final TaskExecutor cleanUpTaskExecutor
    ); @Override void get(final URI sourceFileUri, final File destinationFile); @Override void get(final Set<Pair<URI, File>> sourceDestinationPairs);  }