From 1a81c450e00cbe32aeb296b9409ab91ae2b9bcf9 Mon Sep 17 00:00:00 2001 From: Diaz Novandi Date: Tue, 27 Mar 2018 07:26:42 +0200 Subject: [PATCH] Add the non-Spring DynamoDB samples in the AWS module --- aws/pom.xml | 38 ++++++ .../baeldung/dynamodb/entity/ProductInfo.java | 51 ++++++++ .../repository/AbstractRepository.java | 49 +++++++ .../repository/ProductInfoRepository.java | 6 + .../ProductInfoRepositoryIntegrationTest.java | 121 ++++++++++++++++++ .../dynamodb/rule/LocalDbCreationRule.java | 35 +++++ aws/src/test/resources/test.properties | 3 + 7 files changed, 303 insertions(+) create mode 100644 aws/src/main/java/com/baeldung/dynamodb/entity/ProductInfo.java create mode 100644 aws/src/main/java/com/baeldung/dynamodb/repository/AbstractRepository.java create mode 100644 aws/src/main/java/com/baeldung/dynamodb/repository/ProductInfoRepository.java create mode 100644 aws/src/test/java/com/baeldung/dynamodb/ProductInfoRepositoryIntegrationTest.java create mode 100644 aws/src/test/java/com/baeldung/dynamodb/rule/LocalDbCreationRule.java create mode 100644 aws/src/test/resources/test.properties diff --git a/aws/pom.xml b/aws/pom.xml index 33fc3b0cba..c62b1b9913 100644 --- a/aws/pom.xml +++ b/aws/pom.xml @@ -22,6 +22,8 @@ 4.12 2.8.9 3.8.0 + 1.11.86 + https://s3-us-west-2.amazonaws.com/dynamodb-local/release @@ -88,6 +90,13 @@ gson ${gson.version} + + + com.amazonaws + DynamoDBLocal + ${dynamodblocal.version} + test + @@ -108,6 +117,35 @@ + + + org.apache.maven.plugins + maven-dependency-plugin + 2.10 + + + copy-dependencies + test-compile + + copy-dependencies + + + test + so,dll,dylib + ${project.basedir}/native-libs + + + + + + + + dynamodb-local + DynamoDB Local Release Repository + ${dynamodblocal.repository.url} + + + \ No newline at end of file diff --git a/aws/src/main/java/com/baeldung/dynamodb/entity/ProductInfo.java b/aws/src/main/java/com/baeldung/dynamodb/entity/ProductInfo.java new file mode 100644 index 0000000000..9af5d926c9 --- /dev/null +++ b/aws/src/main/java/com/baeldung/dynamodb/entity/ProductInfo.java @@ -0,0 +1,51 @@ +package com.baeldung.dynamodb.entity; + +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAutoGeneratedKey; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable; + +@DynamoDBTable(tableName = "ProductInfo") +public class ProductInfo { + + private String id; + private String msrp; + private String cost; + + public ProductInfo() { + } + + public ProductInfo(String cost, String msrp) { + this.msrp = msrp; + this.cost = cost; + } + + @DynamoDBHashKey + @DynamoDBAutoGeneratedKey + public String getId() { + return id; + } + + @DynamoDBAttribute + public String getMsrp() { + return msrp; + } + + @DynamoDBAttribute + public String getCost() { + return cost; + } + + public void setId(String id) { + this.id = id; + } + + public void setMsrp(String msrp) { + this.msrp = msrp; + } + + public void setCost(String cost) { + this.cost = cost; + } + +} diff --git a/aws/src/main/java/com/baeldung/dynamodb/repository/AbstractRepository.java b/aws/src/main/java/com/baeldung/dynamodb/repository/AbstractRepository.java new file mode 100644 index 0000000000..79934c17e9 --- /dev/null +++ b/aws/src/main/java/com/baeldung/dynamodb/repository/AbstractRepository.java @@ -0,0 +1,49 @@ +package com.baeldung.dynamodb.repository; + +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression; + +import java.io.Serializable; +import java.lang.reflect.ParameterizedType; +import java.util.List; + +public abstract class AbstractRepository { + + protected DynamoDBMapper mapper; + protected Class entityClass; + + protected AbstractRepository() { + ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass(); + + // This entityClass refers to the actual entity class in the subclass declaration. + + // For instance, ProductInfoDAO extends AbstractDAO + // In this case entityClass = ProductInfo, and ID is String type + // which refers to the ProductInfo's partition key string value + this.entityClass = (Class) genericSuperclass.getActualTypeArguments()[0]; + } + + public void save(T t) { + mapper.save(t); + } + + public T findOne(ID id) { + return mapper.load(entityClass, id); + } + + /** + * WARNING: It is not recommended to perform full table scan + * targeting the real production environment. + * + * @return All items + */ + public List findAll() { + DynamoDBScanExpression scanExpression = new DynamoDBScanExpression(); + return mapper.scan(entityClass, scanExpression); + } + + public void setMapper(DynamoDBMapper dynamoDBMapper) { + this.mapper = dynamoDBMapper; + } + +} diff --git a/aws/src/main/java/com/baeldung/dynamodb/repository/ProductInfoRepository.java b/aws/src/main/java/com/baeldung/dynamodb/repository/ProductInfoRepository.java new file mode 100644 index 0000000000..0dfec8e05c --- /dev/null +++ b/aws/src/main/java/com/baeldung/dynamodb/repository/ProductInfoRepository.java @@ -0,0 +1,6 @@ +package com.baeldung.dynamodb.repository; + +import com.baeldung.dynamodb.entity.ProductInfo; + +public class ProductInfoRepository extends AbstractRepository { +} diff --git a/aws/src/test/java/com/baeldung/dynamodb/ProductInfoRepositoryIntegrationTest.java b/aws/src/test/java/com/baeldung/dynamodb/ProductInfoRepositoryIntegrationTest.java new file mode 100644 index 0000000000..929a985067 --- /dev/null +++ b/aws/src/test/java/com/baeldung/dynamodb/ProductInfoRepositoryIntegrationTest.java @@ -0,0 +1,121 @@ +package com.baeldung.dynamodb; + +import com.amazonaws.auth.BasicAWSCredentials; +import com.amazonaws.services.dynamodbv2.AmazonDynamoDB; +import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient; +import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper; +import com.amazonaws.services.dynamodbv2.model.CreateTableRequest; +import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput; +import com.amazonaws.services.dynamodbv2.model.ResourceInUseException; +import com.baeldung.dynamodb.entity.ProductInfo; +import com.baeldung.dynamodb.repository.ProductInfoRepository; +import com.baeldung.dynamodb.rule.LocalDbCreationRule; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Optional; +import java.util.Properties; + +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; + +public class ProductInfoRepositoryIntegrationTest { + + @ClassRule + public static LocalDbCreationRule dynamoDB = new LocalDbCreationRule(); + + private static DynamoDBMapper dynamoDBMapper; + private static AmazonDynamoDB amazonDynamoDB; + + private ProductInfoRepository repository; + + private static final String DYNAMODB_ENDPOINT = "amazon.dynamodb.endpoint"; + private static final String AWS_ACCESSKEY = "amazon.aws.accesskey"; + private static final String AWS_SECRETKEY = "amazon.aws.secretkey"; + + private static final String EXPECTED_COST = "20"; + private static final String EXPECTED_PRICE = "50"; + + @BeforeClass + public static void setupClass() { + Properties testProperties = loadFromFileInClasspath("test.properties") + .filter(properties -> !isEmpty(properties.getProperty(AWS_ACCESSKEY))) + .filter(properties -> !isEmpty(properties.getProperty(AWS_SECRETKEY))) + .filter(properties -> !isEmpty(properties.getProperty(DYNAMODB_ENDPOINT))) + .orElseThrow(() -> new RuntimeException("Unable to get all of the required test property values")); + + String amazonAWSAccessKey = testProperties.getProperty(AWS_ACCESSKEY); + String amazonAWSSecretKey = testProperties.getProperty(AWS_SECRETKEY); + String amazonDynamoDBEndpoint = testProperties.getProperty(DYNAMODB_ENDPOINT); + + amazonDynamoDB = new AmazonDynamoDBClient(new BasicAWSCredentials(amazonAWSAccessKey, amazonAWSSecretKey)); + amazonDynamoDB.setEndpoint(amazonDynamoDBEndpoint); + dynamoDBMapper = new DynamoDBMapper(amazonDynamoDB); + } + + @Before + public void setup() { + try { + repository = new ProductInfoRepository(); + repository.setMapper(dynamoDBMapper); + + CreateTableRequest tableRequest = dynamoDBMapper.generateCreateTableRequest(ProductInfo.class); + + tableRequest.setProvisionedThroughput(new ProvisionedThroughput(1L, 1L)); + + amazonDynamoDB.createTable(tableRequest); + } catch (ResourceInUseException e) { + // Do nothing, table already created + } + + // TODO How to handle different environments. i.e. AVOID deleting all entries in ProductInfo on table + dynamoDBMapper.batchDelete((List) repository.findAll()); + } + + @Test + public void givenItemWithExpectedCost_whenRunFindAll_thenItemIsFound() { + + ProductInfo productInfo = new ProductInfo(EXPECTED_COST, EXPECTED_PRICE); + repository.save(productInfo); + + List result = (List) repository.findAll(); + assertThat(result.size(), is(greaterThan(0))); + assertThat(result.get(0).getCost(), is(equalTo(EXPECTED_COST))); + } + + private static boolean isEmpty(String inputString) { + return inputString == null || "".equals(inputString); + } + + private static Optional loadFromFileInClasspath(String fileName) { + InputStream stream = null; + try { + Properties config = new Properties(); + Path configLocation = Paths.get(ClassLoader.getSystemResource(fileName).toURI()); + stream = Files.newInputStream(configLocation); + config.load(stream); + return Optional.of(config); + } catch (Exception e) { + return Optional.empty(); + } finally { + if (stream != null) { + try { + stream.close(); + } catch (IOException e) { + } + } + } + } + +} diff --git a/aws/src/test/java/com/baeldung/dynamodb/rule/LocalDbCreationRule.java b/aws/src/test/java/com/baeldung/dynamodb/rule/LocalDbCreationRule.java new file mode 100644 index 0000000000..45a107103d --- /dev/null +++ b/aws/src/test/java/com/baeldung/dynamodb/rule/LocalDbCreationRule.java @@ -0,0 +1,35 @@ +package com.baeldung.dynamodb.rule; + +import com.amazonaws.services.dynamodbv2.local.main.ServerRunner; +import com.amazonaws.services.dynamodbv2.local.server.DynamoDBProxyServer; +import org.junit.rules.ExternalResource; + +public class LocalDbCreationRule extends ExternalResource { + + protected DynamoDBProxyServer server; + + public LocalDbCreationRule() { + System.setProperty("sqlite4java.library.path", "native-libs"); + } + + @Override + protected void before() throws Exception { + String port = "8000"; + this.server = ServerRunner.createServerFromCommandLineArgs(new String[]{"-inMemory", "-port", port}); + server.start(); + } + + @Override + protected void after() { + this.stopUnchecked(server); + } + + protected void stopUnchecked(DynamoDBProxyServer dynamoDbServer) { + try { + dynamoDbServer.stop(); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + +} diff --git a/aws/src/test/resources/test.properties b/aws/src/test/resources/test.properties new file mode 100644 index 0000000000..a7051d0244 --- /dev/null +++ b/aws/src/test/resources/test.properties @@ -0,0 +1,3 @@ +amazon.dynamodb.endpoint=http://localhost:8000/ +amazon.aws.accesskey=key +amazon.aws.secretkey=key2 \ No newline at end of file