// Copyright 2018 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.ads.googleads.lib; import com.google.api.gax.rpc.ApiException; import com.google.common.annotations.VisibleForTesting; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.Message; import io.grpc.Metadata; import io.grpc.Status; import java.util.Optional; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Base class for GoogleAdsException. Provides a generic holder for GoogleAdsFailures which can be * replaced with statically typed subclasses for a specific version of GoogleAdsFailure. */ public abstract class BaseGoogleAdsException extends ApiException { private static final Logger logger = LoggerFactory.getLogger(BaseGoogleAdsException.class); private static final Metadata.Key<String> REQUEST_ID_HEADER_KEY = Metadata.Key.of("request-id", Metadata.ASCII_STRING_MARSHALLER); private final Metadata metadata; private final Message failure; /** Create from ApiException, GoogleAdsFailure (as Message) and metadata. */ public <T extends Message> BaseGoogleAdsException( ApiException original, T failure, Metadata metadata) { super( failure.toString(), original.getCause(), original.getStatusCode(), original.isRetryable()); this.metadata = metadata; this.failure = failure; } /** * Get the request id returned in the RPC trailers. Returns null if the RPC has not completed or * no request id was received. */ public String getRequestId() { return getHeader(REQUEST_ID_HEADER_KEY); } /** * Get a string value from the trailers returned by the RPC. If the given key is not present, * returns null. */ <T> T getHeader(Metadata.Key<T> headerKey) { if (metadata == null) { return null; } return metadata.get(headerKey); } /** * Return the decoded GoogleAdsFailure. Subclasses can override this method to return version * specific types. */ public Message getGoogleAdsFailure() { return failure; } /** * Optionally create a GoogleAdsException from a ApiException. * * <p>Returns an Optional containing the underlying GoogleAdsException if the ApiException * contains the appropriate metadata. * * <p>Returns an empty Optional if the required metadata is not present or is not parsable. */ public abstract static class Factory<T extends BaseGoogleAdsException, U extends Message> { protected static Metadata.Key<byte[]> createKey(String trailerKey) { return Metadata.Key.of(trailerKey, Metadata.BINARY_BYTE_MARSHALLER); } public Optional<T> createGoogleAdsException(ApiException source) { if (source == null) { return Optional.empty(); } Throwable cause = source.getCause(); if (cause == null) { return Optional.empty(); } Metadata metadata = Status.trailersFromThrowable(cause); if (metadata == null) { return Optional.empty(); } byte[] protoData = metadata.get(getTrailerKey()); if (protoData == null) { return Optional.empty(); } try { return Optional.of(createException(source, protoData, metadata)); } catch (InvalidProtocolBufferException e) { logger.error("Failed to decode GoogleAdsFailure", e); return Optional.empty(); } } protected abstract T createException(ApiException source, byte[] protoData, Metadata metadata) throws InvalidProtocolBufferException; /** * Returns a Metadata.Key representing the key which the GoogleAdsFailure is returned in the * metadata. */ public abstract Metadata.Key<byte[]> getTrailerKey(); /** Create an empty GoogleAdsFailure instance for this version. */ @VisibleForTesting public abstract U createGoogleAdsFailure(); public abstract U createGoogleAdsFailure(byte[] serializedBytes) throws InvalidProtocolBufferException; } }