Skip to main content
Version: Next

Examples

The following examples show common patterns with the LittleHorse Quarkus extension. Full source code is available in the lh-quarkus repository.

Basic

A minimal workflow that executes two tasks: greetings and print.

@LHTask
public class GreetingsTask {

public static final String GREETINGS_TASK = "greetings";
public static final String PRINT_TASK = "print";
public static final String NAME_VARIABLE = "name";
public static final String GREETINGS_WORKFLOW = "greetings";

@LHWorkflow(GREETINGS_WORKFLOW)
public void greetingsWorkflow(WorkflowThread wf) {
WfRunVariable name = wf.declareStr(NAME_VARIABLE);
TaskNodeOutput message = wf.execute(GREETINGS_TASK, name);
wf.execute(PRINT_TASK, message);
}

@LHTaskMethod(GREETINGS_TASK)
public String greetings(String name) {
return "Hello %s".formatted(name);
}

@LHTaskMethod(PRINT_TASK)
public void print(String message) {
System.out.println(message);
}
}

Run the workflow:

lhctl run greetings name Anakin

User Tasks

Use @LHUserTaskForm to define a user task form. The extension registers it at runtime.

@LHUserTaskForm(ApproveForm.APPROVE_USER_TASK)
public class ApproveForm {

public static final String APPROVE_USER_TASK = "approve-user-task";

@UserTaskField(
displayName = "Approved?",
description = "Reply 'true' if this is an acceptable request.")
public boolean isApproved;
}

Run the workflow and complete the user task:

lhctl run execute-order-66 executor Anakin
lhctl execute userTaskRun <wfRunId> <userTaskGuid>

Structs

Use @LHStructDef to define a StructDef for structured data.

@LHStructDef("person")
public class Person {
private String firstName;
private String lastName;

public Person() {}

public Person(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}

public String getFirstName() { return firstName; }
public void setFirstName(String firstName) { this.firstName = firstName; }
public String getLastName() { return lastName; }
public void setLastName(String lastName) { this.lastName = lastName; }
}

Use the struct in workflows and tasks:

@LHWorkflow("my-workflow")
public void workflow(WorkflowThread wf) {
WfRunVariable person = wf.declareStruct("person", Person.class).required();
wf.execute("my-task", person);
}

@LHTaskMethod("my-task")
public void myTask(Person person) {
System.out.println(person);
}

Child Workflow

Define parent and child workflows with the parent attribute of @LHWorkflow:

@LHTask
public class ChildWorkflow {
public static final String GREETINGS_TASK = "child-wf-greetings";
public static final String NAME_VARIABLE = "name";
public static final String PARENT_WF = "parent-wf";
public static final String CHILD_WF = "child-wf";

@LHWorkflow(PARENT_WF)
public void parent(WorkflowThread wf) {
wf.execute(GREETINGS_TASK, PARENT_WF, wf.declareStr(NAME_VARIABLE).asPublic());
}

@LHWorkflow(value = CHILD_WF, parent = PARENT_WF)
public void child(WorkflowThread wf) {
wf.execute(GREETINGS_TASK, CHILD_WF, wf.declareStr(NAME_VARIABLE).asInherited());
}

@LHTaskMethod(GREETINGS_TASK)
public void greetings(String wfName, String name) {
System.out.printf("Hello %s from %s %n", name, wfName);
}
}

Run parent and child:

lhctl run --wfRunId my-parent-wf parent-wf name Anakin
lhctl run --parentWfRunId my-parent-wf --wfRunId my-child-wf child-wf

Reactive

Use LittleHorseReactiveStub for reactive workflows:

@ApplicationScoped
public class ReactiveService {
private final LittleHorseReactiveStub reactiveStub;

public ReactiveService(LittleHorseReactiveStub reactiveStub) {
this.reactiveStub = reactiveStub;
}

public Uni<String> runWf(String id, String message) {
String wfRunId = StringUtils.isBlank(id)
? UUID.randomUUID().toString().replace("-", "") : id;

RunWfRequest request = RunWfRequest.newBuilder()
.setWfSpecName(ReactiveWorkflow.REACTIVE_WORKFLOW)
.putVariables(ReactiveWorkflow.MESSAGE_VARIABLE, LHLibUtil.objToVarVal(message))
.setId(wfRunId)
.build();

return reactiveStub
.runWf(request)
.map(wfRun -> AwaitWorkflowEventRequest.newBuilder()
.addEventDefIds(WorkflowEventDefId.newBuilder()
.setName(ReactiveWorkflow.NOTIFY_EVENT))
.setWfRunId(wfRun.getId())
.build())
.chain(reactiveStub::awaitWorkflowEvent)
.map(wfEvent -> wfEvent.getId().getWfRunId().getId());
}
}

REST Endpoint

Inject LittleHorseBlockingStub and call it from a REST resource:

@ApplicationScoped
public class GreetingsService {
private final LittleHorseBlockingStub blockingStub;

public GreetingsService(LittleHorseBlockingStub blockingStub) {
this.blockingStub = blockingStub;
}

public String runWf(String id, String name) {
String wfRunId = StringUtils.isBlank(id)
? UUID.randomUUID().toString().replace("-", "") : id;

RunWfRequest request = RunWfRequest.newBuilder()
.setWfSpecName(GreetingsWorkflow.GREETINGS_WORKFLOW)
.putVariables(GreetingsWorkflow.NAME_VARIABLE, LHLibUtil.objToVarVal(name))
.setId(wfRunId)
.build();

return blockingStub.runWf(request).getId().getId();
}
}
@Path("/hello")
public class GreetingsResource {
private final GreetingsService service;

public GreetingsResource(GreetingsService service) {
this.service = service;
}

@GET
public String hello(@QueryParam("name") String name, @QueryParam("id") String id) {
return service.runWf(id, name);
}
}

Call the endpoint:

http :8080/hello name=="Anakin"
http :8080/hello name=="Leia" id==my-id-1