Skip to content

Commit 5b76704

Browse files
Bennett-LynchBennett Lynch
and
Bennett Lynch
authored
Add customization.config support for setting default RetryMode (#2637)
This change allows for the SDK's default RetryMode to be configurable via setting an SdkClientOption as part of the ClientBuilder's `mergeInternalDefaults` logic, or via setting the `defaultRetryMode` variable in an accompanying customization.config file. Co-authored-by: Bennett Lynch <[email protected]>
1 parent 8399d61 commit 5b76704

File tree

13 files changed

+104
-33
lines changed

13 files changed

+104
-33
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"category": "AWS SDK for Java v2",
3+
"contributor": "",
4+
"type": "feature",
5+
"description": "Add customization.config support for setting default RetryMode"
6+
}

codegen/src/main/java/software/amazon/awssdk/codegen/model/config/customization/CustomizationConfig.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,15 @@
1919
import java.util.HashMap;
2020
import java.util.List;
2121
import java.util.Map;
22+
import software.amazon.awssdk.core.retry.RetryMode;
2223
import software.amazon.awssdk.core.traits.PayloadTrait;
2324
import software.amazon.awssdk.utils.AttributeMap;
2425

26+
/**
27+
* {@code service-2.json} models can be manually modified via defining properties in an associated {@code customization.config}
28+
* file. This class defines the Java bean representation that will be used to parse the JSON customization file. The bean can
29+
* then be later queried in the misc. codegen steps.
30+
*/
2531
public class CustomizationConfig {
2632

2733
/**
@@ -188,6 +194,8 @@ public class CustomizationConfig {
188194
private UnderscoresInNameBehavior underscoresInNameBehavior;
189195

190196
private String userAgent;
197+
198+
private RetryMode defaultRetryMode;
191199

192200
private CustomizationConfig() {
193201
}
@@ -485,4 +493,12 @@ public CustomizationConfig withUserAgent(String userAgent) {
485493
this.userAgent = userAgent;
486494
return this;
487495
}
496+
497+
public RetryMode getDefaultRetryMode() {
498+
return defaultRetryMode;
499+
}
500+
501+
public void setDefaultRetryMode(RetryMode defaultRetryMode) {
502+
this.defaultRetryMode = defaultRetryMode;
503+
}
488504
}

codegen/src/main/java/software/amazon/awssdk/codegen/poet/builder/BaseClientBuilderClass.java

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import com.squareup.javapoet.TypeVariableName;
3030
import java.util.Collections;
3131
import java.util.List;
32+
import java.util.Optional;
3233
import javax.lang.model.element.Modifier;
3334
import software.amazon.awssdk.annotations.SdkInternalApi;
3435
import software.amazon.awssdk.auth.signer.Aws4Signer;
@@ -45,6 +46,7 @@
4546
import software.amazon.awssdk.core.endpointdiscovery.providers.DefaultEndpointDiscoveryProviderChain;
4647
import software.amazon.awssdk.core.interceptor.ClasspathInterceptorChainFactory;
4748
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
49+
import software.amazon.awssdk.core.retry.RetryMode;
4850
import software.amazon.awssdk.core.signer.Signer;
4951
import software.amazon.awssdk.http.Protocol;
5052
import software.amazon.awssdk.http.SdkHttpConfigurationOption;
@@ -92,9 +94,7 @@ public TypeSpec poetSpec() {
9294
builder.addMethod(serviceNameMethod());
9395
builder.addMethod(mergeServiceDefaultsMethod());
9496

95-
if (model.getCustomizationConfig().getUserAgent() != null) {
96-
builder.addMethod(mergeInternalDefaultsMethod());
97-
}
97+
mergeInternalDefaultsMethod().ifPresent(builder::addMethod);
9898

9999
builder.addMethod(finalizeServiceConfigurationMethod());
100100
builder.addMethod(defaultSignerMethod());
@@ -175,19 +175,31 @@ private MethodSpec mergeServiceDefaultsMethod() {
175175
return builder.build();
176176
}
177177

178-
private MethodSpec mergeInternalDefaultsMethod() {
178+
private Optional<MethodSpec> mergeInternalDefaultsMethod() {
179179
String userAgent = model.getCustomizationConfig().getUserAgent();
180+
RetryMode defaultRetryMode = model.getCustomizationConfig().getDefaultRetryMode();
181+
182+
// If none of the options are customized, then we do not need to bother overriding the method
183+
if (userAgent == null && defaultRetryMode == null) {
184+
return Optional.empty();
185+
}
180186

181187
MethodSpec.Builder builder = MethodSpec.methodBuilder("mergeInternalDefaults")
182188
.addAnnotation(Override.class)
183189
.addModifiers(PROTECTED, FINAL)
184190
.returns(SdkClientConfiguration.class)
185191
.addParameter(SdkClientConfiguration.class, "config")
186-
.addCode("return config.merge(c -> c.option($T.INTERNAL_USER_AGENT, $S)\n",
187-
SdkClientOption.class, userAgent);
188-
189-
builder.addCode(");");
190-
return builder.build();
192+
.addCode("return config.merge(c -> {\n");
193+
if (userAgent != null) {
194+
builder.addCode("c.option($T.INTERNAL_USER_AGENT, $S);\n",
195+
SdkClientOption.class, userAgent);
196+
}
197+
if (defaultRetryMode != null) {
198+
builder.addCode("c.option($T.DEFAULT_RETRY_MODE, $T.$L);\n",
199+
SdkClientOption.class, RetryMode.class, defaultRetryMode.name());
200+
}
201+
builder.addCode("});\n");
202+
return Optional.of(builder.build());
191203
}
192204

193205
private MethodSpec finalizeServiceConfigurationMethod() {

codegen/src/test/java/software/amazon/awssdk/codegen/poet/builder/BuilderClassTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public void baseClientBuilderClass() throws Exception {
4040

4141
@Test
4242
public void baseClientBuilderClassWithInternalUserAgent() throws Exception {
43-
assertThat(new BaseClientBuilderClass(ClientTestModels.internalConfigModels()), generatesTo("test-client-builder-internal-user-agent-class.java"));
43+
assertThat(new BaseClientBuilderClass(ClientTestModels.internalConfigModels()), generatesTo("test-client-builder-internal-defaults-class.java"));
4444
}
4545

4646
@Test
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import software.amazon.awssdk.core.client.config.SdkClientOption;
1111
import software.amazon.awssdk.core.interceptor.ClasspathInterceptorChainFactory;
1212
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
13+
import software.amazon.awssdk.core.retry.RetryMode;
1314
import software.amazon.awssdk.core.signer.Signer;
1415
import software.amazon.awssdk.utils.CollectionUtils;
1516

@@ -37,7 +38,10 @@ protected final SdkClientConfiguration mergeServiceDefaults(SdkClientConfigurati
3738

3839
@Override
3940
protected final SdkClientConfiguration mergeInternalDefaults(SdkClientConfiguration config) {
40-
return config.merge(c -> c.option(SdkClientOption.INTERNAL_USER_AGENT, "md/foobar"));
41+
return config.merge(c -> {
42+
c.option(SdkClientOption.INTERNAL_USER_AGENT, "md/foobar");
43+
c.option(SdkClientOption.DEFAULT_RETRY_MODE, RetryMode.STANDARD);
44+
});
4145
}
4246

4347
@Override

codegen/src/test/resources/software/amazon/awssdk/codegen/poet/client/c2j/internalconfig/customization.config

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@
22
"authPolicyActions" : {
33
"skip" : true
44
},
5-
"userAgent": "md/foobar"
5+
"userAgent": "md/foobar",
6+
"defaultRetryMode": "STANDARD"
67
}

core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/builder/AwsDefaultClientBuilder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ private RetryPolicy resolveAwsRetryPolicy(SdkClientConfiguration config) {
235235
RetryMode retryMode = RetryMode.resolver()
236236
.profileFile(() -> config.option(SdkClientOption.PROFILE_FILE))
237237
.profileName(config.option(SdkClientOption.PROFILE_NAME))
238+
.defaultRetryMode(config.option(SdkClientOption.DEFAULT_RETRY_MODE))
238239
.resolve();
239240
return AwsRetryPolicy.forRetryMode(retryMode);
240241
}

core/sdk-core/src/main/java/software/amazon/awssdk/core/client/builder/SdkDefaultClientBuilder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ private RetryPolicy resolveRetryPolicy(SdkClientConfiguration config) {
259259
RetryMode retryMode = RetryMode.resolver()
260260
.profileFile(() -> config.option(SdkClientOption.PROFILE_FILE))
261261
.profileName(config.option(SdkClientOption.PROFILE_NAME))
262+
.defaultRetryMode(config.option(SdkClientOption.DEFAULT_RETRY_MODE))
262263
.resolve();
263264
return RetryPolicy.forRetryMode(retryMode);
264265
}

core/sdk-core/src/main/java/software/amazon/awssdk/core/client/config/SdkClientOption.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import software.amazon.awssdk.core.ServiceConfiguration;
2626
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
2727
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
28+
import software.amazon.awssdk.core.retry.RetryMode;
2829
import software.amazon.awssdk.core.retry.RetryPolicy;
2930
import software.amazon.awssdk.http.SdkHttpClient;
3031
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
@@ -149,6 +150,13 @@ public final class SdkClientOption<T> extends ClientOption<T> {
149150
*/
150151
public static final SdkClientOption<String> INTERNAL_USER_AGENT = new SdkClientOption<>(String.class);
151152

153+
/**
154+
* Option to specify the default retry mode.
155+
*
156+
* @see RetryMode.Resolver#defaultRetryMode(RetryMode)
157+
*/
158+
public static final SdkClientOption<RetryMode> DEFAULT_RETRY_MODE = new SdkClientOption<>(RetryMode.class);
159+
152160
private SdkClientOption(Class<T> valueClass) {
153161
super(valueClass);
154162
}

core/sdk-core/src/main/java/software/amazon/awssdk/core/retry/RetryMode.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,11 @@ public static Resolver resolver() {
9191
* Allows customizing the variables used during determination of a {@link RetryMode}. Created via {@link #resolver()}.
9292
*/
9393
public static class Resolver {
94+
private static final RetryMode SDK_DEFAULT_RETRY_MODE = LEGACY;
95+
9496
private Supplier<ProfileFile> profileFile;
9597
private String profileName;
98+
private RetryMode defaultRetryMode;
9699

97100
private Resolver() {
98101
}
@@ -114,12 +117,20 @@ public Resolver profileName(String profileName) {
114117
return this;
115118
}
116119

120+
/**
121+
* Configure the {@link RetryMode} that should be used if the mode is not specified anywhere else.
122+
*/
123+
public Resolver defaultRetryMode(RetryMode defaultRetryMode) {
124+
this.defaultRetryMode = defaultRetryMode;
125+
return this;
126+
}
127+
117128
/**
118129
* Resolve which retry mode should be used, based on the configured values.
119130
*/
120131
public RetryMode resolve() {
121132
return OptionalUtils.firstPresent(Resolver.fromSystemSettings(), () -> fromProfileFile(profileFile, profileName))
122-
.orElse(RetryMode.LEGACY);
133+
.orElseGet(this::fromDefaultMode);
123134
}
124135

125136
private static Optional<RetryMode> fromSystemSettings() {
@@ -150,5 +161,9 @@ private static Optional<RetryMode> fromString(String string) {
150161
throw new IllegalStateException("Unsupported retry policy mode configured: " + string);
151162
}
152163
}
164+
165+
private RetryMode fromDefaultMode() {
166+
return defaultRetryMode != null ? defaultRetryMode : SDK_DEFAULT_RETRY_MODE;
167+
}
153168
}
154169
}

core/sdk-core/src/test/java/software/amazon/awssdk/core/retry/RetryModeTest.java

Lines changed: 25 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,9 @@
2222
import java.nio.file.Paths;
2323
import java.util.Arrays;
2424
import java.util.Collection;
25+
import java.util.concurrent.Callable;
2526
import org.junit.After;
2627
import org.junit.Before;
27-
import org.junit.BeforeClass;
2828
import org.junit.Test;
2929
import org.junit.runner.RunWith;
3030
import org.junit.runners.Parameterized;
@@ -44,25 +44,26 @@ public class RetryModeTest {
4444
public static Collection<Object> data() {
4545
return Arrays.asList(new Object[] {
4646
// Test defaults
47-
new TestData(null, null, null, RetryMode.LEGACY),
48-
new TestData(null, null, "PropertyNotSet", RetryMode.LEGACY),
47+
new TestData(null, null, null, null, RetryMode.LEGACY),
48+
new TestData(null, null, "PropertyNotSet", null, RetryMode.LEGACY),
4949

5050
// Test precedence
51-
new TestData("standard", "legacy", "PropertySetToLegacy", RetryMode.STANDARD),
52-
new TestData("standard", null, null, RetryMode.STANDARD),
53-
new TestData(null, "standard", "PropertySetToLegacy", RetryMode.STANDARD),
54-
new TestData(null, "standard", null, RetryMode.STANDARD),
55-
new TestData(null, null, "PropertySetToStandard", RetryMode.STANDARD),
51+
new TestData("standard", "legacy", "PropertySetToLegacy", RetryMode.LEGACY, RetryMode.STANDARD),
52+
new TestData("standard", null, null, RetryMode.LEGACY, RetryMode.STANDARD),
53+
new TestData(null, "standard", "PropertySetToLegacy", RetryMode.LEGACY, RetryMode.STANDARD),
54+
new TestData(null, "standard", null, RetryMode.LEGACY, RetryMode.STANDARD),
55+
new TestData(null, null, "PropertySetToStandard", RetryMode.LEGACY, RetryMode.STANDARD),
56+
new TestData(null, null, null, RetryMode.STANDARD, RetryMode.STANDARD),
5657

5758
// Test invalid values
58-
new TestData("wrongValue", null, null, null),
59-
new TestData(null, "wrongValue", null, null),
60-
new TestData(null, null, "PropertySetToUnsupportedValue", null),
59+
new TestData("wrongValue", null, null, null, IllegalStateException.class),
60+
new TestData(null, "wrongValue", null, null, IllegalStateException.class),
61+
new TestData(null, null, "PropertySetToUnsupportedValue", null, IllegalStateException.class),
6162

6263
// Test capitalization standardization
63-
new TestData("sTaNdArD", null, null, RetryMode.STANDARD),
64-
new TestData(null, "sTaNdArD", null, RetryMode.STANDARD),
65-
new TestData(null, null, "PropertyMixedCase", RetryMode.STANDARD),
64+
new TestData("sTaNdArD", null, null, null, RetryMode.STANDARD),
65+
new TestData(null, "sTaNdArD", null, null, RetryMode.STANDARD),
66+
new TestData(null, null, "PropertyMixedCase", null, RetryMode.STANDARD),
6667
});
6768
}
6869

@@ -76,7 +77,7 @@ public void methodSetup() {
7677
}
7778

7879
@Test
79-
public void differentCombinationOfConfigs_shouldResolveCorrectly() {
80+
public void differentCombinationOfConfigs_shouldResolveCorrectly() throws Exception {
8081
if (testData.envVarValue != null) {
8182
ENVIRONMENT_VARIABLE_HELPER.set(SdkSystemSetting.AWS_RETRY_MODE.environmentVariable(), testData.envVarValue);
8283
}
@@ -92,10 +93,12 @@ public void differentCombinationOfConfigs_shouldResolveCorrectly() {
9293
System.setProperty(ProfileFileSystemSetting.AWS_CONFIG_FILE.property(), diskLocationForFile);
9394
}
9495

95-
if (testData.expected == null) {
96-
assertThatThrownBy(RetryMode::defaultRetryMode).isInstanceOf(RuntimeException.class);
96+
Callable<RetryMode> result = RetryMode.resolver().defaultRetryMode(testData.defaultMode)::resolve;
97+
if (testData.expected instanceof Class<?>) {
98+
Class<?> expectedClassType = (Class<?>) testData.expected;
99+
assertThatThrownBy(result::call).isInstanceOf(expectedClassType);
97100
} else {
98-
assertThat(RetryMode.defaultRetryMode()).isEqualTo(testData.expected);
101+
assertThat(result.call()).isEqualTo(testData.expected);
99102
}
100103
}
101104

@@ -108,12 +111,14 @@ private static class TestData {
108111
private final String envVarValue;
109112
private final String systemProperty;
110113
private final String configFile;
111-
private final RetryMode expected;
114+
private final RetryMode defaultMode;
115+
private final Object expected;
112116

113-
TestData(String systemProperty, String envVarValue, String configFile, RetryMode expected) {
117+
TestData(String systemProperty, String envVarValue, String configFile, RetryMode defaultMode, Object expected) {
114118
this.envVarValue = envVarValue;
115119
this.systemProperty = systemProperty;
116120
this.configFile = configFile;
121+
this.defaultMode = defaultMode;
117122
this.expected = expected;
118123
}
119124
}

services/dynamodb/src/main/java/software/amazon/awssdk/services/dynamodb/DynamoDbRetryPolicy.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ public static RetryPolicy resolveRetryPolicy(SdkClientConfiguration config) {
7373
RetryMode retryMode = RetryMode.resolver()
7474
.profileFile(() -> config.option(SdkClientOption.PROFILE_FILE))
7575
.profileName(config.option(SdkClientOption.PROFILE_NAME))
76+
.defaultRetryMode(config.option(SdkClientOption.DEFAULT_RETRY_MODE))
7677
.resolve();
7778

7879
switch (retryMode) {

services/kinesis/src/main/java/software/amazon/awssdk/services/kinesis/KinesisRetryPolicy.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public static RetryPolicy resolveRetryPolicy(SdkClientConfiguration config) {
4242
RetryMode retryMode = RetryMode.resolver()
4343
.profileFile(() -> config.option(SdkClientOption.PROFILE_FILE))
4444
.profileName(config.option(SdkClientOption.PROFILE_NAME))
45+
.defaultRetryMode(config.option(SdkClientOption.DEFAULT_RETRY_MODE))
4546
.resolve();
4647
return AwsRetryPolicy.forRetryMode(retryMode)
4748
.toBuilder()

0 commit comments

Comments
 (0)