<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Yegor</title>
    <description>The latest articles on DEV Community by Yegor (@byegor).</description>
    <link>https://dev.to/byegor</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F341161%2Ff246b8a8-be62-46e5-a25c-971ef0a82683.jpg</url>
      <title>DEV Community: Yegor</title>
      <link>https://dev.to/byegor</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/byegor"/>
    <language>en</language>
    <item>
      <title>DynamoDB Client using Micronaut, Maven and GraalVM</title>
      <dc:creator>Yegor</dc:creator>
      <pubDate>Sat, 25 Apr 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/byegor/dynamodb-client-using-micronaut-maven-and-graalvm-31fe</link>
      <guid>https://dev.to/byegor/dynamodb-client-using-micronaut-maven-and-graalvm-31fe</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--q7P_xK48--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://byegor.github.io/assets/micronaut-dynamodb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--q7P_xK48--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://byegor.github.io/assets/micronaut-dynamodb.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Overview
&lt;/h1&gt;

&lt;p&gt;It will be a simple how-to article where I will be showing how to implement simple rest DynamoDB client using &lt;a href="https://micronaut.io/"&gt;Micronaut Framework&lt;/a&gt; and Maven,build a native image with &lt;a href="https://www.graalvm.org/"&gt;GraalVM&lt;/a&gt; and simple comparison in resource usage between clients on Spring Boot and on Micronaut with GraalVM.&lt;/p&gt;

&lt;p&gt;For those who are not familiar with Micronaut - it is a framework for building microservices and serverless applications.One of the key differences between Spring Boot and Micronaut is that Micronaut doesn’t use reflection to do IoC, so application startup time and memory consumption are not bound to the size of project codebase.&lt;/p&gt;

&lt;p&gt;So our task is to handle HTTP requests for retrieving or storing some &lt;code&gt;Event(id:string, body: string)&lt;/code&gt;. Events will be stored in DynamoDB.&lt;/p&gt;

&lt;p&gt;It might be easier to simply look at the &lt;a href="https://github.com/byegor/poc/tree/master/micronaut-dynamodb-async"&gt;code on Github&lt;/a&gt; and follow it there.&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Maven
&lt;/h1&gt;

&lt;p&gt;Let’s start with Maven runtime dependencies for Micronaut and DynamoDB SDK&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependencyManagement&amp;gt;
    &amp;lt;dependencies&amp;gt;
        &amp;lt;dependency&amp;gt;
            &amp;lt;groupId&amp;gt;io.micronaut&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;micronaut-bom&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;${micronaut.version}&amp;lt;/version&amp;gt;
            &amp;lt;type&amp;gt;pom&amp;lt;/type&amp;gt;
            &amp;lt;scope&amp;gt;import&amp;lt;/scope&amp;gt;
        &amp;lt;/dependency&amp;gt;
    &amp;lt;/dependencies&amp;gt;
&amp;lt;/dependencyManagement&amp;gt;

&amp;lt;dependencies&amp;gt;
    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;io.micronaut&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;micronaut-inject-java&amp;lt;/artifactId&amp;gt;
    &amp;lt;/dependency&amp;gt;
    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;io.micronaut&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;micronaut-runtime&amp;lt;/artifactId&amp;gt;
    &amp;lt;/dependency&amp;gt;
    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;io.micronaut&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;micronaut-http-server-netty&amp;lt;/artifactId&amp;gt;
    &amp;lt;/dependency&amp;gt;

    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;com.amazonaws&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;aws-java-sdk-dynamodb&amp;lt;/artifactId&amp;gt;
        &amp;lt;version&amp;gt;1.11.762&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;
