Skip to content

Commit 68f931a

Browse files
Nicholas DeJacodagnir
Nicholas DeJaco
authored andcommitted
Add tests for execution attribute precedence and fix PR comments
1 parent 5d5411f commit 68f931a

File tree

8 files changed

+214
-51
lines changed

8 files changed

+214
-51
lines changed

core/aws-core/src/main/java/software/amazon/awssdk/awscore/client/handler/AwsClientHandlerUtils.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,10 +101,16 @@ static <InputT extends SdkRequest, OutputT extends SdkResponse> ExecutionContext
101101
.putAttribute(SdkInternalExecutionAttribute.DISABLE_HOST_PREFIX_INJECTION,
102102
clientConfig.option(SdkAdvancedClientOption.DISABLE_HOST_PREFIX_INJECTION));
103103

104-
Optional.ofNullable(clientConfig.option(SdkClientOption.EXECUTION_ATTRIBUTES)).ifPresent(clientOverrideConfiguration ->
105-
executionAttributes.putAllAttributes(clientOverrideConfiguration));
106-
originalRequest.overrideConfiguration().ifPresent(requestOverrideConfiguration ->
107-
executionAttributes.putAllAttributes(requestOverrideConfiguration.executionAttributes()));
104+
ExecutionAttributes clientOverrideExecutionAttributes = clientConfig.option(SdkClientOption.EXECUTION_ATTRIBUTES);
105+
if (clientOverrideExecutionAttributes != null) {
106+
executionAttributes = clientOverrideExecutionAttributes.merge(executionAttributes);
107+
}
108+
109+
Optional<RequestOverrideConfiguration> requestOverrideConfiguration =
110+
(Optional<RequestOverrideConfiguration>) originalRequest.overrideConfiguration();
111+
if (requestOverrideConfiguration.isPresent()) {
112+
executionAttributes = requestOverrideConfiguration.get().executionAttributes().merge(executionAttributes);
113+
}
108114

109115
ExecutionInterceptorChain executionInterceptorChain =
110116
new ExecutionInterceptorChain(clientConfig.option(SdkClientOption.EXECUTION_INTERCEPTORS));

