Skip to content

Commit ad9a6d3

Browse files
Nicholas DeJacodagnir
Nicholas DeJaco
authored andcommitted
Make execution attributes immutable and fix other PR comments
1 parent 68f931a commit ad9a6d3

File tree

9 files changed

+143
-25
lines changed

9 files changed

+143
-25
lines changed

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

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,7 @@
4040
import software.amazon.awssdk.core.client.config.SdkClientOption;
4141
import software.amazon.awssdk.core.client.handler.ClientExecutionParams;
4242
import software.amazon.awssdk.core.http.ExecutionContext;
43-
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
44-
import software.amazon.awssdk.core.interceptor.ExecutionInterceptorChain;
45-
import software.amazon.awssdk.core.interceptor.InterceptorContext;
46-
import software.amazon.awssdk.core.interceptor.SdkExecutionAttribute;
47-
import software.amazon.awssdk.core.interceptor.SdkInternalExecutionAttribute;
43+
import software.amazon.awssdk.core.interceptor.*;
4844
import software.amazon.awssdk.core.metrics.CoreMetric;
4945
import software.amazon.awssdk.core.signer.Signer;
5046
import software.amazon.awssdk.http.SdkHttpFullRequest;
@@ -106,8 +102,7 @@ static <InputT extends SdkRequest, OutputT extends SdkResponse> ExecutionContext
106102
executionAttributes = clientOverrideExecutionAttributes.merge(executionAttributes);
107103
}
108104

