// Path: credentials/java/com/google/auth/RequestMetadataCallback.java
// public interface RequestMetadataCallback {
//   /**
//    * Called when metadata is successfully produced.
//    *
//    * @param metadata Metadata returned for the request.
//    */
//   void onSuccess(Map<String, List<String>> metadata);
// 
//   /**
//    * Called when metadata generation failed.
//    *
//    * @param exception The thrown exception which caused the request metadata fetch to fail.
//    */
//   void onFailure(Throwable exception);
// }
// 
// Path: credentials/java/com/google/auth/ServiceAccountSigner.java
// public interface ServiceAccountSigner {
// 
//   class SigningException extends RuntimeException {
// 
//     private static final long serialVersionUID = -6503954300538947223L;
// 
//     public SigningException(String message, Exception cause) {
//       super(message, cause);
//     }
// 
//     @Override
//     public boolean equals(Object obj) {
//       if (obj == this) {
//         return true;
//       }
//       if (!(obj instanceof SigningException)) {
//         return false;
//       }
//       SigningException other = (SigningException) obj;
//       return Objects.equals(getCause(), other.getCause())
//           && Objects.equals(getMessage(), other.getMessage());
//     }
// 
//     @Override
//     public int hashCode() {
//       return Objects.hash(getMessage(), getCause());
//     }
//   }
// 
//   /**
//    * Returns the service account associated with the signer.
//    *
//    * @return The service account associated with the signer.
//    */
//   String getAccount();
// 
//   /**
//    * Signs the provided bytes using the private key associated with the service account.
//    *
//    * @param toSign bytes to sign
//    * @return signed bytes
//    * @throws SigningException if the attempt to sign the provided bytes failed
//    */
//   byte[] sign(byte[] toSign);
// }
// 
// Path: oauth2_http/java/com/google/auth/http/HttpTransportFactory.java
// public interface HttpTransportFactory {
// 
//   /**
//    * Creates a {@code HttpTransport} instance.
//    *
//    * @return The HttpTransport instance.
//    */
//   HttpTransport create();
// }


