Skip to content

OpenLiberty/draft-guide-jakarta-concurrency

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

59 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Running tasks concurrently or asynchronously in Java microservices

Note
This repository contains the guide documentation source. To view the guide in published form, view it on the Open Liberty website.

Learn how to run tasks concurrently or asynchronously in Java microservices by using Jakarta Concurrency.

What you’ll learn

Jakarta Concurrency enables the creation of managed executors to run tasks in separate threads with access to the context of the submitter. It also enables the creation of managed thread factories to create threads that run with the thread context of the component that looks up the managed thread factory. The threads are managed by the application server and safely interact in the Enterprise Java Beans and Web containers. To learn more about Jakarta Concurrency, see the Jakarta Concurrency specification.

The application that you will build provides REST APIs to retrieve system properties and write information about system loads, including CPU load and memory usage, to the database.

You will update the application to learn how to run tasks in parallel by enhancing the GET /api/system/properties/{prefix} endpoint that retrieves system properties with the provided prefix. You will learn two different ways to create asynchronous task by implementing the GET /api/system/systemLoad/cpuLoad and /api/system/systemLoad/memoryUsage endpoints that calculate the CPU load and memory usage and persist the data to the database. Also, you will learn how to create a scheduled asynchronous task that calculates and records the current system load to the database for every 10 seconds.

Go to the http://localhost:9080/api/system/properties/os URL to see all the os.* properties. You will see an output similar to the following example:

{
  "os.arch": "aarch64",
  "os.name": "Mac OS X",
  "os.version": "15.4.1",
  "os.encoding": "UTF-8"
}

If you are interested, you can check out the GET /api/system/properties/{prefix} endpoint by the http://localhost:9080/api/system/properties/user and http://localhost:9080/api/system/properties/java URLs.

Check out the application with the http://localhost:9080/api/system/systemLoad/cpuLoad and http://localhost:9080/api/system/systemLoad/memoryUsage URLs. Both requests return to you immediately with a message. After 5 seconds, go to the http://localhost:9080/api/system/systemLoad URL to see all the system loads. You will see an output similar to the following example:

[
  {
    "id": 1,
    "time": "2025-05-06T15:30:06.308192",
    "cpuLoad": 5.074851387343854e-06
  },
  {
    "id": 2,
    "time": "2025-05-06T15:30:08.732892",
    "memoryUsage": 0.7126722484827042
  }
]

Go to the http://localhost:9080/api/system/schedule/toggle URL to toggle the schedule that records the current system load in every 10 seconds. After 10 seconds, go to the http://localhost:9080/api/system/systemLoad URL to see all the system loads.

[
  ...
  {
    "id": 3,
    "time": "2025-05-06T15:34:30.001636",
    "cpuLoad": 1.8270538029075587e-06,
    "memoryUsage": 0.6712260656058788
  },
  ...
]

Running tasks in parallel

Starting to learn Jakarta Concurrency, you will learn how to use the ManagedExecutorService to run tasks in parallel.

Navigate to the start directory to begin.

SystemResource.java

link:start/src/main/java/io/openliberty/guides/system/SystemResource.java[role=include]

start/SystemProperties.java

link:start/src/main/java/io/openliberty/guides/system/SystemProperties.java[role=include]

server.xml

link:finish/src/main/liberty/config/server.xml[role=include]

The starting application provides the /properties/{prefix} endpoint. Go to the http://localhost:9080/api/system/properties/java URL to see all the java.* properties. Notice that the URL takes several seconds to get the result. Each getSystemPropertyTask() call takes at least a second.

To improve the performance, enhance the getProperties() method to call the getSystemPropertyTask() method in parallel.

The Jakarta Concurrency feature is enabled for you in the Liberty server.xml configuration file.

Update the SystemProperties class to use Jakarta Concurrency managed executor service to provide a way to start asynchronous tasks within an application server environment. It propagates various thread contexts that are relevant to Jakarta EE applications.

SystemProperties.java

link:finish/src/main/java/io/openliberty/guides/system/SystemProperties.java[role=include]
Replace the SystemProperties.java file.
src/main/java/io/openliberty/guides/system/SystemProperties.java

The SystemProperties class is annotated by the @ManagedExecutorDefinition annotation to provide a managed executor instance through the ManagedExecutorService injection point.

The getProperties() method constructs all tasks for collecting different property values. Each task calls the getSystemPropertyTask() method. Then, the getProperties() method uses the managedExecutor instance to invoke all tasks in parallel. The invokeAll() method waits for all tasks to complete.

You started the Open Liberty in dev mode at the beginning of this section, so all the changes are automatically picked up.

Go to the http://localhost:9080/api/system/properties/java URL to see all the java.* properties. Notice that the URL takes a second to get the result.

Creating asynchronous tasks

Implement asynchronous tasks to get the current CPU load and memory usage.

SystemConcurrency.java

link:finish/src/main/java/io/openliberty/guides/system/SystemConcurrency.java[role=include]
Create the SystemConcurrency.java file.
src/main/java/io/openliberty/guides/system/SystemConcurrency.java

persistence.xml

link:finish/src/main/resources/META-INF/persistence.xml[role=include]

The SystemConcurrency class is annotated by the @ManagedScheduledExecutorDefinition annotation to provide a managed scheduled executor instance through the ManagedScheduledExecutorService injection point.

The @ManagedScheduledExecutorDefinition annotation and the ManagedScheduledExecutorService interface are used in this bean because you will implement a scheduled task in the following section. For more information about different mananged services, see their annotations and classes in the jakarta.enterprise.concurrent JavaDoc.

