v0.5.0
Java SDK

Get started with Java

Build an HTTP worker, submit a typed order pipeline, and inspect its execution with the Java SDK.

The Java SDK lets your application submit workflows and execute their stages. This guide creates two processes: a worker that handles orders, and a command-line client that submits an order and waits for the result. The example validates an amount and records a message in pipeline context. It does not charge a card, reserve stock, or call an external order service.

Before you start

You need JDK 17 or newer, a running Pipelogiq server, its API base URL, and an API key for an application. The worker and submitting client use the same application's key. Obtain the key from your Pipelogiq application configuration; a dashboard login is not a worker credential.

The HTTP worker needs access to the Pipelogiq API. It does not open a RabbitMQ connection itself. Pipelogiq's server infrastructure still needs to be running.

Installation status: the current source version is 0.5.0, with Maven group ID com.pipelogiq. Maven Central publication is being prepared. The supported installation path below builds from the SDK checkout and installs into your local Maven repository. These instructions do not assume the packages are downloadable from Central. Java imports remain io.pipelogiq.sdk.*.

Download the complete example project.

Install the current SDK from source

Download the matching SDK source archive, extract it, and open its pipelogiq-sdk-java directory in a terminal. The archive includes the current com.pipelogiq source and Maven wrapper. It contains no application credentials.

Run from that directory:

sh
export PIPELOGIQ_SDK_DIR="$PWD"
chmod +x mvnw
./mvnw -pl sdk-http -am install -Dmaven.test.skip=true

The wrapper uses Maven 3.9.16. -am builds the parent and required core library as well as the HTTP module. install writes your local Maven repository; it does not publish a package. -Dmaven.test.skip=true keeps this installation separate from the SDK's infrastructure-dependent test suites.

Create a separate application directory:

sh
cd ..
mkdir -p pipelogiq-java-demo/src/main/java/example
cd pipelogiq-java-demo

Create pom.xml:

xml
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>example</groupId>
  <artifactId>pipelogiq-java-demo</artifactId>
  <version>1.0.0</version>
  <properties>
    <maven.compiler.release>17</maven.compiler.release>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <dependencies>
    <dependency>
      <groupId>com.pipelogiq</groupId>
      <artifactId>pipelogiq-sdk-http</artifactId>
      <version>0.5.0</version>
    </dependency>
  </dependencies>
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.16.0</version>
      </plugin>
    </plugins>
  </build>
</project>

Write the worker and client

Create src/main/java/example/OrdersApp.java:

java
package example;

import com.fasterxml.jackson.databind.JsonNode;
import io.pipelogiq.sdk.core.PipelogiqOptions;
import io.pipelogiq.sdk.core.api.PipelogiqApiClient;
import io.pipelogiq.sdk.core.builders.PipelineBuilder;
import io.pipelogiq.sdk.core.builders.StageOptions;
import io.pipelogiq.sdk.core.execution.StageContext;
import io.pipelogiq.sdk.core.execution.StageHandler;
import io.pipelogiq.sdk.core.execution.StageResult;
import io.pipelogiq.sdk.http.HttpPipelineRunner;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.Instant;
import java.util.Set;

public final class OrdersApp {
  public record Order(String orderId, BigDecimal amount) {}

  public static final class ValidateOrder implements StageHandler<Order> {
    @Override
    public StageResult execute(Order order, StageContext context) {
      context.throwIfCancelled();
      if (order.orderId() == null || order.orderId().isBlank()) {
        return StageResult.validationError("Order ID is required");
      }
      if (order.amount() == null || order.amount().signum() <= 0) {
        return StageResult.validationError("Amount must be positive");
      }
      context.put("validatedOrder", order);
      context.logInfo("Validated order " + order.orderId());
      return StageResult.success("Order validated");
    }
  }

  public static PipelogiqOptions options(String workerName) {
    String key = System.getenv("PIPELOGIQ_API_KEY");
    if (key == null || key.isBlank()) {
      throw new IllegalArgumentException("Set PIPELOGIQ_API_KEY");
    }
    return PipelogiqOptions.builder()
        .apiUrl(System.getenv().getOrDefault("PIPELOGIQ_API_URL", "http://localhost:8081"))
        .apiKey(key)
        .workerName(workerName)
        .maxConcurrency(4)
        .build();
  }

  public static JsonNode waitFor(PipelogiqApiClient api, long id) throws Exception {
    Instant deadline = Instant.now().plusSeconds(60);
    String lastStatus = "";
    while (Instant.now().isBefore(deadline)) {
      JsonNode pipeline = api.getPipeline(id);
      String status = pipeline.path("status").asText();
      if (!status.equals(lastStatus)) {
        System.out.println("Pipeline " + id + ": " + status);
        lastStatus = status;
      }
      if (Set.of("Completed", "Failed", "Cancelled", "WaitingForApproval").contains(status)) {
        return pipeline;
      }
      Thread.sleep(500);
    }
    throw new IllegalStateException("Still waiting after 60 seconds; inspect pipeline " + id);
  }

