How to run Jakarta Batchlets with WildFly

Jakarta Batchlets provide a streamlined approach to handle non-data-oriented processing tasks within batch jobs. Whether you are new to batch processing or familiar with Jakarta Batch, this tutorial will guide you through the essentials of using Jakarta Batchlets effectively.

Use Cases for Batchlets vs standard Batch executions

Batch processing plays a vital role in modern enterprise applications, enabling the execution of repetitive and resource-intensive tasks. In the first article about Jakarta Batch API we have discussed about building scalable batches with Chunk Jobs: Jakarta Batch API: Building Scalable Batch Solutions

On the other hand, Jakarta Batchlets offer a lightweight and focused mechanism to handle specific actions or tasks within a batch job.

The use cases for a Jakarta Batchlet and a standard Jakarta Batch execution differ based on the nature and complexity of the processing tasks. Here are some scenarios where each approach is commonly used:

Use cases for Jakarta Batchlet

  • Non-Data-Oriented Processing: Jakarta Batchlets are suitable for scenarios where the processing logic is not directly related to data manipulation. For example, invoking external services, performing file operations, or executing custom business logic.
  • Small-Scale Processing: When the processing task is relatively small and does not require chunk-oriented processing or complex item processing, using a Batchlet can provide a simpler and more concise solution.
  • Single Action or Task: Jakarta Batchlets are designed for handling a single action or task within a batch job. If the processing logic does not fit into a chunk-based model and can be encapsulated within a single method, a Batchlet is a suitable choice.

Use cases for Standard Jakarta Batch Execution

  • Data-Intensive Processing: When the processing task involves large volumes of data that can be efficiently divided into chunks, the standard Jakarta Batch execution with chunk-oriented processing is more appropriate. This allows for parallel processing and optimal utilization of system resources.
  • Complex Item Processing: If the processing logic requires more sophisticated operations on individual data items, such as complex transformations, validations, or filtering, the standard Jakarta Batch execution with item readers, processors, and writers provides the necessary flexibility and control.

A sample Batchlet

We will now define a Batchlet that will be used for persisting one Entity (named User) into a relational database using JPA. Firstly, let’s write our JSL file named simplebatchlet.xml, which includes a batchlet element in the step1:

<job id="simplebatchlet" xmlns=""

        <step id="step1">
                        <property name="user" value="user1" />
                        <property name="password" value="password1" />
                <batchlet ref="databaseBatchlet" />

As you can see, the above batchlet references a CDI bean named DatabaseBatchlet and receives two properties named respectively “user” and “password” that will be used to create the Entity User on the database.

We will now code our DatabaseBatchlet class, which is going to be a Stateless EJB annotated with the @Named annotation so that it can be referenced in our job definition file.

As it’s self-evident, creating a Batchlet class is just a matter of extending jakarta.batch.api.AbstractBatchlet and implementing the process() method:

public class DatabaseBatchlet extends AbstractBatchlet {

        @Inject StepContext stepContext;
        EntityManager em;
        public String process() {
                String user = stepContext.getProperties().getProperty("user");
                String password = stepContext.getProperties().getProperty("password");

                User u = new User();

                System.out.println("User created!");
                return "COMPLETED";

The logic contained in the Batchlet class is quite simple: the User data is persisted on a database via JPA in the process method. At the end of it, we are explicitly setting the exit status of the Batchlet to “COMPLETED” meaning that the job has successfully completed. In case of failure, the process method will end with a batch status of “FAILED”.

Starting the Batch Execution

We will delegate the execution of the Batchlet to a simple Servlet Class that will submit a Job Execution through the JobOperator Class:

@WebServlet(urlPatterns = { "/TestBatchlet" })
public class TestBatchlet extends HttpServlet {

	protected void processRequest(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		PrintWriter out = response.getWriter();
		try {
			JobOperator jo = BatchRuntime.getJobOperator();

			long id = jo.start("simplebatchlet", null);

			out.println("Batchlet submitted: " + id);

		} catch (JobStartException | JobSecurityException ex) {
			out.println("Error submitting Job! " + ex.getMessage());

  // doGet and doPost here

Our project is now complete. You should have the following tree-structure within it:

jakarta batchlet tutorial step-by-step

Deploy the project on a Jakarta EE container such as WildFly:

mvn install wildfly.deploy

Then, request the TestBatchlet Servlet and verify that it manages to persist the User:

16:45:59,816 INFO  [org.wildfly.extension.undertow] (ServerService Thread Pool -- 89) WFLYUT0021: Registered web context: '/batchletdemo' for server 'default-server'
16:45:59,832 INFO  [] (management-handler-thread - 2) WFLYSRV0010: Deployed "batchletdemo.war" (runtime-name : "batchletdemo.war")
16:46:06,260 INFO  [stdout] (Batch Thread - 2) User created!


By mastering Jakarta Batchlets, you have expanded your batch processing toolkit and gained the ability to handle specific actions or tasks with simplicity and efficiency. With their lightweight nature and focused functionality, Batchlets provide an excellent solution for implementing custom processing logic within your batch jobs.

Source code: