Skip to content

OpenLiberty/draft-guide-jakarta-concurrency

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

34 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Running tasks parallelly 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 parallelly or asynchronously in Java microservices by using Jakarta Concurrency APIs.

What you’ll learn

Jakarta Concurrency is a Java API specification of Jakarta EE that provides a framework for developing concurrent and asynchronous Java applications. The Jakarta Concurrency APIs provide concurrency utilities to make developers easier writing thread-safe and scalable applications to execute tasks parallelly or asynchronously and coordinate the completion of multiple asynchronous tasks in a more efficient manner. To learn more about Jakarta Concurrency, see the Jakarta Concurrency specification.

The application in this guide consists of two microservices, system and inventory. The system microservice provides GET REST APIs to retreive the Java system properties, heap size, memory usage, and system load. The inventory microservice provides different REST APIs to register systems, update their memory usage and system load, and query their information from the inventory.


Application architecture where inventory microservice uses the Jakarta Concurrency APIs to call the system microservice.


You’ll learn how to run tasks in parallel by implementing the POST /api/inventory/system/{hostname} endpoint that registers a system to the inventory, create asynchronous task by the PUT /api/inventory/systems/memoryUsed endpoint that updates the memory usage of the systems in the inventory, and synchronize asynchronous tasks by the PUT /api/inventory/systems/systemLoad endpoint that updates the system load of the systems.

Try what you’ll build

The finish directory in the root of this guide contains the finished application. Give it a try before you proceed.

To try out the application, navigate to the finish directory and run the following command to deploy the system microservice to Open Liberty:

mvn -pl system liberty:run

Open another command-line session. Navigate to the finish directory and run the following command to deploy the inventory service to Open Liberty:

mvn -pl inventory liberty:run

When you see the following message in both command-line sessions, both your services are ready.

The defaultServer server is ready to run a smarter planet.

Visit the http://localhost:9081/openapi/ui URL to try out the REST endpoints of the inventory microservice.

First, make a POST request to the /api/inventory/system/{hostname} endpoint. To make this request, expand the POST endpoint on the UI, click the Try it out button, provide localhost for the hostname parameter, then click the Execute button. The POST request registers the localhost system to the inventory.

Visit the http://localhost:9081/api/inventory/systems URL to see the inventory. You will see an output similar to the following:

[
  {
    "heapSize": 8589934592,
    "hostname": "localhost",
    "javaVersion": "17.0.12",
    "memoryUsage": 0,
    "osName": "Mac OS X",
    "systemLoad": 0
  }
]

Repeat the above POST endpoint for the hostname parameter with the value of 127.0.0.1 and your local system hostname.

Then, make a PUT request to the /api/inventory/systems/memoryUsed endpoint with 5 seconds for the after parameter and a PUT request to the /api/inventory/systems/systemLoad endpoint with 5 seconds for the after parameter. After 5 seconds, visit the http://localhost:9081/api/inventory/systems URL again to see that the memoryUsage and systemLoad values are updated to non-zero. If you interest, try out the other endpoints.

After you are finished checking out the application, stop the Liberty instance by pressing CTRL+C in the command-line session where you ran the inventory services. Alternatively, you can run the liberty:stop goal from the finish directory in another command-line session:

mvn -pl inventory liberty:stop

Let the system microservice running. In the following sections, you’ll implement the inventory microservice to learn how to create parallel or asynchronous tasks.

Running tasks in parallel

Navigate to the start directory to begin.

When you run Open Liberty in dev mode, dev mode listens for file changes and automatically recompiles and deploys your updates whenever you save a new change.

Run the following goal to start the inventory microservice in dev mode:

mvn -pl inventory liberty:dev

When you see the following message, your Liberty instance is ready in dev mode:

**************************************************************
*    Liberty is running in dev mode.

Dev mode holds your command-line session to listen for file changes. Open another command-line session to continue, or open the project in your editor.

server.xml

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

The Jakarta Concurrency feature has already been enabled for you in the Liberty server.xml configuration file.

In this section, you will learn how to implement a task that calls the system microservice in parallel to get different system data.

InventoryAsyncTask.java

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

This bean provides different utilities for the inventory service. Annotating the managedExecutor field with the @Resource annotation, an instance of the ManagedScheduledExecutorService requested resource will be injected when the bean is initialized by Liberty runtime.

The getClientData() method uses the managedExecutor to submit multiple tasks in parallel. Each task uses the getSystemClient() method to get the REST client for the given system hostname, and use the client to retieve the os.name property, java.version property, and the heap size of the system.

The submit() tasks run in parallel and return Future<> object immediatedly for the result of asynchronous task. Use the get() method to wait the task completion, and then retrieves the result.

Implement the POST /api/inventory/system/{hostname} endpoint of the inventory microservice to register a system to the inventory.

InventoryResource.java

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

Inject the InventoryAsyncTask bean into the InventoryResource class. Create the POST /system/{hostname} endpoint that uses the bean’s getClientData() method to collect the properties from the system microservice and addes the system into the inventory.

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

Visit the http://localhost:9081/openapi/ui URL and make a POST request to the /api/inventory/system/{hostname} endpoint with the value of localhost. Then, visit the http://localhost:9081/api/inventory/systems URL to see the inventory. You will see an output similar to the following:

