This guide outlines how to customize several of the more advanced aspects of the
Java client library. A common pattern is that many of these features rely on the
underlying `Callable` rather than the standard convenience methods. The callable
is generally a good place to look for other per-RPC features that aren't
documented here.

## Timeout

The Java library provides a surface for setting timeouts on a per-call level.
The default value is set based on the `method_config/timeout` setting in
[`googleads_grpc_service_config.json`](https://github.com/googleapis/googleapis/tree/HEAD/google/ads/googleads/v26/googleads_grpc_service_config.json). Set a lower value
if you need to enforce a shorter limit on the maximum time for an API call.

To use this feature, call the `Callable` object directly. For example, when
calling `GoogleAdsService.searchStream()`, set the timeout as follows:

    try (GoogleAdsServiceClient googleAdsServiceClient =
        googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {
      // Constructs the SearchGoogleAdsStreamRequest.
      SearchGoogleAdsStreamRequest request =
          SearchGoogleAdsStreamRequest.newBuilder()
              .setCustomerId(Long.toString(customerId))
              .setQuery("SELECT campaign.id, campaign.name FROM campaign")
              .build();

      // Executes the API call with a timeout of 5 minutes.
      ServerStream<SearchGoogleAdsStreamResponse> stream =
          googleAdsServiceClient
              .searchStreamCallable()
              .call(
                  request,
                  GrpcCallContext.createDefault()
                      .withTimeout(Duration.of(5, ChronoUnit.MINUTES)));
      for (SearchGoogleAdsStreamResponse response : stream) {
        // Processes the response rows.
      }
    }

You can set the timeout to 2 hours or more, but the API may still time out
extremely long-running requests and return a
[`DEADLINE_EXCEEDED`](https://developers.google.com/google-ads/api/reference/rpc/v26/InternalErrorEnum.InternalError) error.
If this becomes an issue, it is usually best to split the query up and
execute the chunks in parallel; this avoids the situation where a long-running
request fails and the only way to recover is to trigger the request again from
the start.

## Retry settings

The Java library also provides a surface for configuring retry settings on a
per-call level. To use this feature, call the `Callable` object directly. For
example, when calling `GoogleAdsService.searchStream()`, configure the retry
settings as follows:

    try (GoogleAdsServiceClient googleAdsServiceClient =
        googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {
      SearchGoogleAdsStreamRequest request =
          SearchGoogleAdsStreamRequest.newBuilder()
              .setCustomerId(Long.toString(customerId))
              .setQuery("SELECT campaign.id, campaign.name FROM campaign")
              .build();

      // Creates a context object with the custom retry settings.
      GrpcCallContext context =
          GrpcCallContext.createDefault()
              .withRetrySettings(
                  RetrySettings.newBuilder()
                      .setInitialRetryDelay(Duration.ofMillis(10L))
                      .setMaxRetryDelay(Duration.ofSeconds(10L))
                      .setRetryDelayMultiplier(1.4)
                      .setMaxAttempts(10)
                      .setLogicalTimeout(Duration.ofSeconds(30L))
                      .build());

      // Issues the streaming search request.
      ServerStream<SearchGoogleAdsStreamResponse> stream =
          googleAdsServiceClient.searchStreamCallable().call(request, context);
      for (SearchGoogleAdsStreamResponse response : stream) {
        // Processes the response rows.
      }
    }

## Startup time performance optimization

You may notice a small delay the first time a `GoogleAdsClient` instance is
created. This is due to the fluent interface for services
(`GoogleAdsClient.getLatestVersion()`), which loads the API service classes at
once to provide a convenient mechanism for constructing service clients.

If the first request performance is on the critical path for your application,
follow these steps:

1. Create the `GoogleAdsClient` on startup, before serving user requests.

2. Send a few warm-up requests to the Google Ads API when the process first starts.
   For example:

       // Runs some warm-up requests.
       try (GoogleAdsServiceClient googleAdsServiceClient =
           googleAdsClient.getLatestVersion().createGoogleAdsServiceClient()) {
         // Runs 5 warm-up requests. In our profiling we see that 90% of
         // performance loss is only experienced on the first API call. After 3
         // subsequent calls we saw a negligible improvement in performance.
         for (int i = 0; i < 5; ++i) {
           // Warm-up queries are run with a nonexistent CID so the calls will
           // fail. If you have a CID that you know will be accessible with the
           // OAuth credentials provided you may want to provide that instead and
           // avoid the try-catch.
           try {
             googleAdsServiceClient.search("-1", "Warm-up query");
           } catch (ApiException ex) {
             // Do nothing, we're expecting this to fail.
           }
         }
       }

The warm-up requests only need to be run once per process. Every subsequent
service client creation automatically reuses the preloaded classes.

## Service client reuse

You should reuse service client instances where practical because each call to
`GoogleAdsClient.getLatestVersion().createYYYServiceClient()` (or a
version-specific accessor such as `getVersion26()`)
creates a new underlying connection and associated resources.

> [!NOTE]
> **Note:** Both `GoogleAdsClient` and individual generated service clients (such as `GoogleAdsServiceClient`) are thread-safe and can be safely shared across multiple threads in your application.

Make sure that you close the service client when it's no longer required. You
can do this in a [try-with-resources](https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html) block or by calling
`close()` on the service client.

If you attempt to use a closed service client to make API requests, the service
client method throws a `java.util.concurrent.RejectedExecutionException`.

## App Engine fails to deploy if JAR \> 32 MB

App Engine has a quota of 32 MB for each uploaded file. The JAR for `google-ads`
is considerably larger than this, especially when using shade or shadow JAR
deployments. If you deploy JARs manually, you might get errors such as:

    ERROR: (gcloud.app.deploy) Cannot upload file [<your-app>/WEB-INF/lib/google-ads-48.1.0.jar],
    which has size [66095767] (greater than maximum allowed size of [33554432])

Instead, deploy using the App Engine [Gradle plugin](https://cloud.google.com/appengine/docs/standard/java-gen2/gradle-reference#appenginestage) or
[Maven plugin](https://cloud.google.com/appengine/docs/standard/java-gen2/maven-reference#appenginedeploy). Each plugin provides an `enableJarSplitting`
option that splits each JAR into 10 MB chunks and uploads those instead.

## Shadow dependencies

If your project has dependencies that conflict with the library's dependencies,
inspect your project's dependency hierarchy using one of the following commands,
and then modify your project's dependencies as needed (or use the
[Bill of Materials](https://developers.google.com/google-ads/api/docs/client-libs/java/bill-of-materials)):

### Maven

```bash
mvn dependency:tree
```

### Gradle

```bash
./gradlew dependencies
```

If resolving dependency conflicts is infeasible, you can depend on the
[shaded](https://maven.apache.org/plugins/maven-shade-plugin/index.html) version of the library instead:

### Maven

```xml
<dependency>
  <groupId>com.google.api-ads</groupId>
  <artifactId>google-ads-shadowjar</artifactId>
  <version>48.1.0</version>
</dependency>
```

### Gradle

```java
implementation 'com.google.api-ads:google-ads-shadowjar:48.1.0'
```