The getCpuLoad() method uses the managedExecutor service to create and schedule an asynchronous task that delays the execution with 5 seconds. The task calculates the cpuLoadData CPU load. Then, it calls the persist() method to persist the data into the database through the EntityManager interface that interacts with the jpa-unit persistence context in the Liberty runtime.

A transaction is required on a cascading PERSIST operation. Inside the asynchronous task, it retrieves the named UserTransaction object from the Liberty runtime. Then, it calls the begin() method to create a transaction and the commit() method to complete the transaction. If there is any exception, it calls the rollback() method to roll back the transaction that associates to the thread.

An alternative to create a similar asynchronous task is using @Asynchronous annotation. The getMemoryUsage() method is annotated with the @Asynchronous annotation to make it run asynchronously. The task calculates the memoryUsageData memory usage and calls the persist() method to persist the data into the database. To create a new transaction, annotate the getMemoryUsage() method by the @Transactional annotation.

Jakarta Concurrency feature plays an important role to manage the application context in order to make the jpa-unit persistence context available to the asynchronous task.

Implement two REST endpoints to run the asynchronous tasks.

SystemResource.java

link:finish/src/main/java/io/openliberty/guides/system/SystemResource.java[role=include]
Replace the SystemResource.java file.
src/main/java/io/openliberty/guides/system/SystemResource.java

The GET /systemLoad/cpuLoad endpoint is added to call the getCpuLoad() asynchronous method and returns a message. The GET /systemLoad/memoryUsage endpoint is added to call the getMemoryUsage() asynchronous method and returns a message.

Try out the http://localhost:9080/api/system/systemLoad/cpuLoad and http://localhost:9080/api/system/systemLoad/memoryUsage URLs. Both requests return to you immediately with a message.

To check the system loads after 5 seconds, go to the http://localhost:9080/api/system/systemLoad URL.

[
  {
    "id": 1,
    "time": "2025-05-06T15:30:06.308192",
    "cpuLoad": 5.074851387343854e-06
  },
  {
    "id": 2,
    "time": "2025-05-06T15:30:08.732892",
    "memoryUsage": 0.7126722484827042
  }
]

Scheduling asynchronous task

Starting from Jakarta Concurrency 3.1, Scheduled asynchronous methods replaces the @Schedule annotation of Jakarta Entperprise Beans.

In this section, you will implement a schedule task to get the current system load for every 10 seconds by using a scheduled asynchronous method.

SystemConcurrency.java

link:finish/src/main/java/io/openliberty/guides/system/SystemConcurrency.java[role=include]
Replace the SystemConcurrency.java file.
src/main/java/io/openliberty/guides/system/SystemConcurrency.java

Similarly, the schedule() method calculates the systemLoadData system load data and calls the persist() method to persist the data into the database. To create a new transaction, annotate the schedule() method by the @Transactional annotation.

The schedule() method is annotated with the @Asynchronous annotation that configures the schedule with the "*/10 * * * * *" cron expression to make the task run asynchronously every 10 seconds (after the seconds of each minute that are divisible by 10). For more information about CRON syntax, see the jakarta.enterprise.concurrent.CronTrigger JavaDoc. A null value is returned to make the schedule to keep running. A CompletableFuture<> object is returned to complete the task and stop the schedule.

Implement a REST endpoint to toggle the schedule.

SystemResource.java

link:finish/src/main/java/io/openliberty/guides/system/SystemResource.java[role=include]
Replace the SystemResource.java file.
src/main/java/io/openliberty/guides/system/SystemResource.java

The added GET /schedule/toggle endpoint calls the schedule() scheduled asynchronous method if the schedule is not started.

Go to the http://localhost:9080/api/system/schedule/toggle URL to start the schedule. After 10 seconds, go to the http://localhost:9080/api/system/systemLoad URL to check out the system loads.

[
  ...
  {
    "id": 3,
    "time": "2025-05-06T15:34:30.001636",
    "cpuLoad": 1.8270538029075587e-06,
    "memoryUsage": 0.6712260656058788
  },
  ...
]

After you are finished checking out the schedule, go to the http://localhost:9080/api/system/schedule/toggle URL again to stop the schedule.

Testing the application

Although you can test your application manually, automated tests make sure consistent code quality by triggering a failure whenever a code change introduces a defect. Now, create integration tests for the endpoints.

SystemEndpointIT.java

link:finish/src/test/java/it/io/openliberty/guides/system/SystemEndpointIT.java[role=include]
Replace the SystemEndpointIT.java file.
src/test/java/it/io/openliberty/guides/system/SystemEndpointIT.java

The testGetProperties() tests the GET /api/system/properties/os endpoint and confirms that 4 os.* properties are returned.

The testGetCpuLoad() tests the GET /api/system/systemLoad/cpuLoad endpoint and confirms that the CPU load is not null after several seconds.

The testGetMemoryUsage() tests the GET /api/system/systemLoad/memoryUsage endpoint and confirms that the memory usage is not null after several seconds.

The testToggleSchedule() tests the GET /api/systems/schedule/toggle endpoint and confirms that 2 system loads are recorded after 20 seconds.

If the tests pass, you see an output similar to the following example:

Running it.io.openliberty.guides.system.SystemEndpointIT
[INFO    ] Getting the os.arch property...
[INFO    ] Getting the os.version property...
[INFO    ] Getting the os.encoding property...
[INFO    ] Getting the os.name property...
...
Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 57.06 s -- in it.io.openliberty.guides.system.SystemEndpointIT

Results:

Tests run: 4, Failures: 0, Errors: 0, Skipped: 0

To learn more about asynchronous and reactive programming, check out the related reactive guides.

Great work! You’re done!

You just developed tasks that run concurrently or asynchronously in a Java microservice by using Jakarta Concurrency in Open Liberty.

About

No description, website, or topics provided.

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •