Developing with Java

The Akka Serverless Java SDK offers an idiomatic, annotation-based Java language SDK for writing components. This page describes prerequisites for Java development and basic requirements for a development project.

Your development project needs to include the Akka Serverless Java SDK and logic to start the gRPC server. You define your components in gRPC descriptors and use protoc to compile them. Finally, you implement business logic for service components.

To save the work of starting from scratch, the Java code generation tool creates a project, complete with descriptors and implementations. Or, you can start from one of our example applications.

Prerequisites

The following are required to develop services in Java:

Java

Akka Serverless requires at least Java 8, though we recommend using Java 11, which has better support for running in containers.

Build tool

Akka Serverless does not require any particular build tool, you can select your own.

protoc

Since Akka Serverless is based on gRPC, you need a protoc compiler to compile gRPC protobuf descriptors. While this can be done by downloading, installing and running protoc manually, most popular build tools have a protoc plugin which will automatically compile protobuf descriptors during your build.

Docker

Akka Serverless requires Docker new tab 19.03 for building your service images. Most popular build tools have plugins that assist in building Docker images.

Reference the Akka Serverless SDK

The following examples show how to install the SDK to build your services with Gradle, Maven, or sbt. The code generation tools include an Akka Serverless Maven archetype that generates the recommended project structure, including a .pom file with the necessary references.

Maven

In your .pom file, add the following:

<dependencies>
    <dependency>
        <groupId>com.akkaserverless</groupId>
        <artifactId>akkaserverless-java-sdk</artifactId>
        <version>0.7.0-beta.18</version>
    </dependency>
    <dependency>
        <groupId>com.akkaserverless</groupId>
        <artifactId>akkaserverless-java-sdk-testkit</artifactId>
        <version>0.7.0-beta.18</version>
        <scope>test</scope>
    </dependency>
</dependencies>
Gradle

In your build.gradle file, add the following:

compile group: 'com.akkaserverless', name: 'akkaserverless-java-sdk', version: '0.7.0-beta.18'
sbt

In your dependencies file, add the following:

libraryDependencies ++= Seq(
    "com.akkaserverless" % "akkaserverless-java-sdk" % "0.7.0-beta.18",
    "com.akkaserverless" % "akkaserverless-java-sdk-testkit" % "0.7.0-beta.18" % Test
  )

Configure JSON formatted logging

Akka Serverless supports JSON formatted logging to provide multi-line messages formatted in readable JSON syntax. Always use JSON formatted logging for your Akka Serverless projects to efficiently analyze and easily leverage journal data.

Build and deploy the IoT example to see JSON formatted logging in action.
JSON formatted logging is available for Java only. Support for JavaScript and other languages is planned for future releases.

To enable JSON formatted logging:

  1. Update the akkaserverless sdk to a recent version, in the project pom.xml.

    <akkaserverless-sdk.version>0.7.0-beta.18</akkaserverless-sdk.version>
  2. Add the following dependency:

    <dependency>
      <!-- for JSON formatted logging -->
      <groupId>ch.qos.logback.contrib</groupId>
      <artifactId>logback-json-classic</artifactId>
      <version>0.1.5</version>
    </dependency>
  3. Create the src/main/logback.xml file as shown here:

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
        <appender name="JSON-STDOUT" target="System.out" class="ch.qos.logback.core.ConsoleAppender">
          <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="com.akkaserverless.javasdk.logging.LogbackJsonLayout">
                <timestampFormat>yyyy-MM-dd'T'HH:mm:ss.SSSX</timestampFormat>
                <timestampFormatTimezoneId>Etc/UTC</timestampFormatTimezoneId>
                <appendLineSeparator>true</appendLineSeparator>
                <jsonFormatter class="ch.qos.logback.contrib.jackson.JacksonJsonFormatter">
                    <prettyPrint>false</prettyPrint>
                </jsonFormatter>
            </layout>
          </encoder>
        </appender>
    
        <appender name="ASYNC-JSON-STDOUT" class="ch.qos.logback.classic.AsyncAppender">
            <queueSize>8192</queueSize>
            <neverBlock>true</neverBlock>
            <appender-ref ref="JSON-STDOUT"/>
        </appender>
        <appender name="STDOUT" target="System.out" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>[%date{ISO8601}] [%-5level] [%logger] - %msg%n</pattern>
            </encoder>
        </appender>
    
        <logger name="com.akkaserverless" level="INFO"/>
        <logger name="akka" level="INFO"/>
        <logger name="akka.http" level="INFO"/>
        <logger name="io.grpc" level="INFO"/>
    
        <root level="DEBUG">
            <!--appender-ref ref="STDOUT"/-->
            <appender-ref ref="ASYNC-JSON-STDOUT"/>
        </root>
    </configuration>

Create a main class

Your main class will be responsible for creating the gRPC server, registering the services and components for it to serve, and starting it. The following code snippet shows an example that registers an Event Sourced Entity and starts the server:

package shopping;

import com.akkaserverless.javasdk.AkkaServerless;

public final class Main {
  private static final Logger LOG = LoggerFactory.getLogger(Main.class);

  public static final AkkaServerless SERVICE =
      new AkkaServerless()
          // event sourced shopping cart entity
          // receives commands from outside the service and persists events to its journal/event log
          .registerEventSourcedEntity(
              ShoppingCartEntity.class,
              ShoppingCartApi.getDescriptor().findServiceByName("ShoppingCartService"),
              ShoppingCartDomain.getDescriptor())

  public static final void main(String[] args) throws Exception {
    LOG.info("started");
    SERVICE.start().toCompletableFuture().get();
  }
}