&amp;lt;/dependencies&amp;gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As Micronaut doesn’t use reflection/annotation processing during startup but does it during build - we need to add annotation processors to maven-compiler-plugin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;plugin&amp;gt;
    &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;3.8.1&amp;lt;/version&amp;gt;
    &amp;lt;configuration&amp;gt;
        &amp;lt;compilerArgs&amp;gt;
            &amp;lt;arg&amp;gt;-parameters&amp;lt;/arg&amp;gt;
        &amp;lt;/compilerArgs&amp;gt;
        &amp;lt;annotationProcessorPaths&amp;gt;
            &amp;lt;path&amp;gt;
                &amp;lt;groupId&amp;gt;io.micronaut&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;micronaut-inject-java&amp;lt;/artifactId&amp;gt;
                &amp;lt;version&amp;gt;${micronaut.version}&amp;lt;/version&amp;gt;
            &amp;lt;/path&amp;gt;
            &amp;lt;path&amp;gt;
                &amp;lt;groupId&amp;gt;io.micronaut&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;micronaut-validation&amp;lt;/artifactId&amp;gt;
                &amp;lt;version&amp;gt;${micronaut.version}&amp;lt;/version&amp;gt;
            &amp;lt;/path&amp;gt;
        &amp;lt;/annotationProcessorPaths&amp;gt;
    &amp;lt;/configuration&amp;gt;
    &amp;lt;executions&amp;gt;
        &amp;lt;execution&amp;gt;
            &amp;lt;id&amp;gt;test-compile&amp;lt;/id&amp;gt;
            &amp;lt;goals&amp;gt;
                &amp;lt;goal&amp;gt;testCompile&amp;lt;/goal&amp;gt;
            &amp;lt;/goals&amp;gt;
            &amp;lt;configuration&amp;gt;
                &amp;lt;compilerArgs&amp;gt;
                    &amp;lt;arg&amp;gt;-parameters&amp;lt;/arg&amp;gt;
                &amp;lt;/compilerArgs&amp;gt;
                &amp;lt;annotationProcessorPaths&amp;gt;
                    &amp;lt;path&amp;gt;
                        &amp;lt;groupId&amp;gt;io.micronaut&amp;lt;/groupId&amp;gt;
                        &amp;lt;artifactId&amp;gt;micronaut-inject-java&amp;lt;/artifactId&amp;gt;
                        &amp;lt;version&amp;gt;${micronaut.version}&amp;lt;/version&amp;gt;
                    &amp;lt;/path&amp;gt;
                    &amp;lt;path&amp;gt;
                        &amp;lt;groupId&amp;gt;io.micronaut&amp;lt;/groupId&amp;gt;
                        &amp;lt;artifactId&amp;gt;micronaut-validation&amp;lt;/artifactId&amp;gt;
                        &amp;lt;version&amp;gt;${micronaut.version}&amp;lt;/version&amp;gt;
                    &amp;lt;/path&amp;gt;
                &amp;lt;/annotationProcessorPaths&amp;gt;
            &amp;lt;/configuration&amp;gt;
        &amp;lt;/execution&amp;gt;
    &amp;lt;/executions&amp;gt;
&amp;lt;/plugin&amp;gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  3. DynamoDB
&lt;/h1&gt;

&lt;h6&gt;
  
  
  3.1 Configuration
&lt;/h6&gt;

&lt;p&gt;A simple config where we set up connection to DynamoDB. For test purpose we need to specify &lt;code&gt;dynamoEndpoint&lt;/code&gt;.In case of real application we need to specify region instead of endpoint.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Factory
public class Config {

    @Bean
    AmazonDynamoDBAsync dynamoDbAsyncClient(Environment environment) {
        Optional&amp;lt;String&amp;gt; secretKey = environment.get("aws.secretkey", String.class);
        Optional&amp;lt;String&amp;gt; accessKey = environment.get("aws.accesskey", String.class);
        String endpoint = environment.get("dynamo.endpoint", String.class, "http://localhost:8000");
        if (!secretKey.isPresent() || !accessKey.isPresent()) {
            throw new IllegalArgumentException("Aws credentials not provided");
        }
        BasicAWSCredentials credentials = new BasicAWSCredentials(accessKey.get(), secretKey.get());
        AmazonDynamoDBAsyncClientBuilder clientBuilder = AmazonDynamoDBAsyncClientBuilder.standard()
                .withCredentials(new AWSStaticCredentialsProvider(credentials))
                .withEndpointConfiguration(
                        new AwsClientBuilder.EndpointConfiguration(endpoint, null)
                );

        return clientBuilder.build();
    }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h6&gt;
  
  
  3.2 Async DynamoDB Service
&lt;/h6&gt;

&lt;p&gt;Simple service for saving/retrieving event to/from DynamoDB. All async requests to aws are wrapped into RxJava constructions for easy handling of futures.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Singleton
public class DynamoDBService {

    public static final String TABLE_NAME = "events";
    public static final String ID_COLUMN = "id";
    public static final String BODY_COLUMN = "body";

    private final AmazonDynamoDBAsync client;

    public DynamoDBService(AmazonDynamoDBAsync client) {
        this.client = client;
    }

    //Create DynamoDB table if not exists
    @PostConstruct
    public void createTableIfNotExists() {
        if (!isTableExists()) {
            createTable();
        }
    }

    public Maybe&amp;lt;Event&amp;gt; getEvent(String eventId) {
        Map&amp;lt;String, AttributeValue&amp;gt; searchCriteria = new HashMap&amp;lt;&amp;gt;();
        searchCriteria.put(ID_COLUMN, new AttributeValue().withS(eventId));
        // Building request to get event by Id
        GetItemRequest request = new GetItemRequest()
                .withTableName(TABLE_NAME)
                .withKey(searchCriteria)
                .withAttributesToGet(BODY_COLUMN); // lets retrieve only body as id we already have
        return Maybe.fromFuture(client.getItemAsync(request))
                .subscribeOn(Schedulers.io())
                .filter(result -&amp;gt; result.getItem() != null) // check that request returned something
                .map(result -&amp;gt; new Event(eventId, result.getItem().get(BODY_COLUMN).getS())); //building Event from response
    }

    public Single&amp;lt;String&amp;gt; saveEvent(String eventBody) {
        String id = UUID.randomUUID().toString();

        Map&amp;lt;String, AttributeValue&amp;gt; item = new HashMap&amp;lt;&amp;gt;();
        item.put(ID_COLUMN, new AttributeValue().withS(id));
        item.put(BODY_COLUMN, new AttributeValue().withS(eventBody));

        PutItemRequest putRequest = new PutItemRequest()
                .withTableName(TABLE_NAME)
                .withItem(item);

        return Single.fromFuture(client.putItemAsync(putRequest))
                .subscribeOn(Schedulers.io())
                .map(result -&amp;gt; id);
    }

