|
Note
|
The Creating a MicroProfile application guide has been archived and is replaced by the Creating a RESTful web service guide and the Injecting dependencies into microservices guide. |
Learn how to use JAX-RS, CDI, and JSON-P to build a MicroProfile application.
MicroProfile is an initiative to optimize Enterprise Java for a microservices architecture. You will write REST web services with JAX-RS and JSON-P and also use CDI to inject dependencies and help manage the lifecycle of your application.
You will create an application that provides an inventory service, which allows you to retrieve
the information for a particular host or a list of all previously registered hosts.
If the requested host is not in the inventory, the inventory service attempts to retrieve
that host’s information by calling its system service.
For example, you will be able to access the following URL to retrieve a list of all hosts in the inventory:
http://localhost:9080/inventory/hostsThe service responds with a JSON message that contains host information for all previously registered hosts, such as in the following example:
{
"hosts": {
"localhost": {
"os.name": "Mac OS X",
"user.name": "foo"
},
"jon-doe": {
"os.name": "Windows 10",
"user.name": "bar"
}
},
"total": 2
}You will be able to retrieve information about a specific host by calling this address:
http://localhost:9080/inventory/hosts/<hostname>The service responds with a JSON representation of the system properties that are retrieved
from the system service of the specified host. The system service is a simple REST service
that returns the system properties of a host. This service is implemented for you.
You will be running both system and the inventory services locally, on a single server.
First, create the JAX-RS service that serves as the hosts endpoint:
Create src/main/java/io/openliberty/guides/microprofile/InventoryResource.java:
link:finish/src/main/java/io/openliberty/guides/microprofile/InventoryResource.java[role=include]The @Path annotation identifies the URI path template that the resource responds to.
All @GET annotated methods do not yet have implemented bodies, which are planned to be written at a later point.
The getPropertiesForHost method responds to a GET HTTP request and returns the system properties
JSON object for the specified host. You can use the @PathParam annotation inside the method
signature to retrieve the hostname as an argument.
link:finish/src/main/java/io/openliberty/guides/microprofile/InventoryResource.java[role=include]The listContents method also responds to a GET request and returns the currently stored
system properties, the contents of the inventory.
link:finish/src/main/java/io/openliberty/guides/microprofile/InventoryResource.java[role=include]The Inventory Manager is responsible for storing the data about the systems in the inventory and managing them.
Create src/main/java/io/openliberty/guides/microprofile/InventoryManager.java:
link:finish/src/main/java/io/openliberty/guides/microprofile/InventoryManager.java[role=include]For simplicity, use a ConcurrentMap to store the data. Alternatively, you can use
a database or another service to do so.
This class will contain three simple methods: add, get, and list.
The add method adds the JSON object that contains the system properties to the inventory.
link:finish/src/main/java/io/openliberty/guides/microprofile/InventoryManager.java[role=include]The get method returns a JSON object that contains the system properties for the specified hostname.
Notice the call to InventoryUtil. This class contains utility methods that test connections
and access the system service. InventoryUtil is covered in more detail in a later section.
link:finish/src/main/java/io/openliberty/guides/microprofile/InventoryManager.java[role=include]The list method returns a JSON representation of the current contents of the inventory.
For simplicity, list returns only the OS name and username of each registered host.
link:finish/src/main/java/io/openliberty/guides/microprofile/InventoryManager.java[role=include]CDI is a Java EE specification that makes it simple for developers to use enterprise beans in web applications. It simplifies dependency and lifecycle management and enables developers to focus on their code.
To perform a dependency injection, use the @Inject annotation .
In the previous section, you created an InventoryManager bean. Inject that bean into
the InventoryResource class to use what the bean provides without the need for its instantiation.
link:finish/src/main/java/io/openliberty/guides/microprofile/InventoryResource.java[role=include]With the injection in place, implement the @GET annotated methods:
link:finish/src/main/java/io/openliberty/guides/microprofile/InventoryResource.java[role=include]link:finish/src/main/java/io/openliberty/guides/microprofile/InventoryResource.java[role=include]The CDI specification defines multiple scopes. Use the following annotations to define the scopes that you need for your objects:
-
The
@RequestScopedannotation indicates that a new instance is created for each request. -
The
@ApplicationScopedannotation indicates that only one instance is created for each application.
Annotate InventoryResource with @ApplicationScoped because it does not depend on any request-specific state.
In general, annotate all resources with @ApplicationScoped unless they need to be created once
every request. For example, when you use a real database resource instead of a 'Map', you improve performance because
the container will not re-initialize database resources on each request.
link:finish/src/main/java/io/openliberty/guides/microprofile/InventoryResource.java[role=include]Annotate InventoryManager with @ApplicationScoped because it, too, does not depend on any request-specific state.
link:finish/src/main/java/io/openliberty/guides/microprofile/InventoryManager.java[role=include]You can have a beans.xml file under the META-INF directory or under the WEB-INF directory
of your web application for CDI scanning. Since CDI 1.1 and Java EE 7, scanning is enabled by default, so
you do not need a beans.xml file here. Annotated beans are automatically discovered by default.
Recall the InventoryUtil.getProperties(hostname) call from Creating the Inventory Manager.
This class is used to access the system service from the inventory service as a JAX-RS client.
link:finish/src/main/java/io/openliberty/guides/microprofile/util/InventoryUtil.java[role=include]The InventoryUtil class contains a getProperties method that makes an HTTP GET request to
the system service and returns the retrieved JSON object. The responseOk method tests for a valid
connection to the system service at the specified host. Together, these methods enable interaction with
the system service.
Finally, create an application class and identify the base URI where the application serves requests.
Create the following JAX-RS application class: src/main/java/io/openliberty/guides/microprofile/InventoryApplication.java
link:finish/src/main/java/io/openliberty/guides/microprofile/InventoryApplication.java[role=include]You now have your inventory service, and your application has a starting point:
http://localhost:9080/inventory/hosts
To see the new application in action, run the Maven liberty:start-server from the start directory:
mvn liberty:start-serverOnce the server is running, you can find both services at the following locations:
The application provides a simple inventory management REST service that you can access at the following URLs:
http://localhost:9080/inventory/hosts
http://localhost:9080/inventory/hosts/{hostname}
# Example URL to retrieve or register a system
http://localhost:9080/inventory/hosts/localhostThe first URL returns the current contents of the inventory. The second URL returns the system
properties for the specified hostname. The service retrieves these properties from the inventory.
If the inventory does not have an entry for the specified hostname, the service instead calls
the system service that runs on the hostname to retrieve system properties. The system properties
that are retrieved from the system service are then stored in the inventory and returned.
Once you start the server, you can access the two previous URLs from your browser to manually test the application. However, you should rely on automated tests because they will trigger a failure if a code change introduces a defect. JUnit and the JAX-RS Client API provide a simple environment to test the application.
Create a test class, src/test/java/it/io/openliberty/guides/microprofile/EndpointTest.java.
link:finish/src/test/java/it/io/openliberty/guides/microprofile/EndpointTest.java[role=include]Mark each test method with the @Test annotation.
Use the @Before and @After annotations to perform setup and teardown tasks for each
of your individual tests.
link:finish/src/test/java/it/io/openliberty/guides/microprofile/EndpointTest.java[role=include]The test code needs some information about the application in order to make requests. The server port and the application context root are key and are dictated by the server configuration. To make the information easier to change, specify it in a single place like the Maven pom.xml file instead of hardcoding it.
link:finish/pom.xml[role=include]These Maven properties can then be passed to the Java test program as a series of system properties:
link:finish/pom.xml[role=include]You can then access these properties with the System.getProperties method. Use these properties
to create URL representations for each running service:
link:finish/src/test/java/it/io/openliberty/guides/microprofile/EndpointTest.java[role=include]You can use the JAX-RS client to make the REST call and convert the payload to and from a JSON-P
representation. To do so, configure the client with the JsrJsonpProvider property:
link:finish/src/test/java/it/io/openliberty/guides/microprofile/EndpointTest.java[role=include]Normally, you cannot control the order of execution for test methods. However, you can place
each test method within a single container method. This method is then annotated with @Test.
The following test suite contains four test methods, which run in the order they appear in the suite:
link:finish/src/test/java/it/io/openliberty/guides/microprofile/EndpointTest.java[role=include]Create a test called testEmptyInventory. This test asserts that the inventory
is empty when the inventory service first starts. Use the Response object to receive a
response from the target URL. You can then retrieve the returned JSON from this response.
link:finish/src/test/java/it/io/openliberty/guides/microprofile/EndpointTest.java[role=include]Write a getResponse helper method to reuse the same line of code to retrieve a response from a
particular URL. This method helps keep your code neat and organized.
link:finish/src/test/java/it/io/openliberty/guides/microprofile/EndpointTest.java[role=include]Write another helper called assertResponse. This method ensures that the received response code is
valid and returns a status code 200.
link:finish/src/test/java/it/io/openliberty/guides/microprofile/EndpointTest.java[role=include]Use the returned Response object to retrieve the JSON file:
link:finish/src/test/java/it/io/openliberty/guides/microprofile/EndpointTest.java[role=include]Write an assertion to make sure that the total field is set to 0, which indicates that the inventory is empty.
Close the response before the method returns.
link:finish/src/test/java/it/io/openliberty/guides/microprofile/EndpointTest.java[role=include]Create a test called testHostRegistration. This test asserts that registering the localhost system
is successful.
link:finish/src/test/java/it/io/openliberty/guides/microprofile/EndpointTest.java[role=include]Write a helper method called visitLocalhost. This method makes a simple GET request to the
system service at localhost, registering the localhost system.
link:finish/src/test/java/it/io/openliberty/guides/microprofile/EndpointTest.java[role=include]Create a test called testSystemPropertiesMatch. This test asserts that the currently stored system
properties for localhost match the ones returned by the system service.
link:finish/src/test/java/it/io/openliberty/guides/microprofile/EndpointTest.java[role=include]Write a helper method called assertProperty. Use this method to assert that the properties
os.name and user.name match.
link:finish/src/test/java/it/io/openliberty/guides/microprofile/EndpointTest.java[role=include]Finally, create a test called testUnknownHost. This test asserts that the inventory service
returns an error when you make a registration attempt on an unknown hostname.
link:finish/src/test/java/it/io/openliberty/guides/microprofile/EndpointTest.java[role=include]To rebuild and run the tests, navigate to the start directory and run the mvn clean install command
from the command line.
# If the server is still running from previous steps, stop it first
mvn liberty:stop-server
# Then execute
mvn clean installIt might take some time before the tests are executed. If the tests pass, you will see the following output:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.microprofile.EndpointTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.668 sec - in it.io.openliberty.guides.microprofile.EndpointTest
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0You have just built a MicroProfile application on top of Open Liberty using JAX-RS, CDI, and JSON-P.