Skip to content

Commit 28465dc

Browse files
committed
Introduce "EndpointDiscoveryFailedException", which is thrown when an operation requires endpoint discovery, but endpoint discovery fails.
1 parent f2a3559 commit 28465dc

File tree

9 files changed

+437
-25
lines changed

9 files changed

+437
-25
lines changed

codegen/src/main/java/software/amazon/awssdk/codegen/poet/endpointdiscovery/EndpointDiscoveryAsyncCacheLoaderGenerator.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.squareup.javapoet.TypeSpec;
2828
import java.time.Instant;
2929
import java.time.temporal.ChronoUnit;
30+
import java.util.List;
3031
import java.util.concurrent.CompletableFuture;
3132
import software.amazon.awssdk.annotations.SdkInternalApi;
3233
import software.amazon.awssdk.codegen.emitters.GeneratorTaskParams;
@@ -38,6 +39,7 @@
3839
import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryCacheLoader;
3940
import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryEndpoint;
4041
import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryRequest;
42+
import software.amazon.awssdk.utils.Validate;
4143

4244
public class EndpointDiscoveryAsyncCacheLoaderGenerator implements ClassSpec {
4345

@@ -100,12 +102,15 @@ private MethodSpec discoverEndpoint(OperationModel opModel) {
100102
.returns(returnType);
101103

102104
if (!opModel.getInputShape().isHasHeaderMember()) {
105+
ClassName endpointClass = poetExtensions.getModelClass("Endpoint");
103106
methodBuilder.addCode("return $L.$L($L.builder().build()).thenApply(r -> {",
104107
CLIENT_FIELD,
105108
opModel.getMethodName(),
106109
poetExtensions.getModelClass(opModel.getInputShape().getC2jName()))
107-
.addStatement("$T endpoint = r.endpoints().get(0)",
108-
poetExtensions.getModelClass("Endpoint"))
110+
.addStatement("$T<$T> endpoints = r.endpoints()", List.class, endpointClass)
111+
.addStatement("$T.notEmpty(endpoints, \"Endpoints returned by service for endpoint discovery must "
112+
+ "not be empty.\")", Validate.class)
113+
.addStatement("$T endpoint = endpoints.get(0)", endpointClass)
109114
.addStatement("return $T.builder().endpoint(toUri(endpoint.address(), $L.defaultEndpoint()))" +
110115
".expirationTime($T.now().plus(endpoint.cachePeriodInMinutes(), $T.MINUTES)).build()",
111116
EndpointDiscoveryEndpoint.class,

codegen/src/main/java/software/amazon/awssdk/codegen/poet/endpointdiscovery/EndpointDiscoveryCacheLoaderGenerator.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.squareup.javapoet.TypeSpec;
2828
import java.time.Instant;
2929
import java.time.temporal.ChronoUnit;
30+
import java.util.List;
3031
import java.util.concurrent.CompletableFuture;
3132
import software.amazon.awssdk.annotations.SdkInternalApi;
3233
import software.amazon.awssdk.codegen.emitters.GeneratorTaskParams;
@@ -38,6 +39,7 @@
3839
import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryCacheLoader;
3940
import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryEndpoint;
4041
import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryRequest;
42+
import software.amazon.awssdk.utils.Validate;
4143

4244
public class EndpointDiscoveryCacheLoaderGenerator implements ClassSpec {
4345

@@ -101,14 +103,18 @@ private MethodSpec discoverEndpoint(OperationModel opModel) {
101103
.returns(returnType);
102104

103105
if (!opModel.getInputShape().isHasHeaderMember()) {
106+
ClassName endpointClass = poetExtensions.getModelClass("Endpoint");
104107
methodBuilder.addCode("return $T.supplyAsync(() -> {", CompletableFuture.class)
105108
.addStatement("$T response = $L.$L($L.builder().build())",
106109
poetExtensions.getModelClass(opModel.getOutputShape().getC2jName()),
107110
CLIENT_FIELD,
108111
opModel.getMethodName(),
109112
poetExtensions.getModelClass(opModel.getInputShape().getC2jName()))
110-
.addStatement("$T endpoint = response.endpoints().get(0)",
111-
poetExtensions.getModelClass("Endpoint"))
113+
.addStatement("$T<$T> endpoints = response.endpoints()", List.class, endpointClass)
114+
.addStatement("$T.notEmpty(endpoints, \"Endpoints returned by service for endpoint discovery must "
115+
+ "not be empty.\")", Validate.class)
116+
.addStatement("$T endpoint = endpoints.get(0)",
117+
endpointClass)
112118
.addStatement("return $T.builder().endpoint(toUri(endpoint.address(), $L.defaultEndpoint()))" +
113119
".expirationTime($T.now().plus(endpoint.cachePeriodInMinutes(), $T.MINUTES)).build()",
114120
EndpointDiscoveryEndpoint.class,

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/endpointdiscovery/test-async-cache-loader.java

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
import java.time.Instant;
44
import java.time.temporal.ChronoUnit;
5+
import java.util.List;
56
import java.util.concurrent.CompletableFuture;
67
import software.amazon.awssdk.annotations.Generated;
78
import software.amazon.awssdk.annotations.SdkInternalApi;
89
import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryCacheLoader;
910
import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryEndpoint;
1011
import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryRequest;
1112
import software.amazon.awssdk.services.endpointdiscoverytest.model.Endpoint;
13+
import software.amazon.awssdk.utils.Validate;
1214

1315
@SdkInternalApi
1416
@Generated("software.amazon.awssdk:codegen")
@@ -26,14 +28,17 @@ public static EndpointDiscoveryTestAsyncEndpointDiscoveryCacheLoader create(Endp
2628
@Override
2729
public CompletableFuture<EndpointDiscoveryEndpoint> discoverEndpoint(EndpointDiscoveryRequest endpointDiscoveryRequest) {
2830
return client.describeEndpoints(
29-
software.amazon.awssdk.services.endpointdiscoverytest.model.DescribeEndpointsRequest.builder().build())
30-
.thenApply(
31-
r -> {
32-
Endpoint endpoint = r.endpoints().get(0);
33-
return EndpointDiscoveryEndpoint.builder()
34-
.endpoint(toUri(endpoint.address(), endpointDiscoveryRequest.defaultEndpoint()))
35-
.expirationTime(Instant.now().plus(endpoint.cachePeriodInMinutes(), ChronoUnit.MINUTES))
36-
.build();
37-
});
31+
software.amazon.awssdk.services.endpointdiscoverytest.model.DescribeEndpointsRequest.builder().build())
32+
.thenApply(
33+
r -> {
34+
List<Endpoint> endpoints = r.endpoints();
35+
Validate.notEmpty(endpoints,
36+
"Endpoints returned by service for endpoint discovery must not be empty.");
37+
Endpoint endpoint = endpoints.get(0);
38+
return EndpointDiscoveryEndpoint.builder()
39+
.endpoint(toUri(endpoint.address(), endpointDiscoveryRequest.defaultEndpoint()))
40+
.expirationTime(Instant.now().plus(endpoint.cachePeriodInMinutes(), ChronoUnit.MINUTES))
41+
.build();
42+
});
3843
}
3944
}

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/endpointdiscovery/test-sync-cache-loader.java

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.time.Instant;
44
import java.time.temporal.ChronoUnit;
5+
import java.util.List;
56
import java.util.concurrent.CompletableFuture;
67
import software.amazon.awssdk.annotations.Generated;
78
import software.amazon.awssdk.annotations.SdkInternalApi;
@@ -10,6 +11,7 @@
1011
import software.amazon.awssdk.core.endpointdiscovery.EndpointDiscoveryRequest;
1112
import software.amazon.awssdk.services.endpointdiscoverytest.model.DescribeEndpointsResponse;
1213
import software.amazon.awssdk.services.endpointdiscoverytest.model.Endpoint;
14+
import software.amazon.awssdk.utils.Validate;
1315

1416
@SdkInternalApi
1517
@Generated("software.amazon.awssdk:codegen")
@@ -28,12 +30,14 @@ public static EndpointDiscoveryTestEndpointDiscoveryCacheLoader create(EndpointD
2830
public CompletableFuture<EndpointDiscoveryEndpoint> discoverEndpoint(EndpointDiscoveryRequest endpointDiscoveryRequest) {
2931
return CompletableFuture.supplyAsync(() -> {
3032
DescribeEndpointsResponse response = client
31-
.describeEndpoints(software.amazon.awssdk.services.endpointdiscoverytest.model.DescribeEndpointsRequest
32-
.builder().build());
33-
Endpoint endpoint = response.endpoints().get(0);
33+
.describeEndpoints(software.amazon.awssdk.services.endpointdiscoverytest.model.DescribeEndpointsRequest
34+
.builder().build());
35+
List<Endpoint> endpoints = response.endpoints();
36+
Validate.notEmpty(endpoints, "Endpoints returned by service for endpoint discovery must not be empty.");
37+
Endpoint endpoint = endpoints.get(0);
3438
return EndpointDiscoveryEndpoint.builder()
35-
.endpoint(toUri(endpoint.address(), endpointDiscoveryRequest.defaultEndpoint()))
36-
.expirationTime(Instant.now().plus(endpoint.cachePeriodInMinutes(), ChronoUnit.MINUTES)).build();
39+
.endpoint(toUri(endpoint.address(), endpointDiscoveryRequest.defaultEndpoint()))
40+
.expirationTime(Instant.now().plus(endpoint.cachePeriodInMinutes(), ChronoUnit.MINUTES)).build();
3741
});
3842
}
3943
}

core/sdk-core/src/main/java/software/amazon/awssdk/core/endpointdiscovery/EndpointDiscoveryCacheLoader.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@
1818
import java.net.URI;
1919
import java.net.URISyntaxException;
2020
import java.util.concurrent.CompletableFuture;
21-
import software.amazon.awssdk.annotations.SdkInternalApi;
21+
import software.amazon.awssdk.annotations.SdkProtectedApi;
2222
import software.amazon.awssdk.core.exception.SdkClientException;
2323

24-
@SdkInternalApi
24+
@SdkProtectedApi
2525
public interface EndpointDiscoveryCacheLoader {
2626
CompletableFuture<EndpointDiscoveryEndpoint> discoverEndpoint(EndpointDiscoveryRequest endpointDiscoveryRequest);
2727

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
package software.amazon.awssdk.core.endpointdiscovery;
17+
18+
import software.amazon.awssdk.annotations.SdkPublicApi;
19+
import software.amazon.awssdk.core.exception.SdkClientException;
20+
import software.amazon.awssdk.utils.Validate;
21+
22+
/**
23+
* This exception is thrown when the SDK was unable to retrieve an endpoint from AWS. The cause describes what specific part of
24+
* the endpoint discovery process failed.
25+
*/
26+
@SdkPublicApi
27+
public class EndpointDiscoveryFailedException extends SdkClientException {
28+
29+
private static final long serialVersionUID = 1L;
30+
31+
private EndpointDiscoveryFailedException(Builder b) {
32+
super(b);
33+
Validate.paramNotNull(b.cause(), "cause");
34+
}
35+
36+
public static Builder builder() {
37+
return new BuilderImpl();
38+
}
39+
40+
public static EndpointDiscoveryFailedException create(Throwable cause) {
41+
return builder().message("Failed when retrieving a required endpoint from AWS.")
42+
.cause(cause)
43+
.build();
44+
}
45+
46+
@Override
47+
public Builder toBuilder() {
48+
return new BuilderImpl(this);
49+
}
50+
51+
public interface Builder extends SdkClientException.Builder {
52+
@Override
53+
Builder message(String message);
54+
55+
@Override
56+
Builder cause(Throwable cause);
57+
58+
@Override
59+
EndpointDiscoveryFailedException build();
60+
}
61+
62+
protected static final class BuilderImpl extends SdkClientException.BuilderImpl implements Builder {
63+
64+
protected BuilderImpl() {
65+
}
66+
67+
protected BuilderImpl(EndpointDiscoveryFailedException ex) {
68+
super(ex);
69+
}
70+
71+
@Override
72+
public Builder message(String message) {
73+
this.message = message;
74+
return this;
75+
}
76+
77+
@Override
78+
public Builder cause(Throwable cause) {
79+
this.cause = cause;
80+
return this;
81+
}
82+
83+
@Override
84+
public EndpointDiscoveryFailedException build() {
85+
return new EndpointDiscoveryFailedException(this);
86+
}
87+
}
88+
}

core/sdk-core/src/main/java/software/amazon/awssdk/core/endpointdiscovery/EndpointDiscoveryRefreshCache.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,11 @@
2020
import java.util.Map;
2121
import java.util.concurrent.CompletableFuture;
2222
import java.util.concurrent.ConcurrentHashMap;
23+
import java.util.concurrent.ExecutionException;
2324
import software.amazon.awssdk.annotations.SdkProtectedApi;
24-
import software.amazon.awssdk.utils.Logger;
2525

2626
@SdkProtectedApi
2727
public final class EndpointDiscoveryRefreshCache {
28-
29-
private static final Logger log = Logger.loggerFor(EndpointDiscoveryRefreshCache.class);
30-
3128
private final Map<String, EndpointDiscoveryEndpoint> cache = new ConcurrentHashMap<>();
3229

3330
private final EndpointDiscoveryCacheLoader client;
@@ -63,7 +60,7 @@ public URI get(String accessKey, EndpointDiscoveryRequest request) {
6360

6461
if (endpoint == null) {
6562
if (request.required()) {
66-
return cache.computeIfAbsent(key, k -> discoverEndpoint(request).join()).endpoint();
63+
return cache.computeIfAbsent(key, k -> getAndJoin(request)).endpoint();
6764
} else {
6865
EndpointDiscoveryEndpoint tempEndpoint = EndpointDiscoveryEndpoint.builder()
6966
.endpoint(request.defaultEndpoint())
@@ -90,6 +87,17 @@ public URI get(String accessKey, EndpointDiscoveryRequest request) {
9087
return endpoint.endpoint();
9188
}
9289

90+
private EndpointDiscoveryEndpoint getAndJoin(EndpointDiscoveryRequest request) {
91+
try {
92+
return discoverEndpoint(request).get();
93+
} catch (InterruptedException e) {
94+
Thread.currentThread().interrupt();
95+
throw EndpointDiscoveryFailedException.create(e);
96+
} catch (ExecutionException e) {
97+
throw EndpointDiscoveryFailedException.create(e.getCause());
98+
}
99+
}
100+
93101
private void refreshCacheAsync(EndpointDiscoveryRequest request, String key) {
94102
discoverEndpoint(request).thenApply(v -> cache.put(key, v));
95103
}

0 commit comments

Comments
 (0)