Getting `S3Exception` when executing download operation with `S3TransferManager` AWS SDK 2.19

515 Views Asked by At

I am using the S3TransferManager with S3AsyncClient in my service code. I am testing this code with localstack docker S3 service. I have the local stack S3 service and created the bucket test and uploaded one file test.txt on docker service startup.

S3Config.java

@Configuration
public class S3Configuration {
  @Value("${aws.access-key}")
  private String accessKey;
  @Value("${aws.secret-key}")
  private String secretKey;
  @Value("${aws.region}")
  private String region;
  @Value("${aws.endpoint:#{null}}")
  private String endpoint;

  @Bean
  public S3AsyncClient s3AsyncClient(AwsCredentialsProvider awsCredentialsProvider) {
    S3CrtAsyncClientBuilder s3CrtAsyncClientBuilder = S3AsyncClient.crtBuilder()
      .region(Region.of(region))
      .credentialsProvider(awsCredentialsProvider)
      .targetThroughputInGbps(20.0)
      .minimumPartSizeInBytes(8 * MB);
    if (StringUtils.hasText(endpoint)) {
      s3CrtAsyncClientBuilder.endpointOverride(URI.create(endpoint));
    }
    return s3CrtAsyncClientBuilder.build();
  }
  @Bean
  AwsCredentialsProvider awsCredentialsProvider() {
    return () -> AwsBasicCredentials.create(accessKey, secretKey);
  }
}

S3StorageServiceImpl:

@Service
@Primary
public class S3StorageServiceImpl implements S3StorageService {

  public static final String ERROR_MSG_FORMAT = "Error performing S3 client operation";
  private static final Logger LOG = LoggerFactory.getLogger(S3StorageServiceImpl.class);
  private final S3AsyncClient s3AsyncClient;

  public S3StorageServiceImpl(S3AsyncClient s3AsyncClient) {
    this.s3AsyncClient = s3AsyncClient;
  }

  @Override
  public byte[] downloadAsByteArray(String bucketName, String keyName) {
    try (S3TransferManager transferManager = S3TransferManager.builder().s3Client(s3AsyncClient).build()) {
      DownloadRequest < ResponseBytes < GetObjectResponse >> downloadFileRequest =
        DownloadRequest.builder()
        .getObjectRequest(req -> req.bucket(bucketName).key(keyName))
        .responseTransformer(AsyncResponseTransformer.toBytes())
        .addTransferListener(LoggingTransferListener.create())
        .build();

      // Wait for the transfer to complete
      return transferManager.download(downloadFileRequest).completionFuture()
        .thenApply(CompletedDownload::result)
        .thenApply(BytesWrapper::asByteArray)
        .join();
    } catch (CompletionException e) {
      LOG.error(ERROR_MSG_FORMAT, e);
      return new byte[0];
    }
  }
}

docker compose for localstack service:

version: '3.4'
  localstack:
    container_name: "localstack_main"
    image: localstack/localstack
    ports:
      - "4566-4599:4566-4599"
    environment:
      - SERVICES=s3
      - AWS_DEFAULT_REGION=us-east-1
    volumes:
      - ./docker/files/test.txt:/home/docker/data/test.txt
      - ./docker/files/init-s3-bucket.sh:/docker-entrypoint-initaws.d/init-s3-bucket.sh

Above /docker/files/ is local directory from project. And init-s3-bucket.sh contains the AWS cli code to create the test bucket and upload the test.txt file.

Spring Boot test :

@SpringBootTest(classes = {
  S3StorageServiceImpl.class,
  S3Configuration.class
})
class S3StorageServiceImplTest {

  @Autowired
  private S3StorageService s3StorageService;

  @Test
  void downloadAsByteArray() {
    byte[] byteArray = s3StorageService.downloadAsByteArray("test", "test.txt");
    assertThat(byteArray).isNotEmpty();
  }
}

applicaton-test.yml has aws.endpoint=http://localhost:4566 so that it can connect localstack container.

When I am running the test getting below error

