19

The Amazon Java SDK has marked the Constructors for AmazonS3Client deprecated in favor of some AmazonS3ClientBuilder.defaultClient(). Following the recommendation, though, does not result in an AmazonS3 client that works the same. In particular, the client has somehow failed to account for Region. If you run the tests below, the thisFails test demonstrates the problem.

public class S3HelperTest {
  @Test
  public void thisWorks() throws Exception {
    AmazonS3 s3Client = new AmazonS3Client();  // this call is deprecated
    s3Client.setS3ClientOptions(S3ClientOptions.builder().setPathStyleAccess(true).build());
    assertNotNull(s3Client);
  }

  @Test
  public void thisFails() throws Exception {
    AmazonS3 s3Client = AmazonS3ClientBuilder.defaultClient();
    /*
     * The following line throws like com.amazonaws.SdkClientException:
     * Unable to find a region via the region provider chain. Must provide an explicit region in the builder or
     * setup environment to supply a region.
     */
    s3Client.setS3ClientOptions(S3ClientOptions.builder().setPathStyleAccess(true).build());
  }
}

com.amazonaws.SdkClientException: Unable to find a region via the region provider chain. Must provide an explicit region in the builder or setup environment to supply a region.
    at com.amazonaws.client.builder.AwsClientBuilder.setRegion(AwsClientBuilder.java:371)
    at com.amazonaws.client.builder.AwsClientBuilder.configureMutableProperties(AwsClientBuilder.java:337)
    at com.amazonaws.client.builder.AwsSyncClientBuilder.build(AwsSyncClientBuilder.java:46)
    at com.amazonaws.services.s3.AmazonS3ClientBuilder.defaultClient(AmazonS3ClientBuilder.java:54)
    at com.climate.tenderfoot.service.S3HelperTest.thisFails(S3HelperTest.java:21)
    ...

Is this an AWS SDK Bug? Is there some "region default provider chain" or some mechanism to derive the region from the Environment and set it into the client? It seems really weak that the method to replace the deprecation doesn't result in the same capability.

Bob Kuhar
  • 10,838
  • 11
  • 62
  • 115
  • Below is how I create the s3client: AmazonS3 s3client = AmazonS3ClientBuilder.standard().build(); – John Hanley Oct 11 '17 at 21:05
  • @JohnHanley I'm a little reluctant to hard code the `withRegion("us-east-1")` as this application due to be deployed to multiple regions. I guess I could derive it from the Environment, but I'm not familiar enough with how instance credentials work and am worried that something I get working on my desktop will fail when it deploys into AWS. I was kind-of hoping for some way to leverage DefaultAwsRegionProviderChain. – Bob Kuhar Oct 11 '17 at 21:44
  • The standard way is to using "Environment variables". I have not used DefaultAwsRegionProviderChain as I know where my bucket is and I put that in the environment variables. – John Hanley Oct 11 '17 at 21:57

4 Answers4

31

Looks like a region is required for the builder. Probably this thread is related (I would use .withRegion(Regions.US_EAST_1) though in the 3rd line):

To emulate the previous behavior (no region configured), you'll need to also enable "forced global bucket access" in the client builder:

AmazonS3 client = 
        AmazonS3ClientBuilder.standard()
                             .withRegion("us-east-1") // The first region to try your request against
                             .withForceGlobalBucketAccess(true) // If a bucket is in a different region, try again in the correct region
                             .build();

This will suppress the exception you received and automatically retry the request under the region in the exception. It is made explicit in the builder so you are aware of this cross-region behavior. Note: The SDK will cache the bucket region after the first failure, so that every request against this bucket doesn't have to happen twice.


Also, from the AWS documentation if you want to use AmazonS3ClientBuilder.defaultClient(); then you need to have ~/.aws/credentials and ~/.aws/config files

~/.aws/credentials contents:

[default]
aws_access_key_id = your_id
aws_secret_access_key = your_key

~/.aws/config contents:

[default]
region = us-west-1

From the same AWS documentation page, if you don't want to hardcode the region/credentials, you can have it as environment variables in your Linux machine the usual way:

export AWS_ACCESS_KEY_ID=your_access_key_id
export AWS_SECRET_ACCESS_KEY=your_secret_access_key
export AWS_REGION=your_aws_region
Battle_Slug
  • 2,055
  • 1
  • 34
  • 60
  • This answer is mostly correct, or pointed me in the right direction at least. If I just create that `~/.aws/config` file and get the default region set up in there, then ```AmazonS3ClientBuilder.standard().withPathStyleAccessEnabled(true) .build();``` gets the job done – Bob Kuhar Oct 12 '17 at 00:20
  • Defining `AWS_REGION` in the environment gets it done too. – Bob Kuhar Oct 21 '17 at 18:18
  • Defining AWS_REGION in the environment does not work on windows it seems. – Jakob Sep 11 '18 at 08:30
0
    BasicAWSCredentials creds = new BasicAWSCredentials("key_ID", "Access_Key");
    AWSStaticCredentialsProvider provider = new 
    AWSStaticCredentialsProvider(creds);

    AmazonSQS sqs =AmazonSQSClientBuilder.standard()
    .withCredentials(provider)
    .withRegion(Regions.US_EAST_2)
    .build();
Aly Radwan
  • 199
  • 3
  • 6
0

Create file named "config" under .aws. And place below content.

~/.aws/config contents:

[default]
region = us-west-1
output = json
0
 AmazonSQS sqsClient = AmazonSQSClientBuilder
                .standard()
                .withRegion(Regions.AP_SOUTH_1)
                .build();
NeeruKSingh
  • 1,545
  • 3
  • 22
  • 26