Basic Activity - Java SDK
Develop a basic Activity
One of the primary things that Workflows do is orchestrate the execution of Activities. An Activity is a normal function or method execution that's intended to execute a single, well-defined action (either short or long-running), such as querying a database, calling a third-party API, or transcoding a media file. An Activity can interact with world outside the Temporal Platform or use a Temporal Client to interact with a Temporal Service. For the Workflow to be able to execute the Activity, we must define the Activity Definition.
An Activity Definition is a combination of the Temporal Java SDK Activity Class implementing a specially annotated interface.
An Activity interface is annotated with @ActivityInterface and an Activity implementation implements this Activity interface.
To handle Activity types that do not have an explicitly registered handler, you can directly implement a dynamic Activity.
@ActivityInterface
public interface GreetingActivities {
String composeGreeting(String greeting, String language);
}
Each method defined in the Activity interface defines a separate Activity method.
You can annotate each method in the Activity interface with the @ActivityMethod annotation, but this is completely optional.
The following example uses the @ActivityMethod annotation for the method defined in the previous example.
@ActivityInterface
public interface GreetingActivities {
@ActivityMethod
String composeGreeting(String greeting, String language);
}
An Activity implementation is a Java class that implements an Activity annotated interface.
// Implementation for the GreetingActivities interface example from in the previous section
static class GreetingActivitiesImpl implements GreetingActivities {
@Override
public String composeGreeting(String greeting, String name) {
return greeting + " " + name + "!";
}
}
Define Activity parameters
There is no explicit limit to the total number of parameters that an Activity Definition may support. However, there is a limit to the total size of the data that ends up encoded into a gRPC message Payload.
A single argument is limited to a maximum size of 2 MB. And the total size of a gRPC message, which includes all the arguments, is limited to a maximum of 4 MB.
Also, keep in mind that all Payload data is recorded in the Workflow Execution Event History and large Event Histories can affect Worker performance. This is because the entire Event History could be transferred to a Worker Process with a Workflow Task.
Some SDKs require that you pass context objects, others do not. When it comes to your application data—that is, data that is serialized and encoded into a Payload—we recommend that you use a single object as an argument that wraps the application data passed to Activities. This is so that you can change what data is passed to the Activity without breaking a function or method signature.
An Activity interface can have any number of parameters. All inputs should be serializable by the default Jackson JSON Payload Converter.
When implementing Activities, be mindful of the amount of data that you transfer using the Activity invocation parameters or return values as these are recorded in the Workflow Execution Events History. Large Events Histories can adversely impact performance.
You can create a custom object, and pass it to the Activity interface, as shown in the following example.
@ActivityInterface
public interface YourActivities {
String getCustomObject(CustomObj customobj);
void sendCustomObject(CustomObj customobj, String abc);
}
The execute method in the dynamic Activity interface implementation takes in EncodedValues that are inputs to the Activity Execution, as shown in the following example.
// Dynamic Activity implementation
public static class DynamicActivityImpl implements DynamicActivity {
@Override
public Object execute(EncodedValues args) {
String activityType = Activity.getExecutionContext().getInfo().getActivityType();
return activityType
+ ": "
+ args.get(0, String.class)
+ " "
+ args.get(1, String.class)
+ " from: "
+ args.get(2, String.class);
}
}
For more details, see Dynamic Activity Reference.
Define Activity return values
All data returned from an Activity must be serializable.
Activity return values are subject to payload size limits in Temporal. The default payload size limit is 2MB, and there is a hard limit of 4MB for any gRPC message size in the Event History transaction (see Cloud limits here). Keep in mind that all return values are recorded in a Workflow Execution Event History.
Activity return values must be serializable and deserializable by the provided DataConverter.
The execute method for DynamicActivity can return type Object.
Ensure that your Workflow or Client can handle an Object type return or is able to convert the Object type response.
- Data Converter
- Java DataConverter reference: https://www.javadoc.io/doc/io.temporal/temporal-sdk/latest/io/temporal/common/converter/DataConverter.html
Customize your Activity Type
Each Activity has a Type, which may also be referred to as the Activity 'name'. This name appears in the Workflow Execution Event History in the Summary tab for each Activity Task. The name lets you identify Activity Types called during the Execution.
Custom Activity Type names prevent name collisions across interfaces and Workflows. They offer descriptive Activity method names without concerns about re-using those names elsewhere in your project. They also support code management, especially in larger projects with many Activities. For example, you might use a prefix to group related activities together. Custom names also distinguish keys for gathering metrics without name conflicts.
The following examples show how to set custom names for your Activity Type.
Default behavior
By default, an Activity Type is the method name with the first letter capitalized:
@ActivityInterface
public interface GreetingActivities {
String sendMessage(String input);
@ActivityMethod
String composeGreeting(String greeting, String language);
}
- Method Name:
sendMessage - Activity Type:
SendMessage - Method Name:
composeGreeting - Activity Type:
ComposeGreeting
Custom Prefix
Using the namePrefix parameter in the @ActivityInterface annotation adds a prefix to each Activity Type name mentioned in the interface, unless the prefix is specifically overridden:
@ActivityInterface(namePrefix = "Messaging_")
public interface GreetingActivities {
String sendMessage(String input);
@ActivityMethod
String composeGreeting(String greeting, String language);
}
- Method Name:
sendMessage - Activity Type:
Messaging_SendMessage - Method Name:
composeGreeting - Activity Type:
Messaging_ComposeGreeting
The Activity Type is capitalized, even when using a prefix.
Custom Name
To override the default name and any inherited prefixes, use the name parameter in the @ActivityMethod annotation:
@ActivityInterface(namePrefix = "Messaging_")
public interface GreetingActivities {
String sendMessage(String input);
@ActivityMethod
String composeGreeting(String greeting, String language);
@ActivityMethod(name = "farewell")
String composeFarewell(String farewell, String language);
}
Using the name parameter won't automatically capitalize the result:
- Method Name:
sendMessage - Activity Type:
Messaging_SendMessage - Method Name:
composeGreeting - Activity Type:
Messaging_ComposeGreeting - Method Name:
composeFarewell - Activity Type:
farewell
Be cautious with names that contain special characters, as these can be used as metric tags. Systems such as Prometheus may ignore metrics with tags using unsupported characters.