109-
Optional<RequestOverrideConfiguration> requestOverrideConfiguration =
110-
(Optional<RequestOverrideConfiguration>) originalRequest.overrideConfiguration();
105+
Optional<? extends RequestOverrideConfiguration> requestOverrideConfiguration = originalRequest.overrideConfiguration();
111106
if (requestOverrideConfiguration.isPresent()) {
112107
executionAttributes = requestOverrideConfiguration.get().executionAttributes().merge(executionAttributes);
113108
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import software.amazon.awssdk.annotations.SdkPublicApi;
3030
import software.amazon.awssdk.core.interceptor.ExecutionAttribute;
3131
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
32+
import software.amazon.awssdk.core.interceptor.UnmodifiableExecutionAttributes;
3233
import software.amazon.awssdk.core.signer.Signer;
3334
import software.amazon.awssdk.metrics.MetricPublisher;
3435
import software.amazon.awssdk.utils.CollectionUtils;
@@ -58,7 +59,7 @@ protected RequestOverrideConfiguration(Builder<?> builder) {
5859
this.apiCallAttemptTimeout = Validate.isPositiveOrNull(builder.apiCallAttemptTimeout(), "apiCallAttemptTimeout");
5960
this.signer = builder.signer();
6061
this.metricPublishers = Collections.unmodifiableList(new ArrayList<>(builder.metricPublishers()));
61-
this.executionAttributes = builder.executionAttributes().build();
62+
this.executionAttributes = new UnmodifiableExecutionAttributes(builder.executionAttributes().build());
6263
}
6364

6465
/**

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import software.amazon.awssdk.core.interceptor.ExecutionAttribute;
2929
import software.amazon.awssdk.core.interceptor.ExecutionAttributes;
3030
import software.amazon.awssdk.core.interceptor.ExecutionInterceptor;
31+
import software.amazon.awssdk.core.interceptor.UnmodifiableExecutionAttributes;
3132
import software.amazon.awssdk.core.retry.RetryMode;
3233
import software.amazon.awssdk.core.retry.RetryPolicy;
3334
import software.amazon.awssdk.core.sync.ResponseTransformer;
@@ -74,7 +75,7 @@ private ClientOverrideConfiguration(Builder builder) {
7475
this.defaultProfileFile = builder.defaultProfileFile();
7576
this.defaultProfileName = builder.defaultProfileName();
7677
this.metricPublishers = Collections.unmodifiableList(new ArrayList<>(builder.metricPublishers()));
77-
this.executionAttributes = builder.executionAttributes().build();
78+
this.executionAttributes = new UnmodifiableExecutionAttributes(builder.executionAttributes().build());
7879
}
7980

8081
@Override

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

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
package software.amazon.awssdk.core.interceptor;
1717

1818
import software.amazon.awssdk.annotations.SdkPublicApi;
19+
import software.amazon.awssdk.core.RequestOverrideConfiguration;
20+
21+
import java.util.Objects;
1922

2023
/**
2124
* An attribute attached to a particular execution, stored in {@link ExecutionAttributes}.
@@ -57,4 +60,32 @@ public ExecutionAttribute(String name) {
5760
public String toString() {
5861
return name;
5962
}
63+
64+
/**
65+
* This override considers execution attributes with the same name
66+
* to be the same object for the purpose of attribute merge.
67+
* @return boolean indicating whether the objects are equal or not.
68+
*/
69+
@Override
70+
public boolean equals(Object o) {
71+
if (this == o) {
72+
return true;
73+
}
74+
if (o == null || getClass() != o.getClass()) {
75+
return false;
76+
}
77+
78+
ExecutionAttribute that = (ExecutionAttribute) o;
79+
return that.name.equals(this.name);
80+
}
81+
82+
/**
83+
* This override considers execution attributes with the same name
84+
* to be the same object for the purpose of attribute merge.
85+
* @return hash code
86+
*/
87+
@Override
88+
public int hashCode() {
89+
return Objects.hashCode(name);
90+
}
6091
}

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

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,14 @@
3131
*/
3232
@SdkPublicApi
3333
@NotThreadSafe
34-
public final class ExecutionAttributes implements ToCopyableBuilder<ExecutionAttributes.Builder, ExecutionAttributes> {
34+
public class ExecutionAttributes implements ToCopyableBuilder<ExecutionAttributes.Builder, ExecutionAttributes> {
3535
private final Map<ExecutionAttribute<?>, Object> attributes;
3636

3737
public ExecutionAttributes() {
3838
this.attributes = new HashMap<>();
3939
}
4040

41-
private ExecutionAttributes(Map<? extends ExecutionAttribute<?>, ?> attributes) {
41+
protected ExecutionAttributes(Map<? extends ExecutionAttribute<?>, ?> attributes) {
4242
this.attributes = new HashMap<>(attributes);
4343
}
4444

@@ -66,20 +66,13 @@ public <U> ExecutionAttributes putAttribute(ExecutionAttribute<U> attribute, U v
6666
return this;
6767
}
6868

69-
/**
70-
* Add all execution attributes from the provided collection to the internal collection of attributes.
71-
*/
72-
public <U> ExecutionAttributes putAllAttributes(Map<ExecutionAttribute<?>, ?> executionAttributes) {
73-
this.attributes.putAll(executionAttributes);
74-
return this;
75-
}
76-
7769
/**
7870
* Merge attributes of a lower precedence into the current higher precedence current collection.
7971
*/
80-
public <U> ExecutionAttributes merge(ExecutionAttributes lowerPrecedenceExecutionAttributes) {
81-
lowerPrecedenceExecutionAttributes.getAttributes().forEach(this.attributes::putIfAbsent);
82-
return this;
72+
public ExecutionAttributes merge(ExecutionAttributes lowerPrecedenceExecutionAttributes) {
73+
Map<ExecutionAttribute<?>, Object> copiedAttributes = new HashMap<>(this.attributes);
74+
lowerPrecedenceExecutionAttributes.getAttributes().forEach(copiedAttributes::putIfAbsent);
75+
return new ExecutionAttributes(copiedAttributes);
8376
}
8477

8578
/**
@@ -110,7 +103,7 @@ public static final class Builder implements CopyableBuilder<ExecutionAttributes
110103
private Builder() {
111104
}
112105

113-
@SuppressWarnings("unchecked") // Cast is safe due to implementation of {@link #put}
106+
114107
public <T> T get(ExecutionAttribute<T> key) {
115108
Validate.notNull(key, "Key to retrieve must not be null.");
116109
return (T) executionAttributes.get(key);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
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.interceptor;
17+
18+
public class UnmodifiableExecutionAttributes extends ExecutionAttributes {
19+
public UnmodifiableExecutionAttributes(ExecutionAttributes executionAttributes) {
20+
super(executionAttributes.getAttributes());
21+
}
22+
23+
@Override
24+
public <U> ExecutionAttributes putAttribute(ExecutionAttribute<U> attribute, U value) {
25+
throw new UnsupportedOperationException();
26+
}
27+
28+
@Override
29+
public <U> ExecutionAttributes putAttributeIfAbsent(ExecutionAttribute<U> attribute, U value) {
30+
throw new UnsupportedOperationException();
31+
}
32+
}

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818

1919
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.junit.Assert.fail;
2021
import static org.mockito.Mockito.mock;
2122

2223
import java.util.ArrayList;
@@ -26,6 +27,7 @@
2627
import java.util.List;
2728
import java.util.Map;
2829
import org.junit.Test;
30+
import software.amazon.awssdk.core.client.config.ClientOverrideConfiguration;
2931
import software.amazon.awssdk.core.interceptor.ExecutionAttribute;
3032
import software.amazon.awssdk.metrics.MetricPublisher;
3133
import software.amazon.awssdk.utils.ImmutableMap;
@@ -183,6 +185,25 @@ public void executionAttributes_createsCopy() {
183185
assertThat(overrideConfig.executionAttributes().getAttribute(testAttribute)).isEqualTo(expectedValue);
184186
}
185187

188+
@Test
189+
public void executionAttributes_isImmutable() {
190+
Map<ExecutionAttribute<?>, Object> executionAttributes = new HashMap<>();
191+
192+
ExecutionAttribute testAttribute = new ExecutionAttribute("TestAttribute");
193+
String expectedValue = "Value1";
194+
executionAttributes.put(testAttribute, expectedValue);
195+
196+
SdkRequestOverrideConfiguration overrideConfig = SdkRequestOverrideConfiguration.builder()
197+
.executionAttributes(executionAttributes)
198+
.build();
199+
try {
200+
overrideConfig.executionAttributes().putAttribute(testAttribute, 2);
201+
fail("Expected unsupported operation exception");
202+
} catch(Exception ex) {
203+
assertThat(ex instanceof UnsupportedOperationException).isTrue();
204+
}
205+
}
206+
186207
@Test
187208
public void executionAttributes_maintainsAllAdded() {
188209
Map<ExecutionAttribute, Object> executionAttributeObjectMap = new HashMap<>();

core/sdk-core/src/test/java/software/amazon/awssdk/core/client/config/ClientOverrideConfigurationTest.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@
1616
package software.amazon.awssdk.core.client.config;
1717

1818
import static org.assertj.core.api.Assertions.assertThat;
19-
import static org.junit.Assert.assertFalse;
20-
import static org.junit.Assert.assertTrue;
19+
import static org.junit.Assert.*;
2120
import static org.mockito.Mockito.mock;
2221

2322
import java.util.ArrayList;
@@ -198,6 +197,26 @@ public void executionAttributes_createsCopy() {
198197
assertThat(overrideConfig.executionAttributes().getAttribute(testAttribute)).isEqualTo(expectedValue);
199198
}
200199

200+
@Test
201+
public void executionAttributes_isImmutable() {
202+
Map<ExecutionAttribute<?>, Object> executionAttributes = new HashMap<>();
203+
204+
ExecutionAttribute testAttribute = new ExecutionAttribute("TestAttribute");
205+
String expectedValue = "Value1";
206+
executionAttributes.put(testAttribute, expectedValue);
207+
208+
ClientOverrideConfiguration overrideConfig = ClientOverrideConfiguration.builder()
209+
.executionAttributes(executionAttributes)
210+
.build();
211+
212+
try {
213+
overrideConfig.executionAttributes().putAttribute(testAttribute, 2);
214+
fail("Expected unsupported operation exception");
215+
} catch(Exception ex) {
216+
assertThat(ex instanceof UnsupportedOperationException).isTrue();
217+
}
218+
}
219+
201220
@Test
202221
public void executionAttributes_maintainsAllAdded() {
203222
Map<ExecutionAttribute, Object> executionAttributeObjectMap = new HashMap<>();

test/codegen-generated-classes-test/src/test/java/software/amazon/awssdk/services/ExecutionAttributesTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,29 @@ public void asyncClient_requestOverrideExecutionAttributesHavePrecedence() {
149149
async.close();
150150
}
151151
}
152+
153+
@Test
154+
public void testExecutionAttributesMerge() {
155+
ExecutionAttributes lowerPrecedence = new ExecutionAttributes();
156+
for (int i = 0; i < 3; i++) {
157+
lowerPrecedence.putAttribute(getAttribute(i), 1);
158+
}
159+
160+
ExecutionAttributes higherPrecendence = new ExecutionAttributes();
161+
for (int i = 2; i < 4; i++) {
162+
higherPrecendence.putAttribute(getAttribute(i), 2);
163+
}
164+
165+
ExecutionAttributes expectedAttributes = new ExecutionAttributes();
166+
for (int i = 0; i < 4; i++) {
167+
expectedAttributes.putAttribute(getAttribute(i), i >= 2 ? 2 : 1);
168+
}
169+
170+
ExecutionAttributes mergedAttributes = higherPrecendence.merge(lowerPrecedence);
171+
assertThat(mergedAttributes.getAttributes()).isEqualTo(expectedAttributes.getAttributes());
172+
}
173+
174+
private ExecutionAttribute getAttribute(int i) {
175+
return new ExecutionAttribute<>("TestAttribute" + i);
176+
}
152177
}

0 commit comments

Comments
 (0)