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 use Contexts and Dependency Injection to manage and inject dependencies into microservices.
You will learn how to use Contexts and Dependency Injection (CDI) in a simple inventory management application to manage scopes and inject dependencies.
The application that you will be working with is an inventory
service,
which stores the information about various JVMs that run on different systems.
Whenever a request is made to the inventory
service to retrieve the JVM
system properties of a particular host, the inventory
service communicates with the system
service on that host to get these system properties. The system properties are then stored and returned.
You will use scopes to bind objects in this application to their well-defined contexts. CDI provides a variety of scopes for you to work with and while you will not use all of them in this guide, there is one for almost every scenario that you may encounter. Scopes are defined by using CDI annotations. You will also use dependency injection to inject one bean into another to make use of its functionalities. This enables you to inject the bean in its specified context without having to instantiate it yourself.
The implementation of the application and its services are provided for you in the start/src
directory.
The system
service can be found in start/src/main/java/io/openliberty/guides/system
, and the inventory
service can be found
in start/src/main/java/io/openliberty/guides/inventory
. If you want to learn more about RESTful web services and how to build them, see
Creating a RESTful web service for details about how to build the system
service.
The inventory
service is built in a similar way.
Contexts and Dependency Injection (CDI) defines a rich set of complementary services that improve the application structure. The most fundamental services that are provided by CDI are contexts that bind the lifecycle of stateful components to well-defined contexts, and dependency injection that is the ability to inject components into an application in a typesafe way. With CDI, the container does all the daunting work of instantiating dependencies, and controlling exactly when and how these components are instantiated and destroyed.
The finish
directory in the root of this guide contains the finished inventory application. Give it a try before you proceed.
To try out the application, first navigate to the finish
directory and then run the following
Maven goals to build the application and run it inside Open Liberty:
cd finish
mvn install liberty:start-server
Point your browser to the http://localhost:9080/inventory/systems URL. This is the starting point of the inventory
service and it displays the current contents of the inventory. As you might expect, these are empty since
nothing is stored in the inventory yet. Next, point your browser to the http://localhost:9080/inventory/systems/localhost URL.
You see a result in JSON format with the system properties of your local JVM. When you visit this URL, these system
properties are automatically stored in the inventory. Go back to http://localhost:9080/inventory/systems and
you see a new entry for localhost
. For simplicity, only the OS name and username are shown here for
each host. You can repeat this process for your own hostname or any other machine that is running
the system
service.
After you are done checking out the application, stop the Open Liberty server:
mvn liberty:stop-server
You will use CDI to inject dependencies into the inventory manager application and learn how to manage the life cycles of your objects.
Navigate to the start
directory to begin.
Create the inventory manager class in the src/main/java/io/openliberty/guides/inventory/InventoryManager.java
file:
link:finish/src/main/java/io/openliberty/guides/inventory/InventoryManager.java[role=include]
This bean contains three simple functions. The get()
function is for retrieving entries from calling the system
service.
The add()
function is for adding entries to the inventory.
The list()
function is for listing all the entries currently stored in the inventory.
This bean must be persistent between all the clients, which means multiple clients need to share the same instance.
To achieve this by using CDI, you can simply add the @ApplicationScoped
annotation on the class.
This annotation indicates that this particular
bean is to be initialized once per application.
By making it application-scoped, the container ensures that the same instance of the bean is used whenever
it is injected into the application.
Next, create the inventory
resource class in the src/main/java/io/openliberty/guides/inventory/InventoryResource.java
file:
link:finish/src/main/java/io/openliberty/guides/inventory/InventoryResource.java[role=include]
The inventory resource is a RESTful service that is served at the inventory/systems
endpoint. It enables the client to
communicate with the inventory manager through HTTP methods.
Add the @RequestScoped
annotation on the class to indicate that this bean is
to be initialized once for every request. In other words, the bean is instantiated when the request
is received and destroyed when a response is sent back to the client. While this bean can
also be application-scoped, request scope is short-lived and is therefore ideal for HTTP requests.
Refer to the inventory
resource class you created above:
src/main/java/io/openliberty/guides/inventory/InventoryResource.java
.
The @Inject
annotation indicates a dependency injection.
You are injecting your InventoryManager
bean into the InventoryResource
class.
This injects the bean in its specified context (application-scoped) and makes all of its functionalities
available without the need of instantiating it yourself.
The injected bean can then be invoked directly through the manager.get(hostname)
and manager.list()
function calls.
Finally, you have a client component SystemClient
that can be found in src/main/java/io/openliberty/guides/inventory/client
.
This class communicates with the system
service to
retrieve the JVM system properties for a particular host that exposes them.
This class also contains detailed Javadocs that you can read for reference.
Your inventory application is now completed.
You can find the inventory
and system
services at the following URLs:
While you can test your application manually, you should rely on automated tests since they trigger a failure whenever a code change introduces a defect. Since the application is a RESTful web service application, you can use JUnit and the RESTful web service Client API to write tests. In testing the functionality of the application, the scopes and dependencies are being tested.
Create the test class in the src/test/java/it/io/openliberty/guides/inventory/InventoryEndpointTest.java
file:
link:finish/src/test/java/it/io/openliberty/guides/inventory/InventoryEndpointTest.java[role=include]
The @BeforeClass
annotation is placed on a method that runs before any of the test cases.
In this case, the oneTimeSetup
method retrieves the port number for the Open Liberty server and builds
a base URL string that is used throughout the tests.
The @Before
and @After
annotations are placed on methods that run before and after every test case.
These methods are generally used to perform any setup and teardown tasks. In this case, the setup
method
creates a JAX-RS client, which makes HTTP requests to the inventory
service. This client must
also be registered with a JSON-P provider (JsrJsonpProvider
) to process JSON resources. The teardown
method simply destroys this client instance.
See the following descriptions of the test cases:
-
testEmptyInventory()
verifies that the inventory is initially empty when the server first starts up. -
testHostRegistration()
verifies that a host is correctly added to the inventory. -
testSystemPropertiesMatch()
verifies that the JVM system properties returned by thesystem
service match the ones stored in theinventory
service. -
testUnknownHost()
verifies that an unknown host or a host that does not expose their JVM system properties is correctly handled as an error.
To force these test cases to run in a particular order, put them in a testSuite()
method and label
it with a @Test
annotation so that it automatically runs when your test class run.
Finally, a system
test class in the src/test/java/it/io/openliberty/guides/system/SystemEndpointTest.java
file
is included for you to test the basic functionality of the system
service.
If a test failure occurs, then you might have introduced a bug into the code.
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.system.SystemEndpointTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.99 sec - in it.io.openliberty.guides.system.SystemEndpointTest
Running it.io.openliberty.guides.inventory.InventoryEndpointTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.325 sec - in it.io.openliberty.guides.inventory.InventoryEndpointTest
Results :
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
To see whether the tests detect a failure, change the endpoint for the inventory
service in
the src/main/java/io/openliberty/guides/inventory/InventoryResource.java
file to something else, then
run the Maven build again. You see a test failure occur.
You have just completed building a simple inventory application on top of Open Liberty using CDI services.
You can continue to try one of the related guides, which demonstrate new technologies that you can learn and expand on top what you built in this guide.