Skip to main content

Overview

The Restate Java/Kotlin SDK is open source and can be found on GitHub: (sdk-java repo).

🚀Set up your project in seconds

Have a look at the quickstart for Java or Kotlin!

The Restate SDK lets you implement handlers. Handlers can either be part of a Service, a Virtual Object, or a Workflow. Let's have a look at how to define them.

Services

Services and their handlers are defined as follows:

@Service
public class MyService {
@Handler
public String myHandler(Context ctx, String greeting) {
return greeting + "!";
}
public static void main(String[] args) {
RestateHttpEndpointBuilder.builder().bind(new MyService()).buildAndListen();
}
}
  • Use the @Service and @Handler annotations
  • Handlers have the Context parameter (JavaDocs) as the first parameter. Within the handler, you use the Context to interact with Restate. The SDK stores the actions you do on the context in the Restate journal to make them durable.
  • You can retrieve the key of the object you are in via ctx.key().
  • The input parameter (at most one) and return type are optional and can be of any type, as long as they are serializable/deserializable using Jackson Databind (see serialization docs).
  • The service will be reachable under the simple class name MyService. You can override it by using the annotation field name.
  • Create an endpoint and bind the service(s) to the Restate endpoint. Listen on the specified port (default 9080) for connections and requests.

Virtual Objects

Virtual Objects and their handlers are defined similarly to services, with the following differences:

@VirtualObject
public class MyVirtualObject {
@Handler
public String myHandler(ObjectContext ctx, String greeting) {
String objectId = ctx.key();
return greeting + " " + objectId + "!";
}
@Shared
public String myConcurrentHandler(SharedObjectContext ctx, String input) {
return "my-output";
}
public static void main(String[] args) {
RestateHttpEndpointBuilder.builder().bind(new MyVirtualObject()).buildAndListen();
}
}
  • Use the @VirtualObject annotation.
  • The first argument of the handler must be the ObjectContext parameter (JavaDocs). Handlers with the ObjectContext parameter can write to the K/V state store. Only one handler can be active at a time, to ensure consistency.
  • You can retrieve the key of the object you are in via ctx.key().
  • If you want to have a handler that executes concurrently to the others and doesn't have write access to the K/V state, use the @Shared annotation and the SharedObjectContext. You can use these handlers, for example, to read K/V state and expose it to the outside world, or to interact with the blocking handler (e.g. resolve awakeables).

Workflows

Workflows are a special type of Virtual Objects, their definition is similar but with the following differences:

@Workflow
public class MyWorkflow {
@Workflow
public String run(WorkflowContext ctx, String input) {
// implement workflow logic here
return "success";
}
@Shared
public void interactWithWorkflow(SharedWorkflowContext ctx, String input) {
// implement interaction logic here
}
public static void main(String[] args) {
RestateHttpEndpointBuilder.builder().bind(new MyWorkflow()).buildAndListen();
}
}
  • Create the workflow
  • Every workflow implementation needs to have a handler called run that implements the workflow logic. This handler uses the WorkflowContext to interact with the SDK. The run handler executes exactly one time per workflow execution/object.
  • The other handlers of the workflow are used to interact with the workflow: either query it, or signal it. They use the SharedWorkflowContext to interact with the SDK. These handlers can run concurrently with the run handler and can still be called after the run handler has finished.
  • Have a look at the workflow docs to learn more.

Now that you have a high-level idea of what a Restate service might look like, let's have a look at what the Restate Context allows you to do.

💡tip

The Java SDK generates code for service clients when you compile your project. Turn on IntelliJ IDEA annotation processing support, to be able to re-run code generation by pressing CTRL + F9.

Annotating interfaces

Annotations can also be placed on interfaces. This is useful, for example, if you want to split your service in two packages, one containing the interface and the generated clients, and one containing the implementation.

Manual project setup

You can use the build tool of your choice with the Java/Kotlin SDK. The following instructions use Gradle (Kotlin script).

To set up your Java project, run:

gradle init --type java-application

Add the following dependencies:

annotationProcessor("dev.restate:sdk-api-gen:1.2.0")
implementation("dev.restate:sdk-api:1.2.0")

When serializing composite types/POJOs with Jackson Databind (default), add the following dependency:

implementation("dev.restate:sdk-serde-jackson:1.2.0")

Depending on the deployment target, add one of the following dependencies:

  • To run as HTTP endpoint: dev.restate:sdk-http-vertx:1.2.0
  • To run on AWS Lambda: dev.restate:sdk-lambda:1.2.0

Manual service definition without annotation processing In case you don't want to use annotation processing, you can manually define your service by using the class dev.restate.sdk.common.syscalls.ServiceDefinition. Check the respective JavaDocs/KTDocs for more details.