Skip to main content

LittleHorse Runtime (Quarkus)

This guide will walk you through using the LittleHorse Runtime Extension for Quarkus in a complete local development setup.

You will do the following:

  • Spin up a local LittleHorse environment using Docker
  • Spin up a local Quarkus environment that depends on the LittleHorse Runtime Extension for Quarkus
    • LittleHorse Runtime will register our WfSpec and TaskDefs, and start Task Workers according to our code
  • Query Quarkus REST endpoints that wrap LittleHorse gRPC client functionality, such as:
    • Running a WfRun
    • Getting the state of WfRun Variables
    • Posting an ExternalEvent
    • Searching for WfRuns using variable filters

What is LittleHorse Runtime?

LittleHorse Runtime is a powerful Quarkus-based runtime for interacting with LittleHorse Kernel and Pony ID. Packaged as a Quarkus Extension, LittleHorse Runtime can perform automatic Workflow, Task, and UserTask Registration. The runtime can also manage the lifecycle of your Task Workers and report Health Checks using SmallRye Health. Out of the box, we provide several beans for injecting a LittleHorse gRPC client into your services. When you combine a LittleHorse gRPC client bean with other Quarkus extensions, such as Quarkus REST, the runtime becomes a powerful tool for integrating LittleHorse Client functionality into external systems.

When you're ready to put LittleHorse Runtime into production, Quarkus allows you to turn your JVM application into a native executable optimized to run in serverless and containerized environments. LittleHorse Enterprises has an upcoming product will allow you to run applications using the LH Runtime in a serverless application, stay tuned for that 😉.

Setup

Your system needs:

  • Java 17 or greater
  • Docker configured with at least 4GB of RAM

First, we will run a LittleHorse Server and Dashboard for development purposes in docker (using the lh-standalone docker image):

Important

Make sure you have at least 2GB of RAM allocated to Docker and ports 2023, 8080, and 9092 must be free.

docker run --pull always --name lh-standalone --rm -d -p 2023:2023 -p 8080:8080  -p 9092:9092 \
ghcr.io/littlehorse-enterprises/littlehorse/lh-standalone:latest
tip

If you are on Linux, you can simplify the command to run the container by making use of Docker's host network. This option has multiple bugs with MacOS:

docker run --pull always --name lh-standalone --rm -d --net=host \
ghcr.io/littlehorse-enterprises/littlehorse/lh-standalone:latest

Other Dependencies

You'll need to clone our lh-examples repository on GitHub:

git clone https://github.com/littlehorse-enterprises/lh-examples

# Move into the LH Runtime Quickstart directory
cd lh-examples/quickstart/lh-quarkus

Running the Quickstart

The quickstart workflow we will use today models an Identity Verification flow.

Starting LH Runtime

To start your LH Runtime application for development, just run the following command:

./gradlew quarkusDev

This application will expose a few REST endpoints at localhost:9000. We'll use these endpoints to interact with and query workflow data from LittleHorse.

The LH Runtime extension for Quarkus will automatically register any WfSpecs, TaskDefs, and UserTasks defined in your codebase that use the provided LH Runtime annotations, such as @LHWorkflow and @LHTask. These signal to LH Runtime that it should manage the lifecycle and execution of your workflows and tasks.

Take a look at the following classes:

  • IdentityVerificationWorkflow.java defines the WfSpec logic.
  • VerifyIdentityTask.java defines our verify-identity TaskDef.

As you can see, with LH Runtime you just need to define your WfSpecs and TaskDefs in code, while LH Runtime will take care of the registration of those objects. The LH Runtime extension will also start Task Workers for the TaskDefs define in your code.

You can check out the workflow in the LittleHorse Dashboard:

A screenshot of the LH Dashboard showing the Identity Verification workflow.
Identity Verification WfSpec

Run the WfRun

Let's run the workflow! We're going to verify the identity of Obi-Wan Kenobi.

In the IdentityResource class, we defined a POST endpoint /identity-verification/start that takes in a JSON Body with a full name, email, and SSN. This endpoint calls the IdentityService to handle the business logic of starting our Identity Verification workflow. This endpoint returns the ID of your Identity Verification WfRun so you can query the status later.

Using your HTTP client of choice, make a POST call to the /identity-verification/start endpoint with the following JSON body:

{
"fullName": "Obi-Wan Kenobi",
"email": "obiwankenobi@jedi.temple",
"ssn": 123456789
}

This endpoint should return a JSON Object with a unique WfRunId, for example:

{
"wfRunId": "374063862026437a9d9e289280ea4bf1"
}

Save this WfRunId for later, we'll use it to query the status of the Identity Verification workflow.

Dashboard View

