/**
   * 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));
  }