    private boolean isTableExists() {
        ListTablesRequest tablesRequest = new ListTablesRequest()
                .withExclusiveStartTableName(TABLE_NAME);
        ListTablesResult result = client.listTables(tablesRequest);
        return result.getTableNames().contains(TABLE_NAME);
    }

    private CreateTableResult createTable() {
        KeySchemaElement keyDefinitions = new KeySchemaElement()
                .withAttributeName(ID_COLUMN)
                .withKeyType(KeyType.HASH);

        AttributeDefinition keyType = new AttributeDefinition()
                .withAttributeName(ID_COLUMN)
                .withAttributeType(ScalarAttributeType.S);

        CreateTableRequest request = new CreateTableRequest()
                .withTableName(TABLE_NAME)
                .withKeySchema(keyDefinitions)
                .withAttributeDefinitions(keyType)
                .withBillingMode(BillingMode.PAY_PER_REQUEST);

        return client.createTable(request);
    }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  4. Controller
&lt;/h1&gt;

&lt;p&gt;Here we gonna expose our REST Api with GET method for retrieving event from DynamoDB and POST for storing event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Controller("/event")
public class SimpleController {

    private final DynamoDBService dynamoDBService;

    public SimpleController(DynamoDBService dynamoDBService) {
        this.dynamoDBService = dynamoDBService;
    }

    @Get("/{eventId}")
    @Produces(MediaType.APPLICATION_JSON) 
    public Maybe&amp;lt;Event&amp;gt; getEvent(@PathVariable String eventId) {
        Maybe&amp;lt;Event&amp;gt; event = dynamoDBService.getEvent(eventId);
        return event;
    }

