
The Payara Monthly Catch -September 2025
Welcome aboard the September issue of The Monthly Catch! With summer holidays wrapping up, the Java world is back […]
One of the most common questions about Jakarta Batch specification is “How do steps actually work under the hood?” Today’s nugget takes a look at batch processing in enterprise Java applications by exploring step execution and the different types of steps available in Jakarta Batch. So grab your favorite beverage and let’s dig in!
When building batch applications, you may often struggle with choosing between different step types and understanding how they behave during execution. Should you use a chunk-oriented step or a batchlet? How do you configure them properly? And what about execution monitoring? These decisions can significantly impact your batch application’s performance and maintainability.
The Jakarta Batch specification provides two main types of steps: chunk-oriented steps and batchlets. Each type requires proper configuration in both Java code and the Job XML. Let’s break them down.
Chunk steps are perfect for data-intensive operations that process items in a “read-process-write” pattern. Here’s a complete example including both code and configuration.
First, let’s define our batch artifacts:
@Named
public class CustomerReader implements ItemReader {
@Inject
@BatchProperty(name = "input.file")
private String inputFile;
@Override
public Object readItem() throws Exception {
// Read the next customer record
return customer;
}
}
@Named
public class CustomerProcessor implements ItemProcessor {
@Override
public Object processItem(Object item) throws Exception {
Customer customer = (Customer) item;
// Enrich or transform customer data
return customer;
}
}
@Named
public class CustomerWriter implements ItemWriter {
@Override
public void writeItems(List<Object> items) throws Exception {
// Write the chunk of customers to the database
}
}
Then let’s configure the step in our Job XML:
<step id="processCustomers">
<chunk item-count="10">
<reader ref="customerReader">
<properties>
<property name="input.file" value="customers.csv"/>
</properties>
</reader>
<processor ref="customerProcessor"/>
<writer ref="customerWriter"/>
</chunk>
</step>
The key thing to understand about chunk processing is that it:
Batchlets are perfect for task-oriented processing that doesn’t fit the item-oriented pattern. Think file transfer, system command execution, or any other “do something once” type operation.
@Named
public class DataCleanupBatchlet implements Batchlet {
@Inject
@BatchProperty(name = "retention.days")
private String retentionDays;
@Override
public String process() throws Exception {
// Perform cleanup operation
cleanupStaleData(Integer.parseInt(retentionDays));
return "COMPLETED";
}
@Override
public void stop() throws Exception {
// Handle stop request gracefully
}
}
And its corresponding Job XML configuration:
<step id="cleanup">
<batchlet ref="dataCleanupBatchlet">
<properties>
<property name="retention.days" value="30"/>
</properties>
</batchlet>
</step>
Listeners allow you to monitor and intercept step execution at various points. They’re perfect for logging, metrics collection and cross-cutting concerns. There are several types:
Here’s an example of a step listener:
@Named
public class CustomerStepListener implements StepListener {
@Override
public void beforeStep() throws Exception {
// Called once before step execution begins
logger.info("Starting customer processing step");
}
@Override
public void afterStep() throws Exception {
// Called once after step execution completes
logger.info("Completed customer processing step");
}
}
And then we can configure it in your Job XML:
<step id="processCustomers">
<listeners>
<listener ref="customerStepListener"/>
</listeners>
<chunk item-count="10">
<!-- chunk configuration as before -->
</chunk>
</step>
With partitioning, a step can execute multiple instances of itself in parallel, with each instance working on a subset of the data. This is configured through a partition plan or mapper:
@Named
public class CustomerPartitionMapper implements PartitionMapper {
@Override
public PartitionPlan mapPartitions() throws Exception {
PartitionPlan plan = new PartitionPlanImpl();
plan.setPartitions(3); // Run on 3 threads
Properties[] props = new Properties[3];
// Set up properties for each partition
for(int i = 0; i < 3; i++) {
props[i] = new Properties();
props[i].setProperty("partition.id", String.valueOf(i));
props[i].setProperty("chunk.start", String.valueOf(i * 1000));
props[i].setProperty("chunk.end", String.valueOf((i + 1) * 1000));
}
plan.setPartitionProperties(props);
return plan;
}
}
Configure partitioning in your Job XML:
<step id="processCustomers">
<chunk item-count="10">
<!-- chunk configuration as before -->
</chunk>
<partition>
<mapper ref="customerPartitionMapper"/>
</partition>
</step>
Understanding these step components helps you:
Steps are the fundamental components of Jakarta Batch jobs. Learning about the different types of steps and their supporting components, such as listeners and partitioning, will help you to create more resilient and efficient batch applications.
That’s it for this week’s nugget! Download your free copy of Payara Platform Community and start building better batch applications today. Happy Friday and happy coding!
Share:
Welcome aboard the September issue of The Monthly Catch! With summer holidays wrapping up, the Java world is back […]
We’re excited to announce that Payara Platform Community 7 Beta application server is now fully certified as Jakarta EE 11 […]
If your Java EE 8 applications run on Red Hat JBoss Enterprise Application Platform (EAP) 7, you can’t afford […]