Developing with Java or Scala

Select your preferred language (Java/Scala) above.

The Akka Serverless Java Scala SDK guides you through implementing components matching the protobuf APIs you design. This page describes prerequisites for Java Scala development and basic requirements for a development project.

Lightbend provides Tier 1 support for the Java Scala SDK. See an explanation of support tiers for more information.

Your development project needs to include the Akka Serverless Java Scala 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 from a template, complete with descriptors and implementations. Or, you can start from one of our fully implemented Quickstart applications.

Prerequisites

The following are required to develop services in Java:

Java

Akka Serverless requires Java 11 or later.

Apache Maven

For Java-based services, Akka Serverless relies on Apache Maven new tab as build tool. The Akka Serverless Maven plugin creates initial Java code for your components and the required surrounding code.

sbt

For Scala-based services, Akka Serverless relies on sbt new tab as build tool. The Akka Serverless sbt plugin creates initial Scala code for your components and the required surrounding code.

Docker

Akka Serverless requires Docker new tab 20.10.8 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 Maven. The code generation tools include an Akka Serverless project template that generates the recommended project structure, including a .pom file build.sbt with the necessary references.

In your .pom file build.sbt, add the following:

Java
<dependencies>
    <dependency>
        <groupId>com.akkaserverless</groupId>
        <artifactId>akkaserverless-java-sdk</artifactId>
        <version>0.10.1</version>
    </dependency>
    <dependency>
        <groupId>com.akkaserverless</groupId>
        <artifactId>akkaserverless-java-sdk-testkit</artifactId>
        <version>0.10.1</version>
        <scope>test</scope>
    </dependency>
</dependencies>
Scala
scalaVersion := "2.13.6"

enablePlugins(AkkaserverlessPlugin)

And in project/plugins.sbt:

addSbtPlugin("com.akkaserverless" % "sbt-akkaserverless" % "0.10.1")

Configure JSON formatted logging

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

Build and deploy the Quickstart example to see JSON formatted logging in action.

JSON formatted logging is enabled by default in the projects created by the Akka Serverless project template. It includes a transitive dependency on logback-json-classic and a logback.xml file as shown here:

src/main/resources/logback.xml
<configuration>
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%date{ISO8601} %-5level %logger - %msg%n</pattern>
        </encoder>
    </appender>

    <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>

    <logger name="akka" level="INFO"/>
    <logger name="com.akkaserverless" level="INFO"/>
    <logger name="akka.http" level="INFO"/>
    <logger name="io.grpc" level="INFO"/>

    <root level="INFO">
<!--        <appender-ref ref="STDOUT"/>-->
        <appender-ref ref="ASYNC-JSON-STDOUT"/>
    </root>
</configuration>

In the logback.xml you may want to adjust the log level for different loggers (typically a package or class name).

For local development you can switch to the STDOUT appender to make the logs more readable, or use <prettyPrint>true</prettyPrint> in the jsonFormatter. Don’t use prettyPrint in production since the logging infrastructure will not handle multi-line log messages.

There is a separate src/test/resources/logback-test.xml that is used when running tests.

Create a main class

The Akka Serverless plugin will create a main class for you and make sure all components get registered with the Akka Serverless server. The following code snippet shows an example that registers an Event Sourced Entity and starts the server:

Java
package com.example.shoppingcart;

import com.akkaserverless.javasdk.AkkaServerless;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.example.shoppingcart.domain.ShoppingCart;
import com.example.shoppingcart.view.ShoppingCartViewServiceImpl;

public final class Main {

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

  public static AkkaServerless createAkkaServerless() {
    // The AkkaServerlessFactory automatically registers any generated Actions, Views or Entities,
    // and is kept up-to-date with any changes in your protobuf definitions.
    // If you prefer, you may remove this and manually register these components in a
    // `new AkkaServerless()` instance.
    return AkkaServerlessFactory.withComponents(
        ShoppingCart::new,
        ShoppingCartViewServiceImpl::new
    );
  }

  public static void main(String[] args) throws Exception {
    LOG.info("starting the Akka Serverless service");
    createAkkaServerless().start();
  }
}
Scala
package customer

import com.akkaserverless.scalasdk.AkkaServerless
import customer.action.CustomerActionImpl
import customer.domain.CustomerValueEntity
import customer.view.CustomerByEmailView
import customer.view.CustomerByNameView
import customer.view.CustomerSummaryByNameView
import customer.view.CustomersResponseByNameView
import org.slf4j.LoggerFactory

object Main {

  private val log = LoggerFactory.getLogger("customer.Main")

  def createAkkaServerless(): AkkaServerless = {
    // The AkkaServerlessFactory automatically registers any generated Actions, Views or Entities,
    // and is kept up-to-date with any changes in your protobuf definitions.
    // If you prefer, you may remove this and manually register these components in a
    // `AkkaServerless()` instance.
    AkkaServerlessFactory.withComponents(
      new CustomerValueEntity(_),
      new CustomerActionImpl(_),
      new CustomerByEmailView(_),
      new CustomerByNameView(_),
      new CustomerSummaryByNameView(_),
      new CustomersResponseByNameView(_))
      /*
      return AkkaServerlessFactory.withComponents(
        new CustomerValueEntity(_),
        new CustomerByNameView(_))
      */
  }

  def main(args: Array[String]): Unit = {
    log.info("starting the Akka Serverless service")
    createAkkaServerless().start()
  }
}