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();
   }
 }