---
title: "Get started with Java"
description: "Run a worker with configuration and business stages, then submit and inspect a typed pipeline."
category: "Java SDK"
---

Build an orders worker and a separate producer with **one SDK dependency**. They are separate processes in the example and should remain separate services in a real system. The worker stays online and executes stages; the producer submits work without hosting handlers. The demonstration records `recorded=true`; it does not write to a database or charge a customer.

[Download the runnable Java demo](/downloads/pipelogiq-java-demo.zip). It includes the files below plus independent-producer, retry and schedule recipes. The [matching SDK source](/downloads/pipelogiq-java-sdk-0.5.0-source.zip) supplies the local packages used by every example on these pages.

## Before you start

You need JDK 17 or 21, a running Pipelogiq API and an application API key. Use that application's key in both terminals. These instructions use macOS/Linux; Windows command equivalents are included below.

No Spring, broker URL or model provider is needed for the first run. HTTP is the default transport. Java package names start with `io.pipelogiq.sdk`; the Maven group is `com.pipelogiq`.

## Install the current SDK from source

Extract the matching source archive, open its root directory containing `mvnw` and `pom.xml`, and run:

```sh
export PIPELOGIQ_SDK_DIR="$PWD"
chmod +x mvnw
./mvnw -B -pl sdk-runtime -am install -DskipTests
```

This builds the recommended `pipelogiq-sdk` artifact and its dependencies into your local Maven repository. It does not publish anything. The wrapper downloads Maven automatically. This guide deliberately works without assuming version `0.5.0` has already been published to Maven Central.

Then extract the Java demo and open `pipelogiq-java-demo`. Alternatively, create the following files yourself. The SDK path variable must remain the absolute path to the SDK source root, not the demo directory.

`pom.xml`:

```xml
<project xmlns="http://maven.apache.org/POM/4.0.0">
  <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</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>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>3.8.1</version>
      </plugin>
    </plugins>
  </build>
</project>
```

`pipelogiq.yml` in the demo's root directory:

```yaml
pipelogiq:
  api-url: ${PIPELOGIQ_API_URL:http://localhost:8081}
  api-key: ${PIPELOGIQ_API_KEY}
  worker-name: orders-java
  environment: development
  transport: ${PIPELOGIQ_TRANSPORT:http}
  max-concurrency: 4
```

## Write the worker and client

Create `src/main/java/example/` and put each public type in its own file. These five files are included in the download. The download also contains a separate inspection and testing CLI.

### Order.java

```java
package example;

import java.math.BigDecimal;

public record Order(String orderId, BigDecimal amount) {}
```

### ValidateOrder.java

```java
package example;

import io.pipelogiq.sdk.core.execution.*;

@Stage("ValidateOrder")
public 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("Order validated");
    return StageResult.success();
  }
}
```

### RecordOrder.java

```java
package example;

import io.pipelogiq.sdk.core.execution.*;

@Stage("RecordOrder")
public final class RecordOrder implements NoInputStageHandler {
  @Override
  public StageResult execute(StageContext context) {
    Order order = context.require("validatedOrder", Order.class);
    context.put("recorded", true);
    return StageResult.success("Recorded demo order " + order.orderId());
  }
}
```

`@Stage` supplies a stable wire name. The SDK infers the input type from `StageHandler<Order>`; `NoInputStageHandler` reads required context without a dummy input argument. `context.require` classifies missing or incompatible data for you.

### OrdersExample.java

```java
package example;

import io.pipelogiq.sdk.Pipelogiq;

/** Run from the examples directory; the SDK owns startup, readiness and SIGTERM shutdown. */
public final class OrdersExample {
  public static void main(String[] args) {
    Pipelogiq.worker().config("pipelogiq.yml").stages(ValidateOrder.class, RecordOrder.class).run();
  }
}
```

The SDK owns client creation, transport selection, registration, readiness, leases, input conversion, result delivery and shutdown. Deploy this entry point as its own continuously running service. Worker startup does not submit a pipeline.

### CreateOrder.java

```java
package example;

import io.pipelogiq.sdk.Pipelogiq;
import java.math.BigDecimal;
import java.time.Duration;

/** Run with OrdersExample (or SpringOrdersApplication) already listening. */
public final class CreateOrder {
  public static void main(String[] args) {
    try (var client = Pipelogiq.connect("pipelogiq.yml")) {
      var created = client.pipeline("Process order")
          .idempotencyKey("demo-order-intro-123")
          .stage(ValidateOrder.class, new Order("intro-123", new BigDecimal("42.50")))
          .stage(RecordOrder.class)
          .send();
      var completed = client.pipelines()
          .waitForCompletion(created.id(), Duration.ofSeconds(60));
      System.out.println("Pipeline " + completed.id() + ": " + completed.status());
      System.out.println("Recorded: " + completed.context("recorded", Boolean.class));
    }
  }
}
```

The producer owns one typed client for its operation. Pipeline drafts do not open separate connections and do not need their own `close()`. There is no application polling loop: `waitForCompletion` is bounded by the timeout supplied to it.

## Build and start the worker

From `pipelogiq-java-demo`, build once:

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

Keep this terminal running. It represents the independently deployed worker service. After successful bootstrap, the dashboard shows worker `orders-java` online in the application associated with your API key. The example does not promise a particular console readiness message.

For Windows PowerShell, install the SDK with `.\mvnw.cmd -B -pl sdk-runtime -am install -DskipTests` from its root, save that absolute path in `$env:PIPELOGIQ_SDK_DIR`, then use these commands from the demo root:

```powershell
& "$env:PIPELOGIQ_SDK_DIR\mvnw.cmd" -q compile dependency:copy-dependencies
$env:PIPELOGIQ_API_URL = "http://localhost:8081"
$env:PIPELOGIQ_API_KEY = "your-application-api-key"
java -cp 'target/classes;target/dependency/*' example.OrdersExample
```

Other commands on these pages use the macOS/Linux `:` classpath separator. Use `;` on Windows. In a new terminal, export the API settings again. Set `PIPELOGIQ_SDK_DIR` again only if you need another build.

## Submit and observe

In a second terminal, open the same demo directory, set the same API variables, and run the small producer. In production this code normally lives in an API service, scheduled job or another application; it never starts the worker:

```sh
java -cp 'target/classes:target/dependency/*' example.CreateOrder
```

It submits the order and waits up to 60 seconds. Expect:

```text
Pipeline <id>: COMPLETED
Recorded: true
```

Open that pipeline in the dashboard to inspect both completed stages, logs and context. Running `CreateOrder` again reuses its fixed `demo-order-intro-123` idempotency key and returns the existing pipeline. Change the business ID and key together for a deliberately new order.

### Use the optional inspection CLI

The downloadable project also includes `CreatePipelineExample`, a utility for submitting different orders, inspecting status, and exercising failures and idempotency. It is not required by the five-file application above. If you created those files manually, obtain this optional utility from the demo download before running these commands:

```sh
java -cp 'target/classes:target/dependency/*' example.CreatePipelineExample submit order-001 42.50
```

It prints `Pipeline ID: <id>`. Replace `123` below with that ID:

```sh
java -cp 'target/classes:target/dependency/*' example.CreatePipelineExample get 123
java -cp 'target/classes:target/dependency/*' example.CreatePipelineExample wait 123 60
```

`get` reads a snapshot, which may still be pending or running. `wait` prints `COMPLETED`, `Recorded: true` and the statuses of `ValidateOrder` and `RecordOrder`. This utility uses `demo-order-<orderId>` as its idempotency key; choose a fresh order ID for a new request.

Try the two expected-behavior checks:

```sh
java -cp 'target/classes:target/dependency/*' example.CreatePipelineExample validate-failure rejected-001
java -cp 'target/classes:target/dependency/*' example.CreatePipelineExample idempotency repeated-001
```

The first prints a `FAILED` pipeline with `VALIDATION_ERROR` and `Recorded: false`. The second prints equal IDs and `Same pipeline: true`, then observes completion. A local wait timeout does not cancel server work; inspect or wait again using the same ID.

Press Ctrl+C in the worker terminal to stop gracefully. To repeat the experiment over AMQP, set `PIPELOGIQ_TRANSPORT=amqp` before starting the worker and use a new order ID. Broker settings come from server bootstrap; see [configuration](/docs/java/configuration#switch-to-amqp).

## If the first run does not complete

| Symptom | First check |
| --- | --- |
| Dependency cannot be resolved | Install the matching source into the same local Maven repository. |
| Missing API key | Export the key in each terminal or service environment. |
| Configuration file not found | Run from the demo root containing `pipelogiq.yml`. |
| Readiness failure | Verify API URL, authentication and transport reachability. |
| Pipeline stays pending | Keep a worker online with the same application key and matching stage names. |
| Previous result appears again | Reusing an order ID reuses its idempotency key. |

Continue with [workflow recipes](/docs/java/workflows), [schedules](/docs/java/schedules), [Spring Boot](/docs/java/spring-boot) or [agent testing](/docs/java/testing). The [troubleshooting guide](/docs/java/troubleshooting) covers failure diagnosis in more detail.