  public static void main(String[] args) throws Exception {
    if (args.length == 0) {
      throw new IllegalArgumentException("Use worker, submit [orderId], or get <pipelineId>");
    }
    var options = options("orders-java");
    if (args[0].equals("worker")) {
      try (var worker = new HttpPipelineRunner(options)) {
        worker.register("ValidateOrder", Order.class, ValidateOrder::new);
        worker.registerInstance("RecordOrder", Void.class, (unused, context) -> {
          Order order = context.get("validatedOrder", Order.class);
          if (order == null) return StageResult.missingRequiredData("Validated order is missing");
          context.put("recorded", true);
          context.logInfo("Demo record created for " + order.orderId());
          return StageResult.success("Recorded in workflow context");
        });
        Runtime.getRuntime().addShutdownHook(new Thread(worker::close));
        var lifetime = worker.start();
        worker.awaitReady(Duration.ofSeconds(30));
        System.out.println("Worker ready. Submit an order from another terminal.");
        lifetime.join();
      }
      return;
    }
    try (var api = new PipelogiqApiClient(options)) {
      if (args[0].equals("get") && args.length == 2) {
        System.out.println(api.getPipeline(Long.parseLong(args[1])).toPrettyString());
        return;
      }
      if (!args[0].equals("submit")) throw new IllegalArgumentException("Unknown command");
      String orderId = args.length > 1 ? args[1] : "demo-001";
      try (var pipeline = PipelineBuilder.create("Process order " + orderId, api)
          .withIdempotencyKey("java-order:" + orderId)
          .addLabel("service", "orders")
          .addContextItem("source", "java-guide")
          .withAction("Validate", "ValidateOrder", new Order(orderId, new BigDecimal("42.50")),
              new StageOptions().timeout(Duration.ofSeconds(30)).maxRetries(2).retryInterval(5))
          .asEvent("Record", "RecordOrder")) {
        JsonNode created = pipeline.send();
        long id = created.path("id").asLong();
        System.out.println("Pipeline ID: " + id);
        System.out.println("Existing submission: " + created.path("wasExisting").asBoolean());
        System.out.println(waitFor(api, id).toPrettyString());
      }
    }
  }
}

A stage's display name (Validate) and wire handler name (ValidateOrder) serve different purposes. The handler name must match the registration exactly. Order.class tells Jackson how to deserialize the stage input. Use Void.class when a stage reads context and has no input.

Build and start the worker

Set the API URL and application key in each terminal. Replace the sample key with your own:

sh
export PIPELOGIQ_API_URL='http://localhost:8081'
export PIPELOGIQ_API_KEY='your-application-api-key'
"$PIPELOGIQ_SDK_DIR/mvnw" -q compile dependency:copy-dependencies
java -cp 'target/classes:target/dependency/*' example.OrdersApp worker

If you open a new terminal, set PIPELOGIQ_SDK_DIR to the SDK checkout's absolute path before invoking its wrapper. A locally installed Maven can also run mvn -q compile dependency:copy-dependencies.

The classpath above is for macOS/Linux shells. On Windows, use mvnw.cmd and separate classpath entries with ; instead of :.

start() returns the lifetime of the worker. awaitReady() waits for transport setup and an accepted online heartbeat. Keep the process running after it prints Worker ready.

Submit and observe

In a second terminal, change to pipelogiq-java-demo, set the same API variables, and run:

sh
java -cp 'target/classes:target/dependency/*' example.OrdersApp submit demo-001

The client prints a pipeline ID, status transitions and the final JSON response. Expect a Completed pipeline containing the Validate and Record stages, stage logs, and context containing the validated order and recorded=true. In the dashboard, open the same application and locate the printed pipeline ID to inspect that execution.

Run the same command again. The idempotency key refers to the same order submission, so the returned pipeline should be the existing one. Use demo-002 to create a new workflow. Retrieve any known pipeline separately:

sh
java -cp 'target/classes:target/dependency/*' example.OrdersApp get 123

Replace 123 with the ID your submission printed. Press Ctrl+C in the worker terminal to request a graceful shutdown. The shutdown hook lets the SDK stop intake and drain active executions.

If the first run does not complete

SymptomCheck
Dependency cannot be resolvedBuild the current SDK into the same local Maven repository. The group is com.pipelogiq, not the earlier local io.pipelogiq coordinate.
Set PIPELOGIQ_API_KEYExport the key in this process's terminal.
API authentication errorUse the application's API key and correct API URL, not a UI access token.
Worker readiness times outCheck server reachability and bootstrap/authentication errors.
Pipeline stays pendingKeep the worker online; verify application key and exact handler registration names.
Pipeline fails validationInspect the stage's lastErrorCode, result and logs; typed inputs must match their records.

Continue with workflow recipes, worker operations, or agents and deterministic tests.

Download this page as MarkdownMatches the 0.5.0 source release

Start here