class GoogleAuthLibraryCallCredentials extends CallCredentials2 {

  @Override
  public void applyRequestMetadata(
      RequestInfo info, Executor appExecutor, final MetadataApplier applier) {
    SecurityLevel security = info.getSecurityLevel();
    if (requirePrivacy && security != SecurityLevel.PRIVACY_AND_INTEGRITY) {
      applier.fail(Status.UNAUTHENTICATED
          .withDescription("Credentials require channel with PRIVACY_AND_INTEGRITY security level. "
              + "Observed security level: " + security));
      return;
    }

    String authority = checkNotNull(info.getAuthority(), "authority");
    final URI uri;
    try {
      uri = serviceUri(authority, info.getMethodDescriptor());
    } catch (StatusException e) {
      applier.fail(e.getStatus());
      return;
    }
    // Credentials is expected to manage caching internally if the metadata is fetched over
    // the network.
    creds.getRequestMetadata(uri, appExecutor, new RequestMetadataCallback() {
      @Override
      public void onSuccess(Map<String, List<String>> metadata) {
        // Some implementations may pass null metadata.

        // Re-use the headers if getRequestMetadata() returns the same map. It may return a
        // different map based on the provided URI, i.e., for JWT. However, today it does not
        // cache JWT and so we won't bother tring to save its return value based on the URI.
        Metadata headers;
        try {
          synchronized (GoogleAuthLibraryCallCredentials.this) {
            if (lastMetadata == null || lastMetadata != metadata) {
              lastHeaders = toHeaders(metadata);
              lastMetadata = metadata;
            }
            headers = lastHeaders;
          }
        } catch (Throwable t) {
          applier.fail(Status.UNAUTHENTICATED
              .withDescription("Failed to convert credential metadata")
              .withCause(t));
          return;
        }
        applier.apply(headers);
      }

      @Override
      public void onFailure(Throwable e) {
        if (e instanceof IOException) {
          // Since it's an I/O failure, let the call be retried with UNAVAILABLE.
          applier.fail(Status.UNAVAILABLE
              .withDescription("Credentials failed to obtain metadata")
              .withCause(e));
        } else {
          applier.fail(Status.UNAUTHENTICATED
              .withDescription("Failed computing credential metadata")
              .withCause(e));
        }
      }
    });
  }

  public  GoogleAuthLibraryCallCredentials(Credentials creds);
  @VisibleForTesting  GoogleAuthLibraryCallCredentials(Credentials creds, JwtHelper jwtHelper);

}