import static com.google.common.base.MoreObjects.firstNonNull;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpBackOffUnsuccessfulResponseHandler;
import com.google.api.client.http.HttpBackOffUnsuccessfulResponseHandler.BackOffRequired;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpResponseException;
import com.google.api.client.http.UrlEncodedContent;
import com.google.api.client.json.GenericJson;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.JsonObjectParser;
import com.google.api.client.json.webtoken.JsonWebSignature;
import com.google.api.client.json.webtoken.JsonWebToken;
import com.google.api.client.util.ExponentialBackOff;
import com.google.api.client.util.GenericData;
import com.google.api.client.util.Joiner;
import com.google.api.client.util.PemReader;
import com.google.api.client.util.PemReader.Section;
import com.google.api.client.util.Preconditions;
import com.google.api.client.util.SecurityUtils;
import com.google.auth.RequestMetadataCallback;
import com.google.auth.ServiceAccountSigner;
import com.google.auth.http.HttpTransportFactory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.Reader;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;

  @VisibleForTesting
  JwtCredentials createSelfSignedJwtCredentials(final URI uri) {
    // Create a JwtCredentials for self signed JWT. See https://google.aip.dev/auth/4111.
    JwtClaims.Builder claimsBuilder =
        JwtClaims.newBuilder().setIssuer(clientEmail).setSubject(clientEmail);

    if (uri == null) {
      // If uri is null, use scopes.
      String scopeClaim = "";
      if (!scopes.isEmpty()) {
        scopeClaim = Joiner.on(' ').join(scopes);
      } else {
        scopeClaim = Joiner.on(' ').join(defaultScopes);
      }
      claimsBuilder.setAdditionalClaims(Collections.singletonMap("scope", scopeClaim));
    } else {
      // otherwise, use audience with the uri.
      claimsBuilder.setAudience(getUriForSelfSignedJWT(uri).toString());
    }
    return JwtCredentials.newBuilder()
        .setPrivateKey(privateKey)
        .setPrivateKeyId(privateKeyId)
        .setJwtClaims(claimsBuilder.build())
        .setClock(clock)
        .build();
  }

  @Override
  public void getRequestMetadata(
// Path: credentials/java/com/google/auth/RequestMetadataCallback.java
// public interface RequestMetadataCallback {
//   /**
//    * Called when metadata is successfully produced.
//    *
//    * @param metadata Metadata returned for the request.
//    */
//   void onSuccess(Map<String, List<String>> metadata);
// 
//   /**
//    * Called when metadata generation failed.
//    *
//    * @param exception The thrown exception which caused the request metadata fetch to fail.
//    */
//   void onFailure(Throwable exception);
// }
// 
// Path: credentials/java/com/google/auth/ServiceAccountSigner.java
// public interface ServiceAccountSigner {
// 
//   class SigningException extends RuntimeException {
// 
//     private static final long serialVersionUID = -6503954300538947223L;
// 
//     public SigningException(String message, Exception cause) {
//       super(message, cause);
//     }
// 
//     @Override
//     public boolean equals(Object obj) {
//       if (obj == this) {
//         return true;
//       }
//       if (!(obj instanceof SigningException)) {
//         return false;
//       }
//       SigningException other = (SigningException) obj;
//       return Objects.equals(getCause(), other.getCause())
//           && Objects.equals(getMessage(), other.getMessage());
//     }
// 
//     @Override
//     public int hashCode() {
//       return Objects.hash(getMessage(), getCause());
//     }
//   }
// 
//   /**
//    * Returns the service account associated with the signer.
//    *
//    * @return The service account associated with the signer.
//    */
//   String getAccount();
// 
//   /**
//    * Signs the provided bytes using the private key associated with the service account.
//    *
//    * @param toSign bytes to sign
//    * @return signed bytes
//    * @throws SigningException if the attempt to sign the provided bytes failed
//    */
//   byte[] sign(byte[] toSign);
// }
// 
// Path: oauth2_http/java/com/google/auth/http/HttpTransportFactory.java
// public interface HttpTransportFactory {
// 
//   /**
//    * Creates a {@code HttpTransport} instance.
//    *
//    * @return The HttpTransport instance.
//    */
//   HttpTransport create();
// }


// Path: oauth2_http/java/com/google/auth/oauth2/ServiceAccountCredentials.java
import static com.google.common.base.MoreObjects.firstNonNull;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpBackOffUnsuccessfulResponseHandler;
import com.google.api.client.http.HttpBackOffUnsuccessfulResponseHandler.BackOffRequired;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpResponse;
import com.google.api.client.http.HttpResponseException;
import com.google.api.client.http.UrlEncodedContent;
import com.google.api.client.json.GenericJson;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.JsonObjectParser;
import com.google.api.client.json.webtoken.JsonWebSignature;
import com.google.api.client.json.webtoken.JsonWebToken;
import com.google.api.client.util.ExponentialBackOff;
import com.google.api.client.util.GenericData;
import com.google.api.client.util.Joiner;
import com.google.api.client.util.PemReader;
import com.google.api.client.util.PemReader.Section;
import com.google.api.client.util.Preconditions;
import com.google.api.client.util.SecurityUtils;
import com.google.auth.RequestMetadataCallback;
import com.google.auth.ServiceAccountSigner;
import com.google.auth.http.HttpTransportFactory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableSet;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.Reader;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.SignatureException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;


  @VisibleForTesting
  JwtCredentials createSelfSignedJwtCredentials(final URI uri) {
    // Create a JwtCredentials for self signed JWT. See https://google.aip.dev/auth/4111.
    JwtClaims.Builder claimsBuilder =
        JwtClaims.newBuilder().setIssuer(clientEmail).setSubject(clientEmail);

    if (uri == null) {
      // If uri is null, use scopes.
      String scopeClaim = "";
      if (!scopes.isEmpty()) {
        scopeClaim = Joiner.on(' ').join(scopes);
      } else {
        scopeClaim = Joiner.on(' ').join(defaultScopes);
      }
      claimsBuilder.setAdditionalClaims(Collections.singletonMap("scope", scopeClaim));
    } else {
      // otherwise, use audience with the uri.
      claimsBuilder.setAudience(getUriForSelfSignedJWT(uri).toString());
    }
    return JwtCredentials.newBuilder()
        .setPrivateKey(privateKey)
        .setPrivateKeyId(privateKeyId)
        .setJwtClaims(claimsBuilder.build())
        .setClock(clock)
        .build();
  }

  @Override
  public void getRequestMetadata(
      final URI uri, Executor executor, final RequestMetadataCallback callback) {