Developing with Java or Scala

Select your preferred language (Java/Scala) above.

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

Kalix requires Java 11 or later.

Apache Maven

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

sbt

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

Docker

Kalix requires Docker new tab 20.10.14 for building your service images. Most popular build tools have plugins that assist in building Docker images.

Reference the Kalix SDK

The following examples show how to install the SDK to build your services with Maven. The code generation tools include a Kalix 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>io.kalix</groupId>
        <artifactId>kalix-java-sdk</artifactId>
        <version>1.1.2</version>
    </dependency>
    <dependency>
        <groupId>io.kalix</groupId>
        <artifactId>kalix-java-sdk-testkit</artifactId>
        <version>1.1.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>
Scala
scalaVersion := "2.13.8"

enablePlugins(KalixPlugin)

And in project/plugins.sbt:

addSbtPlugin("io.kalix" % "sbt-kalix" % "1.1.2")

Configure JSON formatted logging

Kalix supports JSON formatted logging to provide multi-line messages formatted in JSON syntax. Always use JSON formatted logging for your Kalix 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 Kalix 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="kalix.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="kalix" 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 Kalix plugin will create a main class for you and make sure all components get registered with the Kalix server. The following code snippet shows an example that registers an Event Sourced Entity and starts the server:

Java
package com.example.shoppingcart;

import kalix.javasdk.Kalix;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.example.shoppingcart.domain.*;
import com.example.shoppingcart.view.*;

public final class Main {

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

  public static Kalix createKalix() {
    Kalix kalix = new Kalix();
    kalix.register(ShoppingCartProvider.of(ShoppingCart::new));
    kalix.register(ShoppingCartViewServiceViewProvider.of(ShoppingCartViewServiceImpl::new));
    return kalix;
  }

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

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 kalix.scalasdk.Kalix
import org.slf4j.LoggerFactory

// This class was initially generated based on the .proto definition by Kalix tooling.
//
// As long as this file exists it will not be overwritten: you can maintain it yourself,
// or delete it so it is regenerated as needed.

object Main {

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

  def createKalix(): Kalix = {
    // The KalixFactory 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
    // `Kalix()` instance.
    KalixFactory.withComponents(
      new CustomerValueEntity(_),
      new CustomerActionImpl(_),
      new CustomerByEmailView(_),
      new CustomerByNameView(_),
      new CustomerSummaryByNameView(_),
      new CustomersResponseByNameView(_))
  }

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