java.util.concurrent.CompletionException: software.amazon.awssdk.services.s3.model.S3Exception: null (Service: S3, Status Code: 404, Request ID: 98gby23UsHdBcGi4dwT3T4BNjeMa3hyFMS0coOqQMmD1Y3TL4sVu, Extended Request ID: MzRISOwyjmnup7C0E0AFC7D5516C77/JypPGXLh0OVFGcJaaO3KW/hRAqKOpIEEp)
    at software.amazon.awssdk.utils.CompletableFutureUtils.errorAsCompletionException(CompletableFutureUtils.java:65)
    at software.amazon.awssdk.core.internal.http.pipeline.stages.AsyncExecutionFailureExceptionReportingStage.lambda$execute$0(AsyncExecutionFailureExceptionReportingStage.java:51)
    at java.base/java.util.concurrent.CompletableFuture.uniHandle(CompletableFuture.java:934)
    at java.base/java.util.concurrent.CompletableFuture$UniHandle.tryFire(CompletableFuture.java:911)
    at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
    at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2162)
    at software.amazon.awssdk.utils.CompletableFutureUtils.lambda$forwardExceptionTo$0(CompletableFutureUtils.java:79)
    at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863)
    at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841)
    at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
    at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2162)
    at software.amazon.awssdk.core.internal.http.pipeline.stages.AsyncRetryableStage$RetryingExecutor.maybeAttemptExecute(AsyncRetryableStage.java:103)
    at software.amazon.awssdk.core.internal.http.pipeline.stages.AsyncRetryableStage$RetryingExecutor.maybeRetryExecute(AsyncRetryableStage.java:184)
    at software.amazon.awssdk.core.internal.http.pipeline.stages.AsyncRetryableStage$RetryingExecutor.lambda$attemptExecute$1(AsyncRetryableStage.java:170)
    at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863)
    at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841)
    at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
    at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2147)
    at software.amazon.awssdk.core.internal.http.pipeline.stages.MakeAsyncHttpRequestStage.lambda$null$0(MakeAsyncHttpRequestStage.java:105)
    at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863)
    at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841)
    at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
    at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2147)
    at software.amazon.awssdk.core.internal.http.pipeline.stages.MakeAsyncHttpRequestStage.lambda$executeHttpRequest$3(MakeAsyncHttpRequestStage.java:163)
    at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:863)
    at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:841)
    at java.base/java.util.concurrent.CompletableFuture$Completion.run(CompletableFuture.java:482)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: software.amazon.awssdk.services.s3.model.S3Exception: null (Service: S3, Status Code: 404, Request ID: 98gby23UsHdBcGi4dwT3T4BNjeMa3hyFMS0coOqQMmD1Y3TL4sVu, Extended Request ID: MzRISOwyjmnup7C0E0AFC7D5516C77/JypPGXLh0OVFGcJaaO3KW/hRAqKOpIEEp)
    at software.amazon.awssdk.services.s3.model.S3Exception$BuilderImpl.build(S3Exception.java:104)
    at software.amazon.awssdk.services.s3.model.S3Exception$BuilderImpl.build(S3Exception.java:58)
    at software.amazon.awssdk.protocols.query.internal.unmarshall.AwsXmlErrorUnmarshaller.unmarshall(AwsXmlErrorUnmarshaller.java:99)
    at software.amazon.awssdk.protocols.query.unmarshall.AwsXmlErrorProtocolUnmarshaller.handle(AwsXmlErrorProtocolUnmarshaller.java:102)
    at software.amazon.awssdk.protocols.query.unmarshall.AwsXmlErrorProtocolUnmarshaller.handle(AwsXmlErrorProtocolUnmarshaller.java:82)
    at software.amazon.awssdk.core.http.MetricCollectingHttpResponseHandler.lambda$handle$0(MetricCollectingHttpResponseHandler.java:52)
    at software.amazon.awssdk.core.internal.util.MetricUtils.measureDurationUnsafe(MetricUtils.java:63)
    at software.amazon.awssdk.core.http.MetricCollectingHttpResponseHandler.handle(MetricCollectingHttpResponseHandler.java:52)
    at software.amazon.awssdk.core.internal.http.async.AsyncResponseHandler.lambda$prepare$0(AsyncResponseHandler.java:89)
    at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1150)
    at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
    at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2147)
    at software.amazon.awssdk.core.internal.http.async.AsyncResponseHandler$BaosSubscriber.onComplete(AsyncResponseHandler.java:132)
    at software.amazon.awssdk.utils.async.SimplePublisher.doProcessQueue(SimplePublisher.java:275)
    at software.amazon.awssdk.utils.async.SimplePublisher.processEventQueue(SimplePublisher.java:224)
    at software.amazon.awssdk.utils.async.SimplePublisher.complete(SimplePublisher.java:157)
    at java.base/java.util.concurrent.CompletableFuture.uniRunNow(CompletableFuture.java:819)
    at java.base/java.util.concurrent.CompletableFuture.uniRunStage(CompletableFuture.java:803)
    at java.base/java.util.concurrent.CompletableFuture.thenRun(CompletableFuture.java:2195)
    at software.amazon.awssdk.services.s3.internal.crt.S3CrtResponseHandlerAdapter.onErrorResponseComplete(S3CrtResponseHandlerAdapter.java:135)
    at software.amazon.awssdk.services.s3.internal.crt.S3CrtResponseHandlerAdapter.handleError(S3CrtResponseHandlerAdapter.java:124)
    at software.amazon.awssdk.services.s3.internal.crt.S3CrtResponseHandlerAdapter.onFinished(S3CrtResponseHandlerAdapter.java:93)
    at software.amazon.awssdk.crt.s3.S3MetaRequestResponseHandlerNativeAdapter.onFinished(S3MetaRequestResponseHandlerNativeAdapter.java:24)


java.lang.AssertionError: 
Expecting actual not to be empty

I have created repo with miniman reproducible example. Please find it here

0

There are 0 best solutions below