> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tensor9.com/llms.txt
> Use this file to discover all available pages before exploring further.

# AWS Java SDK Guide

> Configure the AWS SDK for Java to use a Cloud Adapter endpoint and verify an object lifecycle.

Cloud Adapter preserves the AWS service model at the application boundary. Your Java code keeps using AWS request and response types; the client factory selects the adapter endpoint.

This example uses AWS SDK for Java 2.x and S3. Check [S3 service coverage](https://docs.tensor9.com/cloud-adapter-early-access/service-catalog/aws/databases-storage/s3.md) for the target environment and operations your application uses.

## Start a local adapter

This guide's bounded example uses S3 backed by Google Cloud Storage:

**1. Install or update the Tensor9 CLI**

Install the customer CLI with Homebrew, then confirm the binary is available:

```bash theme={null}
brew tap tensor9ine/tensor9
brew trust tensor9ine/tensor9
brew install tensor9
tensor9 --version
```

If `tensor9` is already installed, run `brew upgrade tensor9` instead of the install command.

**2. Authenticate with Tensor9**

To create a Tensor9 account and download an API key yourself, follow [Get a Tensor9 API key](https://docs.tensor9.com/byoc/getting-started/api-key.md).

A Tensor9 API key is required. Use the key issued during onboarding, or email [hello@tensor9.com](mailto:hello@tensor9.com) to request one for your organization. Do not share your account or key with another customer or organization.

If your secret manager has already supplied `T9_API_KEY`, skip the prompt. Otherwise, enter your key at a hidden prompt in the terminal where you will run `tensor9`:

> [tab] Bash
>
> ```bash theme={null}
> set +x
> IFS= read -r -s -p 'Tensor9 API key: ' T9_API_KEY
> printf '\n'
> export T9_API_KEY
> ```

> [tab] Zsh
>
> ```zsh theme={null}
> set +x
> IFS= read -r -s 'T9_API_KEY?Tensor9 API key: '
> printf '\n'
> export T9_API_KEY
> ```

Check that the variable is set without printing it, then verify your Tensor9 identity:

```bash theme={null}
: "${T9_API_KEY:?Set T9_API_KEY before continuing}"
tensor9 whoami
```

Confirm that the reported identity belongs to the intended customer account. Do not continue if authentication fails or the account is wrong. While `T9_API_KEY` is set, `tensor9` commands in this terminal use the key instead of any controller profile, so use a separate terminal for this work and run `unset T9_API_KEY` when you finish. Keep shell tracing disabled while using the key. Do not paste the key into commands, shell profiles, source files, screenshots or support messages.

See [Install and Authenticate](https://docs.tensor9.com/cloud-adapter-early-access/getting-started/install-and-authenticate.md) for the planned license lifecycle, entitlements and credential boundaries.

**3. Inspect the S3 to Google Cloud Storage mapping**

Ask Tensor9 to describe the exact directed service mapping used by this guide:

```bash theme={null}
tensor9 explain \
  -origin aws::1.0.0::s3 \
  -target google::1.0.0::gcs \
  -fmt Human
```

Read the supported S3 operations and Google Cloud Storage limitations before starting the process. The mapping is directional: this command describes S3 requests adapted to Google Cloud Storage, not the reverse direction.

**4. Prepare Google Cloud access**

Authenticate with Google Cloud using its standard Application Default Credentials chain. The adapter uses that identity when it calls Google Cloud Storage.

Use a dedicated test project and narrowly scoped credentials. Do not put credentials in the command line, source code, or screenshots.

**5. Start the S3 service adapter**

Run the S3 to Google Cloud Storage adapter as a foreground process:

```bash theme={null}
tensor9 adapt svc run \
  --origin aws::1.0.0::s3 \
  --backend google::1.0.0::gcs
```

Leave this terminal open. This is the current foreground launcher, not the pending Docker launcher for [local development mode](https://docs.tensor9.com/cloud-adapter-early-access/deployment/single-binary.md). It calls your actual target-cloud services; it is separate from the in-memory emulator. Acquisition and license renewal require Tensor9 authentication. Keep the endpoint local to your test.

**6. Copy the listening endpoint**

When the command reports its listening endpoint, copy that exact value into a second terminal:

```bash theme={null}
export T9_CLOUD_ADAPTER_ENDPOINT="<endpoint printed by tensor9>"
```

Keep the endpoint local to the test unless you have deliberately configured network access and authentication for another machine.

**7. Run one bounded S3 request**

Continue with this page's client or tool instructions. Point only S3 at `T9_CLOUD_ADAPTER_ENDPOINT`, then perform one operation whose Google Cloud Storage effect you can verify independently.

Check both sides: read the S3-shaped response and inspect the native object or bucket in Google Cloud Storage. A successful client response alone does not prove that the target state is correct.

**8. Stop the local adapter**

Return to the first terminal and press **Control-C**. Confirm the process exits before deleting the test object or bucket with Google Cloud's native client.

If the request failed, preserve the request ID, adapter endpoint, S3 operation name, Google Cloud request ID, and any `x-t9-explain-*` receipt headers before stopping the process.

## Choose how to set the endpoint

### Set it for the process

The AWS-standard S3 environment variable applies to every S3 client created by a supported SDK in this process:

```bash theme={null}
export AWS_ENDPOINT_URL_S3="$T9_CLOUD_ADAPTER_ENDPOINT"
```

Build the client normally and omit `endpointOverride`:

```java theme={null}
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;

S3Client s3 = S3Client.builder()
    .region(Region.US_EAST_1)
    .credentialsProvider(ProfileCredentialsProvider.create("adapter-test"))
    .forcePathStyle(true)
    .build();
```

### Set it on one client

Keep endpoint selection in one factory rather than scattering it through business logic.

```bash theme={null}
: "${T9_CLOUD_ADAPTER_ENDPOINT:?start the local adapter and copy its endpoint first}"
```

```java theme={null}
import java.net.URI;
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;

URI endpoint = URI.create(System.getenv("T9_CLOUD_ADAPTER_ENDPOINT"));

S3Client s3 = S3Client.builder()
    .endpointOverride(endpoint)
    .region(Region.US_EAST_1)
    .credentialsProvider(ProfileCredentialsProvider.create("adapter-test"))
    .forcePathStyle(true)
    .build();
```

`endpointOverride` changes where the AWS request is sent. It does not change the service model, add unsupported operations, or provide credentials to the target service. Backend credentials stay in the service adapter configuration.

An explicit client override takes precedence over `AWS_ENDPOINT_URL_S3`. Choose one approach for a client factory so configuration remains easy to audit.

For SDK 1.x, use `AmazonS3ClientBuilder` with an endpoint configuration and path-style access. Do not mix 1.x and 2.x configuration examples in the same client factory.

## Verify a lifecycle

Use a unique key and a small payload:

```java theme={null}
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import software.amazon.awssdk.core.ResponseBytes;
import software.amazon.awssdk.core.sync.RequestBody;
import software.amazon.awssdk.services.s3.model.GetObjectRequest;
import software.amazon.awssdk.services.s3.model.GetObjectResponse;

String bucket = System.getenv("TEST_BUCKET");
String key = "java/" + System.currentTimeMillis() + "-hello.txt";
byte[] expected = "hello through Cloud Adapter\n".getBytes(StandardCharsets.UTF_8);

s3.putObject(builder -> builder.bucket(bucket).key(key).contentType("text/plain"),
    RequestBody.fromBytes(expected));
s3.headObject(builder -> builder.bucket(bucket).key(key));

ResponseBytes<GetObjectResponse> downloaded = s3.getObjectAsBytes(
    GetObjectRequest.builder().bucket(bucket).key(key).build());
if (!Arrays.equals(expected, downloaded.asByteArray())) {
    throw new IllegalStateException("Downloaded object differs from uploaded bytes");
}

s3.deleteObject(builder -> builder.bucket(bucket).key(key));
```

Verify the object in the target service after `putObject`, not only through the adapter. That catches name, region, metadata, and identity mistakes that an origin-shaped response alone may hide.

## Preserve failure evidence

Catch service and transport failures separately:

```java theme={null}
import software.amazon.awssdk.core.exception.SdkClientException;
import software.amazon.awssdk.services.s3.model.S3Exception;

try {
    s3.headObject(builder -> builder.bucket(bucket).key("missing-key"));
} catch (S3Exception error) {
    System.err.printf("status=%d requestId=%s code=%s%n",
        error.statusCode(), error.requestId(), error.awsErrorDetails().errorCode());
} catch (SdkClientException error) {
    System.err.println("No service response: " + error.getMessage());
}
```

Do not automatically retry a timed-out mutation until you have checked target state and the operation's idempotency. Retain the SDK request ID and Cloud Adapter diagnostic headers in structured logs, while excluding credentials and payload bodies.

## Explain one SDK request

For a difficult request, send the regular IAM/SigV4-authenticated SDK request to Tensor9's synthetic AWS endpoint, add a signed per-request `x-t9-explain` control, and retain the returned report ID and expiry. `ExecuteAndExplain` preserves the native execution response. A completed `Explain` preview performs no operation and returns an AWS-framed `409 ExplainedNotPerformed` service error with the receipt. Retrieve either report with `T9CloudAdapter.GetExplanation` through the separately configured synthetic AWS `t9` endpoint, signed as `t9` with normal IAM credentials and the report service's region. Its AWS JSON response carries the rendered report as a string in `body`; the ordinary SDK never decodes it as an operation response.

The complete [Explaining SDK Requests](https://docs.tensor9.com/cloud-adapter-early-access/debugging/explain-sdk.md) guide shows:

* the Java 2.x request override, applied before SigV4 signing;
* the exact mode and format values;
* success, service-error and no-response handling;
* report retrieval without replaying the original operation;
* report-owner acknowledgment and out-of-band evidence sharing.

Do not use `x-t9-explain: true` for a dry run: that shorthand selects `ExecuteAndExplain` in Human format.

For an interactive investigation, [retrieve the receipt's existing report through the signed SDK flow](https://docs.tensor9.com/cloud-adapter-early-access/debugging/explain-sdk.md#retrieve-without-replaying) with the same owner's IAM credentials and published t9 endpoint and signing region. Select `fmt: "Json"`, decode the outer AWS JSON response and parse its `body` string as the report. Retrieval neither launches an adapter, repeats the upload nor acknowledges the report.

## Production client checklist

* Make endpoint, origin region, and credential provider explicit.
* Bound connection, request, and retry timeouts for the target path.
* Reuse the client; do not create one per request.
* Test the exact paginator, waiter, conditional request, and multipart features the application uses.
* Keep AWS-origin credentials separate from the adapter's target-cloud identity.
* Run an expected not-found or validation error to verify error translation.

For a deeper failure, preserve the SDK request ID and follow [Debugging Your Adapters](https://docs.tensor9.com/cloud-adapter-early-access/debugging/overview.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.