[
  {
    "heapSize": 8589934592,
    "hostname": "localhost",
    "javaVersion": "17.0.12",
    "memoryUsage": 0,
    "osName": "Mac OS X",
    "systemLoad": 0
  }
]

Repeat the above POST endpoint for the hostname parameter with the value of 127.0.0.1 and your local system hostname.

Creating asynchronous task

In this section, you will learn how to implement an asynchronous task to update the memory usage of the systems in the inventory.

InventoryAsyncTask.java

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

The updateSystemsMemoryUsed() method uses the managedExecutor to schedule multiple tasks that will be delayed with the given after seconds and executed in parallel. Each task calls the client’s getMemoryUsed() method to retieve the memory usage, and calls the system’s setMemoryUsed() method to calculate the memory usage.

Annotate the updateSystemsMemoryUsed() method with the @Asynchronous annotation to make it running asynchronously.

InventoryResource.java

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

Create the PUT /systems/memoryUsed endpoint that uses the task bean’s updateSystemsMemoryUsed() method to update the memory usage of all systems in the inventory and returns immediately.

Visit the http://localhost:9081/openapi/ui URL and make a PUT request to the /api/inventory/systems/memoryUsed endpoint with 5 seconds for the after parameter. After 5 seconds, visit the http://localhost:9081/api/inventory/systems URL to see that the memoryUsage values are updated to non-zero.

Synchronizing asynchronous tasks

In this section, you will learn how to synchronize asynchronous tasks to update the system load of the systems in the inventory.

InventoryAsyncTask.java

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

The getSystemLoad() method calls the client’s getSystemLoad() method to retieve the system load, and returns the system load in CompletableFuture<Double> type by using the Asynchronous.Result.complete() method.

Annotate the getSystemLoad() method with the @Asynchronous annotation to make it running asynchronously.

InventoryResource.java

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

Create the PUT /systems/systemLoad endpoint that multiply calls the task bean’s getSystemLoad() method to retrieve the system load of all systems in the inventory. When the calls return, the thenAcceptAsync() method processes the returned data with the CompletableFuture<Double> type and calls the system’s setSystemLoad() method to store the system load. Exceptions are handled in a callback that is provided to the exceptionally() method, which behaves like a catch block.

A CountDownLatch object is used to track asynchronous requests. The countDown() method is called whenever a request is complete. When the CountDownLatch is at zero, it indicates that all asynchronous requests are complete. By using the await() method of the CountDownLatch, the program waits for all the asynchronous requests to be complete. When all asynchronous requests are complete, the program resumes execution with all required data processed.

Visit the http://localhost:9081/openapi/ui URL and make a PUT request to the /api/inventory/systems/systemLoad endpoint with 5 seconds for the after parameter. The request will take 5 seconds to complete. Visit the http://localhost:9081/api/inventory/systems URL to see that the systemLoad values are updated to non-zero.

Testing the inventory application

Although you can test your application manually, automated tests ensure consistent code quality by triggering a failure whenever a code change introduces a defect. In this section, you’ll create integration tests for the inventory microservice.

Create a RESTful client interface for the inventory microservice.

InventoryResourceClient.java

link:finish/inventory/src/test/java/it/io/openliberty/guides/inventory/InventoryResourceClient.java[role=include]
Create the InventoryResourceClient.java file.
inventory/src/test/java/it/io/openliberty/guides/inventory/InventoryResourceClient.java

This interface declares listContents(), getSystem(), addSystemClient(), updateMemoryUsed(), updateSystemLoad(), removeSystem(), and resetSystems() methods for accessing each of the endpoints that are set up to access the inventory microservice.

InventoryEndpointIT.java

link:finish/inventory/src/test/java/it/io/openliberty/guides/inventory/InventoryEndpointIT.java[role=include]
Create the InventoryEndpointIT.java file.
inventory/src/test/java/it/io/openliberty/guides/inventory/InventoryEndpointIT.java

The testAddSystems() tests the POST /inventory/system/{hostname} endpoint and confirms that three systems are added to the inventory.

The testUpdateMemoryUsed() tests the PUT /inventory/systems/memoryUsed endpoint and confirms that the memory usage of each system is greater than 0.0.

The testUpdateSystemLoad() tests the PUT /inventory/systems/systemLoad endpoint and confirms that the system load of each system is greater than 0.0.

The testResetSystems() tests the PUT /inventory/systems/reset endpoint and confirms that the memory usage and the system load of each system are 0.0.

The testRemoveSystem() tests the DELETE /inventory/systems/{hostname} endpoint and confirms that two systems remain in the inventory.

Running the tests

Because you started Open Liberty in dev mode, you can run the tests by pressing the enter/return key from the command-line session where you started the inventory microservice.

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

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.inventory.InventoryEndpointIT
...
Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 11.62 s -- in it.io.openliberty.guides.inventory.InventoryEndpointIT

Results:

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

When you are done checking out the services, exit dev mode by pressing CTRL+C in the command-line sessions where you ran the system and inventory microservices.

Great work! You’re done!

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

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •