Skip to content

Commit d93d373

Browse files
authored
Log errors instead of throwing errors for duplicate keys in ImmutableMap (#6316)
* Log errors instead of throwing errors for duplicate keys in ImmutableMap * Add allowDuplicateKeys to ImmutableMap Builder * Fix architecture tests * Update from Warn to Debug logging * Revert whitespace deletion
1 parent 6efc7e6 commit d93d373

File tree

6 files changed

+54
-7
lines changed

6 files changed

+54
-7
lines changed
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "feature",
3+
"category": "AWS SDK for Java v2",
4+
"contributor": "",
5+
"description": "Add allowDuplicateKeys to ImmutableMap Builder"
6+
}

codegen-lite/src/main/java/software/amazon/awssdk/codegen/lite/regions/ServiceMetadataGenerator.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -257,7 +257,7 @@ private CodeBlock dnsSuffixesByPartition(Partitions partitions) {
257257
private CodeBlock hostnamesByRegion(Partitions partitions) {
258258
Map<Partition, Service> services = getServiceData(partitions);
259259

260-
CodeBlock.Builder builder = CodeBlock.builder().add("$T.<$T, $T>builder()",
260+
CodeBlock.Builder builder = CodeBlock.builder().add("$T.<$T, $T>builder().allowDuplicateKeys(true)",
261261
ImmutableMap.class, serviceEndpointKeyClass(), String.class);
262262

263263
services.forEach((partition, service) -> {

codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/s3-service-metadata.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ public final class S3ServiceMetadata implements ServiceMetadata {
6969

7070
private static final Map<ServiceEndpointKey, String> HOSTNAMES_BY_REGION = ImmutableMap
7171
.<ServiceEndpointKey, String> builder()
72+
.allowDuplicateKeys(true)
7273
.put(ServiceEndpointKey.builder().region(Region.of("af-south-1")).tags(EndpointTag.of("dualstack")).build(),
7374
"s3.dualstack.af-south-1.amazonaws.com")
7475
.put(ServiceEndpointKey.builder().region(Region.of("ap-east-1")).tags(EndpointTag.of("dualstack")).build(),

codegen-lite/src/test/resources/software/amazon/awssdk/codegen/lite/regions/sts-service-metadata.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ public final class StsServiceMetadata implements ServiceMetadata {
6060

6161
private static final Map<ServiceEndpointKey, String> HOSTNAMES_BY_REGION = ImmutableMap
6262
.<ServiceEndpointKey, String> builder()
63+
.allowDuplicateKeys(true)
6364
.put(ServiceEndpointKey.builder().region(Region.of("aws-global")).build(), "sts.amazonaws.com")
6465
.put(ServiceEndpointKey.builder().region(Region.of("us-east-1")).tags(EndpointTag.of("fips")).build(),
6566
"sts-fips.us-east-1.amazonaws.com")

utils/src/main/java/software/amazon/awssdk/utils/ImmutableMap.java

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
@SdkProtectedApi
5959
public final class ImmutableMap<K, V> implements Map<K, V> {
6060

61+
private static final Logger log = Logger.loggerFor(ImmutableMap.class);
6162
private static final String UNMODIFIABLE_MESSAGE = "This is an immutable map.";
6263
private static final String DUPLICATED_KEY_MESSAGE = "Duplicate keys are provided.";
6364

@@ -198,8 +199,18 @@ public static <K, V> ImmutableMap<K, V> of(K k0, V v0, K k1, V v1,
198199

199200
private static <K, V> void putAndWarnDuplicateKeys(Map<K, V> map, K key,
200201
V value) {
202+
putAndWarnDuplicateKeys(map, key, value, false);
203+
}
204+
205+
private static <K, V> void putAndWarnDuplicateKeys(Map<K, V> map, K key, V value, boolean allowDuplicateKeys) {
201206
if (map.containsKey(key)) {
202-
throw new IllegalArgumentException(DUPLICATED_KEY_MESSAGE);
207+
208+
if (allowDuplicateKeys) {
209+
log.debug(() -> String.format("Duplicate keys are provided for [%s]. The newer value [%s] will be saved, and the "
210+
+ "existing value [%s] will be overwritten.", key, value, map.get(key)));
211+
} else {
212+
throw new IllegalArgumentException(DUPLICATED_KEY_MESSAGE);
213+
}
203214
}
204215
map.put(key, value);
205216
}
@@ -289,6 +300,7 @@ public String toString() {
289300
public static class Builder<K, V> {
290301

291302
private final Map<K, V> entries;
303+
private boolean allowDuplicateKeys;
292304

293305
public Builder() {
294306
this.entries = new HashMap<>();
@@ -297,13 +309,29 @@ public Builder() {
297309
/**
298310
* Add a key-value pair into the built map. The method will throw
299311
* IllegalArgumentException immediately when duplicate keys are
300-
* provided.
312+
* provided and allowDuplicateKeys is false (default value).
313+
*
314+
*
315+
* If duplicate keys are provided and allowDuplicateKeys is true, the latest value will overwrite the existing value, and
316+
* the SDK will log a message at WARN level.
301317
*
302318
* @return Returns a reference to this object so that method calls can
303319
* be chained together.
304320
*/
305321
public Builder<K, V> put(K key, V value) {
306-
putAndWarnDuplicateKeys(entries, key, value);
322+
putAndWarnDuplicateKeys(entries, key, value, allowDuplicateKeys);
323+
return this;
324+
}
325+
326+
/**
327+
* Sets whether duplicate keys are allowed. If true, the latest value will overwrite the existing value. If
328+
* false, an error will be thrown when attempting to put an entry with a duplicate key. Default value is false.
329+
*
330+
* @return Returns a reference to this object so that method calls can
331+
* be chained together.
332+
*/
333+
public Builder<K, V> allowDuplicateKeys(boolean allowDuplicateKeys) {
334+
this.allowDuplicateKeys = allowDuplicateKeys;
307335
return this;
308336
}
309337

utils/src/test/java/software/amazon/awssdk/utils/ImmutableMapTest.java

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ public void testOfBuilder() {
7474
public void testErrorOnDuplicateKeys() {
7575
try {
7676
Map<Integer, String> builtMap = new ImmutableMap.Builder<Integer, String>()
77-
.put(1, "one")
78-
.put(1, "two")
79-
.build();
77+
.put(1, "one")
78+
.put(1, "two")
79+
.build();
8080
fail("IllegalArgumentException expected.");
8181
} catch (IllegalArgumentException iae) {
8282
// Ignored or expected.
@@ -85,6 +85,17 @@ public void testErrorOnDuplicateKeys() {
8585
}
8686
}
8787

88+
@Test
89+
public void putDuplicateKeys_allowDuplicateKeysTrue_doesNotThrowErrorAndKeepsNewestValue() {
90+
Map<Integer, String> builtMap = new ImmutableMap.Builder<Integer, String>()
91+
.allowDuplicateKeys(true)
92+
.put(1, "one")
93+
.put(1, "two")
94+
.build();
95+
96+
assertEquals("two", builtMap.get(1));
97+
}
98+
8899
@Test
89100
public void testMapOperations() {
90101
Map<Integer, String> builtMap = new ImmutableMap.Builder<Integer, String>()

0 commit comments

Comments
 (0)