Overview
The Restate Java/Kotlin SDK is open source and can be found on GitHub: (sdk-java repo).
- JDK >= 17
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:
- Java
- Kotlin
@Servicepublic class MyService {@Handlerpublic String myHandler(Context ctx, String greeting) {return greeting + "!";}public static void main(String[] args) {RestateHttpServer.listen(Endpoint.bind(new MyService()));}}
@Serviceclass MyService {@Handlersuspend fun myHandler(ctx: Context, greeting: String): String {return "$greeting!"}}fun main() {RestateHttpServer.listen(endpoint { bind(MyService()) })}
- Use the
@Service
and@Handler
annotations - Handlers have the
Context
parameter (JavaDocs/KotlinDocs) as the first parameter. Within the handler, you use theContext
to interact with Restate. The SDK stores the actions you do on the context in the Restate journal to make them durable. - The input parameter (at most one) and return type are optional and can be of any type. See serialization for more details.
- The service will be reachable under the simple class name
MyService
. You can override it by using the annotation@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:
- Java
- Kotlin
@VirtualObjectpublic class MyVirtualObject {@Handlerpublic String myHandler(ObjectContext ctx, String greeting) {String objectId = ctx.key();return greeting + " " + objectId + "!";}@Sharedpublic String myConcurrentHandler(SharedObjectContext ctx, String input) {return "my-output";}public static void main(String[] args) {RestateHttpServer.listen(Endpoint.bind(new MyVirtualObject()));}}
@VirtualObjectclass MyVirtualObject {@Handlersuspend fun myHandler(ctx: ObjectContext, greeting: String): String {val objectKey = ctx.key()return "$greeting $objectKey!"}@Sharedsuspend fun myConcurrentHandler(ctx: SharedObjectContext, input: String): String {return "my-output"}}fun main() {RestateHttpServer.listen(endpoint { bind(MyVirtualObject()) })}
- Use the
@VirtualObject
annotation. - The first argument of the handler must be the
ObjectContext
parameter (JavaDocs/KotlinDocs). Handlers with theObjectContext
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 theSharedObjectContext
(JavaDocs/KotlinDocs). 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:
- Java
- Kotlin
@Workflowpublic class MyWorkflow {@Workflowpublic String run(WorkflowContext ctx, String input) {// implement workflow logic herereturn "success";}@Sharedpublic void interactWithWorkflow(SharedWorkflowContext ctx, String input) {// implement interaction logic here}public static void main(String[] args) {RestateHttpServer.listen(Endpoint.bind(new MyWorkflow()));}}
@Workflowclass MyWorkflow {@Workflowsuspend fun run(ctx: WorkflowContext, input: String): String {// implement workflow logic herereturn "success"}@Handlersuspend fun interactWithWorkflow(ctx: SharedWorkflowContext, input: String) {// implement interaction logic here}}fun main() {RestateHttpServer.listen(endpoint { bind(MyWorkflow()) })}
- Create the workflow by using the
@Workflow
annotation. - Every workflow implementation needs to have exactly one handler that implements the workflow logic and has the
@Workflow
annotation. This handler uses theWorkflowContext
(JavaDocs/KotlinDocs) to interact with the SDK. - The workflow handler executes exactly one time per workflow execution/object. You can retrieve the ID of the workflow execution via
ctx.key()
. - The workflow handler executes a set of steps/activities. These can be inlined SDK actions (for example run block or sleep), or abstracted into calls to other handlers.
- The other handlers of the workflow definition are used to interact with the workflow: either query it, or signal it. They use the
SharedWorkflowContext
(JavaDocs/KotlinDocs) to interact with the SDK. These handlers can run concurrently with the workflow handler and can still be called after the workflow 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.
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).
- Java
- Kotlin
To set up your Java project, run:
gradle init --type java-application
Add the following dependencies for deploying as HTTP service:
annotationProcessor("dev.restate:sdk-api-gen:2.0.0")implementation("dev.restate:sdk-java-http:2.0.0")
Add the following dependencies for deploying as Lambda:
annotationProcessor("dev.restate:sdk-api-gen:2.0.0")implementation("dev.restate:sdk-java-lambda:2.0.0")
To set up your Kotlin project, run:
gradle init --type kotlin-application
Add the Kotlin symbol processing and the Kotlin serialization plugin:
plugins {kotlin("plugin.serialization") version "2.0.0"id("com.google.devtools.ksp") version "2.0.0-1.0.21"}
Add the following dependencies for deploying as HTTP service:
ksp("dev.restate:sdk-api-kotlin-gen:2.0.0")implementation("dev.restate:sdk-kotlin-http:2.0.0")
Add the following dependencies for deploying as Lambda:
ksp("dev.restate:sdk-api-kotlin-gen:2.0.0")implementation("dev.restate:sdk-kotlin-lambda:2.0.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.endpoint.definition.ServiceDefinition
and dev.restate.sdk.HandlerRunner
for Java, dev.restate.sdk.HandlerRunner
for Kotlin.