/** * Expected: If multiple threads try to retrieve the same key from a SelfPopulatingCache at the * same time, and that key is not yet in the cache, one thread obtains the lock for that key and * uses the CacheEntryFactory to generate the cache entry and all other threads wait on the lock. * Any and all threads which timeout while waiting for this lock should fail to acquire the lock * for that key and throw an exception. * * <p>This thread tests for this by having several threads to a cache "get" for the same key, * allowing one to acquire the lock and the others to wait. The one that acquires the lock and * attempts to generate the cache entry for the key waits for a period of time long enough to * allow all other threads to timeout waiting for the lock. Any thread that succeeds in acquiring * the lock, including the first to do so, increment a counter when they begin creating the cache * entry using the CacheEntryFactory. It is expected that this counter will only be "1" after all * threads complete since all but the first to acquire it should timeout and throw exceptions. * * <p>We then test that a thread that comes along later increments the counter. */ @Test public void testSelfPopulatingBlocksWithTimeoutSetNull() throws InterruptedException { selfPopulatingCache = new SelfPopulatingCache( new Cache("TestCache", 50, false, false, 0, 0), new NullCachePopulator()); selfPopulatingCache.setTimeoutMillis(200); manager.addCache(selfPopulatingCache); CacheAccessorThread[] cacheAccessorThreads = new CacheAccessorThread[10]; for (int i = 0; i < cacheAccessorThreads.length; i++) { cacheAccessorThreads[i] = new CacheAccessorThread(selfPopulatingCache, "key1"); cacheAccessorThreads[i].start(); // Do a slight delay here so that all the timeouts // don't happen simultaneously - this is key try { Thread.sleep(20); } catch (InterruptedException ignored) { // } } // All of the others should have timed out. The first thread will have returned null. // This thread should be able to have a go, thus setting the count to 2 Thread.sleep(1000); Thread lateThread = new CacheAccessorThread(selfPopulatingCache, "key1"); lateThread.start(); lateThread.join(); assertEquals( "Too many cacheAccessorThreads tried to create selfPopulatingCache entry for key1", 2, cacheEntryFactoryRequests); }
/** Tests when fetch fails. */ @Test public void testFetchFail() throws Exception { final Object value = new Object(); final Exception exception = new Exception("Failed."); final AtomicBoolean throwException = new AtomicBoolean(true); final CacheEntryFactory factory = new CacheEntryFactory() { public Object createEntry(final Object key) throws Exception { if (throwException.get()) { throw exception; } else { return value; } } }; selfPopulatingCache = new SelfPopulatingCache(cache, factory); // Lookup try { selfPopulatingCache.get("key"); fail(); } catch (final Exception e) { Thread.sleep(20); // Check the error assertEquals("Could not fetch object for cache entry with key \"key\".", e.getMessage()); } throwException.set(false); selfPopulatingCache.setTimeoutMillis(1); Element element = null; try { element = selfPopulatingCache.get("key"); } catch (LockTimeoutException e) { fail("Key should not be locked anymore!"); } assertThat(element, is(notNullValue())); assertThat(element.getObjectValue(), is(value)); }