private void update(Tokens tokens, Exception e) {
   synchronized (accessTokenLock) {
     try {
       if (e == null) {
         AccessToken newToken =
             new AccessToken(
                 tokens.getAccessToken(), tokens.getExpiresIn(), System.currentTimeMillis());
         this.accessTokenResult = Optional.of(ValidationE.<AccessToken>success(newToken));
         Log.debug("[oauth] Refreshed access token.");
         scheduleNextRefresh(tokens);
       } else {
         this.accessTokenResult =
             Optional.of(ValidationE.<AccessToken>error(Util.toSphereException(e)));
         Log.error("[oauth] Failed to refresh access token.", e);
       }
     } finally {
       accessTokenLock.notifyAll();
     }
   }
 }
 private void scheduleNextRefresh(Tokens tokens) {
   if (!tokens.getExpiresIn().isPresent()) {
     Log.warn("[oauth] Authorization server did not provide expires_in for the access token.");
     return;
   }
   if (tokens.getExpiresIn().get() * 1000 < Defaults.tokenAboutToExpireMs) {
     Log.warn(
         "[oauth] Authorization server returned an access token with a very short validity of "
             + tokens.getExpiresIn().get()
             + "s!");
     return;
   }
   long refreshTimeout = tokens.getExpiresIn().get() * 1000 - Defaults.tokenAboutToExpireMs;
   Log.debug("[oauth] Scheduling next token refresh " + refreshTimeout / 1000 + "s from now.");
   refreshTimer.schedule(
       new TimerTask() {
         public void run() {
           beginRefresh();
         }
       },
       refreshTimeout);
 }
 public String getAccessToken() {
   synchronized (accessTokenLock) {
     Optional<ValidationE<AccessToken>> tokenResult = waitForToken();
     if (!tokenResult.isPresent()) {
       // Shouldn't happen as the timer should refresh the token soon enough.
       Log.warn("[oauth] Access token expired, blocking until a new one is available.");
       beginRefresh();
       tokenResult = waitForToken();
       if (!tokenResult.isPresent()) {
         throw new SphereClientException("Access token expired immediately after refresh.");
       }
     }
     if (tokenResult.get().isError()) {
       beginRefresh(); // retry on error (essential to recover from backend errors)
       throw tokenResult.get().getError();
     }
     return tokenResult.get().getValue().getAccessToken();
   }
 }