11/*
2- * Copyright 2019 the original author or authors.
2+ * Copyright 2019-2022 the original author or authors.
33 *
44 * Licensed under the Apache License, Version 2.0 (the "License");
55 * you may not use this file except in compliance with the License.
1515 */
1616package com .dasburo .spring .cache .dynamo ;
1717
18- import com .amazonaws .services .dynamodbv2 .AmazonDynamoDB ;
19- import com .amazonaws .services .dynamodbv2 .model .AttributeDefinition ;
20- import com .amazonaws .services .dynamodbv2 .model .AttributeValue ;
21- import com .amazonaws .services .dynamodbv2 .model .CreateTableRequest ;
22- import com .amazonaws .services .dynamodbv2 .model .DeleteItemRequest ;
23- import com .amazonaws .services .dynamodbv2 .model .GetItemRequest ;
24- import com .amazonaws .services .dynamodbv2 .model .GetItemResult ;
25- import com .amazonaws .services .dynamodbv2 .model .KeySchemaElement ;
26- import com .amazonaws .services .dynamodbv2 .model .KeyType ;
27- import com .amazonaws .services .dynamodbv2 .model .ProvisionedThroughput ;
28- import com .amazonaws .services .dynamodbv2 .model .PutItemRequest ;
29- import com .amazonaws .services .dynamodbv2 .model .ResourceNotFoundException ;
30- import com .amazonaws .services .dynamodbv2 .model .ScalarAttributeType ;
31- import com .amazonaws .services .dynamodbv2 .model .ScanRequest ;
32- import com .amazonaws .services .dynamodbv2 .model .TimeToLiveSpecification ;
33- import com .amazonaws .services .dynamodbv2 .model .UpdateTimeToLiveRequest ;
34- import com .amazonaws .services .dynamodbv2 .util .TableUtils ;
3518import com .dasburo .spring .cache .dynamo .rootattribute .RootAttribute ;
36- import com .dasburo .spring .cache .dynamo .util .ByteUtils ;
19+ import com .dasburo .spring .cache .dynamo .util .TableUtils ;
3720import org .springframework .dao .PessimisticLockingFailureException ;
3821import org .springframework .lang .Nullable ;
3922import org .springframework .util .Assert ;
23+ import software .amazon .awssdk .core .SdkBytes ;
24+ import software .amazon .awssdk .services .dynamodb .DynamoDbClient ;
25+ import software .amazon .awssdk .services .dynamodb .model .AttributeDefinition ;
26+ import software .amazon .awssdk .services .dynamodb .model .AttributeValue ;
27+ import software .amazon .awssdk .services .dynamodb .model .CreateTableRequest ;
28+ import software .amazon .awssdk .services .dynamodb .model .DeleteItemRequest ;
29+ import software .amazon .awssdk .services .dynamodb .model .DescribeTableRequest ;
30+ import software .amazon .awssdk .services .dynamodb .model .GetItemRequest ;
31+ import software .amazon .awssdk .services .dynamodb .model .GetItemResponse ;
32+ import software .amazon .awssdk .services .dynamodb .model .KeySchemaElement ;
33+ import software .amazon .awssdk .services .dynamodb .model .KeyType ;
34+ import software .amazon .awssdk .services .dynamodb .model .ProvisionedThroughput ;
35+ import software .amazon .awssdk .services .dynamodb .model .PutItemRequest ;
36+ import software .amazon .awssdk .services .dynamodb .model .ResourceNotFoundException ;
37+ import software .amazon .awssdk .services .dynamodb .model .TimeToLiveSpecification ;
38+ import software .amazon .awssdk .services .dynamodb .model .UpdateTimeToLiveRequest ;
4039
41- import java .nio .ByteBuffer ;
4240import java .time .Duration ;
4341import java .time .Instant ;
44- import java .util .ArrayList ;
4542import java .util .Collections ;
4643import java .util .HashMap ;
4744import java .util .List ;
4845import java .util .Map ;
4946import java .util .NoSuchElementException ;
47+ import java .util .Objects ;
5048import java .util .function .Function ;
5149
50+ import static software .amazon .awssdk .services .dynamodb .model .ScalarAttributeType .S ;
51+
5252/**
5353 * {@link DynamoCacheWriter} implementation capable of reading/writing binary data from/to DynamoDB in {@literal standalone}
54- * and {@literal cluster} environments. Works upon a given {@link AmazonDynamoDB } holds the actual connection.
54+ * and {@literal cluster} environments. Works upon a given {@link DynamoDbClient } holds the actual connection.
5555 * <p>
5656 * {@link DefaultDynamoCacheWriter} can be used in
57- * {@link DynamoCacheWriter#lockingDynamoCacheWriter(AmazonDynamoDB ) locking} or
58- * {@link DynamoCacheWriter#nonLockingDynamoCacheWriter(AmazonDynamoDB ) non-locking} mode. While
59- * {@literal non-locking} aims for maximum performance it may result in overlapping, non atomic, command execution for
57+ * {@link DynamoCacheWriter#lockingDynamoCacheWriter(DynamoDbClient ) locking} or
58+ * {@link DynamoCacheWriter#nonLockingDynamoCacheWriter(DynamoDbClient ) non-locking} mode. While
59+ * {@literal non-locking} aims for maximum performance it may result in overlapping, non- atomic, command execution for
6060 * operations spanning multiple DynamoDB interactions like {@code putIfAbsent}. The {@literal locking} counterpart prevents
6161 * command overlap by setting an explicit lock key and checking against presence of this key which leads to additional
6262 * requests and potential command wait times.
@@ -69,13 +69,13 @@ public class DefaultDynamoCacheWriter implements DynamoCacheWriter {
6969 public static final String ATTRIBUTE_VALUE = "value" ;
7070 public static final String ATTRIBUTE_TTL = "ttl" ;
7171
72- private final AmazonDynamoDB dynamoTemplate ;
72+ private final DynamoDbClient dynamoTemplate ;
7373 private final Duration sleepTime ;
7474
7575 /**
7676 * @param dynamoTemplate must not be {@literal null}.
7777 */
78- DefaultDynamoCacheWriter (AmazonDynamoDB dynamoTemplate ) {
78+ DefaultDynamoCacheWriter (DynamoDbClient dynamoTemplate ) {
7979 this (dynamoTemplate , Duration .ZERO );
8080 }
8181
@@ -84,7 +84,7 @@ public class DefaultDynamoCacheWriter implements DynamoCacheWriter {
8484 * @param sleepTime sleep time between lock request attempts. Must not be {@literal null}. Use {@link Duration#ZERO}
8585 * to disable locking.
8686 */
87- DefaultDynamoCacheWriter (AmazonDynamoDB dynamoTemplate , Duration sleepTime ) {
87+ DefaultDynamoCacheWriter (DynamoDbClient dynamoTemplate , Duration sleepTime ) {
8888 Assert .notNull (dynamoTemplate , "ConnectionFactory must not be null!" );
8989 Assert .notNull (sleepTime , "SleepTime must not be null!" );
9090
@@ -93,7 +93,7 @@ public class DefaultDynamoCacheWriter implements DynamoCacheWriter {
9393 }
9494
9595 @ Override
96- public AmazonDynamoDB getNativeCacheWriter () {
96+ public DynamoDbClient getNativeCacheWriter () {
9797 return dynamoTemplate ;
9898 }
9999
@@ -157,23 +157,24 @@ public void clear(String name) {
157157 Assert .notNull (name , "Name must not be null!" );
158158
159159 execute (name , connection -> {
160-
161160 try {
162161 if (isLockingCacheWriter ()) {
163162 doLock (name );
164163 }
165164
166- ScanRequest req = new ScanRequest (name );
167- List <Map <String , AttributeValue >> items = dynamoTemplate .scan (req ).getItems ();
165+ List <Map <String , AttributeValue >> items = dynamoTemplate .scan (req -> req .tableName (name )).items ();
168166
169167 items .parallelStream ()
170168 .forEach (map -> {
171169 Map <String , AttributeValue > keyToDelete = new HashMap <>();
172170 keyToDelete .put (ATTRIBUTE_KEY , map .get (ATTRIBUTE_KEY ));
173- DeleteItemRequest delReq = new DeleteItemRequest (name , keyToDelete );
171+ DeleteItemRequest delReq = DeleteItemRequest .builder ()
172+ .tableName (name )
173+ .key (keyToDelete )
174+ .build ();
174175 dynamoTemplate .deleteItem (delReq );
175176 });
176- } catch (ResourceNotFoundException ignored ) {
177+ } catch (ResourceNotFoundException ignored ) {
177178 // ignore table not found
178179 } finally {
179180 if (isLockingCacheWriter ()) {
@@ -191,8 +192,10 @@ public boolean createIfNotExists(String name, Duration ttl, Long readCapacityUni
191192
192193 boolean created = false ;
193194 try {
194- dynamoTemplate .describeTable (name );
195- } catch (ResourceNotFoundException e ) {
195+ dynamoTemplate .describeTable (DescribeTableRequest .builder ()
196+ .tableName (name )
197+ .build ());
198+ } catch (ResourceNotFoundException e ) {
196199 created = TableUtils .createTableIfNotExists (dynamoTemplate , createTableRequest (name , readCapacityUnits , writeCapacityUnits ));
197200 if (created && !ttl .isZero ()) {
198201 dynamoTemplate .updateTimeToLive (updateTimeToLiveRequest (name ));
@@ -202,62 +205,65 @@ public boolean createIfNotExists(String name, Duration ttl, Long readCapacityUni
202205 }
203206
204207 private byte [] getInternal (String name , String key ) {
205- final GetItemRequest request = new GetItemRequest ()
206- .withAttributesToGet (ATTRIBUTE_VALUE )
207- .withTableName (name )
208- .withKey (Collections .singletonMap (ATTRIBUTE_KEY , new AttributeValue (key )));
209-
210- final GetItemResult result = dynamoTemplate .getItem (request );
211- if (result .getItem () != null ) {
208+ final GetItemRequest request = GetItemRequest .builder ()
209+ .attributesToGet (ATTRIBUTE_VALUE )
210+ .tableName (name )
211+ .key (Collections .singletonMap (ATTRIBUTE_KEY , AttributeValue .fromS (key )))
212+ .build ();
213+
214+ final GetItemResponse result = dynamoTemplate .getItem (request );
215+ if (result .hasItem ()) {
212216 return getAttributeValue (result );
213217 } else {
214218 throw new NoSuchElementException (String .format ("No entry found for '%s'." , key ));
215219 }
216220 }
217221
218- private byte [] getAttributeValue (GetItemResult result ) {
219- final AttributeValue attribute = result .getItem ().get (ATTRIBUTE_VALUE );
222+ private byte [] getAttributeValue (GetItemResponse result ) {
223+ final AttributeValue attribute = result .item ().get (ATTRIBUTE_VALUE );
220224 if (attribute == null ) {
221225 throw new IllegalStateException (String .format ("Attribute value does not match the expected '%s'." , ATTRIBUTE_VALUE ));
222226 }
223227
224- ByteBuffer element = attribute .getB ();
225- if (element == null && attribute .getNULL ()) {
228+ SdkBytes element = attribute .b ();
229+ if (element == null && attribute .nul ()) {
226230 // TODO to return null is bad style, but how to distinct between null value from getInternal and no entry at all?
227231 return null ;
228232 } else {
229- return ByteUtils . getBytes (element );
233+ return Objects . requireNonNull (element ). asByteArray ( );
230234 }
231235 }
232236
233237 private void putInternal (String name , String key , @ Nullable byte [] value , @ Nullable Duration ttl , @ Nullable List <RootAttribute > rootAttributes ) {
234238 Map <String , AttributeValue > attributeValues = new HashMap <>();
235- attributeValues .put (ATTRIBUTE_KEY , new AttributeValue (). withS (key ));
239+ attributeValues .put (ATTRIBUTE_KEY , AttributeValue . fromS (key ));
236240
237241 if (value == null ) {
238- attributeValues .put (ATTRIBUTE_VALUE , new AttributeValue (). withNULL (true ));
242+ attributeValues .put (ATTRIBUTE_VALUE , AttributeValue . fromNul (true ));
239243 } else {
240- attributeValues .put (ATTRIBUTE_VALUE , new AttributeValue (). withB ( ByteBuffer . wrap (value )));
244+ attributeValues .put (ATTRIBUTE_VALUE , AttributeValue . fromB ( SdkBytes . fromByteArray (value )));
241245 }
242246
243247 if (shouldExpireWithin (ttl )) {
244- attributeValues .put (ATTRIBUTE_TTL , new AttributeValue (). withN (String .valueOf (Instant .now ().plus (ttl ).getEpochSecond ())));
248+ attributeValues .put (ATTRIBUTE_TTL , AttributeValue . fromN (String .valueOf (Instant .now ().plus (ttl ).getEpochSecond ())));
245249 }
246250
247251 if (rootAttributes != null ) {
248252 rootAttributes .forEach (rootAttribute -> attributeValues .put (rootAttribute .getName (), rootAttribute .getAttributeValue ()));
249253 }
250254
251- PutItemRequest putItemRequest = new PutItemRequest ()
252- .withTableName (name )
253- .withItem (attributeValues );
255+ PutItemRequest putItemRequest = PutItemRequest .builder ()
256+ .tableName (name )
257+ .item (attributeValues )
258+ .build ();
254259 dynamoTemplate .putItem (putItemRequest );
255260 }
256261
257262 private void removeInternal (String name , String key ) {
258- dynamoTemplate .deleteItem (new DeleteItemRequest ()
259- .withTableName (name )
260- .withKey (Collections .singletonMap (ATTRIBUTE_KEY , new AttributeValue (key ))));
263+ dynamoTemplate .deleteItem (DeleteItemRequest .builder ()
264+ .tableName (name )
265+ .key (Collections .singletonMap (ATTRIBUTE_KEY , AttributeValue .fromS (key )))
266+ .build ());
261267 }
262268
263269 private void doLock (String name ) {
@@ -289,7 +295,7 @@ private boolean isLockingCacheWriter() {
289295 return !sleepTime .isZero () && !sleepTime .isNegative ();
290296 }
291297
292- private <T > T execute (String name , Function <AmazonDynamoDB , T > callback ) {
298+ private <T > T execute (String name , Function <DynamoDbClient , T > callback ) {
293299 checkAndPotentiallyWaitUntilUnlocked (name );
294300 return callback .apply (dynamoTemplate );
295301 }
@@ -321,25 +327,31 @@ private static String createCacheLockKey(String name) {
321327 }
322328
323329 private CreateTableRequest createTableRequest (String name , Long readCapacityUnits , Long writeCapacityUnits ) {
324- List <AttributeDefinition > attributeDefinitions = new ArrayList <>();
325- attributeDefinitions .add (new AttributeDefinition ().withAttributeName (ATTRIBUTE_KEY ).withAttributeType (ScalarAttributeType .S ));
326-
327- List <KeySchemaElement > keySchema = new ArrayList <>();
328- keySchema .add (new KeySchemaElement ().withAttributeName (ATTRIBUTE_KEY ).withKeyType (KeyType .HASH ));
329-
330- return new CreateTableRequest ()
331- .withTableName (name )
332- .withKeySchema (keySchema )
333- .withAttributeDefinitions (attributeDefinitions )
334- .withProvisionedThroughput (new ProvisionedThroughput (readCapacityUnits , writeCapacityUnits ));
330+ return CreateTableRequest .builder ()
331+ .tableName (name )
332+ .attributeDefinitions (AttributeDefinition .builder ()
333+ .attributeName (ATTRIBUTE_KEY )
334+ .attributeType (S )
335+ .build ())
336+ .keySchema (KeySchemaElement .builder ()
337+ .attributeName (ATTRIBUTE_KEY )
338+ .keyType (KeyType .HASH )
339+ .build ())
340+ .provisionedThroughput (ProvisionedThroughput .builder ()
341+ .readCapacityUnits (readCapacityUnits )
342+ .writeCapacityUnits (writeCapacityUnits )
343+ .build ())
344+ .build ();
335345 }
336346
337347 // TODO to be tested (not implemented in AmazonDynamoDB local)
338348 private UpdateTimeToLiveRequest updateTimeToLiveRequest (String name ) {
339- return new UpdateTimeToLiveRequest ()
340- .withTableName (name )
341- .withTimeToLiveSpecification (new TimeToLiveSpecification ()
342- .withEnabled (true )
343- .withAttributeName (ATTRIBUTE_TTL ));
349+ return UpdateTimeToLiveRequest .builder ()
350+ .tableName (name )
351+ .timeToLiveSpecification (TimeToLiveSpecification .builder ()
352+ .enabled (true )
353+ .attributeName (ATTRIBUTE_TTL )
354+ .build ())
355+ .build ();
344356 }
345357}
0 commit comments