Implementation overview

As mentioned previously, your project needs to include Cloudstate libraries. Each of your service projects will need a gRPC descriptor from which you generate code, and you need to add logic to start the gRPC server. The following sections illustrate how you can accomplish these basics in the context of a shopping cart application.

You can download the complete shopping cart example from: github. To deploy it on Akka Serverless, follow the directions in the top-level README.

Build file examples

The following illustrate how to configure build tools for the shopping cart example:

Gradle

A full example can be found here: Java Shopping Cart - build.gradle new tab:

plugins {
  id "java"
  id "idea"
  id "com.google.protobuf" version "0.8.12"
  id "com.google.cloud.tools.jib" version "2.2.0"
}

repositories {
  mavenCentral()
}

group = "com.example"
version = "0.1.0"

dependencies {
  implementation "io.cloudstate:cloudstate-java-support:0.4.7"
}

protobuf {
  protoc {
    artifact = "com.google.protobuf:protoc:3.9.0"
  }
}

java {
  sourceCompatibility = JavaVersion.VERSION_1_8
  targetCompatibility = JavaVersion.VERSION_1_8
}

jib {
  from {
    image = "adoptopenjdk/openjdk8:debian"
  }
  to {
    image = "lightbend-docker-registry.bintray.io/cloudstate-samples/shopping-cart-java"
    tags = [version]
  }
  container {
    mainClass = "io.cloudstate.samples.shoppingcart.Main"
    ports = ["8080"]
  }
}
Maven

The following Maven example uses the Xolstice Maven Protocol Buffers Plugin and the Fabric8 Docker Maven Plugin.

+

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
                      http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>shopping-cart</artifactId>
  <version>1.0-SNAPSHOT</version>

  <packaging>jar</packaging>

  <build>
    <extensions>
      <extension>
        <groupId>kr.motd.maven</groupId>
        <artifactId>os-maven-plugin</artifactId>
        <version>1.6.0</version>
      </extension>
    </extensions>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <!-- java version -->
          <source>1.8</source>
          <target>1.8</target>
        </configuration>
      </plugin>

      <plugin>
        <groupId>org.xolstice.maven.plugins</groupId>
        <artifactId>protobuf-maven-plugin</artifactId>
        <version>0.6.1</version>
        <configuration>
          <protocArtifact>com.google.protobuf:protoc:3.9.1:exe:${os.detected.classifier}</protocArtifact>
        </configuration>
        <executions>
          <execution>
            <goals>
              <goal>compile</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>2.1</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <!-- <shadedArtifactAttached>true</shadedArtifactAttached>
              <shadedClassifierName>allinone</shadedClassifierName> -->
              <transformers>
                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
                  <resource>reference.conf</resource>
               </transformer>
               <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                <!-- main entry class -->
                <mainClass>io.cloudstate.samples.shoppingcart.Main</mainClass>
              </transformer>
              </transformers>
            </configuration>
          </execution>
        </executions>
      </plugin>

      <plugin>
        <groupId>io.fabric8</groupId>
        <artifactId>docker-maven-plugin</artifactId>
        <version>0.26.1</version>
        <configuration>
          <images>
            <image>
              <!-- Please change it to your Docker repository info -->
              <name>my-docker-repo/shopping-cart-java:%l</name>
              <build>
                <!-- Base Docker image which contains jre-->
                <from>adoptopenjdk/openjdk8:alpine-jre</from>
                <tags>
                  <!-- tag for generated image -->
                  <tag>my-tag</tag>
                </tags>
                <ports>
                  <!-- expose port in Docker container -->
                  <port>8080</port>
                </ports>
                <assembly>
                  <!-- NOTE: (optional) switch to "artifact-with-dependencies" to show dependencies library-->
                  <descriptorRef>artifact</descriptorRef>
                </assembly>
                <entryPoint>
                  <arg>java</arg>
                  <arg>-jar</arg>
                  <arg>/maven/${project.build.finalName}.jar</arg>
                </entryPoint>
              </build>
            </image>
          </images>
        </configuration>
        <executions>
          <execution>
            <id>build-docker-image</id>
            <phase>package</phase>
            <goals>
              <goal>build</goal>
            </goals>
          </execution>
        </executions>
      </plugin>

    </plugins>
  </build>

  <dependencies>
    <dependency>
      <groupId>io.cloudstate</groupId>
      <artifactId>cloudstate-java-support</artifactId>
      <version>0.5.2</version>
    </dependency>
  </dependencies>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
</project>

Subsequent source locations and build commands will assume the above Maven project, and may need to be adapted to your particular build tool and setup.

sbt

In your dependencies file, add the following:

libraryDependencies += "io.cloudstate" % "cloudstate-java-support" % "11"

Protobuf files

The Xolstice Maven plugin assumes a location of src/main/proto for your protobuf files. In addition, it includes any protobuf files from your Java dependencies in the protoc include path, so the Cloudstate protobuf types and the Google standard protobuf types are all automatically available for import.

If you were to build the example shopping cart application shown earlier in gRPC descriptors, you could simply paste that protobuf into src/main/proto/shoppingcart.proto. You may wish to also define the Java package, to ensure the package name used conforms to Java package naming conventions:

option java_package = "com.example";

Now if you run mvn compile, you’ll find your generated protobuf files in target/generated-sources/protobuf/java.

Create a main class

Your main class will be responsible for creating the Cloudstate gRPC server, registering the entities for it to serve, and starting it. To do this, you can use the CloudStatenew tab server builder. The following file contains a full example: Java Shopping Cart - Main.java new tab:

package io.cloudstate.samples.shoppingcart;

import io.cloudstate.javasupport.*;
import com.example.shoppingcart.Shoppingcart;
import static java.util.Collections.singletonMap;

public final class Main {
  public static final void main(String[] args) throws Exception {
    new CloudState()
        .registerEventSourcedEntity(
            ShoppingCartEntity.class,
            Shoppingcart.getDescriptor().findServiceByName("ShoppingCart"),
            com.example.shoppingcart.persistence.Domain.getDescriptor())
        .start()
        .toCompletableFuture()
        .get();
  }
}

We will see more details on registering entities in the coming pages.

Parameter injection

Cloudstate entities work by annotating classes and methods to be instantiated and invoked by the Cloudstate server. The methods and constructors invoked by the server can be injected with parameters of various types from the context of the invocation. For example, an @CommandHandler annotated method may take an argument for the message type for that gRPC service call, in addition it may take a CommandContext parameter.

Exactly which context parameters are available depend on the type of entity and the type of handler, in subsequent pages we’ll detail which parameters are available in which circumstances. The order of the parameters in the method signature can be anything, parameters are matched by type and sometimes by annotation. The following context parameters are available in every context:

Type Annotation Description

Contextnew tab

The super type of all Cloudstate contexts. Every invoker makes a subtype of this available for injection, and method or constructor may accept that sub type, or any super type of that subtype that is a subtype of Context.

java.lang.String

@EntityId

The ID of the entity.