28
28
import software .amazon .awssdk .services .iam .model .CreateAccessKeyResponse ;
29
29
import software .amazon .awssdk .services .iam .model .IamException ;
30
30
import software .amazon .awssdk .services .s3 .S3AsyncClient ;
31
+ import software .amazon .awssdk .services .s3 .model .BucketAlreadyExistsException ;
31
32
import software .amazon .awssdk .services .s3 .model .BucketInfo ;
32
33
import software .amazon .awssdk .services .s3 .model .BucketType ;
33
34
import software .amazon .awssdk .services .s3 .model .CopyObjectRequest ;
47
48
import software .amazon .awssdk .services .s3 .model .ListObjectsV2Request ;
48
49
import software .amazon .awssdk .services .s3 .model .LocationInfo ;
49
50
import software .amazon .awssdk .services .s3 .model .LocationType ;
51
+ import software .amazon .awssdk .services .s3 .model .NoSuchBucketException ;
52
+ import software .amazon .awssdk .services .s3 .model .NoSuchKeyException ;
50
53
import software .amazon .awssdk .services .s3 .model .ObjectIdentifier ;
51
54
import software .amazon .awssdk .services .s3 .model .PutObjectResponse ;
52
55
import software .amazon .awssdk .services .s3 .model .S3Object ;
@@ -181,7 +184,7 @@ public CompletableFuture<WaiterResponse<HeadBucketResponse>> deleteBucketAndObje
181
184
* Lists the objects in an S3 bucket asynchronously.
182
185
*
183
186
* @param s3Client the S3 async client to use for the operation
184
- * @param bucketName the name of the the S3 bucket containing the objects to list
187
+ * @param bucketName the name of the S3 bucket containing the objects to list
185
188
* @return a {@link CompletableFuture} that contains the list of object keys in the specified bucket
186
189
*/
187
190
public CompletableFuture <List <String >> listObjectsAsync (S3AsyncClient s3Client , String bucketName ) {
@@ -218,14 +221,10 @@ public CompletableFuture<ResponseBytes<GetObjectResponse>> getObjectAsync(S3Asyn
218
221
return s3Client .getObject (objectRequest , AsyncResponseTransformer .toBytes ())
219
222
.exceptionally (exception -> {
220
223
Throwable cause = exception .getCause ();
221
- if (cause instanceof S3Exception ) {
224
+ if (cause instanceof NoSuchKeyException ) {
222
225
throw new CompletionException ("Failed to get the object. Reason: " + ((S3Exception ) cause ).awsErrorDetails ().errorMessage (), cause );
223
226
}
224
227
throw new CompletionException ("Failed to get the object" , exception );
225
- })
226
- .thenApply (response -> {
227
- logger .info ("Successfully obtained bytes from an S3 object" );
228
- return response ;
229
228
});
230
229
}
231
230
@@ -318,8 +317,8 @@ public CompletableFuture<CreateBucketResponse> createDirectoryBucketAsync(S3Asyn
318
317
.whenComplete ((response , exception ) -> {
319
318
if (exception != null ) {
320
319
Throwable cause = exception .getCause ();
321
- if (cause instanceof S3Exception ) {
322
- throw new CompletionException ("Error creating bucket: " + ((S3Exception ) cause ).awsErrorDetails ().errorMessage (), cause );
320
+ if (cause instanceof BucketAlreadyExistsException ) {
321
+ throw new CompletionException ("The bucket already exists : " + ((S3Exception ) cause ).awsErrorDetails ().errorMessage (), cause );
323
322
}
324
323
throw new CompletionException ("Unexpected error occurred while creating bucket" , exception );
325
324
}
@@ -351,7 +350,12 @@ public CompletableFuture<WaiterResponse<HeadBucketResponse>> createBucketAsync(S
351
350
})
352
351
.whenComplete ((response , exception ) -> {
353
352
if (exception != null ) {
354
- throw new CompletionException ("Error creating bucket: " + bucketName , exception );
353
+ Throwable cause = exception .getCause ();
354
+ if (cause instanceof BucketAlreadyExistsException ) {
355
+ throw new CompletionException ("The S3 bucket exists: " + cause .getMessage (), cause );
356
+ } else {
357
+ throw new CompletionException ("Failed to create access key: " + exception .getMessage (), exception );
358
+ }
355
359
}
356
360
logger .info (bucketName + " is ready" );
357
361
});
@@ -374,7 +378,12 @@ public CompletableFuture<PutObjectResponse> putObjectAsync(S3AsyncClient s3Clien
374
378
return s3Client .putObject (objectRequest , AsyncRequestBody .fromString (text ))
375
379
.whenComplete ((response , exception ) -> {
376
380
if (exception != null ) {
377
- throw new CompletionException ("Failed to upload file" , exception );
381
+ Throwable cause = exception .getCause ();
382
+ if (cause instanceof NoSuchBucketException ) {
383
+ throw new CompletionException ("The S3 bucket does not exist: " + cause .getMessage (), cause );
384
+ } else {
385
+ throw new CompletionException ("Failed to create access key: " + exception .getMessage (), exception );
386
+ }
378
387
}
379
388
});
380
389
}
@@ -396,15 +405,13 @@ public CompletableFuture<CreateAccessKeyResponse> createAccessKeyAsync(String us
396
405
logger .info ("Access Key Created." );
397
406
} else {
398
407
if (exception == null ) {
399
- throw new CompletionException ( "An unknown error occurred while creating access key." , null );
400
- }
401
-
402
- Throwable cause = exception . getCause ();
403
- if ( cause instanceof IamException ) {
404
- throw new CompletionException ( "IAM error while creating access key: " + cause . getMessage (), cause );
408
+ Throwable cause = exception . getCause ( );
409
+ if ( cause instanceof IamException ) {
410
+ throw new CompletionException ( "IAM error while creating access key: " + cause . getMessage (), cause );
411
+ } else {
412
+ throw new CompletionException ( "Failed to create access key: " + exception . getMessage (), exception );
413
+ }
405
414
}
406
-
407
- throw new CompletionException ("Failed to create access key: " + exception .getMessage (), exception );
408
415
}
409
416
});
410
417
}
@@ -422,7 +429,6 @@ public CompletableFuture<String> selectAvailabilityZoneIdAsync() {
422
429
return getEc2AsyncClient ().describeAvailabilityZones (zonesRequest )
423
430
.thenCompose (response -> {
424
431
List <AvailabilityZone > zonesList = response .availabilityZones ();
425
-
426
432
if (zonesList .isEmpty ()) {
427
433
logger .info ("No availability zones found." );
428
434
return CompletableFuture .completedFuture (null ); // Return null if no zones are found
@@ -458,9 +464,9 @@ public CompletableFuture<String> selectAvailabilityZoneIdAsync() {
458
464
/**
459
465
* Prompts the user to select an Availability Zone from the given list.
460
466
*
461
- * @param zonesList the list of availability zones
467
+ * @param zonesList the list of Availability Zones
462
468
* @param zoneIds the list of zone IDs
463
- * @return the selected AvailabilityZone
469
+ * @return the selected Availability Zone
464
470
*/
465
471
private static AvailabilityZone promptUserForZoneSelection (List <AvailabilityZone > zonesList , List <String > zoneIds ) {
466
472
Scanner scanner = new Scanner (System .in );
@@ -486,14 +492,13 @@ private static AvailabilityZone promptUserForZoneSelection(List<AvailabilityZone
486
492
}
487
493
488
494
/**
489
- * Asynchronously sets up an AWS VPC, including creating a VPC, waiting for it to be available,
490
- * retrieving its associated route table, and creating a VPC endpoint for S3 Express .
495
+ * Asynchronously sets up a new VPC, including creating the VPC, finding the associated route table, and
496
+ * creating a VPC endpoint for the S3 service .
491
497
*
492
- * @return A {@link CompletableFuture} that completes when the VPC setup is finished.
493
- * If an error occurs, a {@link CompletionException} is thrown.
494
- * @throws CompletionException if an EC2-related error occurs or if required resources are missing.
498
+ * @return a {@link CompletableFuture} that, when completed, contains a AbstractMap with the
499
+ * VPC ID and VPC endpoint ID.
495
500
*/
496
- public CompletableFuture <Void > setupVPCAsync () {
501
+ public CompletableFuture <AbstractMap . SimpleEntry < String , String > > setupVPCAsync () {
497
502
String cidr = "10.0.0.0/16" ;
498
503
CreateVpcRequest vpcRequest = CreateVpcRequest .builder ()
499
504
.cidrBlock (cidr )
@@ -502,8 +507,9 @@ public CompletableFuture<Void> setupVPCAsync() {
502
507
return getEc2AsyncClient ().createVpc (vpcRequest )
503
508
.thenCompose (vpcResponse -> {
504
509
String vpcId = vpcResponse .vpc ().vpcId ();
510
+ logger .info ("VPC Created: {}" , vpcId );
505
511
506
- Ec2AsyncWaiter waiter = ec2AsyncClient .waiter ();
512
+ Ec2AsyncWaiter waiter = getEc2AsyncClient () .waiter ();
507
513
DescribeVpcsRequest request = DescribeVpcsRequest .builder ()
508
514
.vpcIds (vpcId )
509
515
.build ();
@@ -521,18 +527,20 @@ public CompletableFuture<Void> setupVPCAsync() {
521
527
.filters (filter )
522
528
.build ();
523
529
524
- return ec2AsyncClient .describeRouteTables (describeRouteTablesRequest )
530
+ return getEc2AsyncClient () .describeRouteTables (describeRouteTablesRequest )
525
531
.thenApply (routeTablesResponse -> {
526
532
if (routeTablesResponse .routeTables ().isEmpty ()) {
527
- throw new CompletionException ("No route tables found for VPC." , null );
533
+ throw new CompletionException ("No route tables found for VPC: " + vpcId , null );
528
534
}
529
- return new AbstractMap .SimpleEntry <>(vpcId , routeTablesResponse .routeTables ().get (0 ).routeTableId ());
535
+ String routeTableId = routeTablesResponse .routeTables ().get (0 ).routeTableId ();
536
+ logger .info ("Route table found: {}" , routeTableId );
537
+ return new AbstractMap .SimpleEntry <>(vpcId , routeTableId );
530
538
});
531
539
})
532
540
.thenCompose (vpcAndRouteTable -> {
533
541
String vpcId = vpcAndRouteTable .getKey ();
534
542
String routeTableId = vpcAndRouteTable .getValue ();
535
- Region region = ec2AsyncClient .serviceClientConfiguration ().region ();
543
+ Region region = getEc2AsyncClient () .serviceClientConfiguration ().region ();
536
544
String serviceName = String .format ("com.amazonaws.%s.s3express" , region .id ());
537
545
538
546
CreateVpcEndpointRequest endpointRequest = CreateVpcEndpointRequest .builder ()
@@ -541,30 +549,24 @@ public CompletableFuture<Void> setupVPCAsync() {
541
549
.serviceName (serviceName )
542
550
.build ();
543
551
544
- return ec2AsyncClient .createVpcEndpoint (endpointRequest )
552
+ return getEc2AsyncClient () .createVpcEndpoint (endpointRequest )
545
553
.thenApply (vpcEndpointResponse -> {
546
554
String vpcEndpointId = vpcEndpointResponse .vpcEndpoint ().vpcEndpointId ();
555
+ logger .info ("VPC Endpoint created: {}" , vpcEndpointId );
547
556
return new AbstractMap .SimpleEntry <>(vpcId , vpcEndpointId );
548
557
});
549
558
})
550
- .whenComplete ((result , exception ) -> {
551
- if (result != null ) {
552
- logger .info ("Created VPC: {}" , result .getKey ());
553
- logger .info ("Created VPC Endpoint: {}" , result .getValue ());
554
- } else {
555
- if (exception == null ) {
556
- throw new CompletionException ("An unknown error occurred during VPC setup." , null );
557
- }
558
-
559
- Throwable cause = exception .getCause ();
560
- if (cause instanceof Ec2Exception ) {
561
- throw new CompletionException ("EC2 error during VPC setup: " + cause .getMessage (), cause );
562
- }
563
-
564
- throw new CompletionException ("VPC setup failed: " + exception .getMessage (), exception );
559
+ .exceptionally (exception -> {
560
+ Throwable cause = exception .getCause () != null ? exception .getCause () : exception ;
561
+ if (cause instanceof Ec2Exception ) {
562
+ logger .error ("EC2 error during VPC setup: {}" , cause .getMessage (), cause );
563
+ throw new CompletionException ("EC2 error during VPC setup: " + cause .getMessage (), cause );
565
564
}
566
- })
567
- .thenAccept (v -> {});
565
+
566
+ logger .error ("VPC setup failed: {}" , cause .getMessage (), cause );
567
+ throw new CompletionException ("VPC setup failed: " + cause .getMessage (), cause );
568
+ });
568
569
}
570
+
569
571
}
570
572
// snippet-end:[s3.java2.directories.actions.main]
0 commit comments