core/sdk-core/src/main/java/software/amazon/awssdk/core/RequestOverrideConfiguration.java

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import software.amazon.awssdk.annotations.Immutable;
2929
import software.amazon.awssdk.annotations.SdkPublicApi;
3030
import software.amazon.awssdk.core.interceptor.ExecutionAttribute;
31+
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
3132
import software.amazon.awssdk.core.signer.Signer;
3233
import software.amazon.awssdk.metrics.MetricPublisher;
3334
import software.amazon.awssdk.utils.CollectionUtils;
@@ -47,7 +48,7 @@ public abstract class RequestOverrideConfiguration {
4748
private final Duration apiCallAttemptTimeout;
4849
private final Signer signer;
4950
private final List<MetricPublisher> metricPublishers;
50-
private final Map<ExecutionAttribute<?>, Object> executionAttributes;
51+
private final ExecutionAttributes executionAttributes;
5152

5253
protected RequestOverrideConfiguration(Builder<?> builder) {
5354
this.headers = CollectionUtils.deepUnmodifiableMap(builder.headers(), () -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER));
@@ -57,7 +58,7 @@ protected RequestOverrideConfiguration(Builder<?> builder) {
5758
this.apiCallAttemptTimeout = Validate.isPositiveOrNull(builder.apiCallAttemptTimeout(), "apiCallAttemptTimeout");
5859
this.signer = builder.signer();
5960
this.metricPublishers = Collections.unmodifiableList(new ArrayList<>(builder.metricPublishers()));
60-
this.executionAttributes = Collections.unmodifiableMap(new HashMap<>(builder.executionAttributes()));
61+
this.executionAttributes = builder.executionAttributes().build();
6162
}
6263

6364
/**
@@ -147,7 +148,7 @@ public List<MetricPublisher> metricPublishers() {
147148
* An attribute value added on the client within the collection of attributes is superseded by an
148149
* attribute value added on the request.
149150
*/
150-
public Map<ExecutionAttribute<?>, Object> executionAttributes() {
151+
public ExecutionAttributes executionAttributes() {
151152
return executionAttributes;
152153
}
153154

@@ -391,16 +392,16 @@ default B putRawQueryParameter(String name, String value) {
391392
* @param executionAttributes Execution attributes map for this request
392393
* @return This object for method chaining.
393394
*/
394-
B executionAttributes(Map<ExecutionAttribute<?>, Object> executionAttributes);
395+
B executionAttributes(Map<ExecutionAttribute<?>, ?> executionAttributes);
395396

396397
/**
397398
* Add an execution attribute to the existing collection of execution attributes.
398399
* @param attribute The execution attribute object
399400
* @param value The value of the execution attribute.
400401
*/
401-
B addExecutionAttribute(ExecutionAttribute attribute, Object value);
402+
<T> B putExecutionAttribute(ExecutionAttribute<T> attribute, T value);
402403

403-
Map<ExecutionAttribute<?>, Object> executionAttributes();
404+
ExecutionAttributes.Builder executionAttributes();
404405

405406
/**
406407
* Create a new {@code SdkRequestOverrideConfiguration} with the properties set on this builder.
@@ -418,7 +419,7 @@ protected abstract static class BuilderImpl<B extends Builder> implements Builde
418419
private Duration apiCallAttemptTimeout;
419420
private Signer signer;
420421
private List<MetricPublisher> metricPublishers = new ArrayList<>();
421-
private Map<ExecutionAttribute<?>, Object> executionAttributes = new HashMap<>();
422+
private ExecutionAttributes.Builder executionAttributes = ExecutionAttributes.builder();
422423

423424
protected BuilderImpl() {
424425
}
@@ -560,20 +561,20 @@ public List<MetricPublisher> metricPublishers() {
560561
}
561562

562563
@Override
563-
public B executionAttributes(Map<ExecutionAttribute<?>, Object> executionAttributes) {
564+
public B executionAttributes(Map<ExecutionAttribute<?>, ?> executionAttributes) {
564565
Validate.paramNotNull(executionAttributes, "executionAttributes");
565-
this.executionAttributes = executionAttributes;
566+
this.executionAttributes = ExecutionAttributes.builder().putAll(executionAttributes);
566567
return (B) this;
567568
}
568569

569570
@Override
570-
public B addExecutionAttribute(ExecutionAttribute executionAttribute, Object value) {
571+
public <T> B putExecutionAttribute(ExecutionAttribute<T> executionAttribute, T value) {
571572
this.executionAttributes.put(executionAttribute, value);
572573
return (B) this;
573574
}
574575

575576
@Override
576-
public Map<ExecutionAttribute<?>, Object> executionAttributes() {
577+
public ExecutionAttributes.Builder executionAttributes() {
577578
return executionAttributes;
578579
}
579580

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

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.util.function.Consumer;
2727
import software.amazon.awssdk.annotations.SdkPublicApi;
2828
import software.amazon.awssdk.core.interceptor.ExecutionAttribute;
29+
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
2930
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
3031
import software.amazon.awssdk.core.retry.RetryMode;
3132
import software.amazon.awssdk.core.retry.RetryPolicy;
@@ -58,7 +59,7 @@ public final class ClientOverrideConfiguration
5859
private final ProfileFile defaultProfileFile;
5960
private final String defaultProfileName;
6061
private final List<MetricPublisher> metricPublishers;
61-
private final Map<ExecutionAttribute<?>, Object> executionAttributes;
62+
private final ExecutionAttributes executionAttributes;
6263

6364
/**
6465
* Initialize this configuration. Private to require use of {@link #builder()}.
@@ -73,7 +74,7 @@ private ClientOverrideConfiguration(Builder builder) {
7374
this.defaultProfileFile = builder.defaultProfileFile();
7475
this.defaultProfileName = builder.defaultProfileName();
7576
this.metricPublishers = Collections.unmodifiableList(new ArrayList<>(builder.metricPublishers()));
76-
this.executionAttributes = Collections.unmodifiableMap(new HashMap<>(builder.executionAttributes()));
77+
this.executionAttributes = builder.executionAttributes().build();
7778
}
7879

7980
@Override
@@ -204,7 +205,7 @@ public List<MetricPublisher> metricPublishers() {
204205
*
205206
* @Return Map of execution attributes.
206207
*/
207-
public Map<ExecutionAttribute<?>, Object> executionAttributes() {
208+
public ExecutionAttributes executionAttributes() {
208209
return executionAttributes;
209210
}
210211

@@ -457,16 +458,16 @@ default Builder retryPolicy(RetryMode retryMode) {
457458
* @param executionAttributes Execution attributes map for this client.
458459
* @return This object for method chaining.
459460
*/
460-
Builder executionAttributes(Map<ExecutionAttribute<?>, Object> executionAttributes);
461+
Builder executionAttributes(Map<ExecutionAttribute<?>, ?> executionAttributes);
461462

462463
/**
463-
* Add an execution attribute to the existing collection of execution attributes.
464+
* Put an execution attribute into to the existing collection of execution attributes.
464465
* @param attribute The execution attribute object
465466
* @param value The value of the execution attribute.
466467
*/
467-
Builder addExecutionAttribute(ExecutionAttribute attribute, Object value);
468+
<T> Builder putExecutionAttribute(ExecutionAttribute<T> attribute, T value);
468469

469-
Map<ExecutionAttribute<?>, Object> executionAttributes();
470+
ExecutionAttributes.Builder executionAttributes();
470471
}
471472

472473
/**
@@ -482,7 +483,7 @@ private static final class DefaultClientOverrideConfigurationBuilder implements
482483
private ProfileFile defaultProfileFile;
483484
private String defaultProfileName;
484485
private List<MetricPublisher> metricPublishers = new ArrayList<>();
485-
private Map<ExecutionAttribute<?>, Object> executionAttributes = new HashMap<>();
486+
private ExecutionAttributes.Builder executionAttributes = ExecutionAttributes.builder();
486487

487488
@Override
488489
public Builder headers(Map<String, List<String>> headers) {
@@ -648,20 +649,20 @@ public List<MetricPublisher> metricPublishers() {
648649
}
649650

650651
@Override
651-
public Builder executionAttributes(Map<ExecutionAttribute<?>, Object> executionAttributes) {
652+
public Builder executionAttributes(Map<ExecutionAttribute<?>, ?> executionAttributes) {
652653
Validate.paramNotNull(executionAttributes, "executionAttributes");
653-
this.executionAttributes = executionAttributes;
654+
this.executionAttributes = ExecutionAttributes.builder().putAll(executionAttributes);
654655
return this;
655656
}
656657

657658
@Override
658-
public Builder addExecutionAttribute(ExecutionAttribute executionAttribute, Object value) {
659+
public <T> Builder putExecutionAttribute(ExecutionAttribute<T> executionAttribute, T value) {
659660
this.executionAttributes.put(executionAttribute, value);
660661
return this;
661662
}
662663

663664
@Override
664-
public Map<ExecutionAttribute<?>, Object> executionAttributes() {
665+
public ExecutionAttributes.Builder executionAttributes() {
665666
return executionAttributes;
666667
}
667668

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import software.amazon.awssdk.annotations.SdkProtectedApi;
2424
import software.amazon.awssdk.core.ClientType;
2525
import software.amazon.awssdk.core.ServiceConfiguration;
26-
import software.amazon.awssdk.core.interceptor.ExecutionAttribute;
26+
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
2727
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
2828
import software.amazon.awssdk.core.retry.RetryPolicy;
2929
import software.amazon.awssdk.http.SdkHttpClient;
@@ -142,8 +142,8 @@ public final class SdkClientOption<T> extends ClientOption<T> {
142142
/**
143143
* Option to specify additional execution attributes to each client call.
144144
*/
145-
public static final SdkClientOption<Map<ExecutionAttribute<?>, Object>> EXECUTION_ATTRIBUTES =
146-
new SdkClientOption<>(new UnsafeValueType(Map.class));
145+
public static final SdkClientOption<ExecutionAttributes> EXECUTION_ATTRIBUTES =
146+
new SdkClientOption<>(new UnsafeValueType(ExecutionAttributes.class));
147147

148148
private SdkClientOption(Class<T> valueClass) {
149149
super(valueClass);

core/sdk-core/src/main/java/software/amazon/awssdk/core/interceptor/ExecutionAttributes.java

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
import java.util.Map;
2020
import software.amazon.awssdk.annotations.NotThreadSafe;
2121
import software.amazon.awssdk.annotations.SdkPublicApi;
22+
import software.amazon.awssdk.utils.Validate;
23+
import software.amazon.awssdk.utils.builder.CopyableBuilder;
24+
import software.amazon.awssdk.utils.builder.ToCopyableBuilder;
2225

2326
/**
2427
* A mutable collection of {@link ExecutionAttribute}s that can be modified by {@link ExecutionInterceptor}s in order to save and
@@ -28,8 +31,16 @@
2831
*/
2932
@SdkPublicApi
3033
@NotThreadSafe
31-
public final class ExecutionAttributes {
32-
private final Map<ExecutionAttribute<?>, Object> attributes = new HashMap<>();
34+
public final class ExecutionAttributes implements ToCopyableBuilder<ExecutionAttributes.Builder, ExecutionAttributes> {
35+
private final Map<ExecutionAttribute<?>, Object> attributes;
36+
37+
public ExecutionAttributes() {
38+
this.attributes = new HashMap<>();
39+
}
40+
41+
private ExecutionAttributes(Map<? extends ExecutionAttribute<?>, ?> attributes) {
42+
this.attributes = new HashMap<>(attributes);
43+
}
3344

3445
/**
3546
* Retrieve the current value of the provided attribute in this collection of attributes. This will return null if the value
@@ -40,6 +51,13 @@ public <U> U getAttribute(ExecutionAttribute<U> attribute) {
4051
return (U) attributes.get(attribute);
4152
}
4253

54+
/**
55+
* Retrieve the collection of attributes.
56+
*/
57+
public Map<ExecutionAttribute<?>, ?> getAttributes() {
58+
return attributes;
59+
}
60+
4361
/**
4462
* Update or set the provided attribute in this collection of attributes.
4563
*/
@@ -51,16 +69,73 @@ public <U> ExecutionAttributes putAttribute(ExecutionAttribute<U> attribute, U v
5169
/**
5270
* Add all execution attributes from the provided collection to the internal collection of attributes.
5371
*/
54-
public <U> ExecutionAttributes putAllAttributes(Map<ExecutionAttribute<?>, Object> executionAttributes) {
72+
public <U> ExecutionAttributes putAllAttributes(Map<ExecutionAttribute<?>, ?> executionAttributes) {
5573
this.attributes.putAll(executionAttributes);
5674
return this;
5775
}
5876

77+
/**
78+
* Merge attributes of a lower precedence into the current higher precedence current collection.
79+
*/
80+
public <U> ExecutionAttributes merge(ExecutionAttributes lowerPrecedenceExecutionAttributes) {
81+
lowerPrecedenceExecutionAttributes.getAttributes().forEach(this.attributes::putIfAbsent);
82+
return this;
83+
}
84+
5985
/**
6086
* Set the provided attribute in this collection of attributes if it does not already exist in the collection.
6187
*/
6288
public <U> ExecutionAttributes putAttributeIfAbsent(ExecutionAttribute<U> attribute, U value) {
6389
attributes.putIfAbsent(attribute, value);
6490
return this;
6591
}
92+
93+
public static Builder builder() {
94+
return new Builder();
95+
}
96+
97+
@Override
98+
public Builder toBuilder() {
99+
return builder().putAll(attributes);
100+
}
101+
102+
public ExecutionAttributes copy() {
103+
return toBuilder().build();
104+
}
105+
106+
public static final class Builder implements CopyableBuilder<ExecutionAttributes.Builder, ExecutionAttributes> {
107+
108+
private final Map<ExecutionAttribute<?>, Object> executionAttributes = new HashMap<>();
109+
110+
private Builder() {
111+
}
112+
113+
@SuppressWarnings("unchecked") // Cast is safe due to implementation of {@link #put}
114+
public <T> T get(ExecutionAttribute<T> key) {
115+
Validate.notNull(key, "Key to retrieve must not be null.");
116+
return (T) executionAttributes.get(key);
117+
}
118+
119+
/**
120+
* Add a mapping between the provided key and value.
121+
*/
122+
public <T> ExecutionAttributes.Builder put(ExecutionAttribute<T> key, T value) {
123+
Validate.notNull(key, "Key to set must not be null.");
124+
executionAttributes.put(key, value);
125+
return this;
126+
}
127+
128+
/**
129+
* Adds all the attributes from the map provided.
130+
*/
131+
public ExecutionAttributes.Builder putAll(Map<? extends ExecutionAttribute<?>, ?> attributes) {
132+
executionAttributes.putAll(attributes);
133+
return this;
134+
}
135+
136+
@Override
137+
public ExecutionAttributes build() {
138+
return new ExecutionAttributes(executionAttributes);
139+
}
140+
}
66141
}

core/sdk-core/src/test/java/software/amazon/awssdk/core/RequestOverrideConfigurationTest.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ public void executionAttributes_createsCopy() {
180180
.build();
181181

182182
executionAttributes.remove(testAttribute);
183-
assertThat(overrideConfig.executionAttributes().get(testAttribute)).isEqualTo(expectedValue);
183+
assertThat(overrideConfig.executionAttributes().getAttribute(testAttribute)).isEqualTo(expectedValue);
184184
}
185185

186186
@Test
@@ -193,11 +193,11 @@ public void executionAttributes_maintainsAllAdded() {
193193
SdkRequestOverrideConfiguration.Builder builder = SdkRequestOverrideConfiguration.builder();
194194

195195
for (Map.Entry<ExecutionAttribute, Object> attributeObjectEntry : executionAttributeObjectMap.entrySet()) {
196-
builder.addExecutionAttribute(attributeObjectEntry.getKey(), attributeObjectEntry.getValue());
196+
builder.putExecutionAttribute(attributeObjectEntry.getKey(), attributeObjectEntry.getValue());
197197
}
198198

199199
SdkRequestOverrideConfiguration overrideConfig = builder.build();
200-
assertThat(overrideConfig.executionAttributes()).isEqualTo(executionAttributeObjectMap);
200+
assertThat(overrideConfig.executionAttributes().getAttributes()).isEqualTo(executionAttributeObjectMap);
201201
}
202202

203203
@Test
@@ -209,10 +209,10 @@ public void executionAttributes_overwritesPreviouslyAdded() {
209209

210210
SdkRequestOverrideConfiguration.Builder builder = SdkRequestOverrideConfiguration.builder();
211211

212-
builder.addExecutionAttribute(new ExecutionAttribute("AddedAttribute"), mock(Object.class));
212+
builder.putExecutionAttribute(new ExecutionAttribute("AddedAttribute"), mock(Object.class));
213213
builder.executionAttributes(executionAttributes);
214214
SdkRequestOverrideConfiguration overrideConfig = builder.build();
215-
assertThat(overrideConfig.executionAttributes()).isEqualTo(executionAttributes);
215+
assertThat(overrideConfig.executionAttributes().getAttributes()).isEqualTo(executionAttributes);
216216
}
217217

218218
@Test
@@ -228,9 +228,9 @@ public void executionAttributes_listPreviouslyAdded_appendedToList() {
228228
ExecutionAttribute addedAttribute = new ExecutionAttribute("AddedAttribute");
229229
Object addedValue = mock(Object.class);
230230

231-
builder.addExecutionAttribute(addedAttribute, addedValue);
231+
builder.putExecutionAttribute(addedAttribute, addedValue);
232232

233233
SdkRequestOverrideConfiguration overrideConfig = builder.build();
234-
assertThat(overrideConfig.executionAttributes().get(addedAttribute)).isEqualTo(addedValue);
234+
assertThat(overrideConfig.executionAttributes().getAttribute(addedAttribute)).isEqualTo(addedValue);
235235
}
236236
}

0 commit comments

Comments
 (0)