@VisibleForTesting
  HeaderCacheElement getHeader() throws ExecutionException, InterruptedException, TimeoutException {
    final Future<HeaderCacheElement> deferredResult;
    // Optimize for the common case: do a volatile read to peek for a Good cache value
    HeaderCacheElement headerCacheUnsync = this.headerCache;
    if (headerCacheUnsync.getCacheState() == CacheState.Good) {
      return headerCacheUnsync;
    }
    // TODO(igorbernstein2): figure out how to make this work with appengine request scoped threads
    synchronized (lock) {
      CacheState state = headerCache.getCacheState();
      switch (state) {
        case Good:
          return headerCache;
        case Stale:
          asyncRefresh();
          return headerCache;
        case Expired:
        case Exception:
          // defer the future resolution (asyncRefresh will spin up a thread that will try to acquire the lock)
          deferredResult = asyncRefresh();
          break;
        default:
          return new HeaderCacheElement(
              Status.UNAUTHENTICATED
                  .withCause(new IllegalStateException("Could not process state: " + state))
          );
      }
    }
    return deferredResult.get(5, TimeUnit.SECONDS);
  }
@VisibleForTesting
  HeaderCacheElement getHeader() throws ExecutionException, InterruptedException, TimeoutException {
    final Future<HeaderCacheElement> deferredResult;
   
    HeaderCacheElement headerCacheUnsync = this.headerCache;
    if (headerCacheUnsync.getCacheState() == CacheState.Good) {
      return headerCacheUnsync;
    }
   
    synchronized (lock) {
      CacheState state = headerCache.getCacheState();
      switch (state) {
        case Good:
          return headerCache;
        case Stale:
          asyncRefresh();
          return headerCache;
        case Expired:
        case Exception:
         
          deferredResult = asyncRefresh();
          break;
        default:
          return new HeaderCacheElement(
              Status.UNAUTHENTICATED
                  .withCause(new IllegalStateException("Could not process state: " + state))
          );
      }
    }
    return deferredResult.get(5, TimeUnit.SECONDS);
  }
@visiblefortesting headercacheelement getheader() throws executionexception, interruptedexception, timeoutexception { final future<headercacheelement> deferredresult; headercacheelement headercacheunsync = this.headercache; if (headercacheunsync.getcachestate() == cachestate.good) { return headercacheunsync; } synchronized (lock) { cachestate state = headercache.getcachestate(); switch (state) { case good: return headercache; case stale: asyncrefresh(); return headercache; case expired: case exception: deferredresult = asyncrefresh(); break; default: return new headercacheelement( status.unauthenticated .withcause(new illegalstateexception("could not process state: " + state)) ); } } return deferredresult.get(5, timeunit.seconds); }