Note
|
This repository contains the guide documentation source. To view the guide in published form, view it on the Open Liberty website. |
You’ll explore how to use Hypermedia as the Engine of Application State (HATEOAS) to drive your RESTful web service on Open Liberty.
You will learn how to use hypermedia to create a specific style of a response JSON, which has contents that you can use to navigate your REST service. You’ll build on top of a simple inventory REST service that you can develop with MicroProfile technologies. You can find the service at the following URL:
http://localhost:9080/inventory/hosts
The service responds with a JSON file that contains an array of registered hosts. Each host has a collection of HATEOAS links:
[
{
"hostname": "foo",
"_links": [
{
"href": "http://localhost:9080/inventory/hosts/foo",
"rel": "self"
}
]
},
{
"hostname": "bar",
"_links": [
{
"href": "http://localhost:9080/inventory/hosts/bar",
"rel": "self"
}
]
},
{
"hostname": "*",
"_links": [
{
"href": "http://localhost:9080/inventory/hosts/*",
"rel": "self"
}
]
}
]
HATEOAS is a constrained form of REST application architecture. With HATEOAS, the client receives information about the available resources from the REST application. The client does not need to be hardcoded to a fixed set of resources, and the application and client can evolve independently. In other words, the application tells the client where it can go and what it can access by providing it with a simple collection of links to other available resources.
When you build a RESTful web service, consider the style of your response files.
Whether they are JSON files, XML files, or in some other format, a good practice is to always have them in
a clean and organized form. In the context of HATEOAS, each resource must contain a link reference to
itself, commonly referred to as self
. Each link also needs a relationship to be associated with it,
although no strict rules exist as to how you need to format this relationship. Contain the collection
of such links within a _links
array, which itself must be a direct property of the
resource object. The underscore in the _links
property is used so that the property does not collide with any
existing fields that are named links
. In this guide, you will use the following structure of HATEOAS links:
"_links": [
{
"href": ,
"rel":
}
]
The following example shows two different links. The first link has a self
relationship with the
resource object and is generated whenever you register a host. The link points to that host
entry in the inventory:
{
"href": "http://localhost:9080/inventory/hosts/<hostname>",
"rel": "self"
}
The second link has a properties
relationship with the resource object and is generated
if the host system
service is running. The link points to the properties resource on the host:
{
"href": "http://<hostname>:9080/system/properties",
"rel": "properties"
}
Although you should stick to the previous format for the purpose of this guide, another common convention has the link as the value of the relationship:
"_links": {
"self": "http://localhost:9080/inventory/hosts/<hostname>",
"properties": "http://<hostname>:9080/system/properties"
}
Navigate to the start
directory.
Begin by building your response JSON, which is composed of the _links
array, as well as the name of
the host machine.
As mentioned before, your starting point is an existing simple inventory REST service.
Take a look at the request handlers
in the src/main/java/io/openliberty/guides/microprofile/InventoryResource.java
file.
InventoryResource.java
link:finish/src/main/java/io/openliberty/guides/microprofile/InventoryResource.java[role=include]
Since the …/inventory/hosts/
URL will no longer respond with a JSON representation of the contents of your inventory, you can discard the listContents
method and integrate it into the getPropertiesForHost
method.
Replace theInventoryResource
class.src/main/java/io/openliberty/guides/microprofile/InventoryResource.java
The contents of your inventory are now under the asterisk (*) wildcard and reside at the following URL:
http://localhost:9080/inventory/hosts/*
The GET
request handler is responsible for handling all GET
requests that are
made to the target URL. This method responds with a JSON that contains HATEOAS links.
The UriInfo
object is what is used to build your HATEOAS links.
The @Context
annotation is a part of CDI and indicates that the UriInfo will be injected when the
resource is instantiated.
Your new InventoryResource
class is now replaced. Next, let’s implement the getSystems
method and build
the response JSON object.
InventoryManager.java
link:finish/src/main/java/io/openliberty/guides/microprofile/InventoryManager.java[role=include]
Take a look at your InventoryManager
and InventoryUtil
files.
Replace theInventoryManager
class.src/main/java/io/openliberty/guides/microprofile/InventoryManager.java
InventoryManager.java
link:finish/src/main/java/io/openliberty/guides/microprofile/InventoryManager.java[role=include]
The getSystems
method accepts a
target URL as an argument and returns a JSON object that contains HATEOAS links.
Replace theInventoryUtil
class.src/main/java/io/openliberty/guides/microprofile/util/InventoryUtil.java
InventoryUtil.java
link:finish/src/main/java/io/openliberty/guides/microprofile/util/InventoryUtil.java[role=include]
The buildHostJson
helper method builds in the InventoryUtil
class. It
creates a JSON object that contains the hostname
of your system and the _links
array,
which is generated separately in the buildLinksForHost
helper method.
This helper accepts a hostname and a target URL as arguments. The helper builds a link that points to the
inventory entry with a self
relationship and also builds a link that points to the system
service with
a properties
relationship:
-
http://localhost:9080/inventory/hosts/<hostname>
-
http://<hostname>:9080/system/properties
InventoryUtil.java
link:finish/src/main/java/io/openliberty/guides/microprofile/util/InventoryUtil.java[role=include]
Consider what happens when one of the return links does not work or when a link should be available
for one object but not for another. In other words, it is important that a resource or service is
available and running before it is linked in the _links
array.
Although this guide does not cover this case, always make sure that you receive a good response code from a service before you link that service. Similarly, make sure that it makes sense for a particular object to access a resource it is linked to. For instance, it doesn’t make sense for an account holder to be able to withdraw money from their account when their balance is 0. Hence, the account holder should not be linked to a resource that provides money withdrawal.
After the server runs, you can find your new hypermedia-driven inventory
service at the following URL:
At the following URLs, access the inventory
service that is now driven by hypermedia:
The first URL returns the current contents of the inventory, and the second URL returns the system properties for
the host name. If the inventory does not contain an entry for the host name that is specified in the URL,
the system
service that is running on the requested host name is called instead. The system
properties are retrieved from that system
service and then stored in the inventory and returned.
If the servers are running, you can point your browser to each of the previous URLs to test the application manually. Nevertheless, you should rely on automated tests since they are more reliable and trigger a failure if a change introduces a defect.
EndpointTest.java
link:finish/src/test/java/it/io/openliberty/guides/hateoas/EndpointTest.java[role=include]
Create theEndpointTest
class.src/test/java/it/io/openliberty/guides/hateoas/EndpointTest.java
You can use the @Before
and @After
annotations to perform any setup and teardown tasks for each
of your individual tests.
Each test method must be marked with the @Test
annotation. Typically, the execution order of test
methods is not controlled, but if control is required, you can place methods in a single container test
method. This container method is then the only one marked with the @Test
annotation. The following
testSuite()
contains two test methods, which run in the order they appear:
The test testLinkForInventoryContents
is responsible for asserting that
the correct HATEOAS link is created for the inventory contents.
The getResponse
helper method reuses the same line of code for retrieving a response from a
specific URL. This technique helps keep your code neat and organized.
The assertResponse
method ensures that the response code that you receive is
valid (200).
The testLinksForSystem
test is responsible for asserting that the correct
HATEOAS links are created for the localhost
system. This method checks for both the self
link that points
to the inventory
service and the properties
link that points to the system
service that is running on the
localhost
system.
Finally, the method visitLocalhost
creates a GET
request to the
system
service, registering the localhost
system.
-------------------------------------------------------
T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.hateoas.EndpointTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.086 sec - in it.io.openliberty.guides.hateoas.EndpointTest
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
You’ve just built and tested a hypermedia-driven RESTful web service on top of Open Liberty.