Skip to content

Commit 1a81c45

Browse files
committed
Add the non-Spring DynamoDB samples in the AWS module
1 parent c6c7ffb commit 1a81c45

7 files changed

Lines changed: 303 additions & 0 deletions

File tree

aws/pom.xml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
<junit.version>4.12</junit.version>
2323
<mockito-core.version>2.8.9</mockito-core.version>
2424
<assertj-core.version>3.8.0</assertj-core.version>
25+
<dynamodblocal.version>1.11.86</dynamodblocal.version>
26+
<dynamodblocal.repository.url>https://s3-us-west-2.amazonaws.com/dynamodb-local/release</dynamodblocal.repository.url>
2527
</properties>
2628

2729
<dependencies>
@@ -88,6 +90,13 @@
8890
<artifactId>gson</artifactId>
8991
<version>${gson.version}</version>
9092
</dependency>
93+
94+
<dependency>
95+
<groupId>com.amazonaws</groupId>
96+
<artifactId>DynamoDBLocal</artifactId>
97+
<version>${dynamodblocal.version}</version>
98+
<scope>test</scope>
99+
</dependency>
91100
</dependencies>
92101

93102
<build>
@@ -108,6 +117,35 @@
108117
</execution>
109118
</executions>
110119
</plugin>
120+
121+
<plugin>
122+
<groupId>org.apache.maven.plugins</groupId>
123+
<artifactId>maven-dependency-plugin</artifactId>
124+
<version>2.10</version>
125+
<executions>
126+
<execution>
127+
<id>copy-dependencies</id>
128+
<phase>test-compile</phase>
129+
<goals>
130+
<goal>copy-dependencies</goal>
131+
</goals>
132+
<configuration>
133+
<includeScope>test</includeScope>
134+
<includeTypes>so,dll,dylib</includeTypes>
135+
<outputDirectory>${project.basedir}/native-libs</outputDirectory>
136+
</configuration>
137+
</execution>
138+
</executions>
139+
</plugin>
111140
</plugins>
112141
</build>
142+
143+
<repositories>
144+
<repository>
145+
<id>dynamodb-local</id>
146+
<name>DynamoDB Local Release Repository</name>
147+
<url>${dynamodblocal.repository.url}</url>
148+
</repository>
149+
</repositories>
150+
113151
</project>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.baeldung.dynamodb.entity;
2+
3+
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAttribute;
4+
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBAutoGeneratedKey;
5+
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBHashKey;
6+
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
7+
8+
@DynamoDBTable(tableName = "ProductInfo")
9+
public class ProductInfo {
10+
11+
private String id;
12+
private String msrp;
13+
private String cost;
14+
15+
public ProductInfo() {
16+
}
17+
18+
public ProductInfo(String cost, String msrp) {
19+
this.msrp = msrp;
20+
this.cost = cost;
21+
}
22+
23+
@DynamoDBHashKey
24+
@DynamoDBAutoGeneratedKey
25+
public String getId() {
26+
return id;
27+
}
28+
29+
@DynamoDBAttribute
30+
public String getMsrp() {
31+
return msrp;
32+
}
33+
34+
@DynamoDBAttribute
35+
public String getCost() {
36+
return cost;
37+
}
38+
39+
public void setId(String id) {
40+
this.id = id;
41+
}
42+
43+
public void setMsrp(String msrp) {
44+
this.msrp = msrp;
45+
}
46+
47+
public void setCost(String cost) {
48+
this.cost = cost;
49+
}
50+
51+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.baeldung.dynamodb.repository;
2+
3+
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
4+
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBScanExpression;
5+
6+
import java.io.Serializable;
7+
import java.lang.reflect.ParameterizedType;
8+
import java.util.List;
9+
10+
public abstract class AbstractRepository<T, ID extends Serializable> {
11+
12+
protected DynamoDBMapper mapper;
13+
protected Class<T> entityClass;
14+
15+
protected AbstractRepository() {
16+
ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();
17+
18+
// This entityClass refers to the actual entity class in the subclass declaration.
19+
20+
// For instance, ProductInfoDAO extends AbstractDAO<ProductInfo, String>
21+
// In this case entityClass = ProductInfo, and ID is String type
22+
// which refers to the ProductInfo's partition key string value
23+
this.entityClass = (Class<T>) genericSuperclass.getActualTypeArguments()[0];
24+
}
25+
26+
public void save(T t) {
27+
mapper.save(t);
28+
}
29+
30+
public T findOne(ID id) {
31+
return mapper.load(entityClass, id);
32+
}
33+
34+
/**
35+
* <strong>WARNING:</strong> It is not recommended to perform full table scan
36+
* targeting the real production environment.
37+
*
38+
* @return All items
39+
*/
40+
public List<T> findAll() {
41+
DynamoDBScanExpression scanExpression = new DynamoDBScanExpression();
42+
return mapper.scan(entityClass, scanExpression);
43+
}
44+
45+
public void setMapper(DynamoDBMapper dynamoDBMapper) {
46+
this.mapper = dynamoDBMapper;
47+
}
48+
49+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.baeldung.dynamodb.repository;
2+
3+
import com.baeldung.dynamodb.entity.ProductInfo;
4+
5+
public class ProductInfoRepository extends AbstractRepository<ProductInfo, String> {
6+
}
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package com.baeldung.dynamodb;
2+
3+
import com.amazonaws.auth.BasicAWSCredentials;
4+
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
5+
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
6+
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBMapper;
7+
import com.amazonaws.services.dynamodbv2.model.CreateTableRequest;
8+
import com.amazonaws.services.dynamodbv2.model.ProvisionedThroughput;
9+
import com.amazonaws.services.dynamodbv2.model.ResourceInUseException;
10+
import com.baeldung.dynamodb.entity.ProductInfo;
11+
import com.baeldung.dynamodb.repository.ProductInfoRepository;
12+
import com.baeldung.dynamodb.rule.LocalDbCreationRule;
13+
import org.junit.Before;
14+
import org.junit.BeforeClass;
15+
import org.junit.ClassRule;
16+
import org.junit.Test;
17+
18+
import java.io.IOException;
19+
import java.io.InputStream;
20+
import java.net.URISyntaxException;
21+
import java.nio.file.Files;
22+
import java.nio.file.Path;
23+
import java.nio.file.Paths;
24+
import java.util.List;
25+
import java.util.Optional;
26+
import java.util.Properties;
27+
28+
import static org.hamcrest.Matchers.greaterThan;
29+
import static org.hamcrest.core.Is.is;
30+
import static org.hamcrest.core.IsEqual.equalTo;
31+
import static org.junit.Assert.assertThat;
32+
33+
public class ProductInfoRepositoryIntegrationTest {
34+
35+
@ClassRule
36+
public static LocalDbCreationRule dynamoDB = new LocalDbCreationRule();
37+
38+
private static DynamoDBMapper dynamoDBMapper;
39+
private static AmazonDynamoDB amazonDynamoDB;
40+
41+
private ProductInfoRepository repository;
42+
43+
private static final String DYNAMODB_ENDPOINT = "amazon.dynamodb.endpoint";
44+
private static final String AWS_ACCESSKEY = "amazon.aws.accesskey";
45+
private static final String AWS_SECRETKEY = "amazon.aws.secretkey";
46+
47+
private static final String EXPECTED_COST = "20";
48+
private static final String EXPECTED_PRICE = "50";
49+
50+
@BeforeClass
51+
public static void setupClass() {
52+
Properties testProperties = loadFromFileInClasspath("test.properties")
53+
.filter(properties -> !isEmpty(properties.getProperty(AWS_ACCESSKEY)))
54+
.filter(properties -> !isEmpty(properties.getProperty(AWS_SECRETKEY)))
55+
.filter(properties -> !isEmpty(properties.getProperty(DYNAMODB_ENDPOINT)))
56+
.orElseThrow(() -> new RuntimeException("Unable to get all of the required test property values"));
57+
58+
String amazonAWSAccessKey = testProperties.getProperty(AWS_ACCESSKEY);
59+
String amazonAWSSecretKey = testProperties.getProperty(AWS_SECRETKEY);
60+
String amazonDynamoDBEndpoint = testProperties.getProperty(DYNAMODB_ENDPOINT);
61+
62+
amazonDynamoDB = new AmazonDynamoDBClient(new BasicAWSCredentials(amazonAWSAccessKey, amazonAWSSecretKey));
63+
amazonDynamoDB.setEndpoint(amazonDynamoDBEndpoint);
64+
dynamoDBMapper = new DynamoDBMapper(amazonDynamoDB);
65+
}
66+
67+
@Before
68+
public void setup() {
69+
try {
70+
repository = new ProductInfoRepository();
71+
repository.setMapper(dynamoDBMapper);
72+
73+
CreateTableRequest tableRequest = dynamoDBMapper.generateCreateTableRequest(ProductInfo.class);
74+
75+
tableRequest.setProvisionedThroughput(new ProvisionedThroughput(1L, 1L));
76+
77+
amazonDynamoDB.createTable(tableRequest);
78+
} catch (ResourceInUseException e) {
79+
// Do nothing, table already created
80+
}
81+
82+
// TODO How to handle different environments. i.e. AVOID deleting all entries in ProductInfo on table
83+
dynamoDBMapper.batchDelete((List<ProductInfo>) repository.findAll());
84+
}
85+
86+
@Test
87+
public void givenItemWithExpectedCost_whenRunFindAll_thenItemIsFound() {
88+
89+
ProductInfo productInfo = new ProductInfo(EXPECTED_COST, EXPECTED_PRICE);
90+
repository.save(productInfo);
91+
92+
List<ProductInfo> result = (List<ProductInfo>) repository.findAll();
93+
assertThat(result.size(), is(greaterThan(0)));
94+
assertThat(result.get(0).getCost(), is(equalTo(EXPECTED_COST)));
95+
}
96+
97+
private static boolean isEmpty(String inputString) {
98+
return inputString == null || "".equals(inputString);
99+
}
100+
101+
private static Optional<Properties> loadFromFileInClasspath(String fileName) {
102+
InputStream stream = null;
103+
try {
104+
Properties config = new Properties();
105+
Path configLocation = Paths.get(ClassLoader.getSystemResource(fileName).toURI());
106+
stream = Files.newInputStream(configLocation);
107+
config.load(stream);
108+
return Optional.of(config);
109+
} catch (Exception e) {
110+
return Optional.empty();
111+
} finally {
112+
if (stream != null) {
113+
try {
114+
stream.close();
115+
} catch (IOException e) {
116+
}
117+
}
118+
}
119+
}
120+
121+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.baeldung.dynamodb.rule;
2+
3+
import com.amazonaws.services.dynamodbv2.local.main.ServerRunner;
4+
import com.amazonaws.services.dynamodbv2.local.server.DynamoDBProxyServer;
5+
import org.junit.rules.ExternalResource;
6+
7+
public class LocalDbCreationRule extends ExternalResource {
8+
9+
protected DynamoDBProxyServer server;
10+
11+
public LocalDbCreationRule() {
12+
System.setProperty("sqlite4java.library.path", "native-libs");
13+
}
14+
15+
@Override
16+
protected void before() throws Exception {
17+
String port = "8000";
18+
this.server = ServerRunner.createServerFromCommandLineArgs(new String[]{"-inMemory", "-port", port});
19+
server.start();
20+
}
21+
22+
@Override
23+
protected void after() {
24+
this.stopUnchecked(server);
25+
}
26+
27+
protected void stopUnchecked(DynamoDBProxyServer dynamoDbServer) {
28+
try {
29+
dynamoDbServer.stop();
30+
} catch (Exception e) {
31+
throw new RuntimeException(e);
32+
}
33+
}
34+
35+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
amazon.dynamodb.endpoint=http://localhost:8000/
2+
amazon.aws.accesskey=key
3+
amazon.aws.secretkey=key2

0 commit comments

Comments
 (0)