I have to test an application which relies heavily on Amazon's DynamoDB. I want the tests to be able to be run separately, which is why I opted for DynamoDB Local .jar
. I am aware of the recent update, making us able to run this without an external bash command call. However, when I try to run the example, that was specified here, i get the following stacktrace:
Exception in thread "main" com.amazonaws.AmazonServiceException: The request processing has failed because of an unknown error, exception or failure. (Service: AmazonDynamoDBv2; Status Code: 500; Error Code: InternalFailure; Request ID: cab7a550-aaa6-4bfe-a591-0b255481cc14)
at com.amazonaws.http.AmazonHttpClient.handleErrorResponse(AmazonHttpClient.java:1275)
at com.amazonaws.http.AmazonHttpClient.executeOneRequest(AmazonHttpClient.java:873)
at com.amazonaws.http.AmazonHttpClient.executeHelper(AmazonHttpClient.java:576)
at com.amazonaws.http.AmazonHttpClient.doExecute(AmazonHttpClient.java:362)
at com.amazonaws.http.AmazonHttpClient.executeWithTimer(AmazonHttpClient.java:328)
at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:307)
at com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient.invoke(AmazonDynamoDBClient.java:1805)
at com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient.listTables(AmazonDynamoDBClient.java:1223)
at com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient.listTables(AmazonDynamoDBClient.java:1235)
This is the code I am trying to run :
public static void main( String[] args ) throws Exception
{
AmazonDynamoDB dynamodb = null;
DynamoDBProxyServer server = null;
final String[] localArgs = { "-inMemory", "-port", "13005" };
server = ServerRunner.createServerFromCommandLineArgs(localArgs);
server.start();
BasicAWSCredentials auth = new BasicAWSCredentials("key", "secret");
dynamodb = new AmazonDynamoDBClient(auth);
dynamodb.setEndpoint("http://127.0.0.1:13005");
// use the DynamoDB API over HTTP
System.out.println(dynamodb.listTables());
// Stop the DynamoDB Local endpoint
if(server != null) {
server.stop();
}
}
I have observed that if I try to run it completely from the Java program itself, that's when the exception is thrown and the specified port is no longer available (An error is thrown stating that this port is taken). But if I start the DynamoDB Local from a command prompt and use the Java program as an access client only, then everthing runs okay. Any suggestions?
There are at least 2 problems with DynamoDBLocal. Resolve both, and you will be running embedded DynamoDB.
First, the
-port
parameter does not work correctly. So Jetty isn't setup on the port you expect. Instead something like port 51205 (or random?) is set as the Jetty listener as a default port.Here is my code for how I start the server avoiding the built-in command-line parsing which is better anyway... After starting the server in this way, http://localhost:19444/shell works, so Jetty is fine. But then you might have another problem with Sqlite4java (see after the code block).
NOTE: code is Kotlin, but the Java would be very similar. Also I have SLF4j configured and don't let the
ServerRunner
classes break the logging, so this is doubly better way to start the server and whatServerRunner
does internally.Once you have this code, you now are running on the expected port.
Now you likely have the 2nd error, such as Sqlite4java not finding its correct binary for your platform. For some versions of Mac OSX it will generate a binary filename that actually does not exist. And DynamoDBLocal hides all Sqlite4java logging forcibly (impossible to override it), so you will not see it.
You can test the sqlite library by downloading a distribution, unzipping and then running:
And it will report what it try to load, and how it failed or not. You just need to find that JAR on your system from wherever Gradle, Maven or you placed it. My output with error was:
If you have an error about the dynamic library loading, read these for how to resolve:
The only answer that seems to work reliably is to either:
java.library.path
/Library/Java/Extensions
(macOS only)Of these I do something like the first option, and add the libraries into the project and make sure the build adds a
-Djava.library.path=./lib/sqlite4java
where the dynamic libraries are unzipped. For more resilient testing you can set thejava.library.path
programatically in your code using this trick (otherwise it is ignored when set in code): http://blog.cedarsoft.com/2010/11/setting-java-library-path-programmatically/It is evil that DynamoDBLocal hides all Sqlite errors, so you have to debug to find silent failures. The library is full of things that adjust logging levels and make it hard to debug since they break your ability to see errors. For example every time when a SqlLite file is opened (class
SQLiteDBAccess
):Closing Notes: I am looking at alternatives, this is not the best built thing on the planet, looking at the code of DynamoDbLocal and decisions made for it give me very little confidence in it. Alternator or Jcabi - Dynamo Mock are next on my list.
In August 2018 Amazon announced new Docker image with Amazon DynamoDB Local onboard. It does not require downloading and running any JARs as well as adding using third-party OS-specific binaries (I'm talking about
sqlite4java
).It is as simple as starting a Docker container before the tests:
You can do that manually for local development, as described above. IDEs usually provide a way to run arbitrary commands before executing a task, so you can make IDE to start the container for you.
Or you can use it in your CI pipeline. Many CI services provide an ability to start additional containers during the pipeline that can provide dependencies for your tests. Here is an example for Gitlab CI/CD:
Or Bitbucket Pipelines: