Quickstart
Standalone Activities are Activities that run independently, without being orchestrated by a
Workflow. Instead of starting an Activity from within a Workflow Definition, you start a Standalone
Activity directly from a Temporal Client using ActivityClient.
The way you write the Activity and register it with a Worker is identical to Workflow Activities. The only difference is that you execute a Standalone Activity directly from your Temporal Client.
This documentation uses source code from the standalone-activities sample project.
Get started with Standalone Activities
Prerequisites:
-
Java 21+ and either Maven or Gradle for the sample project (the Temporal Java SDK requires Java 8+)
-
Temporal Java SDK (v1.39.0 or higher). See the Java Quickstart for install instructions.
-
Temporal CLI v1.9.1 or higher. Install with Homebrew, or see the Temporal CLI install guide for other platforms. Verify the installation with
temporal --version.
Start the Temporal development server with temporal server start-dev.
This command automatically starts the Temporal development server with the Web UI, and creates the default Namespace.
It uses an in-memory database, so do not use it for real use cases.
The Temporal Server will now be available for client connections on localhost:7233, and the
Temporal Web UI will now be accessible at http://localhost:8233.
brew install temporal
temporal --version
temporal server start-dev
Clone the sample
Clone the documentation-sdk-code-examples repository to follow along:
git clone https://github.com/temporalio/documentation-sdk-code-examples.git
cd documentation-sdk-code-examples/java-sandbox/activities/standalone-activities
The sample consists of separate programs in the standaloneactivities package:
src/main/java/io/temporal/docs/activities/standaloneactivities/
├── GreetingActivities.java # Activity interface
├── GreetingActivitiesImpl.java # Activity implementation
├── StandaloneActivityWorker.java # Worker that processes activity tasks
├── ExecuteActivity.java # Starts an activity and waits for the result
├── ExecuteActivityByTypeName.java # Executes the activity by its type name
├── StartActivity.java # Starts an activity without blocking
├── GetActivityHandle.java # Gets a handle to a started activity
├── ListActivities.java # Lists activity executions
└── CountActivities.java # Counts activity executions
Define your Activity
An Activity in the Temporal Java SDK is an interface annotated with @ActivityInterface, with
methods annotated with @ActivityMethod. The way you define a Standalone Activity is identical to
how you define an Activity orchestrated by a Workflow. In fact, the same Activity can be executed
both as a Standalone Activity and as a Workflow Activity.
import io.temporal.activity.ActivityInterface;
import io.temporal.activity.ActivityMethod;
/** Activity interface shared by all programs in this sample. */
@ActivityInterface
public interface GreetingActivities {
@ActivityMethod
String composeGreeting(String greeting, String name);
}
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** Activity implementation. */
public class GreetingActivitiesImpl implements GreetingActivities {
private static final Logger log = LoggerFactory.getLogger(GreetingActivitiesImpl.class);
@Override
public String composeGreeting(String greeting, String name) {
log.info("Composing greeting...");
return greeting + ", " + name + "!";
}
}
- Maven
- Gradle
mvn -q compile exec:java -Dexec.mainClass=io.temporal.docs.activities.standaloneactivities.StandaloneActivityWorker
gradle -q execute -PmainClass=io.temporal.docs.activities.standaloneactivities.StandaloneActivityWorker
Run a Worker with the Activity registered
Running a Worker for Standalone Activities is the same as running a Worker for Workflow Activities —
you create a WorkerFactory, register the Activity implementation, and call factory.start(). The
Worker doesn't need to know whether the Activity will be invoked from a Workflow or as a Standalone
Activity. See Run a Worker for more details on
Worker setup and configuration options.
Open a new terminal, navigate to the java-sandbox/activities/standalone-activities directory, and run the Worker.
Leave this terminal running — the Worker needs to stay up to process activities.
import io.temporal.client.WorkflowClient;
import io.temporal.envconfig.ClientConfigProfile;
import io.temporal.serviceclient.WorkflowServiceStubs;
import io.temporal.worker.Worker;
import io.temporal.worker.WorkerFactory;
import java.io.IOException;
/**
* Worker that processes standalone activity tasks. Run this before executing or starting standalone
* activities with ExecuteActivity or StartActivity.
*/
public class StandaloneActivityWorker {
static final String TASK_QUEUE = "standalone-activity-task-queue";
public static void main(String[] args) throws IOException {
ClientConfigProfile profile = ClientConfigProfile.load();
WorkflowServiceStubs service =
WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());
WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());
WorkerFactory factory = WorkerFactory.newInstance(client);
Worker worker = factory.newWorker(TASK_QUEUE);
worker.registerActivitiesImplementations(new GreetingActivitiesImpl());
factory.start();
System.out.println("Worker running on task queue: " + TASK_QUEUE);
}
}
- Maven
- Gradle
mvn -q compile exec:java -Dexec.mainClass=io.temporal.docs.activities.standaloneactivities.ExecuteActivity
gradle -q execute -PmainClass=io.temporal.docs.activities.standaloneactivities.ExecuteActivity
Execute a Standalone Activity
Use
ActivityClient.execute()
to execute a Standalone Activity and block until it completes. Call this from your application code,
not from inside a Workflow Definition. This durably enqueues your Standalone Activity in the Temporal
Server, waits for it to be executed on your Worker, and then returns the typed result.
The typed execute() API takes the Activity interface class and an unbound method reference. The SDK
uses the method reference to infer the Activity type name and result type at runtime. You can also
call Activities by string type name.
StartActivityOptions requires id, taskQueue, and at least one of startToCloseTimeout or
scheduleToCloseTimeout.
To run it:
-
Make sure the Temporal Server is running (from the Get Started step above).
-
Make sure the Worker is running (from the Run a Worker step above).
-
Open a new terminal, navigate to the
java-sandbox/activities/standalone-activitiesdirectory, and run one of the following, depending on your build tool:# Mavenmvn -q compile exec:java -Dexec.mainClass=io.temporal.docs.activities.standaloneactivities.ExecuteActivity# Gradlegradle -q execute -PmainClass=io.temporal.docs.activities.standaloneactivities.ExecuteActivity
Or use the Temporal CLI.
import static io.temporal.docs.activities.standaloneactivities.StandaloneActivityWorker.TASK_QUEUE;
import io.temporal.client.ActivityClient;
import io.temporal.client.ActivityClientOptions;
import io.temporal.client.StartActivityOptions;
import io.temporal.envconfig.ClientConfigProfile;
import io.temporal.serviceclient.WorkflowServiceStubs;
import java.io.IOException;
import java.time.Duration;
/**
* Executes a standalone activity and waits for the result. Requires a Worker running
* StandaloneActivityWorker.
*/
public class ExecuteActivity {
static final String ACTIVITY_ID = "standalone-activity-id";
public static void main(String[] args) throws IOException {
ClientConfigProfile profile = ClientConfigProfile.load();
WorkflowServiceStubs service =
WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());
ActivityClient client =
ActivityClient.newInstance(
service,
ActivityClientOptions.newBuilder().setNamespace(profile.getNamespace()).build());
StartActivityOptions options =
StartActivityOptions.newBuilder()
.setId(ACTIVITY_ID)
.setTaskQueue(TASK_QUEUE)
.setStartToCloseTimeout(Duration.ofSeconds(10))
.build();
try {
String result =
client.execute(
GreetingActivities.class,
GreetingActivities::composeGreeting,
options,
"Hello",
"World");
System.out.println("Activity result: " + result);
} finally {
service.shutdown();
}
}
}
// Using a string type name
String result = client.execute("ComposeGreeting", String.class, options, "Hello", "World");
temporal activity execute \
--type ComposeGreeting \
--activity-id standalone-activity-id \
--task-queue standalone-activity-task-queue \
--start-to-close-timeout 10s \
--input '"Hello"' \
--input '"World"'
Run with Temporal Cloud
All code samples on this page use
ClientConfigProfile.load()
to configure the Temporal Client connection. It responds to environment
variables and TOML configuration
files, so the same code works against a local dev
server and Temporal Cloud without changes. See Run Standalone Activities with Temporal
Cloud in the feature guide
for mTLS and API key setup.
Next steps
- Standalone Activities feature guide: Start without waiting, get handles, list and count Activities, and connect to Temporal Cloud.
- Build a Job Queue with Standalone Activities: Tutorial that builds a durable job queue, with idempotency keys, server-side deduplication, throughput limits, Priority and Fairness, and heartbeat checkpoints.
- Activity basics: Write and register Activities with the Java SDK.