    @Post("/")
    @Produces(MediaType.APPLICATION_JSON)
    public Single&amp;lt;String&amp;gt; saveEvent(@Body String body) {
        Single&amp;lt;String&amp;gt; event = dynamoDBService.saveEvent(body);
        return event;
    }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  5. Integration Test
&lt;/h1&gt;

&lt;h6&gt;
  
  
  5.1 Maven dependencies
&lt;/h6&gt;

&lt;p&gt;For running integration test with DynamoDB we need DynamoDBLocal dependency, which is not really the DynamoDB, but SQLite with implemented DynamoDB interfaces on top of it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt; &amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;com.amazonaws&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;DynamoDBLocal&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.12.0&amp;lt;/version&amp;gt;
    &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;io.micronaut&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;micronaut-http-client&amp;lt;/artifactId&amp;gt;
    &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;io.micronaut.test&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;micronaut-test-junit5&amp;lt;/artifactId&amp;gt;
    &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;org.junit.jupiter&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;junit-jupiter-api&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;5.6.0&amp;lt;/version&amp;gt;
    &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;

&amp;lt;build&amp;gt;
    &amp;lt;plugins&amp;gt;
        &amp;lt;plugin&amp;gt;
            &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;maven-dependency-plugin&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;2.10&amp;lt;/version&amp;gt;
            &amp;lt;executions&amp;gt;
                &amp;lt;execution&amp;gt;
                    &amp;lt;id&amp;gt;copy&amp;lt;/id&amp;gt;
                    &amp;lt;phase&amp;gt;test-compile&amp;lt;/phase&amp;gt;
                    &amp;lt;goals&amp;gt;
                        &amp;lt;goal&amp;gt;copy-dependencies&amp;lt;/goal&amp;gt;
                    &amp;lt;/goals&amp;gt;
                    &amp;lt;configuration&amp;gt;
                        &amp;lt;includeScope&amp;gt;test&amp;lt;/includeScope&amp;gt;
                        &amp;lt;includeTypes&amp;gt;so,dll,dylib&amp;lt;/includeTypes&amp;gt;
                        &amp;lt;!--Keep an eye on output directory - it will be used for starting dynamodb--&amp;gt;
                        &amp;lt;outputDirectory&amp;gt;${project.basedir}/target/native-libs&amp;lt;/outputDirectory&amp;gt;
                    &amp;lt;/configuration&amp;gt;
                &amp;lt;/execution&amp;gt;
            &amp;lt;/executions&amp;gt;
        &amp;lt;/plugin&amp;gt;
    &amp;lt;/plugins&amp;gt;
&amp;lt;/build&amp;gt;

&amp;lt;repositories&amp;gt;
    &amp;lt;repository&amp;gt;
        &amp;lt;id&amp;gt;dynamodb-local-oregon&amp;lt;/id&amp;gt;
        &amp;lt;name&amp;gt;DynamoDB Local Release Repository&amp;lt;/name&amp;gt;
        &amp;lt;url&amp;gt;https://s3-us-west-2.amazonaws.com/dynamodb-local/release&amp;lt;/url&amp;gt;
    &amp;lt;/repository&amp;gt;
&amp;lt;/repositories&amp;gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h6&gt;
  
  
  5.2 DynamoDB server
&lt;/h6&gt;

&lt;p&gt;Now we need to start DynamoDB before test runs, we can do it with jupiter Extension.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class LocalDynamoDbExtension implements AfterAllCallback, BeforeAllCallback {

    protected DynamoDBProxyServer server;

    public LocalDynamoDbExtension() {
        //here we set the path from "outputDirectory" of maven-dependency-plugin
        System.setProperty("sqlite4java.library.path", "target/native-libs");
    }

    @Override
    public void afterAll(ExtensionContext extensionContext) throws Exception {
        stopUnchecked(server);
    }

    @Override
    public void beforeAll(ExtensionContext extensionContext) throws Exception {
        this.server = ServerRunner
                .createServerFromCommandLineArgs(new String[]{"-inMemory", "-port", "8000"});
        server.start();
    }

    protected void stopUnchecked(DynamoDBProxyServer dynamoDbServer) {
        try {
            dynamoDbServer.stop();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h6&gt;
  
  
  5.3 Running test
&lt;/h6&gt;

&lt;p&gt;Now we can create an integration test and check if our REST Api methods do what we think.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@MicronautTest
@ExtendWith(LocalDynamoDbExtension.class)
public class SimpleControllerTest {

    @Inject
    @Client("/event")
    RxStreamingHttpClient client;

    @Inject
    DynamoDBService dynamoDBService;

    @Test
    public void getEventsTest() {
        //add event to database so we can query it via http
        String eventBody = "testMessage";
        String eventId = dynamoDBService.saveEvent(eventBody).blockingGet();
        HttpRequest request = HttpRequest.GET(eventId);
        HttpResponse&amp;lt;List&amp;lt;Event&amp;gt;&amp;gt; rsp = client.toBlocking().exchange(request, Argument.listOf(Event.class));

        assertEquals(HttpStatus.OK, rsp.getStatus());
        List&amp;lt;Event&amp;gt; body = rsp.body();
        assertEquals(1, body.size());
        assertEquals(eventBody, body.get(0).getBody());
    }

    @Test
    public void saveEventTest() {
        HttpRequest request = HttpRequest.POST("/", "postBody");
        HttpResponse&amp;lt;String&amp;gt; rsp = client.toBlocking().exchange(request, Argument.of(String.class));
        Optional&amp;lt;String&amp;gt; id = rsp.getBody();
        assertTrue(id.isPresent());

        Event event = dynamoDBService.getEvent(id.get()).blockingGet();
        assertEquals(id.get(), event.getId());
        assertEquals("postBody", event.getBody());
    }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  6. Native Image
&lt;/h1&gt;

&lt;p&gt;Using &lt;a href="https://www.graalvm.org/docs/why-graal/"&gt;GraalVM&lt;/a&gt; we can build ahead-of-time compiled native image which is very useful for small applications. Native image includes an application classes, classes from its dependencies, classes from JDK. It does not run on the JVM. So in the end you will get standalone executable image which you can run without any JVM.&lt;/p&gt;

&lt;p&gt;Because image already compiled, linked and partly initialized it will start faster, and you will get lower memory footprint.But keep in mind that there is a price for that - absence of JIT compiler, much simpler GC(SerialGC), platform dependent,hard to use frameworks which heavily relies on reflection(Spring Framework).&lt;/p&gt;

&lt;p&gt;You can build image in several ways:&lt;/p&gt;

&lt;h6&gt;
  
  
  6.1 Building image with Docker multistage
&lt;/h6&gt;

&lt;p&gt;One prons is that you need to know application’s classpath or download all dependencies in folder and point to it during building image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM oracle/graalvm-ce:20.0.0-java11 as graalvm
RUN gu install native-image

COPY . /home/app/micronaut-dynamodb-client
WORKDIR /home/app/micronaut-dynamodb-client

RUN native-image --no-server -cp all-runtime-deps.jar

FROM frolvlad/alpine-glibc
RUN apk update &amp;amp;&amp;amp; apk add libstdc++
EXPOSE 8080
COPY --from=graalvm /home/app/micronaut-dynamodb-client/micronaut-dynamodb-client /srv/micronaut-dynamodb-client
ENTRYPOINT ["/srv/micronaut-dynamodb-client", "-Xmx68m"]

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h6&gt;
  
  
  6.2 Building image with Maven
&lt;/h6&gt;

&lt;p&gt;A bit harder than with a docker. You need to install GrallVM JDK, install native-image tool, set GraalVM as JDK for your project. After that you can add a plugin to maven and plugin will do the job.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;plugin&amp;gt;
    &amp;lt;groupId&amp;gt;org.graalvm.nativeimage&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;native-image-maven-plugin&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;20.0.0&amp;lt;/version&amp;gt;
    &amp;lt;executions&amp;gt;
        &amp;lt;execution&amp;gt;
            &amp;lt;goals&amp;gt;
                &amp;lt;goal&amp;gt;native-image&amp;lt;/goal&amp;gt;
            &amp;lt;/goals&amp;gt;
            &amp;lt;phase&amp;gt;deploy&amp;lt;/phase&amp;gt;
        &amp;lt;/execution&amp;gt;
    &amp;lt;/executions&amp;gt;
    &amp;lt;configuration&amp;gt;
        &amp;lt;mainClass&amp;gt;com.yegor.micronaut.dynamodb.App&amp;lt;/mainClass&amp;gt;
        &amp;lt;buildArgs&amp;gt;-H:Name=dynamodb-client&amp;lt;/buildArgs&amp;gt; &amp;lt;!--Image name--&amp;gt;
        &amp;lt;buildArgs&amp;gt;-H:IncludeResources="logback.xml|application.yml"&amp;lt;/buildArgs&amp;gt; &amp;lt;!--Resources to add to image--&amp;gt;
    &amp;lt;/configuration&amp;gt;
&amp;lt;/plugin&amp;gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;When native image is ready we can build Docker image with it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM frolvlad/alpine-glibc
RUN apk update &amp;amp;&amp;amp; apk add libstdc++
COPY target/micronaut-dynamodb-client /srv/micronaut-dynamodb-client
EXPOSE 8080
ENTRYPOINT ["/srv/micronaut-dynamodb-client"]

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  7. Simple comparision between dockerized Native Image and dockerized Spring Boot app
&lt;/h1&gt;

&lt;p&gt;As an example I’m gonna take spring boot application from &lt;a href="https://dev.to/byegor/spring-webflux-with-async-dynamodb-4ig0"&gt;this post&lt;/a&gt; which is basically doing the same stuff but with a help of Spring.&lt;/p&gt;

&lt;h6&gt;
  
  
  7.1 Image Sizes
&lt;/h6&gt;

&lt;p&gt;First lets look at image sizes by running &lt;code&gt;docker images&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY TAG SIZE
micronaut-dynamodb-native latest 84.4MB
spring-boot-dynamodb latest 364MB

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Obviously, docker with native image uses less space, cause the native image removed everything that won’t be in use, including JVM&lt;/p&gt;

&lt;h6&gt;
  
  
  7.2 App startup
&lt;/h6&gt;

&lt;p&gt;I run each image and just look at logs to get info when application completed startup.Micronaut-Native-Image started in 54 ms. Pretty impressive :)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;io.micronaut.runtime.Micronaut - Startup completed in 54ms. Server Running: http://5330567cbd7c:8080

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Spring Boot application took much longer to start&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;com.example.dynamo_spring.App : Started App in 4.093 seconds (JVM running for 4.736)

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h6&gt;
  
  
  7.3 Memory Footprint
&lt;/h6&gt;

&lt;p&gt;To print Memory and CPU consumption run &lt;code&gt;docker stats&lt;/code&gt;. But as I don’t do any requests to images - CPU numbers are irrelevant.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NAME MEM USAGE                    
micronaut-dynamodb-native 12.63MiB    
spring-boot-dynamodb 152MiB                     

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;






&lt;p&gt;Hurray, you made till the end!&lt;/p&gt;

&lt;p&gt;Happy coding :)&lt;/p&gt;

</description>
      <category>java</category>
      <category>aws</category>
      <category>microservices</category>
      <category>micronaut</category>
    </item>
    <item>
      <title>Async Rest Client to DynamoDB using Spring Boot</title>
      <dc:creator>Yegor</dc:creator>
      <pubDate>Fri, 21 Feb 2020 15:14:54 +0000</pubDate>
      <link>https://dev.to/byegor/spring-webflux-with-async-dynamodb-4ig0</link>
      <guid>https://dev.to/byegor/spring-webflux-with-async-dynamodb-4ig0</guid>
      <description>&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--dfVku9id--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://byegor.github.io/assets/spring_boot_dynamo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--dfVku9id--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://byegor.github.io/assets/spring_boot_dynamo.png" alt=""&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Overview
&lt;/h1&gt;

&lt;p&gt;Starting from Spring framework 5.0 and Spring Boot 2.0, the framework provides support for asynchronous programming, so does AWS SDK starting with 2.0 version.&lt;/p&gt;

&lt;p&gt;In this post i will be exploring using asynchronous DynamoDB API and Spring Webflux by building simple reactive REST application. Let’s say we need to handle HTTP requests for retrieving or storing some Event(id:string, body: string). Event will be stored in DynamoDB.&lt;/p&gt;

&lt;p&gt;It might be easier to simply look at the &lt;a href="https://github.com/yegor-bond/poc/tree/master/spring-dynamodb-async"&gt;code on Github&lt;/a&gt; and follow it there.&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Dependencies
&lt;/h1&gt;

&lt;p&gt;Let’s start with Maven dependencies for WebFlux and DynamoDB SDK&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependencies&amp;gt;
    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;org.springframework.boot&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;spring-boot-starter-webflux&amp;lt;/artifactId&amp;gt;
        &amp;lt;version&amp;gt;2.2.4.RELEASE&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;
    &amp;lt;dependency&amp;gt;
        &amp;lt;groupId&amp;gt;software.amazon.awssdk&amp;lt;/groupId&amp;gt;
        &amp;lt;artifactId&amp;gt;dynamodb&amp;lt;/artifactId&amp;gt;
        &amp;lt;version&amp;gt;2.10.65&amp;lt;/version&amp;gt;
    &amp;lt;/dependency&amp;gt;
&amp;lt;/dependencies&amp;gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  3. DynamoDB
&lt;/h1&gt;

&lt;h6&gt;
  
  
  3.1 Spring Configuration
&lt;/h6&gt;

&lt;p&gt;A simple config were we set up connection to DynamoDB. For test purpose we need to specify &lt;code&gt;dynamoEndpoint&lt;/code&gt; and forreal application we need to specify aws region.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
public class AppConfig {
    @Value("${aws.accessKey}")
    String accessKey;

    @Value("${aws.secretKey}")
    String secretKey;

    @Value("${dynamodb.endpoint:}")
    String dynamoEndpoint;

    @Bean
    AwsBasicCredentials awsBasicCredentials(){
        return AwsBasicCredentials.create(accessKey, secretKey);
    }

    @Bean
    DynamoDbAsyncClient dynamoDbAsyncClient(AwsBasicCredentials awsBasicCredentials){
        DynamoDbAsyncClientBuilder clientBuilder = DynamoDbAsyncClient.builder();
        clientBuilder
                .credentialsProvider(StaticCredentialsProvider.create(awsBasicCredentials));
                if(!dynamoEndpoint.isEmpty()){
                    clientBuilder.endpointOverride(URI.create(dynamoEndpoint));
                }
        return clientBuilder.build();
    }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;application.yaml&lt;/code&gt; with connection details&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws:
  accessKey: any
  secretKey: any
dynamodb:
  endpoint: http://localhost:8000/

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h6&gt;
  
  
  3.2 Reactive DynamoDB Service
&lt;/h6&gt;

&lt;p&gt;Unfortunately, second version of AWS SDK doesn’t have support for DynamoDBMapper yet(you can track mapper’s readiness &lt;a href="https://github.com/aws/aws-sdk-java-v2/issues/35"&gt;here&lt;/a&gt;), so table creation, sending requests and parsing responses need to be done by “low level” API.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;DynamoDbService&lt;/code&gt; we gonna:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create table if its not exists&lt;/li&gt;
&lt;li&gt;Implement methods for saving and retrieving event
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Service
public class DynamoDbService {

    public static final String TABLE_NAME = "events";
    public static final String ID_COLUMN = "id";
    public static final String BODY_COLUMN = "body";

    final DynamoDbAsyncClient client;

    @Autowired
    public DynamoDbService(DynamoDbAsyncClient client) {
        this.client = client;
    }

    //Creating table on startup if not exists
    @PostConstruct
    public void createTableIfNeeded() throws ExecutionException, InterruptedException {
        ListTablesRequest request = ListTablesRequest.builder().exclusiveStartTableName(TABLE_NAME).build();
        CompletableFuture&amp;lt;ListTablesResponse&amp;gt; listTableResponse = client.listTables(request);

        CompletableFuture&amp;lt;CreateTableResponse&amp;gt; createTableRequest = listTableResponse
                .thenCompose(response -&amp;gt; {
                    boolean tableExist = response.tableNames().contains(TABLE_NAME);
                    if (!tableExist) {
                        return createTable();
                    } else {
                        return CompletableFuture.completedFuture(null);
                    }
                });

        //Wait in synchronous manner for table creation
        createTableRequest.get();
    }

    public CompletableFuture&amp;lt;PutItemResponse&amp;gt; saveEvent(Event event) {
        Map&amp;lt;String, AttributeValue&amp;gt; item = new HashMap&amp;lt;&amp;gt;();
        item.put(ID_COLUMN, AttributeValue.builder().s(event.getUuid()).build());
        item.put(BODY_COLUMN, AttributeValue.builder().s(event.getBody()).build());

        PutItemRequest putItemRequest = PutItemRequest.builder()
                .tableName(TABLE_NAME)
                .item(item)
                .build();

        return client.putItem(putItemRequest);
    }

    public CompletableFuture&amp;lt;Event&amp;gt; getEvent(String id) {
        Map&amp;lt;String, AttributeValue&amp;gt; key = new HashMap&amp;lt;&amp;gt;();
        key.put(ID_COLUMN, AttributeValue.builder().s(id).build());

        GetItemRequest getRequest = GetItemRequest.builder()
                .tableName(TABLE_NAME)
                .key(key)
                .attributesToGet(BODY_COLUMN)
                .build();

        return client.getItem(getRequest).thenApply(item -&amp;gt; {
            if (!item.hasItem()) {
                return null;
            } else {
                Map&amp;lt;String, AttributeValue&amp;gt; itemAttr = item.item();
                String body = itemAttr.get(BODY_COLUMN).s();
                return new Event(id, body);
            }
        });
    }

    private CompletableFuture&amp;lt;CreateTableResponse&amp;gt; createTable() {

        CreateTableRequest request = CreateTableRequest.builder()
                .tableName(TABLE_NAME)

                .keySchema(KeySchemaElement.builder().attributeName(ID_COLUMN).keyType(KeyType.HASH).build())
                .attributeDefinitions(AttributeDefinition.builder().attributeName(ID_COLUMN).attributeType(ScalarAttributeType.S).build())
                .billingMode(BillingMode.PAY_PER_REQUEST)
                .build();

        return client.createTable(request);
    }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  4. Reactive REST Controller
&lt;/h1&gt;

&lt;p&gt;A simple controller with GET method for retrieving event by id and POST method for saving events in DynamoDB. We can do it in two ways - implement it with annotations or get rid of annotations and do it in functional way.There is no performance impact, in almost most cases it is absolutely based on individual preference what to use.&lt;/p&gt;

&lt;h6&gt;
  
  
  4.1 Annotated Controllers
&lt;/h6&gt;



&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@RestController
@RequestMapping("/event")
public class AnnotatedController {

    final DynamoDbService dynamoDbService;

    public AnnotatedController(DynamoDbService dynamoDbService) {
        this.dynamoDbService = dynamoDbService;
    }

    @GetMapping(value = "/{eventId}", produces = MediaType.APPLICATION_JSON_VALUE)
    public Mono&amp;lt;Event&amp;gt; getEvent(@PathVariable String eventId) {
        CompletableFuture&amp;lt;Event&amp;gt; eventFuture = dynamoDbService.getEvent(eventId);
        return Mono.fromCompletionStage(eventFuture);
    }

    @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE)
    public void saveEvent(@RequestBody Event event) {
        dynamoDbService.saveEvent(event);
    }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h6&gt;
  
  
  4.2 Functional Endpoints
&lt;/h6&gt;

&lt;p&gt;This is a lightweight functional programming model in which functions are used to route and handle requests.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Configuration
public class HttpRouter {

    @Bean
    public RouterFunction&amp;lt;ServerResponse&amp;gt; eventRouter(DynamoDbService dynamoDbService) {
        EventHandler eventHandler = new EventHandler(dynamoDbService);
        return RouterFunctions
                .route(GET("/eventfn/{id}")
                        .and(accept(APPLICATION_JSON)), eventHandler::getEvent)
                .andRoute(POST("/eventfn")
                        .and(accept(APPLICATION_JSON))
                        .and(contentType(APPLICATION_JSON)), eventHandler::saveEvent);
    }

    static class EventHandler {
        private final DynamoDbService dynamoDbService;

        public EventHandler(DynamoDbService dynamoDbService) {
            this.dynamoDbService = dynamoDbService;
        }

        Mono&amp;lt;ServerResponse&amp;gt; getEvent(ServerRequest request) {
            String eventId = request.pathVariable("id");
            CompletableFuture&amp;lt;Event&amp;gt; eventGetFuture = dynamoDbService.getEvent(eventId);
            Mono&amp;lt;Event&amp;gt; eventMono = Mono.fromFuture(eventGetFuture);
            return ServerResponse.ok().body(eventMono, Event.class);
        }

        Mono&amp;lt;ServerResponse&amp;gt; saveEvent(ServerRequest request) {
            Mono&amp;lt;Event&amp;gt; eventMono = request.bodyToMono(Event.class);
            eventMono.map(dynamoDbService::saveEvent);
            return ServerResponse.ok().build();
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  5. Spring DynamoDB Integration Test
&lt;/h1&gt;

&lt;h6&gt;
  
  
  5.1 Maven dependencies
&lt;/h6&gt;

&lt;p&gt;For running integration test with DynamoDB we need DynamoDBLocal, which is not really the DynamoDB, but SQLite withimplemented DynamoDB interfaces on top of it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;com.amazonaws&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;DynamoDBLocal&amp;lt;/artifactId&amp;gt;
    &amp;lt;version&amp;gt;1.12.0&amp;lt;/version&amp;gt;
    &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;

&amp;lt;build&amp;gt;
    &amp;lt;plugins&amp;gt;
        &amp;lt;plugin&amp;gt;
            &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;
            &amp;lt;artifactId&amp;gt;maven-dependency-plugin&amp;lt;/artifactId&amp;gt;
            &amp;lt;version&amp;gt;2.10&amp;lt;/version&amp;gt;
            &amp;lt;executions&amp;gt;
                &amp;lt;execution&amp;gt;
                    &amp;lt;id&amp;gt;copy&amp;lt;/id&amp;gt;
                    &amp;lt;phase&amp;gt;test-compile&amp;lt;/phase&amp;gt;
                    &amp;lt;goals&amp;gt;
                        &amp;lt;goal&amp;gt;copy-dependencies&amp;lt;/goal&amp;gt;
                    &amp;lt;/goals&amp;gt;
                    &amp;lt;configuration&amp;gt;
                        &amp;lt;includeScope&amp;gt;test&amp;lt;/includeScope&amp;gt;
                        &amp;lt;includeTypes&amp;gt;so,dll,dylib&amp;lt;/includeTypes&amp;gt;
                        &amp;lt;!--Keen an eye on output directory - it will be used for starting dynamodb--&amp;gt;
                        &amp;lt;outputDirectory&amp;gt;${project.basedir}/target/native-libs&amp;lt;/outputDirectory&amp;gt;
                    &amp;lt;/configuration&amp;gt;
                &amp;lt;/execution&amp;gt;
            &amp;lt;/executions&amp;gt;
        &amp;lt;/plugin&amp;gt;
    &amp;lt;/plugins&amp;gt;
&amp;lt;/build&amp;gt;

&amp;lt;repositories&amp;gt;
    &amp;lt;repository&amp;gt;
        &amp;lt;id&amp;gt;dynamodb-local-oregon&amp;lt;/id&amp;gt;
        &amp;lt;name&amp;gt;DynamoDB Local Release Repository&amp;lt;/name&amp;gt;
        &amp;lt;url&amp;gt;https://s3-us-west-2.amazonaws.com/dynamodb-local/release&amp;lt;/url&amp;gt;
    &amp;lt;/repository&amp;gt;
&amp;lt;/repositories&amp;gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h6&gt;
  
  
  5.2 DynamoDB server
&lt;/h6&gt;

&lt;p&gt;Now we need to start DynamoDB before test run. I prefer to do it as JUnit Class Rule, but we can also do it as a spring bean.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;public class LocalDynamoDbRule extends ExternalResource {

    protected DynamoDBProxyServer server;

    public LocalDynamoDbRule() {
        //here we set the path from "outputDirectory" of maven-dependency-plugin
        System.setProperty("sqlite4java.library.path", "target/native-libs");
    }

    @Override
    protected void before() throws Exception {
        this.server = ServerRunner
            .createServerFromCommandLineArgs(new String[]{"-inMemory", "-port", "8000"});
        server.start();
    }

    @Override
    protected void after() {
        this.stopUnchecked(server);
    }

    protected void stopUnchecked(DynamoDBProxyServer dynamoDbServer) {
        try {
            dynamoDbServer.stop();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h6&gt;
  
  
  5.3 Running test
&lt;/h6&gt;

&lt;p&gt;Now we can create an integration test and test get event by id and save event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class IntegrationTest {

    @ClassRule
    public static LocalDynamoDbRule dynamoDbRule = new LocalDynamoDbRule();

    @Autowired
    private WebTestClient webTestClient;

    @Test
    public void getEvent() {
        // Create a GET request to test an endpoint
        webTestClient
                .get().uri("/event/1")
                .accept(MediaType.APPLICATION_JSON)
                .exchange()
                // and use the dedicated DSL to test assertions against the response
                .expectStatus().isOk()
                .expectBody(String.class).isEqualTo(null);
    }

    @Test
    public void saveEvent() throws InterruptedException {
        Event event = new Event("10", "event");
        webTestClient
                .post().uri("/event/")
                .body(BodyInserters.fromValue(event))
                .exchange()
                .expectStatus().isOk();
        Thread.sleep(1500);
        webTestClient
                .get().uri("/event/10")
                .accept(MediaType.APPLICATION_JSON)
                .exchange()
                .expectStatus().isOk()
                .expectBody(Event.class).isEqualTo(event);
    }
}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h1&gt;
  
  
  6. Docker
&lt;/h1&gt;

&lt;p&gt;Here we gonna prepare our application for running in docker, so it will be ready for deploying to AWS.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Small hint:&lt;/em&gt; starting from Java 10 you can specify how much memory JVM will use depending on container memory.&lt;br&gt;
&lt;code&gt;-XX:MaxRAMPercentage=75.0&lt;/code&gt; means JVM won't use more then 75% of a container memory.&lt;/p&gt;
&lt;h6&gt;
  
  
  Dockerfile
&lt;/h6&gt;


&lt;div class="highlight"&gt;&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use our standard java12 baseimage&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; openjdk:12-alpine&lt;/span&gt;

&lt;span class="c"&gt;# Copy the artifact into the container&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; target/dynamodb-spring-*-exec.jar /srv/service.jar&lt;/span&gt;

&lt;span class="c"&gt;# Run the artifact and expose the default port&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /srv&lt;/span&gt;

&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; [ "java", \&lt;/span&gt;
    "-XX:+UnlockExperimentalVMOptions", \
    "-XX:+ExitOnOutOfMemoryError", \
    "-XX:MaxRAMPercentage=75.0", \
    "-Djava.security.egd=file:/dev/./urandom", \
    "-jar", "service.jar", \
    "--spring.profiles.active=prod" ]

&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;p&gt;Building the docker container itself &lt;code&gt;docker build -t spring-dynamo .&lt;/code&gt; &lt;br&gt;
Also let's see what was generated by &lt;code&gt;sudo docker image ls&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
spring-dynamo       latest              a974d880400e        About a minute ago   364MB
openjdk             12-alpine           0c68e7c5b7a0        12 months ago        339MB
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;








&lt;p&gt;Finally, our poc is ready!&lt;/p&gt;

&lt;p&gt;Happy coding :)&lt;/p&gt;

</description>
      <category>java</category>
      <category>aws</category>
      <category>microservices</category>
      <category>spring</category>
    </item>
  </channel>
</rss>
