/**
  * Calls {@link OAuth2Credentials#refreshAccessToken()}. In case of an IOException, retry the call
  * as per the {@link BackOff} policy defined by {@link RetryOptions#createBackoff()}.
  *
  * <p>This method retries until one of the following conditions occurs:
  *
  * <ol>
  *   <li>An OAuth request was completed. If the value is null, return an exception.
  *   <li>A non-IOException Exception is thrown - return an error status
  *   <li>All retries have been exhausted, i.e. when the Backoff.nextBackOffMillis() returns
  *       BackOff.STOP
  *   <li>An interrupt occurs.
  * </ol>
  *
  * @return HeaderCacheElement containing either a valid {@link AccessToken} or an exception.
  */
 protected HeaderCacheElement refreshCredentialsWithRetry() {
   BackOff backoff = null;
   while (true) {
     try {
       logger.info("Refreshing the OAuth token");
       AccessToken newToken = credentials.refreshAccessToken();
       if (newToken == null) {
         // The current implementations of refreshAccessToken() throws an IOException or return
         // a valid token. This handling is future proofing just in case credentials returns null
         // for
         // some reason. This case was caught by a poorly coded unit test.
         logger.info("Refreshed the OAuth token");
         return new HeaderCacheElement(
             new IOException("Could not load the token for credentials: " + credentials));
       } else {
         // Success!
         return new HeaderCacheElement(newToken);
       }
     } catch (IOException exception) {
       logger.warn("Got an unexpected IOException when refreshing google credentials.", exception);
       // An IOException occurred. Retry with backoff.
       if (backoff == null) {
         // initialize backoff.
         backoff = retryOptions.createBackoff();
       }
       // Given the backoff, either sleep for a short duration, or terminate if the backoff has
       // reached its configured timeout limit.
       try {
         RetryState retryState = getRetryState(backoff);
         if (retryState != RetryState.PerformRetry) {
           return new HeaderCacheElement(exception);
         } // else Retry.
       } catch (IOException e) {
         logger.warn("Got an exception while trying to run backoff.nextBackOffMillis()", e);
         return new HeaderCacheElement(exception);
       }
     } catch (Exception e) {
       logger.warn("Got an unexpected exception while trying to refresh google credentials.", e);
       return new HeaderCacheElement(new IOException("Could not read headers", e));
     }
   }
 }