In the LittleHorse Dashboard, you'll see the WfRun waiting for an ExternalEvent as follows:

A screenshot of the LH Dashboard showing a WfRun waiting for an ExternalEvent.
Identity Verification WfRun Waiting for an ExternalEvent

Notice the variables at the bottom of the dashboard, in the next step we will query an endpoint that reveals the status of our Identity Verification workflow based on these variables.

A screenshot of the LH Dashboard showing the variables of an Identity Verification WfRun in the 'PENDING' state.
Identity Verification WfRun Variables from the Dashboard

Get the State of WfRun Variables

Next, let's get the status of our Identity Verification WfRun, which is stored in a few WfRun variables.

In the IdentityResource class, we defined a GET endpoint at /identity-verification/status/{wfRunId}. This endpoint calls the VariablesService to handle the business logic of searching for variables in our WfRun, specifically the full-name, email, and approval-status variables.

Using your HTTP client of choice, make a GET call to the /identity-verification/status/{wfRunId} endpoint, replacing {wfRunId} with the ID we received in the last step:

curl -X GET http://localhost:9000/identity-verification/status/{wfRunId}

At this stage in the workflow, our API should respond that the Identity Verification is PENDING:

{
"fullName": "Obi-Wan Kenobi",
"email": "obiwankenobi@jedi.temple",
"status": "PENDING",
"wfRunId": "374063862026437a9d9e289280ea4bf1"
}

Posting an ExternalEvent

Peeking back at the dashboard, you will notice that the verify-identity task completed successfully but, the workflow is still in the RUNNING state and our approval-status is still PENDING. This is because the workflow is waiting for an external event identity-verified to be posted to the workflow.

Normally in a real-world application, you would have some other service that would post an event to the workflow with webhooks. For this example, we will use another endpoint to post this event to the workflow, simulating a webhook.

tip

The WfSpec utilizes the Correlated Events feature such that the identity-verified event can be matched to a waiting WfRun by the email address. This means our external system doesn't need to know the WfRunId of any pending workflows for this identity!

Using your HTTP client of choice, make a POST call to the /identity-verification/verify endpoint with the following JSON Body:

{
"email": "obiwankenobi@jedi.temple",
"isValid": true
}

Now if we fetch our Identity Verification status again, you will see that the workflow has completed and Obi-Wan has been notified that their identity has been verified.

{
"fullName": "Obi-Wan Kenobi",
"email": "obiwankenobi@jedi.temple",
"status": "APPROVED",
"wfRunId": "374063862026437a9d9e289280ea4bf1"
}

You can see these changes reflected on the LittleHorse Dashboard:

A Completed Identity Verification WfRun in LittleHorse Dashboard
A Completed Identity Verification WfRun in LittleHorse Dashboard

Search for WfRuns using variable filters

Lastly, let's look at a more advanced use case that combines multiple LittleHorse gRPC calls into one REST endpoint.

In the IdentityResource class, we defined a GET endpoint /identity-verification/status/search that takes in 3 query parameters: two search filters email and status, and a bookmark for pagination. This endpoint returns a list of Identity Verification statuses matching your search criteria.

This endpoint calls two services and two LittleHorse gRPC endpoints under the hood:

  1. Calls the WorkflowsService to perform a RPC SearchWfRun request. This searches for many WfRuns filtered by email and status, and returns a list of WfRunIds matching our search.
  2. Calls the IdentityService to perform multiple RPC GetVariable requests. This fetches the status of each Identity Verification workflow, based on the list of WfRunIds from step 1.

Using your HTTP client of choice, make a GET call to the /identity-verification/status/search endpoint with the status=APPROVED and or email=obiwankenobi@jedi.temple query parameters:

curl -X GET "http://localhost:9000/identity-verification/status/search?status=APPROVED"

At this stage in the workflow, our API should respond with one search result:

{
"results": [
{
"fullName": "Obi-Wan Kenobi",
"email": "obiwankenobi@jedi.temple",
"status": "APPROVED",
"wfRunId": "374063862026437a9d9e289280ea4bf1"
}
],
"bookmark": ""
}

Now, we've combined many LittleHorse gRPC requests into one REST endpoint.

Wrapping Up

Congratulations on completing the LH Runtime quickstart!

With a few handy annotations, LH Runtime can automatically deploy our WfSpec, TaskDefs, and start our Task Workers. It also provides us access to the LittleHorse gRPC client bean, which we can combine with other Quarkus extensions like Quarkus REST to abstract away the gRPC endpoints and define our own API for users to interact with.

If you've made it this far, join us on Slack and give us a star on GitHub!

For further reading, check out the LH Runtime Extension for Quarkus on GitHub.