Creating a MicroProfile application

Learn how to use JAX-RS, CDI, and JSON-P to build a MicroProfile application.

What you’ll learn

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, to retrieve a list of all hosts in the inventory, enter the following URL:

http://localhost:9080/inventory/hosts

The 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
}

To retrieve information about a specific host, call 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.

Creating the Inventory Service with JAX-RS

First, create the JAX-RS service that serves as the systems endpoint:

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]

Creating the Inventory Manager

The Inventory Manager is responsible for storing the data about the systems in the inventory and managing them.

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]

Using CDI

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.

Injection

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]

Scope

The CDI specification defines multiple scopes. Use the following annotations to define the scopes that you need for your objects:

  • The @RequestScoped annotation indicates that a new instance is created for each request.

  • The @ApplicationScoped annotation 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.

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-specifc state. 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/InventoryManager.java[role=include]

Enabling CDI

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.

Accessing the System RESTful web service

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.

Creating the JAX-RS application

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/properties

Starting the application

To see the new application in action, run the Maven liberty:start-server from the start directory:

cd start
mvn liberty:start-server

Once the server is running, you can find both services at the following locations:

Testing the MicroProfile application

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/localhost

The 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.

Setting up the tests

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.

<testServerHttpPort>9080</testServerHttpPort>
<testServerHttpsPort>9443</testServerHttpsPort>

These Maven properties can then be passed to the Java test program as a series of system properties:

<systemPropertyVariables>
  <liberty.test.port>${testServerHttpPort}</liberty.test.port>
  <running.bluemix>${running.bluemix}</running.bluemix>
  <cf.context.root>${cf.context.root}</cf.context.root>
</systemPropertyVariables>

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]

Writing the tests

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]

Running the tests

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 install

It 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: 0

Congratulations! You’re done!

Congratulations! You have just built and tested a MicroProfile application with JAX-RS, CDI, JSON-P and Open Liberty.