{"id":18429116,"url":"https://github.com/openliberty/guide-liberty-deep-dive","last_synced_at":"2025-04-07T17:32:42.651Z","repository":{"id":38240516,"uuid":"451568799","full_name":"OpenLiberty/guide-liberty-deep-dive","owner":"OpenLiberty","description":"An end-to-end tutorial on cloud-native Java application development and deployment using Jakarta EE, MicroProfile \u0026 Open Liberty","archived":false,"fork":false,"pushed_at":"2025-03-17T21:56:40.000Z","size":1912,"stargazers_count":7,"open_issues_count":1,"forks_count":7,"subscribers_count":7,"default_branch":"prod","last_synced_at":"2025-03-17T22:51:01.234Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://ibm.biz/LibertyDeepDive","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OpenLiberty.png","metadata":{"files":{"readme":"README.adoc","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-01-24T17:41:46.000Z","updated_at":"2025-03-17T21:56:44.000Z","dependencies_parsed_at":"2024-02-21T18:26:58.208Z","dependency_job_id":"7c357631-0b6c-4dcd-bcaa-a316ab492302","html_url":"https://github.com/OpenLiberty/guide-liberty-deep-dive","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenLiberty%2Fguide-liberty-deep-dive","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenLiberty%2Fguide-liberty-deep-dive/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenLiberty%2Fguide-liberty-deep-dive/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OpenLiberty%2Fguide-liberty-deep-dive/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OpenLiberty","download_url":"https://codeload.github.com/OpenLiberty/guide-liberty-deep-dive/tar.gz/refs/heads/prod","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247697942,"owners_count":20981275,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-06T05:15:53.017Z","updated_at":"2025-04-07T17:32:42.639Z","avatar_url":"https://github.com/OpenLiberty.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"// Copyright (c) 2022, 2025 IBM Corporation and others.\n// Licensed under Creative Commons Attribution-NoDerivatives\n// 4.0 International (CC BY-ND 4.0)\n//   https://creativecommons.org/licenses/by-nd/4.0/\n//\n// Contributors:\n//     IBM Corporation\n//\n:projectid: liberty-deep-dive\n:page-layout: guide-multipane\n:page-duration: 120 minutes\n:page-releasedate: 2022-04-27\n:page-essential: false\n:page-description: Learn how to use Liberty to develop and run Java microservices.\n:page-tags: ['jakarta-ee', 'microprofile']\n:page-related-guides: ['microprofile-openapi', 'cdi-intro', 'microprofile-config', 'jpa-intro', 'microprofile-jwt', 'microprofile-health', 'microprofile-metrics', 'containerize', 'openliberty-operator-intro', 'kubernetes-microprofile-config']\n:page-permalink: /guides/{projectid}\n:common-includes: https://raw.githubusercontent.com/OpenLiberty/guides-common/prod\n:imagesdir: /img/guide/{projectid}\n:source-highlighter: prettify\n:page-seo-title: A hands-on technical deep dive on Liberty\n:page-seo-description: An in-depth and end-to-end tutorial with examples (akin to a \"masterclass\") on how to easily develop cloud-native Java applications and microservices using Jakarta EE and MicroProfile APIs using Open Liberty and WebSphere Liberty as the runtime or framework, and how to build, run and deploy the applications in containers to Kubernetes.\n:guide-author: Open Liberty\n= A Technical Deep Dive on Liberty\n\n[.hidden]\nNOTE: This repository contains the documentation source. To view this exercise in published form, view it on the https://openliberty.io/guides/{projectid}.html[Open Liberty website].\n\nLiberty is a cloud-optimized Java runtime that is fast to start up with a low memory footprint and a https://openliberty.io/docs/latest/development-mode.html[dev mode^], for quick iteration. With Liberty, adopting the latest open cloud-native Java APIs, like MicroProfile and Jakarta EE, is as simple as adding features to your Liberty configuration. The Liberty zero migration architecture lets you focus on what's important and not the APIs changing under you.\n\n== What you'll learn\n\nYou will learn how to build a RESTful microservice on Liberty with Jakarta EE and MicroProfile. You will use Maven throughout this exercise to build the microservice and to interact with the running Liberty instance. Then, you’ll build a container image for the microservice and deploy it to Kubernetes in a Liberty Docker container. You will also learn how to secure the REST endpoints and use JSON Web Tokens to communicate with the provided `system` secured microservice.\n\nThe microservice that you’ll work with is called `inventory`. The `inventory` microservice persists data into a PostgreSQL database. \n\nimage::inventory.png[Inventory microservice,align=\"center\",height=85%,width=85%]\n\n== Additional prerequisites\n\nDocker must be installed before you start the **Persisting Data** module. For installation instructions, refer to the https://docs.docker.com/get-docker/[official Docker documentation^]. You'll build and run the application in Docker containers.\n\nMake sure to start your Docker daemon before you proceed.\n\nAlso, if you are using Linux, Kubernetes must be installed before you start the **Deploying the microservice to Kubernetes**.\n\nYou will use `Minikube` as a single-node Kubernetes cluster that runs locally in a virtual machine. Make sure that you have `kubectl` installed. If you need to install `kubectl`, see the https://kubernetes.io/docs/tasks/tools/install-kubectl/#install-kubectl-on-linux[kubectl installation instructions^].\nFor Minikube installation instructions, see the https://github.com/kubernetes/minikube#installation[Minikube documentation^].\n\n\n== Getting started\n\nifdef::cloud-hosted[]\nTo open a new command-line session,\nselect ***Terminal*** \u003e ***New Terminal*** from the menu of the IDE.\n\nRun the following command to navigate to the **/home/project** directory:\n\n```bash\ncd /home/project\n```\nendif::[]\n\nClone the https://github.com/OpenLiberty/guide-liberty-deep-dive.git[Git repository^]:\n\n[role='command']\n```\ngit clone https://github.com/openliberty/guide-liberty-deep-dive.git\ncd guide-liberty-deep-dive\n```\n\nThe `start` directory is an empty directory where you will build the `inventory` service.\n\nThe `finish` directory contains the finished projects of different modules that you will build.\n\nifndef::cloud-hosted[]\nBefore you begin, make sure you have all the necessary prerequisites.\nendif::[]\n\nifdef::cloud-hosted[]\nIn this IBM Cloud environment, you need to change the user home to ***/home/project*** by running the following command:\n```bash\nsudo usermod -d /home/project theia\n```\nendif::[]\n\n== Getting started with Liberty and REST\n\nLiberty now offers an easier way to get started with developing your application: the Open Liberty Starter. This tool provides a simple and quick way to get the necessary files to start building an application on Liberty. Through this tool, you can specify your application and project name. You can also choose a build tool from either Maven or Gradle, and pick the Java SE, Jakarta EE, and MicroProfile versions for your application.\n\nIn this workshop, the Open Liberty Starter is used to create the starting point of the application. Maven is used as the selected build tool and the application uses of Jakarta EE 10 and MicroProfile 7.\n\nTo get started with this tool, see the Getting Started page: https://openliberty.io/start/[https://openliberty.io/start/^]\n\nOn that page, enter the following properties in the **Create a starter application** wizard.\n\n* Under Group specify: `io.openliberty.deepdive`\n* Under Artifact specify: `inventory`\n* Under Build Tool select: `Maven`\n* Under Java SE Version select: `17`\n* Under Java EE/Jakarta EE Version select: `10.0`\n* Under MicroProfile Version select: `7.0`\n\nifndef::cloud-hosted[]\nThen, click `Generate Project`, which downloads the starter project as `inventory.zip` file. \n\nNext, extract the `inventory.zip` file on your system. Move the contents of this extracted `inventory` directory to the `start` directory of this project, which is at the following path: `guide-liberty-deepdive/start/inventory`.\n\nInstead of manually downloading and extracting the project, run the following commands in the `start` directory:\n\n[.tab_link.windows_link]\n`*WINDOWS*`\n[.tab_link.mac_link]\n`*MAC*`\n[.tab_link.linux_link]\n`*LINUX*`\n\n[.tab_content.windows_section]\n--\n[role='command']\n```\ncurl -o inventory.zip 'https://start.openliberty.io/api/start?a=inventory\u0026b=maven\u0026e=10.0\u0026g=io.openliberty.deepdive\u0026j=17\u0026m=7.0'\nExpand-Archive -Path .\\inventory.zip\n```\n--\n\n[.tab_content.mac_section.linux_section]\n--\n[role='command']\n```\ncurl -o inventory.zip 'https://start.openliberty.io/api/start?a=inventory\u0026b=maven\u0026e=10.0\u0026g=io.openliberty.deepdive\u0026j=17\u0026m=7.0'\nunzip inventory.zip -d inventory\n```\n--\nendif::[]\n\nifdef::cloud-hosted[]\nIn this Skills Network environment, instead of manually downloading and extracting the project, run the following commands:\n```bash\ncd /home/project/guide-liberty-deep-dive/start\ncurl -o inventory.zip 'https://start.openliberty.io/api/start?a=inventory\u0026b=maven\u0026e=10.0\u0026g=io.openliberty.deepdive\u0026j=17\u0026m=7.0'\nunzip inventory.zip -d inventory\n```\n\nAfter getting the ***inventory*** project, switch the workspace to the ***/home/project/guide-liberty-deep-dive/start/inventory*** directory.\n\u003e - Select **File** \u003e **Close Workspace** from the menu of the IDE.\n\u003e   - Click the OK button to confirm to close.\n\u003e   - Wait for the IDE to refresh.\n\u003e - Select **File** \u003e **Open...** from the menu of the IDE.\n\u003e   - In the **Open** window, select the ***home*** directory from the top dropdown list, and then select the ***/home/project/guide-liberty-deep-dive/start/inventory*** directory and click the **Open** button.\n\u003e   - Wait for the IDE to refresh.\n\u003e   - Click **Yes** to trust the workspace.\nendif::[]\n\n=== Building the application\n\nThis application is configured to be built with Maven. Every Maven-configured project contains a `pom.xml` file that defines the project configuration, dependencies, and plug-ins.\n\npom.xml\n[source, xml, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-getting-started/pom.xml[]\n----\n\nYour [hotspot]`pom.xml` file is located in the `start/inventory` directory and is configured to include the [hotspot=libertyMavenPlugin]`liberty-maven-plugin`. Using the plug-in, you can install applications into Liberty and manage the associated Liberty instances.\n\nTo begin, open a command-line session and navigate to your application directory. \n\nifndef::cloud-hosted[]\n[role='command']\n```\ncd start/inventory\n```\nendif::[]\n\nifdef::cloud-hosted[]\n```bash\ncd /home/project/guide-liberty-deep-dive/start/inventory\n```\nendif::[]\n\nBuild and deploy the `inventory` microservice to Liberty by running the Maven `liberty:run` goal:\n\n[role='command']\n```\nmvn liberty:run\n```\n\nThe `mvn` command initiates a Maven build, during which the target directory is created to store all build-related files.\n\nThe `liberty:run` argument specifies the Liberty `run` goal, which starts a Liberty instance in the foreground. As part of this phase, a Liberty runtime is downloaded and installed into the `target/liberty/wlp` directory. Additionally, a Liberty instance is created and configured in the `target/liberty/wlp/usr/servers/defaultServer` directory, and the application is installed into that Liberty instance by using https://www.ibm.com/support/knowledgecenter/en/SSEQTP_liberty/com.ibm.websphere.wlp.doc/ae/rwlp_loose_applications.html[loose config^].\n\nFor more information about the Liberty Maven plug-in, see its https://github.com/WASdev/ci.maven[GitHub repository^].\n\nWhile the Liberty instance starts up, various messages display in your command-line session. Wait for the following message, which indicates that the instance startup is complete:\n\n[source, role=\"no_copy\"]\n----\n[INFO] [AUDIT] CWWKF0011I: The server defaultServer is ready to run a smarter planet.\n----\n\nWhen you need to stop the Liberty instance, press `CTRL+C` in the command-line session where you ran the Liberty.\n\n\n=== Starting and stopping the Liberty in the background\n\nAlthough you can start and stop the Liberty instance in the foreground by using the Maven `liberty:run` goal, you can also start and stop the instance in the background with the Maven `liberty:start` and `liberty:stop` goals:\n\n[role='command']\n----\nmvn liberty:start\nmvn liberty:stop\n----\n\n\n=== Updating the Liberty configuration without restarting the instance\n\nThe Liberty Maven plug-in includes a `dev` goal that listens for any changes in the project, including application source code or configuration. The Liberty instance automatically reloads the configuration without restarting. This goal allows for quicker turnarounds and an improved developer experience.\n\nIf the Liberty instance is running, stop it and restart it in dev mode by running the `liberty:dev` goal in the `start/inventory` directory:\n\n[role='command']\n```\nmvn liberty:dev\n```\n\nAfter you see the following message, your Liberty instance is ready in dev mode:\n\n[role=\"no_copy\"]\n----\n**************************************************************\n*    Liberty is running in dev mode.\n----\n\nDev mode automatically picks up changes that you make to your application and allows you to run tests by pressing the `enter/return` key in the active command-line session. When you’re working on your application, rather than rerunning Maven commands, press the `enter/return` key to verify your change.\n\n=== Developing a RESTful microservice\n\nNow that a basic Liberty application is running, the next step is to create the additional application and resource classes that the application needs. Within these classes, you use Jakarta REST and other MicroProfile and Jakarta APIs.\n\nifdef::cloud-hosted[]\nOpen another command-line session by selecting **Terminal** \u003e **New Terminal** from the menu of the IDE. Go to the ***start/inventory*** directory.\n```bash\ncd /home/project/guide-liberty-deep-dive/start/inventory\n```\nendif::[]\n\n[role=\"code_command hotspot file=0\", subs=\"quotes\"]\n----\n#Create the `Inventory` class.#\n`src/main/java/io/openliberty/deepdive/rest/Inventory.java`\n----\n\nInventory.java\n[source, Java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-getting-started/src/main/java/io/openliberty/deepdive/rest/Inventory.java[]\n----\n\nThis `Inventory` class stores a record of all systems and their system properties. The [hotspot=getSystem file=0]`getSystem()` method within this class retrieves and returns the system data from the system. The [hotspot=add file=0]`add()` method enables the addition of a system and its data to the inventory. The [hotspot=update file=0]`update()` method enables a system and its data on the inventory to be updated. The [hotspot=removeSystem file=0]`removeSystem()` method enables the deletion of a system from the inventory.\n\n\nCreate the `model` subdirectory, then create the `SystemData` class. The `SystemData` class is a Plain Old Java Object (POJO) that represents a single inventory entry. \n\nifndef::cloud-hosted[]\n[.tab_link.windows_link]\n`*WINDOWS*`\n[.tab_link.mac_link]\n`*MAC*`\n[.tab_link.linux_link]\n`*LINUX*`\n\n[.tab_content.windows_section]\n--\n[role='command']\n```\nmkdir src\\main\\java\\io\\openliberty\\deepdive\\rest\\model\n```\n--\n\n[.tab_content.mac_section.linux_section]\n--\n[role='command']\n```\nmkdir src/main/java/io/openliberty/deepdive/rest/model\n```\n--\nendif::[]\n\nifdef::cloud-hosted[]\n```bash\nmkdir /home/project/guide-liberty-deep-dive/start/inventory/src/main/java/io/openliberty/deepdive/rest/model\n```\nendif::[]\n\n[role=\"code_command hotspot file=1\", subs=\"quotes\"]\n----\n#Create the `SystemData` class.#\n`src/main/java/io/openliberty/deepdive/rest/model/SystemData.java`\n----\n\nSystemData.java\n[source, Java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-getting-started/src/main/java/io/openliberty/deepdive/rest/model/SystemData.java[]\n----\n\nThe `SystemData` class contains the hostname, operating system name, Java version, and heap size properties. The various methods within this class allow the viewing or editing the properties of each system in the inventory.\n\n\n[role=\"code_command hotspot file=2\", subs=\"quotes\"]\n----\n#Create the `SystemResource` class.#\n`src/main/java/io/openliberty/deepdive/rest/SystemResource.java`\n----\n\nSystemResource.java\n[source, Java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-getting-started/src/main/java/io/openliberty/deepdive/rest/SystemResource.java[]\n----\n\nRestApplication.java\n[source, Java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-getting-started/src/main/java/io/openliberty/deepdive/rest/RestApplication.java[]\n----\n\nIn Jakarta RESTful Web Services, a single class like the `SystemResource.java` class must represent a single resource, or a group of resources of the same type. In this application, a resource might be a system property, or a set of system properties. It is efficient to have a single class handle multiple different resources, but keeping a clean separation between types of resources helps with maintainability.\n\nThe [hotspot=path file=2]`@Path` annotation on this class indicates that this resource responds to the `/systems` path in the RESTful application. The [hotspot=applicationPath file=3]`@ApplicationPath` annotation in the `RestApplication` class, together with the [hotspot=path file=2]`@Path` annotation in the `SystemResource` class, indicates that this resource is available at the `/api/systems` path.\n\nThe Jakarta RESTful Web Services API maps the HTTP methods on the URL to the methods of the class by using annotations. This application uses the `GET` annotation to map an HTTP `GET` request to the `/api/systems` path.\n\nThe [hotspot=getListContents file=2]`@GET` annotation on the `listContents` method indicates that the method is to be called for the HTTP `GET` method. The [hotspot=producesListContents file=2]`@Produces` annotation indicates the format of the content that is returned. The value of the [hotspot=producesListContents file=2]`@Produces` annotation is specified in the HTTP `Content-Type` response header. For this application, a JSON structure is returned for these `Get` methods. The `Content-Type` for a JSON response is `application/json` with `MediaType.APPLICATION_JSON` instead of the `String` content type. Using a constant such as `MediaType.APPLICATION_JSON` is better as in case of a spelling error, a compile failure occurs.\n\nThe Jakarta RESTful Web Services API supports a number of ways to marshal JSON. The Jakarta RESTful Web Services specification mandates JSON-Binding (JSON-B). The method body returns the result of [hotspot=getSystems file=2]`inventory.getSystems()`. Because the method is annotated with [hotspot=producesListContents file=2]`@Produces(MediaType.APPLICATION_JSON)`, the Jakarta RESTful Web Services API uses JSON-B to automatically convert the returned object to JSON data in the HTTP response.\n\n\n=== Running the application\n\nBecause you started the Liberty in dev mode at the beginning of this exercise, all the changes were automatically picked up.\n\nCheck out the service that you created at the http://localhost:9080/inventory/api/systems URL. If successful, it returns `[]` to you.\n\nifdef::cloud-hosted[]\nOpen another command-line session by selecting **Terminal** \u003e **New Terminal** from the menu of the IDE, and run the following curl command:\n```bash\ncurl 'http://localhost:9080/inventory/api/systems'\n```\n\nYou can expect to see the following output:\n\n```\n[]\n```\nendif::[]\n\n\n== Documenting APIs\n\nNext, you will investigate how to document and filter RESTful APIs from annotations, POJOs, and static OpenAPI files by using MicroProfile OpenAPI.\n\nThe OpenAPI specification, previously known as the Swagger specification, defines a standard interface for documenting and exposing RESTful APIs. This specification allows both humans and computers to understand or process the functionalities of services without requiring direct access to underlying source code or documentation. The MicroProfile OpenAPI specification provides a set of Java interfaces and programming models that allow Java developers to natively produce OpenAPI v3 documents from their RESTful applications.\n\npom.xml\n[source, xml, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-openapi/pom.xml[]\n----\n\nserver.xml\n[source, xml, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-openapi/src/main/liberty/config/server.xml[]\n----\n\nThe MicroProfile OpenAPI API is included in the [hotspot=mp5 file=0]`microProfile` dependency that is specified in your `pom.xml` file. The [hotspot=mp5 file=1]`microProfile` feature that includes the `mpOpenAPI` feature is also enabled in the `server.xml` configuration file.\n\n=== Generating the OpenAPI document\n\nBecause the Jakarta RESTful Web Services framework handles basic API generation for Jakarta RESTful Web Services annotations, a skeleton OpenAPI tree can be generated from the existing inventory service. You can use this tree as a starting point and augment it with annotations and code to produce a complete OpenAPI document.\n\nTo see the generated OpenAPI tree, you can either visit the http://localhost:9080/openapi URL or visit the http://localhost:9080/openapi/ui URL for a more interactive view of the APIs. Click the `interactive UI` link on the welcome page. Within this UI, you can view each of the endpoints that are available in your application and any schemas. Each endpoint is color coordinated to easily identify the type of each request (for example GET, POST, PUT, DELETE, etc.). Clicking each endpoint within this UI enables you to view further details of each endpoint's parameters and responses. This UI is used for the remainder of this workshop to view and test the application endpoints.\n\n\n=== Augmenting the existing Jakarta RESTful Web Services annotations with OpenAPI annotations\n\nBecause all Jakarta RESTful Web Services annotations are processed by default, you can augment the existing code with OpenAPI annotations without needing to rewrite portions of the OpenAPI document that are already covered by the Jakarta RESTful Web Services framework.\n\n[role=\"code_command hotspot file=0\", subs=\"quotes\"]\n----\n#Replace the `SystemResources` class.#\n`src/main/java/io/openliberty/deepdive/rest/SystemResource.java`\n----\n\nSystemResource.java\n[source, Java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-openapi/src/main/java/io/openliberty/deepdive/rest/SystemResource.java[]\n----\n\n\nAdd OpenAPI [hotspot=listContentsAPIResponseSchema hotspot=getSystemAPIResponseSchema file=0]`@APIResponseSchema`, [hotspot=addSystemAPIResponses file=0]`@APIResponses`, [hotspot=addSystemAPIResponse file=0]`@APIResponse`, [hotspot=addSystemParameters file=0]`@Parameters`, [hotspot=addSystemParameter file=0]`@Parameter`, and [hotspot=addSystemOperation file=0]`@Operation` annotations to the REST methods, [hotspot=listContents file=0]`listContents()`, [hotspot=getSystem file=0]`getSystem()`, [hotspot=addSystem file=0]`addSystem()`, [hotspot=updateSystem file=0]`updateSystem()`, [hotspot=removeSystem file=0]`removeSystem()`, and [hotspot=addSystemClient file=0]`addSystemClient()`.\n\nNote, the `@Parameter` annotation can be placed either [hotspot=getSystemParameter file=0]`inline` or [hotspot=removeSystemParameter file=0]`outline`. Examples of both are provided within this workshop.\n\nMany OpenAPI annotations are available and can be used according to what's best for your application and its classes. You can find all the annotations in the https://download.eclipse.org/microprofile/microprofile-open-api-3.1/microprofile-openapi-spec-3.1.html#_annotations[MicroProfile OpenAPI specification^].\n\nBecause the Liberty was started in dev mode at the beginning of this exercise, your changes were automatically picked up. Go to the http://localhost:9080/openapi URL to see the updated endpoint descriptions. The endpoints at which your REST methods are served now more meaningful:\n\nifdef::cloud-hosted[]\n```bash\ncurl http://localhost:9080/openapi\n```\nendif::[]\n\n[source, YAML, role=\"no_copy\"]\n----\n---\nopenapi: 3.0.3\ninfo:\n  title: Generated API\n  version: \"1.0\"\nservers:\n- url: http://localhost:9080/inventory\n- url: https://localhost:9443/inventory\npaths:\n  /api/systems:\n    get:\n      summary: List contents.\n      description: Returns the currently stored host:properties pairs in the inventory.\n      operationId: listContents\n      responses:\n        \"200\":\n          description: Returns the currently stored host:properties pairs in the inventory.\n          content:\n            application/json:\n              schema:\n                $ref: '#/components/schemas/SystemData'\n...\n----\n\nYou can also visit the http://localhost:9080/openapi/ui URL to see each endpoint's updated description. Click each of the icons within the UI to see the updated descriptions for each of the endpoints.\nifdef::cloud-hosted[]\nIn this Skills Network environment, simply click the following button:\n\n::startApplication{port=\"9080\" display=\"external\" name=\"Visit OpenAPI UI\" route=\"/openapi/ui\"}\nendif::[]\n\n=== Augmenting POJOs with OpenAPI annotations\n\nOpenAPI annotations can also be added to POJOs to describe what they represent. Currently, the OpenAPI document doesn't have a meaningful description of the `SystemData` POJO so it's difficult to tell exactly what this POJO is used for. To describe the `SystemData` POJO in more detail, augment the `SystemData.java` file with some OpenAPI annotations.\n\n[role='code_command hotspot', subs=\"quotes\"]\n----\n#Replace the `SystemData` class.#\n`src/main/java/io/openliberty/deepdive/rest/model/SystemData.java`\n----\n\nSystemData.java\n[source, Java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-openapi/src/main/java/io/openliberty/deepdive/rest/model/SystemData.java[]\n----\n\nAdd OpenAPI `@Schema` annotations to the [hotspot=SystemDataSchema]`SystemData` class and the [hotspot=hostnameSchema]`hostname` variable.\n\n\nRefresh the http://localhost:9080/openapi URL to see the updated OpenAPI tree. You should see much more meaningful data for the Schema:\n\nifdef::cloud-hosted[]\n```bash\ncurl http://localhost:9080/openapi\n```\nendif::[]\n\n[source, YAML, role=\"no_copy\"]\n----\ncomponents:\n  schemas:\n    SystemData:\n      description: POJO that represents a single inventory entry.\n      required:\n      - hostname\n      - properties\n      type: object\n      properties:\n        hostname:\n          type: string\n        properties:\n          type: object\n----\n\nAgain, you can also view this at the http://localhost:9080/openapi/ui URL. Scroll down in the UI to the schemas section and open up the SystemData schema icon.\n\nifdef::cloud-hosted[]\n::startApplication{port=\"9080\" display=\"external\" name=\"Visit OpenAPI UI\" route=\"/openapi/ui\"}\nendif::[]\n\nYou can also use this UI to try out the various endpoints. In the UI, head to the POST request `/api/systems`. This endpoint enables you to create a system. Once you've opened this icon up, click the `Try it out` button. Now enter appropriate values for each of the required parameters and click the `Execute` button.\n\nYou can verify that this system was created by testing the `/api/systems` GET request that returns the currently stored system data in the inventory. Execute this request in the UI, then in the response body you should see your system and its data listed.\n\nYou can follow these same steps for updating and deleting systems: visiting the corresponding endpoint in the UI, executing the endpoint, and then verifying the result by using the `/api/systems` GET request endpoint.\n\nYou can learn more about MicroProfile OpenAPI from the https://openliberty.io/guides/microprofile-openapi.html[Documenting RESTful APIs guide^].\n\n\n==  Configuring the microservice\n\nNext, you can externalize your Liberty configuration and inject configuration for your microservice by using MicroProfile Config.\n\n\n=== Enabling configurable ports and context root\n\nSo far, you used hardcoded values to set the HTTP and HTTPS ports and the context root for the Liberty. These configurations can be externalized so you can easily change their values when you want to deploy your microservice by different ports and context root.\n\n[role='code_command hotspot file=0', subs=\"quotes\"]\n----\n#Replace the Liberty `server.xml` configuration file.#\n`src/main/liberty/config/server.xml`\n----\n\nserver.xml\n[source, xml, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-config/src/main/liberty/config/server.xml[]\n----\n\nAdd variables for the [hotspot=httpPortVariable file=0]`HTTP` port, [hotspot=httpsPortVariable file=0]`HTTPS` port, and the [hotspot=contextRootVariable file=0]`context root` to the `server.xml` configuration file. Change the [hotspot=editedHttpEndpoint file=0]`httpEndpoint` element to reflect the new `http.port` and `https.port` variables and change the [hotspot=editedContextRoot file=0]`contextRoot` to use the new `context.root` variable too.\n\n[role='code_command hotspot file=1', subs=\"quotes\"]\n----\n#Replace the `pom.xml` file.#\n`pom.xml`\n----\n\npom.xml\n[source, xml, linenums, role='code_column hide_tags=copyright']\n----\ninclude::staging/module-config/pom.xml[]\n----\n\nAdd properties for the [hotspot=httpPort file=1]`HTTP` port, [hotspot=httpsPort file=1]`HTTPS` port, and the [hotspot=contextRoot file=1]`context root` to the `pom.xml` file. \n\n* [hotspot=httpPort file=1]`liberty.var.http.port` to `9081`\n* [hotspot=httpsPort file=1]`liberty.var.https.port` to `9445`\n* [hotspot=contextRoot file=1]`liberty.var.context.root` to `/trial`.\n\nBecause you are using dev mode, these changes are automatically picked up by the Liberty instance.\n\nifndef::cloud-hosted[]\nNow, you can access the application by the http://localhost:9081/trial/api/systems URL. Alternatively, for the updated OpenAPI UI, use the following URL http://localhost:9081/openapi/ui/.\nendif::[]\n\nifdef::cloud-hosted[]\nNow, you can access the application by running the following command:\n```bash\ncurl http://localhost:9081/trial/api/systems\n```\n\nAlternatively, for the updated OpenAPI UI, click the following button to visit ***/openapi/ui*** endpoint:\n\n::startApplication{port=\"9081\" display=\"external\" name=\"Visit OpenAPI UI\" route=\"/openapi/ui\"}\nendif::[]\n\n\nWhen you are finished trying out changing this configuration, change the variables back to their original values.\n\n* update [hotspot=httpPort file=2]`liberty.var.http.port` to `9080`\n* update [hotspot=httpsPort file=2]`liberty.var.https.port` to `9443`\n* update [hotspot=contextRoot file=2]`liberty.var.context.root` to `/inventory`.\n\n[role='code_command hotspot file=2', subs=\"quotes\"]\n----\n#Replace the `pom.xml` file.#\n`pom.xml`\n----\n\npom.xml\n[source, xml, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-config/pom.xml[]\n----\n\n\n=== Injecting static configuration\n\nYou can now explore how to use MicroProfile's Config API to inject static configuration into your microservice.\n\nThe MicroProfile Config API is included in the MicroProfile dependency that is specified in your `pom.xml` file. Look for the dependency with the `microprofile` artifact ID. This dependency provides a library that allows the use of the MicroProfile Config API. The `microProfile` feature is also enabled in the `server.xml` configuration file.\n\n\nFirst, you need to edit the `SystemResource` class to inject static configuration into the `CLIENT_PORT` variable.\n\n[role=\"code_command hotspot file=0\", subs=\"quotes\"]\n----\n#Replace the `SystemResource` class.#\n`src/main/java/io/openliberty/deepdive/rest/SystemResource.java`\n----\n\nSystemResource.java\n[source, Java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-config/src/main/java/io/openliberty/deepdive/rest/SystemResource.java[]\n----\n\nThe [hotspot=inject file=0]`@Inject` annotation injects the value from other configuration sources to the `CLIENT_PORT` variable. The [hotspot=configProperty file=0]`@ConfigProperty` defines the external property name as `client.https.port`.\n\nUpdate the [hotspot=printClientPort file=0]`POST` request so that the `/client/{hostname}` endpoint prints the `CLIENT_PORT` value.\n\n\n=== Adding the microprofile-config.properties file\n\nDefine the configurable variables in the `microprofile-config.properties` configuration file for MicroProfile Config at the `src/main/resources/META-INF` directory.\n\nifndef::cloud-hosted[]\n[.tab_link.windows_link]\n`*WINDOWS*`\n[.tab_link.mac_link]\n`*MAC*`\n[.tab_link.linux_link]\n`*LINUX*`\n\n[.tab_content.windows_section]\n--\n[role='command']\n```\nmkdir src\\main\\resources\\META-INF\n```\n--\n\n[.tab_content.mac_section.linux_section]\n--\n[role='command']\n```\nmkdir -p src/main/resources/META-INF\n```\n--\nendif::[]\n\nifdef::cloud-hosted[]\n```bash\nmkdir -p /home/project/guide-liberty-deep-dive/start/inventory/src/main/resources/META-INF\n```\nendif::[]\n\n[role=\"code_command hotspot file=0\", subs=\"quotes\"]\n----\n#Create the `microprofile-config.properties` file.#\n`src/main/resources/META-INF/microprofile-config.properties`\n----\n\nmicroprofile-config.properties\n[source, text, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-config/src/main/resources/META-INF/microprofile-config.properties[]\n----\n\nUsing the [hotspot=ordinal file=0]`config_ordinal` variable in this properties file, you can set the ordinal of this file and thus other configuration sources.\n\nThe [hotspot=configPort file=0]`client.https.port` variable enables the client port to be overwritten.\n\nRevisit the OpenAPI UI http://localhost:9080/openapi/ui to view these changes. Open the `/api/systems/client/{hostname}` endpoint and run it within the UI to view the `CLIENT_PORT` value.\n\nifdef::cloud-hosted[]\n::startApplication{port=\"9080\" display=\"external\" name=\"Visit OpenAPI UI\" route=\"/openapi/ui\"}\nendif::[]\n\nYou can learn more about MicroProfile Config from the https://openliberty.io/guides/microprofile-config.html[Configuring microservices guide^].\n\n== Persisting data\n\nNext, you’ll persist the system data into the PostgreSQL database by using  the https://jakarta.ee/specifications/persistence[Jakarta Persistence API^] (JPA).\n\nNavigate to your application directory. \n\nifndef::cloud-hosted[]\n[role='command']\n```\ncd start/inventory\n```\nendif::[]\n\nifdef::cloud-hosted[]\n```bash\ncd /home/project/guide-liberty-deep-dive/start/inventory\n```\nendif::[]\n\n=== Defining a JPA entity class\n\nTo store Java objects in a database, you must define a JPA entity class. A JPA entity is a Java object whose nontransient and nonstatic fields are persisted to the database. Any POJO class can be designated as a JPA entity. However, the class must be annotated with the `@Entity` annotation, must not be declared final, and must have a public or protected nonargument constructor. JPA maps an entity type to a database table and persisted instances will be represented as rows in the table.\n\nThe [hotspot=SystemData]`SystemData` class is a data model that represents systems in the `inventory` microservice. Annotate it with JPA annotations.\n\n[role=\"code_command hotspot file=0\", subs=\"quotes\"]\n----\n#Replace the `SystemData` class.#\n`src/main/java/io/openliberty/deepdive/rest/model/SystemData.java`\n----\nSystemData.java\n[source, Java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-persisting-data/src/main/java/io/openliberty/deepdive/rest/model/SystemData.java[]\n----\n\nThe following table breaks down the new annotations:\n\n[cols=\"35, 200\", options=\"header\"]\n|===\n| *Annotation* | *Description*\n| [hotspot=Entity]`@Entity` | Declares the class as an entity.\n| [hotspot=Table]`@Table` | Specifies details of the table such as name. \n| [hotspot=findAll hotspot=findSystem]`@NamedQuery` | Specifies a predefined database query that is run by an `EntityManager` instance.\n| [hotspot=Id]`@Id` | Declares the primary key of the entity.\n| [hotspot=GeneratedValue]`@GeneratedValue` | Specifies the strategy that is used for generating the value of the primary key. The `strategy = GenerationType.IDENTITY` code indicates that the database automatically increments the `inventoryid` upon inserting it into the database.\n| [hotspot=columnId hotspot=columnHostname hotspot=columnOsName hotspot=columnJavaVersion hotspot=columnHeapSize]`@Column` | Specifies that the field is mapped to a column in the database table. The `name` attribute is optional and indicates the name of the column in the table.\n|===\n\n=== Performing CRUD operations using JPA\n\nThe create, retrieve, update, and delete (CRUD) operations are defined in the Inventory. To perform these operations by using JPA, you need to update the `Inventory` class. \n\n[role=\"code_command hotspot file=0\", subs=\"quotes\"]\n----\n#Replace the `Inventory` class.#\n`src/main/java/io/openliberty/deepdive/rest/Inventory.java`\n----\n\n// File 0\nInventory.java\n[source, Java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-persisting-data/src/main/java/io/openliberty/deepdive/rest/Inventory.java[]\n----\n\nTo use the entity manager at run time, inject it into your CDI bean through the [hotspot=PersistenceContext file=0]`@PersistenceContext` annotation. The entity manager interacts with the persistence context. Every `EntityManager` instance is associated with a persistence context. The persistence context manages a set of entities and is aware of the different states that an entity can have. The persistence context synchronizes with the database when a transaction commits.\n\n// File 1\nSystemData.java\n[source, Java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-persisting-data/src/main/java/io/openliberty/deepdive/rest/model/SystemData.java[]\n----\n\nThe [hotspot=Inventory file=0]`Inventory` class has a method for each CRUD operation, so let's break them down:\n\n* The [hotspot=add file=0]`add()` method persists an instance of the `SystemData` entity class to the data store by calling the [hotspot=Persist file=0]`persist()` method on an `EntityManager` instance. The entity instance becomes managed and changes to it are tracked by the entity manager.\n\n* The [hotspot=getSystems file=0]`getSystems()` method demonstrates a way to retrieve system objects from the database. This method returns a list of instances of the `SystemData` entity class by using the [hotspot=findAll file=1]`SystemData.findAll` query that is specified in the [hotspot=findAll hotspot=findSystem file=1]`@NamedQuery` annotation on the `SystemData` class. Similarly, the [hotspot=getSystem file=0]`getSystem()` method uses the [hotspot=findSystem file=1]`SystemData.findSystem` named query to find a system with the given hostname. \n\n* The [hotspot=update file=0]`update()` method creates a managed instance of a detached entity instance. The entity manager automatically tracks all managed entity objects in its persistence context for changes and synchronizes them with the database. However, if an entity becomes detached, you must merge that entity into the persistence context by calling the [hotspot=Merge file=0]`merge()` method so that changes to loaded fields of the detached entity are tracked.\n\n* The [hotspot=removeSystem file=0]`removeSystem()` method removes an instance of the `SystemData` entity class from the database by calling the [hotspot=Remove file=0]`remove()` method on an `EntityManager` instance. The state of the entity is changed to removed and is removed from the database upon transaction commit. \n\nDeclare the endpoints with transaction management. \n\n[role=\"code_command hotspot file=2\", subs=\"quotes\"]\n----\n#Replace the `SystemResource` class.#\n`src/main/java/io/openliberty/deepdive/rest/SystemResource.java`\n----\n\n// File 2\nSystemResource.java\n[source, Java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-persisting-data/src/main/java/io/openliberty/deepdive/rest/SystemResource.java[]\n----\n\nThe `@Transactional` annotation is used in the [hotspot=postTransactional file=2]`POST`, [hotspot=putTransactional file=2]`PUT`, and [hotspot=deleteTransactional file=2]`DELETE` endpoints of the `SystemResource` class to declaratively control the transaction boundaries on the [hotspot=inventory file=2]`inventory` CDI bean. This configuration ensures that the methods run within the boundaries of an active global transaction, and therefore you don't need to explicitly begin, commit, or rollback transactions. At the end of the transactional method invocation, the transaction commits and the persistence context flushes any changes to the Event entity instances that it is managing to the database.\n\n=== Configuring JPA\n\nThe [hotspot file=0]`persistence.xml` file is a configuration file that defines a persistence unit. The\npersistence unit specifies configuration information for the entity manager.\n\n[role=\"code_command hotspot file=0\", subs=\"quotes\"]\n----\n#Create the configuration file.#\n`src/main/resources/META-INF/persistence.xml`\n----\n\n// File 0\npersistence.xml\n[source, Xml, linenums, role='code_column']\n----\ninclude::finish/module-persisting-data/src/main/resources/META-INF/persistence.xml[]\n----\n\nThe persistence unit is defined by the [hotspot=persistence-unit file=0]`persistence-unit` XML element. The [hotspot=transaction-type file=0]`name` attribute is required. This attribute identifies the persistent unit when you use the `@PersistenceContext` annotation to inject the entity manager later in this exercise. The [hotspot=transaction-type file=0]`transaction-type=\"JTA\"` attribute specifies to use Java Transaction API (JTA) transaction management. When you use a container-managed entity manager, you must use JTA transactions. \n\nA JTA transaction type requires a JTA data source to be provided. The [hotspot=jta-data file=0]`jta-data-source` element specifies the Java Naming and Directory Interface (JNDI) name of the data source that is used. \n\n\nConfigure the `jdbc/postgresql` data source in the Liberty `server.xml` configuration file.\n\n[role=\"code_command hotspot file=1\", subs=\"quotes\"]\n----\n#Replace the Liberty `server.xml` configuration file.#\n`src/main/liberty/config/server.xml`\n----\n\n// File 1\nserver.xml\n[source, Xml, linenums, role='code_column']\n----\ninclude::finish/module-persisting-data/src/main/liberty/config/server.xml[]\n----\n\nThe [hotspot=postgresqlLibrary file=1]`library` element tells the Liberty where to find the PostgreSQL library. The [hotspot=dataSource file=1]`dataSource` element points to where the Java Database Connectivity (JDBC) driver connects, along with some database vendor-specific properties. For more information, see the https://www.openliberty.io/docs/latest/relational-database-connections-JDBC.html#_data_source_configuration[Data source configuration^] and https://www.openliberty.io/docs/latest/reference/config/dataSource.html[dataSource element^] documentation.\n\nTo use a PostgreSQL database, you need to download its library and store it to the Liberty shared resources directory. Configure the Liberty Maven plug-in in the `pom.xml` file.\n\n[role='code_command hotspot file=2', subs=\"quotes\"]\n----\n#Replace the `pom.xml` configuration file.#\n`pom.xml`\n----\n\n// File 2\npom.xml\n[source, xml, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-persisting-data/pom.xml[]\n----\n\nThe [hotspot=postgresql file=2]`postgresql` dependency ensures that Maven downloads the PostgreSQL library to local project. The [hotspot=copyDependencies file=2]`copyDependencies` configuration tells the Liberty Maven plug-in to copy the library to the Liberty shared resources directory.\n\n\n=== Starting PostgreSQL database ===\n\nUse Docker to run an instance of the PostgreSQL database for a fast installation and setup.\n\nA container file is provided for you. First, navigate to the `finish/postgres` directory. Then, run the following commands to use the `Dockerfile` to build the image, run the image in a Docker container, and map `5432` port from the container to your machine:\n\nifndef::cloud-hosted[]\n[role='command']\n```\ncd ../../finish/postgres\ndocker build -t postgres-sample .\ndocker run --name postgres-container -p 5432:5432 -d postgres-sample\n```\nendif::[]\n\nifdef::cloud-hosted[]\n```bash\ncd /home/project/guide-liberty-deep-dive/finish/postgres\ndocker build -t postgres-sample .\ndocker run --name postgres-container -p 5432:5432 -d postgres-sample\n```\nendif::[]\n\n=== Running the application ===\n\nifndef::cloud-hosted[]\nIn your dev mode console for the `inventory` microservice, type `r` and press `enter/return` key to restart the Liberty instance.\n\nAfter you see the following message, your Liberty instance is ready in dev mode again:\n\n[role=\"no_copy\"]\n----\n**************************************************************\n*    Liberty is running in dev mode.\n----\n\nPoint your browser to the http://localhost:9080/openapi/ui[^] URL. This URL displays the available REST endpoints.\n\nFirst, make a POST request to the `/api/systems/` endpoint. To make this request, expand the first POST endpoint on the UI, click the `Try it out` button, provide values to the `heapSize`, `hostname`, `javaVersion`, and `osName` parameters, and then click the `Execute` button. The POST request adds a system with the specified values to the database.\n\nNext, make a GET request to the `/api/systems` endpoint. To make this request, expand the GET endpoint on the UI, click the `Try it out` button, and then click the `Execute` button. The GET request returns all systems from the database.\n\nNext, make a PUT request to the `/api/systems/{hostname}` endpoint. To make this request, expand the PUT endpoint on the UI, click the `Try it out` button. Then, provide the same value to the `hostname` parameter as in the previous step, provide different values to the `heapSize`, `javaVersion`, and `osName` parameters, and click the `Execute` button. The PUT request updates the system with the specified values. \n\nTo see the updated system, make a GET request to the `/api/systems/{hostname}` endpoint. To make this request, expand the GET endpoint on the UI, click the `Try it out` button, provide the same value to the `hostname` parameter as the previous step, and then click the `Execute` button. The GET request returns the system from the database.\n\nNext, make a DELETE request to the `/api/systems/{hostname}` endpoint. To make this request, expand the DELETE endpoint on the UI, click the `Try it out` button, and then click `Execute`. The DELETE request removes the system from the database. Run the GET request again to see that the system no longer exists in the database. \n\nendif::[]\n\nifdef::cloud-hosted[]\nIn your dev mode console for the ***inventory*** microservice, type `r` and press ***enter/return*** key to restart the Liberty instance.\n\nAfter you see the following message, your Liberty instance is ready in dev mode again:\n\n```\n**************************************************************\n*    Liberty is running in dev mode.\n```\n\nFirst, make a POST request to the ***/api/systems/*** endpoint by the following command. The POST request adds a system with the specified values to the database.\n\n```bash\ncurl -X POST 'http://localhost:9080/inventory/api/systems?heapSize=1048576\u0026hostname=localhost\u0026javaVersion=9\u0026osName=linux'\n```\n\nNext, make a GET request to the ***/api/systems*** endpoint by the following command. The GET request returns all systems from the database.\n\n```bash\ncurl -s 'http://localhost:9080/inventory/api/systems' | jq\n```\n\nNext, make a PUT request to the ***/api/systems/{hostname}*** endpoint with the same value for the ***hostname*** path as in the previous step, and different values to the ***heapSize***, ***javaVersion***, and ***osName*** parameters. The PUT request updates the system with the specified values. \n\n```bash\ncurl -X PUT 'http://localhost:9080/inventory/api/systems/localhost?heapSize=2097152\u0026javaVersion=17\u0026osName=linux'\n```\n\nTo see the updated system, make a GET request to the ***/api/systems/{hostname}*** endpoint with the same value for the ***hostname*** path as in the previous step. The GET request returns the system from the database.\n\n```bash\ncurl -s 'http://localhost:9080/inventory/api/systems/localhost' | jq\n```\n\nNext, make a DELETE request to the ***/api/systems/{hostname}*** endpoint. The DELETE request removes the system from the database.\n\n```bash\ncurl -X DELETE 'http://localhost:9080/inventory/api/systems/localhost'\n```\n\nRun the GET request again to see that the system no longer exists in the database. \n```bash\ncurl 'http://localhost:9080/inventory/api/systems'\n```\nendif::[]\n\n== Securing RESTful APIs\n\nNow you can secure your RESTful APIs. Navigate to your application directory. \n\nifndef::cloud-hosted[]\n[role='command']\n```\ncd start/inventory\n```\nendif::[]\n\nifdef::cloud-hosted[]\n```bash\ncd /home/project/guide-liberty-deep-dive/start/inventory\n```\nendif::[]\n\nBegin by adding some users and user groups to your Liberty `server.xml` configuration file.\n\n[role=\"code_command hotspot file=0\", subs=\"quotes\"]\n----\n#Replace the Liberty `server.xml` configuration file.#\n`src/main/liberty/config/server.xml`\n----\n\n// File 0\nserver.xml\n[source, xml, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-securing/src/main/liberty/config/server.xml[]\n----\n\nThe [hotspot=basicregistry file=0]`basicRegistry` element contains a list of all users for the application and their passwords, as well as all of the user groups. Note that this [hotspot=basicregistry file=0]`basicRegistry` element is a very simple case for learning purposes. For more information about the different user registries, see the https://openliberty.io/docs/latest/user-registries-application-security.html[User registries documentation^]. The [hotspot=myadmins file=0]`admin` group tells the application which of the users are in the administrator group. The [hotspot=myusers file=0]`user` group tells the application that users are in the user group.\n\nThe `security-role` maps the [hotspot=adminrole file=0]`admin` role to the [hotspot=myadmins file=0]`admin` group, meaning that all users in the `admin` group have the administrator role. Similarly, the [hotspot=userrole file=0]`user` role is mapped to the [hotspot=myusers file=0]`user` group, meaning all users in the `user` group have the user role.\n\nYour application has the following users and passwords:\n\n[cols=\"\u003c35, ^200, ^200\"]\n|===\n| *Username* | *Password* | *Role*\n| bob | bobpwd | admin, user\n| alice | alicepwd | user\n|===\n\nNow you can secure the `inventory` service.\n\n[role=\"code_command hotspot file=1\", subs=\"quotes\"]\n----\n#Replace the `SystemResource` class.#\n`src/main/java/io/openliberty/deepdive/rest/SystemResource.java`\n----\n\n// File 1\nSystemResource.java\n[source, java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-securing/src/main/java/io/openliberty/deepdive/rest/SystemResource.java[]\n----\n\nThis class now has role-based access control. The role names that are used in the [hotspot=putRolesAllowed hotspot=deleteRolesAllowed file=1]`@RolesAllowed` annotations are mapped to group names in the groups claim of the JSON Web Token (JWT). This mapping results in an authorization decision wherever the security constraint is applied.\n\nThe [hotspot=putEndpoint file=1]`/{hostname}` endpoint that is annotated with the [hotspot=put file=1]`@PUT` annotation updates a system in the inventory. This PUT endpoint is annotated with the [hotspot=putRolesAllowed file=1]`@RolesAllowed({ \"admin\", \"user\" })` annotation. Only authenticated users with the role of `admin` or `user` can access this endpoint.\n\nThe [hotspot=deleteEndpoint file=1]`/{hostname}` endpoint that is annotated with the [hotspot=delete file=1]`@DELETE` annotation removes a system from the inventory. This DELETE endpoint is annotated with the [hotspot=deleteRolesAllowed file=1]`@RolesAllowed({ \"admin\" })` annotation. Only authenticated users with the role of `admin` can access this endpoint.\n\nYou can manually check that the `inventory` microservice is secured by making requests to the PUT and DELETE endpoints.\n\nBefore making requests, you must add a system to the inventory. Try adding a system by using the POST endpoint `/systems` by running the following command:\n\n[role='command']\n----\ncurl -X POST 'http://localhost:9080/inventory/api/systems?hostname=localhost\u0026osName=mac\u0026javaVersion=17\u0026heapSize=1'\n----\n\nYou can expect the following response:\n\n[source, role=\"no_copy\"]\n----\n{ \"ok\" : \"localhost was added.\" }\n----\n\nThis command calls the `/systems` endpoint and adds a system `localhost` to the inventory. You can validate that the command worked by calling the `/systems` endpoint with a `GET` request to retrieve all the systems in the inventory, with the following curl command:\n\nifndef::cloud-hosted[]\n[role='command']\n----\ncurl -s 'http://localhost:9080/inventory/api/systems'\n----\nendif::[]\n\nifdef::cloud-hosted[]\n```bash\ncurl -s 'http://localhost:9080/inventory/api/systems' | jq\n```\nendif::[]\n\nYou can now expect the following response:\n\n[source, role=no_copy]\n----\n[{\"heapSize\":1,\"hostname\":\"localhost\",\"javaVersion\":\"17\",\"osName\":\"mac\",\"id\":23}]\n----\n\nNow try calling your secure PUT endpoint to update the system that you just added by the following curl command:\n\n[role='command']\n----\ncurl -k --user alice:alicepwd -X PUT 'http://localhost:9080/inventory/api/systems/localhost?heapSize=2097152\u0026javaVersion=17\u0026osName=linux'\n----\n\nAs this endpoint is accessible to the groups `user` and `admin`, you must log in with `user` credentials to update the system.\n\nYou should see the following response:\n\n[source, role=no_copy]\n----\n{ \"ok\" : \"localhost was updated.\" }\n----\n\nThis response means that you logged in successfully as an authenticated `user`, and that the endpoint works as expected.\n\nNow try calling the DELETE endpoint. As this endpoint is only accessible to `admin` users, you can expect this command to fail if you attempt to access it with a user in the `user` group.\n\nYou can check that your application is secured against these requests with the following command:\n\n[role='command']\n----\ncurl -kf --user alice:alicepwd -X DELETE 'https://localhost:9443/inventory/api/systems/localhost'\n----\n\nYou should see the following response:\n\n[source, role=no_copy]\n----\ncurl: (22) The requested URL returned error: 403\n----\n\nAs `alice` is part of the `user` group, this request cannot work. In your dev mode console, you can expect the following output:\n\n[source, role=no_copy]\n----\njakarta.ws.rs.ForbiddenException: Unauthorized\n----\n\nNow attempt to call this endpoint with an authenticated `admin` user that can work correctly. Run the following curl command:\n\n[role='command']\n----\ncurl -k --user bob:bobpwd -X DELETE 'https://localhost:9443/inventory/api/systems/localhost'\n----\n\nYou can expect to see the following response:\n\n[source, role=no_copy]\n----\n{ \"ok\" : \"localhost was removed.\" }\n----\n\nThis response means that your endpoint is secure. Validate that it works correctly by calling the `/systems` endpoint with the following curl command:\n\n[role='command']\n----\ncurl 'http://localhost:9080/inventory/api/systems'\n----\n\nYou can expect to see the following output:\n\n[source, role=no_copy]\n----\n[]\n----\n\nThis response shows that the endpoints work as expected and that the system you added was successfully deleted.\n\n== Consuming the secured RESTful APIs by JWT\n\nYou can now implement JSON Web Tokens (JWT) and configure them as Single Sign On (SSO) cookies to use the RESTful APIs. The JWT that is generated by Liberty is used to communicate securely between the `inventory` and `system` microservices. You can implement the `/client/{hostname}` POST endpoint to collect the properties from the `system` microservices and create a system in the inventory. \n\nThe `system` microservice is provided for you.\n\n=== Writing the RESTful client interface\nCreate the `client` subdirectory. Then, create a RESTful client interface for the `system` microservice in the `inventory` microservice.\n\nifndef::cloud-hosted[]\n[.tab_link.windows_link]\n`*WINDOWS*`\n[.tab_link.mac_link]\n`*MAC*`\n[.tab_link.linux_link]\n`*LINUX*`\n\n[.tab_content.windows_section]\n--\n[role='command']\n```\nmkdir src\\main\\java\\io\\openliberty\\deepdive\\rest\\client\n```\n--\n\n[.tab_content.mac_section.linux_section]\n--\n[role='command']\n```\nmkdir src/main/java/io/openliberty/deepdive/rest/client\n```\n--\nendif::[]\n\nifdef::cloud-hosted[]\n```bash\nmkdir /home/project/guide-liberty-deep-dive/start/inventory/src/main/java/io/openliberty/deepdive/rest/client\n```\nendif::[]\n\n[role=\"code_command hotspot file=0\", subs=\"quotes\"]\n----\n#Create the `SystemClient` interface.#\n`src/main/java/io/openliberty/deepdive/rest/client/SystemClient.java`\n----\n\n// File 0\nSystemClient.java\n[source, java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-jwt/src/main/java/io/openliberty/deepdive/rest/client/SystemClient.java[]\n----\n\n\nThis interface declares methods for accessing each of the endpoints that are set up for you in the `system` service. The MicroProfile Rest Client feature automatically builds and generates a client implementation based on what is defined in the [hotspot file=0]`SystemClient` interface. You don’t need to set up the client and connect with the remote service.\n\nNow create the required exception classes that are used by the `SystemClient` instance.\n\n[role=\"code_command hotspot file=1\", subs=\"quotes\"]\n----\n#Create the `UnknownUriException` class.#\n`src/main/java/io/openliberty/deepdive/rest/client/UnknownUriException.java`\n----\n\n// File 1\nUnknownUriException.java\n[source, java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-jwt/src/main/java/io/openliberty/deepdive/rest/client/UnknownUriException.java[]\n----\n\nThis class is an exception that is thrown when an unknown URI is passed to the `SystemClient`.\n\n[role=\"code_command hotspot file=2\", subs=\"quotes\"]\n----\n#Create the `UnknownUriExceptionMapper` class.#\n`src/main/java/io/openliberty/deepdive/rest/client/UnknownUriExceptionMapper.java`\n----\n\n// File 2\nUnknownUriExceptionMapper.java\n[source, java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-jwt/src/main/java/io/openliberty/deepdive/rest/client/UnknownUriExceptionMapper.java[]\n----\n\nThis class links the `UnknownUriException` class with the corresponding response code through a `ResponseExceptionMapper` mapper class.\n\n=== Implementing the `/client/{hostname}` endpoint\n\nNow implement the `/client/{hostname}` POST endpoint of the `SystemResource` class to consume the secured `system` microservice.\n\n[role=\"code_command hotspot file=0\", subs=\"quotes\"]\n----\n#Replace the `SystemResource` class.#\n`src/main/java/io/openliberty/deepdive/rest/SystemResource.java`\n----\n\n// File 0\nSystemResource.java\n[source, java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-jwt/src/main/java/io/openliberty/deepdive/rest/SystemResource.java[]\n----\n\nThe [hotspot=getSystemClient file=0]`getSystemClient()` method builds and returns a new instance of the `SystemClient` class for the hostname provided. The [hotspot=addSystemClient file=0]`/client/{hostname}` POST endpoint uses this method to create a REST client that is called [hotspot=getCustomRestClient file=0]`customRestClient` to consume the `system` microservice.\n\nA JWT instance is injected to the [hotspot=jwt file=0]`jwt` field variable by the `jwtSso` feature. It is used to create the [hotspot=authHeader file=0]`authHeader` authentication header. It is then passed as a parameter to the endpoints of the [hotspot=customRestClient file=0]`customRestClient` to get the properties from the `system` microservice. A [hotspot=addSystem file=0]`system` is then added to the inventory.\n\n=== Configuring the JSON Web Token\n\nNext, add the JSON Web Token (Single Sign On) feature to the Liberty `server.xml` configuration file for the `inventory` service.\n\n[role=\"code_command hotspot file=0\", subs=\"quotes\"]\n----\n#Replace the Liberty `server.xml` configuration file.#\n`src/main/liberty/config/server.xml`\n----\n\n// File 0\nserver.xml\n[source, xml, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-jwt/src/main/liberty/config/server.xml[]\n----\n\n// File 1\nmicroprofile-config.properties\n[source, xml, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/system/src/main/resources/META-INF/microprofile-config.properties[]\n----\n\nThe [hotspot=jwtSsoFeature file=0]`jwtSso` feature adds the libraries that are required for JWT SSO implementation. Configure the [hotspot=jwtSsoConfig file=0]`jwtSso` feature by adding the [hotspot=jwtBuilder file=0]`jwtBuilder` configuration to your `server.xml` file. Also, configure the MicroProfile [hotspot=mpJwt file=0]`JWT` with the `audiences` and `issuer` properties that match the [hotspot file=1]`microprofile-config.properties` defined at the `system/src/main/resources/META-INF` directory under the `system` project. For more information, see the https://www.openliberty.io/docs/latest/reference/feature/jwtSso-1.0.html[JSON Web Token Single Sign-On feature^], https://www.openliberty.io/docs/latest/reference/config/jwtSso.html[jwtSso element^], and https://www.openliberty.io/docs/latest/reference/config/jwtBuilder.html[jwtBuilder element^] documentation.\n\nThe [hotspot=keyStore file=0]`keyStore` element is used to define the repository of security certificates used for SSL encryption. The `id` attribute is a unique configuration ID that is set to `guideKeyStore`. The `password` attribute is used to load the keystore file, and its value can be stored in clear text or encoded form. To learn more about other attributes, see the https://openliberty.io/docs/latest/reference/config/keyStore.html#keyStore.html[keyStore attribute documentation^]. \n\nTo avoid the conflict with the default ssl configuration, define your own ssl configuration by setting the [hotspot=ssl file=0]`id` attribute to other value, the [hotspot=sslDefault file=0]`sslDefault` element, and the [hotspot=mpJwt file=0]`sslRef` attribute in the `mpJwt` element.\n\nBecause the keystore file is not provided at the `src` directory, Liberty creates a Public Key Cryptography Standards #12 (PKCS12) keystore file for you by default. This file needs to be replaced, as the [hotspot=keyStore file=0]`keyStore` configuration must be the same in both `system` and `inventory` microservices. As the configured `system` microservice is already provided for you, copy the `key.p12` keystore file from the `system` microservice to your `inventory` service.\n\nifndef::cloud-hosted[]\n[.tab_link.windows_link]\n`*WINDOWS*`\n[.tab_link.mac_link]\n`*MAC*`\n[.tab_link.linux_link]\n`*LINUX*`\n\n[.tab_content.windows_section]\n--\n[role='command']\n```\nmkdir src\\main\\liberty\\config\\resources\\security\ncopy ..\\..\\finish\\system\\src\\main\\liberty\\config\\resources\\security\\key.p12 src\\main\\liberty\\config\\resources\\security\\key.p12\n```\n--\n\n[.tab_content.mac_section.linux_section]\n--\n[role='command']\n```\nmkdir -p src/main/liberty/config/resources/security\ncp ../../finish/system/src/main/liberty/config/resources/security/key.p12 src/main/liberty/config/resources/security/key.p12\n```\n--\nendif::[]\n\nifdef::cloud-hosted[]\n```bash\nmkdir -p /home/project/guide-liberty-deep-dive/start/inventory/src/main/liberty/config/resources/security\ncp /home/project/guide-liberty-deep-dive/finish/system/src/main/liberty/config/resources/security/key.p12 \\\n   /home/project/guide-liberty-deep-dive/start/inventory/src/main/liberty/config/resources/security/key.p12\n```\nendif::[]\n\nNow configure the client https port in the `pom.xml` configuration file.\n\n[role=\"code_command hotspot file=2\", subs=\"quotes\"]\n----\n#Replace the `pom.xml` file.#\n`pom.xml`\n----\n\n// File 2\npom.xml\n[source, xml, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-jwt/pom.xml[]\n----\n\nConfigure the client https port by setting the [hotspot=https file=2]`\u003cliberty.var.client.https.port\u003e` to `9444`.\n\nIn your dev mode console for the `inventory` microservice, press `CTRL+C` to stop the Liberty instance. Then, restart the dev mode of the `inventory` microservice.\n[role='command']\n```\nmvn liberty:dev\n```\n\nAfter you see the following message, your Liberty instance is ready in dev mode again:\n\n[role=\"no_copy\"]\n----\n**************************************************************\n*    Liberty is running in dev mode.\n----\n\n=== Running the `/client/{hostname}` endpoint\n\nOpen another command-line session and run the `system` microservice from the `finish` directory.\n\nifndef::cloud-hosted[]\n[role='command']\n----\ncd finish/system\nmvn liberty:run\n----\nendif::[]\n\nifdef::cloud-hosted[]\n```bash\ncd /home/project/guide-liberty-deep-dive/finish/system\nmvn liberty:run\n```\nendif::[]\n\nWait until the following message displays on the `system` microservice console.\n[source]\n----\nCWWKF0011I: The defaultServer server is ready to run a smarter planet. ...\n----\n\nYou can check that the `system` microservice is secured against unauthenticated requests at the https://localhost:9444/system/api/heapsize URL. Open another command-line session and run the following command:\n\n[role='command']\n----\ncurl -kf 'https://localhost:9444/system/api/heapsize'\n----\n\nYou should see the following response:\n\n[source, role=no_copy]\n----\ncurl: (22) The requested URL returned error: 401\n----\n\nYou can expect to see the following error in the console of the `system` microservice:\n\n[source]\n----\nCWWKS5522E: The MicroProfile JWT feature cannot perform authentication because a MicroProfile JWT cannot be found in the request.\n----\n\nYou can check that the `/client/{hostname}` endpoint you updated can access the `system` microservice. \n\nMake an authorized request to the new `/client/{hostname}` endpoint.\nAs this endpoint is restricted to `admin`, you can use the login credentials for `bob`, which is in the `admin` group.\n\n[role='command']\n----\ncurl -k --user bob:bobpwd -X POST 'https://localhost:9443/inventory/api/systems/client/localhost'\n----\n\nYou can expect the following output:\n\n[source, role='no_copy']\n----\n{ \"ok\" : \"localhost was added.\" }\n----\n\nYou can verify that this endpoint works as expected by running the following command:\n\nifndef::cloud-hosted[]\n[role='command']\n----\ncurl 'http://localhost:9080/inventory/api/systems'\n----\nendif::[]\n\nifdef::cloud-hosted[]\n```bash\ncurl -s 'http://localhost:9080/inventory/api/systems' | jq\n```\nendif::[]\n\nYou can expect to see your system listed in the output.\n\n[source]\n----\n[\n  {\n    \"heapSize\": 2999975936,\n    \"hostname\": \"localhost\",\n    \"id\": 11,\n    \"javaVersion\": \"17.0.9\",\n    \"osName\": \"Linux\"\n  }\n]\n----\n\n== Adding health checks\nNext, you'll use https://download.eclipse.org/microprofile/microprofile-health-4.0/microprofile-health-spec-4.0.html[MicroProfile Health^] to report the health status of the microservice and PostgreSQL database connection.\n\nNavigate to your application directory\n\nifndef::cloud-hosted[]\n```\ncd start/inventory\n```\nendif::[]\n\nifdef::cloud-hosted[]\n```bash\ncd /home/project/guide-liberty-deep-dive/start/inventory\n```\nendif::[]\n\nA health report is generated automatically for all health services that enable MicroProfile Health.\n\nAll health services must provide an implementation of the `HealthCheck` interface, which is used to verify their health. MicroProfile Health offers health checks for startup, liveness, and readiness.\n\nA startup check allows applications to define startup probes that are used for initial verification of the application before the liveness probe takes over. For example, a startup check might check which applications require additional startup time on their first initialization.\n\nA liveness check allows third-party services to determine whether a microservice is running. If the liveness check fails, the application can be terminated. For example, a liveness check might fail if the application runs out of memory.\n\nA readiness check allows third-party services, such as Kubernetes, to determine whether a microservice is ready to process requests.\n\nCreate the `health` subdirectory before creating the health check classes.\n\nifndef::cloud-hosted[]\n[.tab_link.windows_link]\n`*WINDOWS*`\n[.tab_link.mac_link]\n`*MAC*`\n[.tab_link.linux_link]\n`*LINUX*`\n\n[.tab_content.windows_section]\n--\n[role='command']\n```\nmkdir src\\main\\java\\io\\openliberty\\deepdive\\rest\\health\n```\n--\n\n[.tab_content.mac_section.linux_section]\n--\n[role='command']\n```\nmkdir src/main/java/io/openliberty/deepdive/rest/health\n```\n--\nendif::[]\n\nifdef::cloud-hosted[]\n```bash\nmkdir /home/project/guide-liberty-deep-dive/start/inventory/src/main/java/io/openliberty/deepdive/rest/health\n```\nendif::[]\n\n[role=\"code_command hotspot file=0\", subs=\"quotes\"]\n----\n#Create the `StartupCheck` class.#\n`src/main/java/io/openliberty/deepdive/rest/health/StartupCheck.java`\n----\n\nStartupCheck.java\n[source, java, linenums, role='code_column tags=StartupCheck hide_tags=copyright']\n----\ninclude::finish/module-health-checks/src/main/java/io/openliberty/deepdive/rest/health/StartupCheck.java[]\n----\n\nThe [hotspot=Startup file=0]`@Startup` annotation indicates that this class is a startup health check procedure. Navigate to the http://localhost:9080/health/started URL to check the status of the startup health check. In this case, you are checking the cpu usage. If more than 95% of the cpu is being used, a status of `DOWN` is returned.\nifdef::cloud-hosted[]\n```bash\ncurl -s http://localhost:9080/health/started | jq\n```\nendif::[]\n\n[role=\"code_command hotspot file=1\", subs=\"quotes\"]\n----\n#Create the `LivenessCheck` class.#\n`src/main/java/io/openliberty/deepdive/rest/health/LivenessCheck.java`\n----\n\nLivenessCheck.java\n[source, java, linenums, role='code_column tags=LivenessCheck hide_tags=copyright']\n----\ninclude::finish/module-health-checks/src/main/java/io/openliberty/deepdive/rest/health/LivenessCheck.java[]\n----\n\nThe [hotspot=Liveness file=1]`@Liveness` annotation indicates that this class is a liveness health check procedure. Navigate to the http://localhost:9080/health/live URL to check the status of the liveness health check. In this case, you are checking the heap memory usage. If more than 90% of the maximum memory is being used, a status of `DOWN` is returned.\n\nifdef::cloud-hosted[]\n```bash\ncurl -s http://localhost:9080/health/live | jq\n```\nendif::[]\n\n[role=\"code_command hotspot file=2\", subs=\"quotes\"]\n----\n#Create the `ReadinessCheck` class.#\n`src/main/java/io/openliberty/deepdive/rest/health/ReadinessCheck.java`\n----\n\nReadinessCheck.java\n[source, java, linenums, role='code_column tags=ReadinessCheck hide_tags=copyright']\n----\ninclude::finish/module-health-checks/src/main/java/io/openliberty/deepdive/rest/health/ReadinessCheck.java[]\n----\nThe [hotspot=Readiness file=2]`@Readiness` annotation indicates that this class is a readiness health check procedure. Navigate to the http://localhost:9080/health/ready URL to check the status of the readiness health check. This readiness check tests the connection to the PostgreSQL container that was created earlier in the guide. If the connection is refused, a status of `DOWN` is returned.\n\nifdef::cloud-hosted[]\n```bash\ncurl -s http://localhost:9080/health/ready | jq\n```\nendif::[]\n\nOr, you can visit the http://localhost:9080/health URL to see the overall health status of the application.\n\nifdef::cloud-hosted[]\n```bash\ncurl -s http://localhost:9080/health | jq\n```\nendif::[]\n\n== Providing metrics\n\nNext, you can learn how to use https://download.eclipse.org/microprofile/microprofile-metrics-4.0/microprofile-metrics-spec-4.0.html[MicroProfile Metrics^] to provide metrics from the `inventory` microservice.\n\nGo to your application directory.\n\nifndef::cloud-hosted[]\n```\ncd start/inventory\n```\nendif::[]\n\nifdef::cloud-hosted[]\n```bash\ncd /home/project/guide-liberty-deep-dive/start/inventory\n```\nendif::[]\n\nEnable the `bob` user to access the `/metrics` endpoints.\n\n[role=\"code_command hotspot file=0\", subs=\"quotes\"]\n----\n#Replace the Liberty `server.xml` configuration file.#\n`src/main/liberty/config/server.xml`\n----\n\n// File 0\nserver.xml\n[source, xml, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-metrics/src/main/liberty/config/server.xml[]\n----\n\nAdd the [hotspot=mpMetrics file=0]`mpMetrics` feature because MicroProfile Metric is a standalone feature in MicroProfile 7.\n\nThe [hotspot=administrator file=0]`administrator-role` configuration authorizes the `bob` user as an administrator.\n\nUse annotations that are provided by MicroProfile Metrics to instrument the `inventory` microservice to provide application-level metrics data.\n\n[role=\"code_command hotspot file=1\", subs=\"quotes\"]\n----\n#Replace the `SystemResource` class.#\n`src/main/java/io/openliberty/deepdive/rest/SystemResource.java`\n----\n\n// File 1\nSystemResource.java\n[source, java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-metrics/src/main/java/io/openliberty/deepdive/rest/SystemResource.java[]\n----\n\nImport the [hotspot=metricsImport file=1]`Counted` annotation and apply it to the [hotspot=metricsAddSystem file=1]`POST /api/systems`, [hotspot=metricsUpdateSystem file=1]`PUT /api/systems/{hostname}`, [hotspot=metricsRemoveSystem file=1]`DELETE /api/systems/{hostname}`, and [hotspot=metricsAddSystemClient file=1]`POST /api/systems/client/{hostname}` endpoints to monotonically count how many times that the endpoints are accessed. \n\nAdditional information about the annotations that MicroProfile metrics provides, relevant metadata fields, and more are available at the https://openliberty.io/docs/latest/reference/javadoc/microprofile-7.0-javadoc.html?package=org/eclipse/microprofile/metrics/annotation/package-frame.html\u0026class=overview-summary.html[MicroProfile Metrics Annotation Javadoc^].\n\nifndef::cloud-hosted[]\nPoint your browser to the http://localhost:9080/openapi/ui[^] URL to try out your application and call some of the endpoints that you annotated.\nendif::[]\n\nifdef::cloud-hosted[]\nRun the following commands to call some of the endpoints that you annotated:\n\n```bash\ncurl -k --user bob:bobpwd -X DELETE \\\n  'https://localhost:9443/inventory/api/systems/localhost'\n```\n\n```bash\ncurl -X POST 'http://localhost:9080/inventory/api/systems?heapSize=1048576\u0026hostname=localhost\u0026javaVersion=9\u0026osName=linux'\n```\n\n```bash\ncurl -k --user alice:alicepwd -X PUT \\\n  'http://localhost:9080/inventory/api/systems/localhost?heapSize=2097152\u0026javaVersion=17\u0026osName=linux'\n```\n\n```bash\ncurl -s 'http://localhost:9080/inventory/api/systems' | jq\n```\nendif::[]\n\nMicroProfile Metrics provides 4 different REST endpoints.\n\n* The `/metrics` endpoint provides you with all the metrics in text format. \n* The `/metrics?scope=application` endpoint provides you with application-specific metrics.\n* The `/metrics?scope=base` endpoint provides you with metrics that are defined in MicroProfile specifications. Metrics in the base scope are intended to be portable between different MicroProfile-compatible runtimes.\n* The `/metrics?scope=vendor` endpoint provides you with metrics that are specific to the runtime.\n\nifndef::cloud-hosted[]\nPoint your browser to the https://localhost:9443/metrics[^] URL to review all the metrics that are enabled through MicroProfile Metrics. Log in with `bob` as your username and `bobpwd` as your password. You can see the metrics in text format.\n\nTo see only the application metrics, point your browser to https://localhost:9443/metrics?scope=application[^]. You can expect to see your application metrics in the output.\n\n[source, role=\"no_copy\"]\n----\n# HELP updateSystem_total Number of times updating a system endpoint is called\n# TYPE updateSystem_total counter\nupdateSystem_total{mp_scope=\"application\",} 1.0\n# HELP removeSystem_total Number of times removing a system endpoint is called\n# TYPE removeSystem_total counter\nremoveSystem_total{mp_scope=\"application\",} 1.0\n# HELP addSystemClient_total Number of times adding a system by client is called\n# TYPE addSystemClient_total counter\naddSystemClient_total{mp_scope=\"application\",} 0.0\n# HELP addSystem_total Number of times adding system endpoint is called\n# TYPE addSystem_total counter\naddSystem_total{mp_scope=\"application\",} 1.0\n----\n\nYou can see the system metrics at the https://localhost:9443/metrics?scope=base[^] URL. You can also see the vendor metrics at the https://localhost:9443/metrics?scope=vendor[^] URL.\nendif::[]\n\n// cloud-hosted guide instructions:\nifdef::cloud-hosted[]\nRun the following curl command to see the application metrics that are enabled through MicroProfile Metrics:\n```bash\ncurl -k --user bob:bobpwd https://localhost:9443/metrics?scope=application\n```\n\nYou can expect to see your application metrics in text format as the following output:\n\n```\n# HELP updateSystem_total Number of times updating a system endpoint is called\n# TYPE updateSystem_total counter\nupdateSystem_total{mp_scope=\"application\",} 1.0\n# HELP removeSystem_total Number of times removing a system endpoint is called\n# TYPE removeSystem_total counter\nremoveSystem_total{mp_scope=\"application\",} 1.0\n# HELP addSystemClient_total Number of times adding a system by client is called\n# TYPE addSystemClient_total counter\naddSystemClient_total{mp_scope=\"application\",} 0.0\n# HELP addSystem_total Number of times adding system endpoint is called\n# TYPE addSystem_total counter\naddSystem_total{mp_scope=\"application\",} 1.0\n```\n\nTo see the system metrics, run the following curl command:\n```bash\ncurl -k --user bob:bobpwd https://localhost:9443/metrics\\?scope=base\n```\n\nTo see the vendor metrics, run the following curl command:\n```bash\ncurl -k --user bob:bobpwd https://localhost:9443/metrics\\?scope=vendor\n```\n\nTo review all the metrics, run the following curl command:\n```bash\ncurl -k --user bob:bobpwd https://localhost:9443/metrics\n```\nendif::[]\n\n\n== Building the container \n\nPress `CTRL+C` in the command-line session to stop the `mvn liberty:dev` dev mode that you started in the previous section.\n\nNavigate to your application directory:\n\nifndef::cloud-hosted[]\n```\ncd start/inventory\n```\nendif::[]\n\nifdef::cloud-hosted[]\n```bash\ncd /home/project/guide-liberty-deep-dive/start/inventory\n```\nendif::[]\n\nThe first step to containerizing your application inside of a Docker container is creating a Dockerfile. A Dockerfile is a collection of instructions for building a Docker image that can then be run as a container. \n\nMake sure to start your Docker daemon before you proceed.\n\n[role=\"code_command hotspot file=0\",subs=\"quotes\"]\n----\n#Replace the `Dockerfile` in the `start/inventory` directory.#\n`Dockerfile`\n----\n\nDockerfile\n[source, text, linenums, role=\"code_column\"]\n----\ninclude::finish/module-kubernetes/Dockerfile[]\n----\n\nThe [hotspot=from file=0]`FROM` instruction initializes a new build stage and indicates the parent image from which your image is built. In this case, you’re using the `icr.io/appcafe/open-liberty:full-java17-openj9-ubi` image that comes with the latest Open Liberty runtime as your parent image.\n\nTo help you manage your images, you can label your container images with the [hotspot=label file=0]`LABEL` command. \n\nThe [hotspot=copy file=0]`COPY` instructions are structured as `COPY` `[--chown=\u003cuser\u003e:\u003cgroup\u003e]` `\u003csource\u003e` `\u003cdestination\u003e`. They copy local files into the specified destination within your Docker image. In this case, the first [hotspot=copy-config file=0]`COPY` instruction copies the Liberty configuration file that is at `src/main/liberty/config/server.xml` to the `/config/` destination directory. Similarly, the second [hotspot=copy-war file=0]`COPY` instruction copies the `.war` file to the `/config/apps` destination directory. The third [hotspot=copy-postgres file=0]`COPY` instruction copies the PostgreSQL library file to the Liberty shared resources directory.\n\n\n=== Developing the application in a container\n\nMake the PostgreSQL database configurable in the Liberty `server.xml` configuraton file.\n\n[role='code_command hotspot file=0', subs=\"quotes\"]\n----\n#Replace the Liberty `server.xml` configuraton file.#\n`src/main/liberty/config/server.xml`\n----\n\nserver.xml\n[source, xml, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-kubernetes/src/main/liberty/config/server.xml[]\n----\n\nInstead of the hard-coded `serverName`, `portNumber`, `user`, and `password` values in the [hotspot=postgresProperties file=0]`properties.postgresql` properties, use `${postgres/hostname}`, `${postgres/portnum}`, `${postgres/username}`, and `${postgres/password}`, which are defined by the [hotspot=variables file=0]`variable` elements.\n\nYou can use the Dockerfile to try out your application with the PostGreSQL database by running the `devc` goal.\n\nThe Open Liberty Maven plug-in includes a `devc` goal that simplifies developing your application in a container by starting dev mode with container support. This goal builds a Docker image, mounts the required directories, binds the required ports, and then runs the application inside of a container. Dev mode also listens for any changes in the application source code or configuration and rebuilds the image and restarts the container as necessary.\n\nRetrieve the PostgreSQL container IP address by running the following command:\n\n[role='command']\n```\ndocker inspect -f \"{{.NetworkSettings.IPAddress }}\" postgres-container\n```\n\nThe command returns the PostgreSQL container IP address:\n\n[role=\"no_copy\"]\n----\n172.17.0.2\n----\n\nBuild and run the container by running the `devc` goal with the PostgreSQL container IP address from the `start/inventory` directory. If your PostgreSQL container IP address is not `172.17.0.2`, replace the command with the right IP address.\n\nifndef::cloud-hosted[]\n[role='command']\n```\nmvn liberty:devc -DdockerRunOpts=\"-e POSTGRES_HOSTNAME=172.17.0.2\" -DserverStartTimeout=240\n```\nendif::[]\n\nifdef::cloud-hosted[]\n```bash\nchmod 777 /home/project/guide-liberty-deep-dive/start/inventory/target/liberty/wlp/usr/servers/defaultServer/logs\nPOSTGRES_IP=`docker inspect -f \"{{.NetworkSettings.IPAddress }}\" postgres-container`\nmvn liberty:devc \\\n  -DdockerRunOpts=\"-e POSTGRES_HOSTNAME=$POSTGRES_IP\" \\\n  -DserverStartTimeout=240\n```\nendif::[]\n\nYou need to wait a while to let dev mode start. After you see the following message, your Liberty instance is ready in dev mode:\n[role=\"no_copy\"]\n----\n**************************************************************\n*    Liberty is running in dev mode.\n*    ...\n*    Docker network information:\n*        Container name: [ liberty-dev ]\n*        IP address [ 172.17.0.2 ] on Docker network [ bridge ]\n*    ...\n----\n\nOpen another command-line session and run the following command to make sure that your container is running and didn’t crash:\n\n[role='command']\n```\ndocker ps \n```\n\nYou can see something similar to the following output:\n\n[role=\"no_copy\"]\n----\nCONTAINER ID  IMAGE               COMMAND                 CREATED        STATUS        PORTS                                                                   NAMES\nee2daf0b33e1  inventory-dev-mode  \"/opt/ol/helpers/run…\"  2 minutes ago  Up 2 minutes  0.0.0.0:7777-\u003e7777/tcp, 0.0.0.0:9080-\u003e9080/tcp, 0.0.0.0:9443-\u003e9443/tcp  liberty-dev\n----\n\nifndef::cloud-hosted[]\nPoint your browser to the http://localhost:9080/openapi/ui URL to try out your application. \nendif::[]\n\nifdef::cloud-hosted[]\nTry out your application by the following commands:\n\n```bash\ncurl -s http://localhost:9080/health | jq\n```\n\n```bash\ncurl 'http://localhost:9080/inventory/api/systems'\n```\nendif::[]\n\nWhen you're finished trying out the microservice, press `CTRL+C` in the command-line session where you started dev mode to stop and remove the container.\n\nAlso, run the following commands to stop the PostgreSQL container that was started in the previous section.\n\n[role='command']\n```\ndocker stop postgres-container\ndocker rm postgres-container\n```\n\n=== Building the container image\n\nRun the `mvn package` command from the `start/inventory` directory so that the `.war` file resides in the `target` directory.\n\nifdef::cloud-hosted[]\n```bash\ncd /home/project/guide-liberty-deep-dive/start/inventory\n```\nendif::[]\n\n[role='command']\n```\nmvn package\n```\n\nBuild your Docker image with the following commands:\n\n[role='command']\n```\ndocker build -t liberty-deepdive-inventory:1.0-SNAPSHOT .\n```\n\nifdef::cloud-hosted[]\nIn this Skills Network environment, you need to push the image to your container registry on IBM Cloud by running the following commands:\n```bash\ndocker tag liberty-deepdive-inventory:1.0-SNAPSHOT us.icr.io/$SN_ICR_NAMESPACE/liberty-deepdive-inventory:1.0-SNAPSHOT\ndocker push us.icr.io/$SN_ICR_NAMESPACE/liberty-deepdive-inventory:1.0-SNAPSHOT\n```\nendif::[]\n\nWhen the build finishes, run the following command to list all local Docker images:\n[role='command']\n```\ndocker images\n```\n\nVerify that the `liberty-deepdive-inventory:1.0-SNAPSHOT` image is listed among the Docker images, for example:\n[source, role=\"no_copy\"]\n----\nREPOSITORY                    TAG\nliberty-deepdive-inventory    1.0-SNAPSHOT\nicr.io/appcafe/open-liberty   full-java17-openj9-ubi\n----\n\n== Testing the microservice with Testcontainers\n\nAlthough you can test your microservice manually, you should rely on automated tests. In this section, you can learn how to use Testcontainers to verify your microservice in the same Docker container that you’ll use in production.\n\nFirst, create the `test` directory at the `src` directory of your Maven project.\n\nifndef::cloud-hosted[]\n[.tab_link.windows_link]\n`*WINDOWS*`\n[.tab_link.mac_link]\n`*MAC*`\n[.tab_link.linux_link]\n`*LINUX*`\n\n[.tab_content.windows_section]\n--\n[role='command']\n```\nmkdir src\\test\\java\\it\\io\\openliberty\\deepdive\\rest\nmkdir src\\test\\resources\n```\n--\n\n[.tab_content.mac_section.linux_section]\n--\n[role='command']\n```\nmkdir -p src/test/java/it/io/openliberty/deepdive/rest\nmkdir src/test/resources\n```\n--\nendif::[]\n\nifdef::cloud-hosted[]\n```bash\nmkdir -p /home/project/guide-liberty-deep-dive/start/inventory/src/test/java/it/io/openliberty/deepdive/rest\nmkdir /home/project/guide-liberty-deep-dive/start/inventory/src/test/resources\n```\nendif::[]\n\nCreate a RESTful client interface for the `inventory` microservice.\n\n[role=\"code_command hotspot file=0\", subs=\"quotes\"]\n----\n#Create the `SystemResourceClient.java` file.#\n`src/test/java/it/io/openliberty/deepdive/rest/SystemResourceClient.java`\n----\n\n// File 0\nSystemResourceClient.java\n[source, java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-testcontainers/src/test/java/it/io/openliberty/deepdive/rest/SystemResourceClient.java[]\n----\n\nThis interface declares [hotspot=listContents file=0]`listContents()`, [hotspot=getSystem file=0]`getSystem()`, [hotspot=addSystem file=0]`addSystem()`, [hotspot=updateSystem file=0]`updateSystem()`, and [hotspot=removeSystem file=0]`removeSystem()` methods for accessing each of the endpoints that are set up to access the `inventory` microservice. \n\n\nCreate the `SystemData` data model for each system in the inventory.\n\n[role=\"code_command hotspot file=1\", subs=\"quotes\"]\n----\n#Create the `SystemData.java` file.#\n`src/test/java/it/io/openliberty/deepdive/rest/SystemData.java`\n----\n\n// File 1\nSystemData.java\n[source, java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-testcontainers/src/test/java/it/io/openliberty/deepdive/rest/SystemData.java[]\n----\n\nThe [hotspot=fields file=1]`SystemData` class contains the ID, hostname, operating system name, Java version, and heap size properties. The various [hotspot=getMethods file=1]`get` and [hotspot=setMethods file=1]`set` methods within this class enable you to view and edit the properties of each system in the inventory.\n\nCreate the test container class that access the `inventory` docker image that you built in previous section.\n\n[role=\"code_command hotspot file=2\", subs=\"quotes\"]\n----\n#Create the `LibertyContainer.java` file.#\n`src/test/java/it/io/openliberty/deepdive/rest/LibertyContainer.java`\n----\n\n// File 2\nLibertyContainer.java\n[source, java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-testcontainers/src/test/java/it/io/openliberty/deepdive/rest/LibertyContainer.java[]\n----\n\nThe [hotspot=createRestClient file=2]`createRestClient()` method creates a REST client instance with the `SystemResourceClient` interface. The [hotspot=getBaseURL file=2]`getBaseURL()` method constructs the URL that can access the `inventory` docker image.\n\nNow, you can create your integration test cases.\n\n[role=\"code_command hotspot file=3\", subs=\"quotes\"]\n----\n#Create the `SystemResourceIT.java` file.#\n`src/test/java/it/io/openliberty/deepdive/rest/SystemResourceIT.java`\n----\n\n// File 3\nSystemResourceIT.java\n[source, java, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-testcontainers/src/test/java/it/io/openliberty/deepdive/rest/SystemResourceIT.java[]\n----\n\nDefine the [hotspot=postgresSetup file=3]`postgresContainer` test container to start up the PostgreSQL docker image, and define the [hotspot=libertySetup file=3]`libertyContainer` test container to start up the `inventory` docker image. Make sure that both containers use the same [hotspot=network hotspot=pNetwork hotspot=lNetwork file=3]`network`. The [hotspot=health file=3]`/health/ready` endpoint can tell you whether the container is ready to start testing.\n\nThe [hotspot=testAddSystem file=3]`testAddSystem()` verifies the [hotspot=addSystem file=3]`addSystem` and [hotspot=listContents file=3]`listContents` endpoints.\n\nThe [hotspot=testUpdateSystem file=3]`testUpdateSystem()` verifies the [hotspot=updateSystem file=3]`updateSystem` and [hotspot=getSystem file=3]`getSystem` endpoints.\n\nThe [hotspot=testRemoveSystem file=3]`testRemoveSystem()` verifies the [hotspot=removeSystem file=3]`removeSystem` endpoint.\n\n\nCreate the log4j properites that are required by the Testcontainers framework.\n\n[role=\"code_command hotspot file=4\", subs=\"quotes\"]\n----\n#Create the `log4j.properties` file.#\n`src/test/resources/log4j.properties`\n----\n\n// File 4\nlog4j.properties\n[source, text, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-testcontainers/src/test/resources/log4j.properties[]\n----\n\nUpdate the Maven configuration file with the required dependencies.\n\n[role=\"code_command hotspot file=5\", subs=\"quotes\"]\n----\n#Replace the `pom.xml` file.#\n`pom.xml`\n----\n\n// File 5\npom.xml\n[source, xml, linenums, role='code_column hide_tags=copyright']\n----\ninclude::finish/module-testcontainers/pom.xml[]\n----\n\nAdd each required [hotspot=testDependenies file=5]`dependency` with `test` scope, including JUnit5, Testcontainers, Log4J, JBoss RESTEasy client, Glassfish JSON, and Vert.x libraries. Also, add the [hotspot=failsafe file=5]`maven-failsafe-plugin` plugin, so that the integration test can be run by the Maven `verify` goal.\n\n=== Running the tests\n\nYou can run the Maven `verify` goal, which compiles the java files, starts the containers, runs the tests, and then stops the containers.\n\nifndef::cloud-hosted[]\n[.tab_link.windows_link]\n`*WINDOWS*`\n[.tab_link.mac_link]\n`*MAC*`\n[.tab_link.linux_link]\n`*LINUX*`\n\n[.tab_content.windows_section.mac_section]\n--\n[role='command']\n```\nmvn verify\n```\n--\n\n[.tab_content.linux_section]\n--\n[role='command']\n```\nexport TESTCONTAINERS_RYUK_DISABLED=true\nmvn verify\n```\n--\nendif::[]\n\nifdef::cloud-hosted[]\nIn this Skills Network environment, you can test the HTTP protcol only.\n```bash\nexport TESTCONTAINERS_RYUK_DISABLED=true\nmvn verify\n```\nendif::[]\n\n\nYou will see the following output:\n\n[source,role=\"no_copy\"]\n----\n-------------------------------------------------------\n T E S T S\n-------------------------------------------------------\nRunning it.io.openliberty.deepdive.rest.SystemResourceIT\n...\nTests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 17.413 s - in it.io.openliberty.deepdive.rest.SystemResourceIT\n\nResults :\n\nTests run: 3, Failures: 0, Errors: 0, Skipped: 0\n----\n\n// Starting and preparing your cluster for deployment\nifndef::cloud-hosted[]\n== Starting and preparing your cluster for deployment\n\nIf you are using Linux, you can continue this section.\n\nStart your Kubernetes cluster.\n\nRun the following command from a command-line session:\n\n[role=command]\n```\nminikube start\n```\n\nNext, validate that you have a healthy Kubernetes environment by running the following command from the active command-line session.\n\n[role=command]\n```\nkubectl get nodes\n```\n\nThis command should return a `Ready` status for the `minikube` node.\n\nRun the following command to configure the Docker CLI to use Minikube's Docker daemon.\nAfter you run this command, you will be able to interact with Minikube's Docker daemon and build new\nimages directly to it from your host machine:\n\n[role=command]\n```\neval $(minikube docker-env)\n```\n\nRebuild your Docker image under Minikube with the following commands:\n\n[role='command']\n```\ndocker build -t liberty-deepdive-inventory:1.0-SNAPSHOT .\n```\nendif::[]\n\n== Deploying the microservice to Kubernetes\n\nifndef::cloud-hosted[]\nIf you are using Linux, you can continue this section.\nendif::[]\n\nNow that the containerized application is built and tested, deploy it to a local Kubernetes cluster. \n\n=== Installing the Open Liberty Operator \n\nifndef::cloud-hosted[]\nInstall the Open Liberty Operator to deploy the microservice to Kubernetes.\n\nFirst, install the `cert-manager` to your Kubernetes cluster by running the following command:\n[role='command']\n```\nkubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.12.3/cert-manager.yaml\n```\n\nNext, install Custom Resource Definitions (CRDs) for the Open Liberty Operator by running the following command:\n[role='command']\n```\nkubectl apply --server-side -f https://raw.githubusercontent.com/OpenLiberty/open-liberty-operator/main/deploy/releases/1.2.1/kubectl/openliberty-app-crd.yaml\n```\nCustom Resources extend the Kubernetes API and enhance its functionality.\n\nSet environment variables for namespaces for the Open Liberty Operator by running the following commands:\n\n[role='command']\n```\nOPERATOR_NAMESPACE=default\nWATCH_NAMESPACE='\"\"'\n```\n\nNext, run the following commands to install cluster-level role-based access:\n\n[role='command']\n```\ncurl -L https://raw.githubusercontent.com/OpenLiberty/open-liberty-operator/main/deploy/releases/1.2.1/kubectl/openliberty-app-rbac-watch-all.yaml \\\n  | sed -e \"s/OPEN_LIBERTY_OPERATOR_NAMESPACE/${OPERATOR_NAMESPACE}/\" \\\n  | kubectl apply -f -\n```\n\nFinally, run the following commands to install the Operator:\n\n[role='command']\n```\ncurl -L https://raw.githubusercontent.com/OpenLiberty/open-liberty-operator/main/deploy/releases/1.2.1/kubectl/openliberty-app-operator.yaml \\\n  | sed -e \"s/OPEN_LIBERTY_WATCH_NAMESPACE/${WATCH_NAMESPACE}/\" \\\n  | kubectl apply -n ${OPERATOR_NAMESPACE} -f -\n```\nendif::[]\n\nifdef::cloud-hosted[]\nIn this Skills Network environment, the Open Liberty Operator is already installed by the administrator. If you would like to learn how to install the Open Liberty Operator, see the [Deploying a microservice to Kubernetes by using Open Liberty Operator](https://openliberty.io/guides/openliberty-operator-intro.html) guide or the Open Liberty Operator [documentation](https://github.com/OpenLiberty/open-liberty-operator/tree/main/deploy/releases/1.2.1#readme).\nendif::[]\n\nTo check that the Open Liberty Operator is installed successfully, run the following command to view all the supported API resources that are available through the Open Liberty Operator:\n\n[role='command']\n```\nkubectl api-resources --api-group=apps.openliberty.io\n```\n\nLook for the following output, which shows the https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/[custom resource definitions^] (CRDs) that can be used by the Open Liberty Operator:\n\n[role='no_copy']\n```\nNAME                      SHORTNAMES         APIGROUP              NAMESPACED   KIND\nopenlibertyapplications   olapp,olapps       apps.openliberty.io   true         OpenLibertyApplication\nopenlibertydumps          oldump,oldumps     apps.openliberty.io   true         OpenLibertyDump\nopenlibertytraces         oltrace,oltraces   apps.openliberty.io   true         OpenLibertyTrace\n```\n\nEach CRD defines a kind of object that can be used, which is specified in the previous example by the `KIND` value. The `SHORTNAME` value specifies alternative names that you can substitute in the configuration to refer to an object kind. For example, you can refer to the `OpenLibertyApplication` object kind by one of its specified shortnames, such as `olapps`. \n\nThe `openlibertyapplications` CRD defines a set of configurations for deploying an Open Liberty-based application, including the application image, number of instances, and storage settings. The Open Liberty Operator watches for changes to instances of the `OpenLibertyApplication` object kind and creates Kubernetes resources that are based on the configuration that is defined in the CRD.\n\n=== Deploying the container image\n\n\n[role=\"code_command hotspot file=0\",subs=\"quotes\"]\n----\n#Create the `inventory.yaml` in the `start/inventory` directory.#\n`inventory.yaml`\n----\n\n// File 0\ninventory.yaml\n[source, yaml, linenums, role='code_column']\n----\ninclude::finish/module-kubernetes/inventory.init.yaml[]\n----\n\nIn the [hotspot file=0]`inventory.yaml` file, the custom resource (CR) is specified to be [hotspot=kind file=0]`OpenLibertyApplication`. The CR triggers the Open Liberty Operator to create, update, or delete Kubernetes resources that are needed by the application to run on your cluster. Additionally, the [hotspot=applicationImage file=0]`applicationImage` field must be specified and set to the image that was created in the previous module. \n\n// File 1\npostgres.yaml\n[source, yaml, linenums, role='code_column']\n----\ninclude::finish/postgres/postgres.yaml[]\n----\n\nSimilarly, a Kubernetes resource definition is provided in the [hotspot file=1]`postgres.yaml` file at the `finish/postgres` directory. In the `postgres.yaml` file, the deployment for the PostgreSQL database is defined. \n\n// The [hotspot=deployment file=1]`Deployment` definition acts to deploy the necessary Kubernetes resources to serve the database. Finally, the [hotspot=service file=1]`Service` definition exposes the PostgreSQL service on a cluster-internal IP, so that the inventory service can make requests to it.\n\nCreate a Kubernetes Secret to configure the credentials for the `admin` user to access the database.\n[role='command']\n```\nkubectl create secret generic post-app-credentials --from-literal username=admin --from-literal password=adminpwd\n```\n\nThe credentials are passed to the PostgreSQL database service as environment variables in the [hotspot=env file=1]`env` field.\n\nifndef::cloud-hosted[]\nRun the following command to deploy the application and database:\n[role='command']\n```\nkubectl apply -f ../../finish/postgres/postgres.yaml\nkubectl apply -f inventory.yaml\n```\nendif::[]\n\nifdef::cloud-hosted[]\nTo deploy the **inventory** microservice and ***Postgres*** database in this Skills Network environment, you need to update the image name so that the image in your IBM Cloud container registry is used, and add the **pullSecret** and ***pullPolicy*** settings. Run the following commands:\n\n```bash\nsed -i 's=namespace: default=namespace: '\"$SN_ICR_NAMESPACE\"'=g' /home/project/guide-liberty-deep-dive/finish/postgres/postgres.yaml\nkubectl apply -f /home/project/guide-liberty-deep-dive/finish/postgres/postgres.yaml\nsed -i 's=liberty-deepdive-inventory:1.0-SNAPSHOT=us.icr.io/'\"$SN_ICR_NAMESPACE\"'/liberty-deepdive-inventory:1.0-SNAPSHOT\\n  pullPolicy: Always\\n  pullSecret: icr=g' /home/project/guide-liberty-deep-dive/start/inventory/inventory.yaml\nkubectl apply -f /home/project/guide-liberty-deep-dive/start/inventory/inventory.yaml\n```\nendif::[]\n\nWhen your pods are deployed, run the following command to check their status:\n[role='command']\n```\nkubectl get pods\n```\n\nIf all the pods are working correctly, you see an output similar to the following example:\n\n[role=\"no_copy\"]\n----\nNAME                                    READY   STATUS    RESTARTS   AGE\ninventory-deployment-75f9dc56d9-g9lzl   1/1     Running   0          35s\npostgres-58bd9b55c7-6vzz8               1/1     Running   0          13s\nolo-controller-manager-6fc6b456dc-s29wl 1/1     Running   0          10m\n----\n\nPause briefly to give the inventory service time to initialize. After it has started, use the following command to configure port forwarding to access the `inventory` microservice:\n\n[role='command']\n```\nkubectl port-forward svc/inventory-deployment 9443\n```\n\nThe `port-forward` command pauses the command-line session until you press **Ctrl+C** after you try out the microservice.\n\nifndef::cloud-hosted[]\nThe application might take some time to get ready. See the https://localhost:9443/health URL to confirm that the `inventory` microservice is up and running.\n\nOnce your application is up and running, you can check out the service at the https://localhost:9443/openapi/ui/[https://localhost:9443/openapi/ui/^] URL. The servers dropdown list shows the `\\https://localhost:9443/inventory` URL. Or, you can run the following command to access the inventory microservice:\n[role='command']\n```\ncurl -k https://localhost:9443/inventory/api/systems\n```\nendif::[]\n\nifdef::cloud-hosted[]\nThe application might take some time to get ready. To confirm that the ***inventory*** microservice is up and running, run the following curl command:\n\n```bash\ncurl -k https://localhost:9443/health | jq\n```\n\nIf the application is up and running, you are ready to access the microservice.\nIn another command-line session, access the microservice by running the following commands:\n\n```bash\ncurl -k --user bob:bobpwd -X DELETE \\\n  'https://localhost:9443/inventory/api/systems/localhost'\n```\n\n```bash\ncurl -k -X POST 'https://localhost:9443/inventory/api/syste","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenliberty%2Fguide-liberty-deep-dive","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopenliberty%2Fguide-liberty-deep-dive","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenliberty%2Fguide-liberty-deep-dive/lists"}