Getting started with Open Liberty

duration 25 minutes

Prerequisites:

Learn how to develop a Java application on Open Liberty with Maven and Docker.

What you’ll learn

You will learn how to run and update a simple REST microservice on Open Liberty. You will use Maven throughout the guide to build and deploy the microservice as well as to interact with the running Liberty instance.

Open Liberty is an open application framework designed for the cloud. It’s small, lightweight, and designed with modern cloud-native application development in mind. It supports the full MicroProfile and Jakarta EE APIs and is composable, meaning that you can use only the features that you need, keeping everything lightweight, which is great for microservices. It also deploys to every major cloud platform, including Docker, Kubernetes, and Cloud Foundry.

Maven is an automation build tool that provides an efficient way to develop Java applications. Using Maven, you will build a simple microservice, called system, that collects basic system properties from your laptop and displays them on an endpoint that you can access in your web browser.

You’ll also explore how to package your application with Open Liberty so that it can be deployed anywhere in one go. You will then make Liberty configuration and code changes and see how they are immediately picked up by a running instance.

Finally, you will package the application along with the server configuration into a Docker image and run that image as a container.

Getting started

The fastest way to work through this guide is to clone the Git repository and use the projects that are provided inside:

git clone https://github.com/openliberty/guide-getting-started.git
cd guide-getting-started

The start directory contains the starting project that you will build upon.

The finish directory contains the finished project that you will build.

Before you begin, make sure you have all the necessary prerequisites.

Building and running the application

Your application is configured to be built with Maven. Every Maven-configured project contains a pom.xml file, which defines the project configuration, dependencies, plug-ins, and so on.

Your pom.xml file is located in the start directory and is configured to include the liberty-maven-plugin, which allows you to install applications into Open Liberty and manage the server instances.

pom.xml

  1<?xml version='1.0' encoding='utf-8'?>
  2<project xmlns="http://maven.apache.org/POM/4.0.0"
  3         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5    <modelVersion>4.0.0</modelVersion>
  6
  7    <groupId>io.openliberty.guides</groupId>
  8    <artifactId>guide-getting-started</artifactId>
  9    <version>1.0-SNAPSHOT</version>
 10    <packaging>war</packaging>
 11
 12    <properties>
 13        <maven.compiler.source>1.8</maven.compiler.source>
 14        <maven.compiler.target>1.8</maven.compiler.target>
 15        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 16        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 17        <!-- Liberty configuration -->
 18        <liberty.var.default.http.port>9080</liberty.var.default.http.port>
 19        <liberty.var.default.https.port>9443</liberty.var.default.https.port>
 20    </properties>
 21
 22    <dependencies>
 23        <!-- Provided dependencies -->
 24        <dependency>
 25            <groupId>jakarta.platform</groupId>
 26            <artifactId>jakarta.jakartaee-api</artifactId>
 27            <version>8.0.0</version>
 28            <scope>provided</scope>
 29        </dependency>
 30        <dependency>
 31            <groupId>org.eclipse.microprofile</groupId>
 32            <artifactId>microprofile</artifactId>
 33            <version>4.0.1</version>
 34            <type>pom</type>
 35            <scope>provided</scope>
 36        </dependency>
 37        <!-- For tests -->
 38        <dependency>
 39            <groupId>org.junit.jupiter</groupId>
 40            <artifactId>junit-jupiter</artifactId>
 41            <version>5.7.1</version>
 42            <scope>test</scope>
 43        </dependency>
 44        <dependency>
 45            <groupId>org.apache.cxf</groupId>
 46            <artifactId>cxf-rt-rs-client</artifactId>
 47            <version>3.4.3</version>
 48            <scope>test</scope>
 49        </dependency>
 50        <dependency>
 51            <groupId>org.apache.cxf</groupId>
 52            <artifactId>cxf-rt-rs-extension-providers</artifactId>
 53            <version>3.4.3</version>
 54            <scope>test</scope>
 55        </dependency>
 56        <dependency>
 57            <groupId>org.glassfish</groupId>
 58            <artifactId>javax.json</artifactId>
 59            <version>1.1.4</version>
 60            <scope>test</scope>
 61        </dependency>
 62    </dependencies>
 63
 64    <build>
 65        <finalName>${project.artifactId}</finalName>
 66        <plugins>
 67            <!-- Enable liberty-maven plugin -->
 68            <!-- tag::libertyMavenPlugin[] -->
 69            <plugin>
 70                <groupId>io.openliberty.tools</groupId>
 71                <artifactId>liberty-maven-plugin</artifactId>
 72                <version>3.3.4</version>
 73            </plugin>
 74            <!-- end::libertyMavenPlugin[] -->
 75            <plugin>
 76                <groupId>org.apache.maven.plugins</groupId>
 77                <artifactId>maven-war-plugin</artifactId>
 78                <version>3.3.1</version>
 79            </plugin>
 80            <plugin>
 81                <groupId>org.apache.maven.plugins</groupId>
 82                <artifactId>maven-surefire-plugin</artifactId>
 83                <version>2.22.2</version>
 84            </plugin>
 85            <!-- Plugin to run functional tests -->
 86            <plugin>
 87                <groupId>org.apache.maven.plugins</groupId>
 88                <artifactId>maven-failsafe-plugin</artifactId>
 89                <version>2.22.2</version>
 90                <configuration>
 91                    <systemPropertyVariables>
 92                        <http.port>${liberty.var.default.http.port}</http.port>
 93                        <context.root>/dev</context.root>
 94                    </systemPropertyVariables>
 95                </configuration>
 96            </plugin>
 97        </plugins>
 98    </build>
 99</project>

To begin, navigate to the start directory. Build the system microservice that is provided and deploy it to Open Liberty by running the Maven liberty:run goal:

cd start
mvn liberty:run

The mvn command initiates a Maven build, during which the target directory is created to store all build-related files.

The liberty:run argument specifies the Open Liberty run goal, which starts an Open Liberty server instance in the foreground. As part of this phase, an Open Liberty server runtime is downloaded and installed into the target/liberty/wlp directory, a server instance is created and configured in the target/liberty/wlp/usr/servers/defaultServer directory, and the application is installed into that server via loose config.

For more information about the Liberty Maven plug-in, see its GitHub repository.

When the server begins starting up, various messages display in your command-line session. Wait for the following message, which indicates that the server startup is complete:

[INFO] [AUDIT] CWWKF0011I: The server defaultServer is ready to run a smarter planet.

To access the system microservice, see the http://localhost:9080/system/properties URL, and you see a list of the various system properties of your JVM:

{
    "os.name": "Mac OS X",
    "java.version": "1.8.0_151",
    ...
}

When you need to stop the server, press CTRL+C in the command-line session where you ran the server, or run the liberty:stop goal from the start directory in another command-line session:

mvn liberty:stop

Starting and stopping the Open Liberty server in the background

Although you can start and stop the server in the foreground by using the Maven liberty:run goal, you can also start and stop the server in the background with the Maven liberty:start and liberty:stop goals:

mvn liberty:start
mvn liberty:stop

Updating the server configuration without restarting the server

The Open Liberty Maven plug-in includes a dev goal that listens for any changes in the project, including application source code or configuration. The Open Liberty server automatically reloads the configuration without restarting. This goal allows for quicker turnarounds and an improved developer experience.

Stop the Open Liberty server if it is running, and start it in dev mode by running the liberty:dev goal in the start directory:

mvn liberty:dev

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

As before, you can see that the application is running by going to the http://localhost:9080/system/properties URL.

Now try updating the server configuration while the server is running in dev mode. The system microservice does not currently include health monitoring to report whether the server and the microservice that it runs are healthy. You can add health reports with the MicroProfile Health feature, which adds a /health endpoint to your application. If you try to access this endpoint now at the http://localhost:9080/health/ URL, you see a 404 error because the /health endpoint does not yet exist:

Error 404: java.io.FileNotFoundException: SRVE0190E: File not found: /health

To add the MicroProfile Health feature to the server, include the mpHealth feature in the server.xml.

Replace the server configuration file.
src/main/liberty/config/server.xml

server.xml

 1<server description="Sample Liberty server">
 2    <!-- tag::features[] -->
 3    <featureManager>
 4        <feature>jaxrs-2.1</feature>
 5        <feature>jsonp-1.1</feature>
 6        <feature>cdi-2.0</feature>
 7        <feature>mpMetrics-3.0</feature>
 8        <!-- tag::mpHealth[] -->
 9        <feature>mpHealth-3.0</feature>
10        <!-- end::mpHealth[] -->
11        <feature>mpConfig-2.0</feature>
12    </featureManager>
13    <!-- end::features[] -->
14
15    <variable name="default.http.port" defaultValue="9080"/>
16    <variable name="default.https.port" defaultValue="9443"/>
17
18    <webApplication location="guide-getting-started.war" contextRoot="/" />
19    
20    <mpMetrics authentication="false"/>
21
22    <!-- tag::logging[] -->
23    <logging traceSpecification="com.ibm.ws.microprofile.health.*=all" />
24    <!-- end::logging[] -->
25
26    <httpEndpoint host="*" httpPort="${default.http.port}" 
27        httpsPort="${default.https.port}" id="defaultHttpEndpoint"/>
28
29    <variable name="io_openliberty_guides_system_inMaintenance" value="false"/>
30</server>

After you make the file changes, Open Liberty automatically reloads its configuration. When enabled, the mpHealth feature automatically adds a /health endpoint to the application. You can see the server being updated in the server log displayed in your command-line session:

[INFO] [AUDIT] CWWKG0016I: Starting server configuration update.
[INFO] [AUDIT] CWWKT0017I: Web application removed (default_host): http://foo:9080/
[INFO] [AUDIT] CWWKZ0009I: The application io.openliberty.guides.getting-started has stopped successfully.
[INFO] [AUDIT] CWWKG0017I: The server configuration was successfully updated in 0.284 seconds.
[INFO] [AUDIT] CWWKT0016I: Web application available (default_host): http://foo:9080/health/
[INFO] [AUDIT] CWWKF0012I: The server installed the following features: [mpHealth-3.0].
[INFO] [AUDIT] CWWKF0008I: Feature update completed in 0.285 seconds.
[INFO] [AUDIT] CWWKT0016I: Web application available (default_host): http://foo:9080/
[INFO] [AUDIT] CWWKZ0003I: The application io.openliberty.guides.getting-started updated in 0.173 seconds.

Try to access the /health endpoint again by visiting the http://localhost:9080/health URL. You see the following JSON:

{
    "checks":[],
    "status":"UP"
}

Now you can verify whether your server is up and running.

Updating the source code without restarting the server

The JAX-RS application that contains your system microservice runs in a server from its .class file and other artifacts. Open Liberty automatically monitors these artifacts, and whenever they are updated, it updates the running server without the need for the server to be restarted.

Look at your pom.xml file.

pom.xml

 1<?xml version='1.0' encoding='utf-8'?>
 2<project xmlns="http://maven.apache.org/POM/4.0.0"
 3         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 5    <modelVersion>4.0.0</modelVersion>
 6
 7    <groupId>io.openliberty.guides</groupId>
 8    <artifactId>guide-getting-started</artifactId>
 9    <version>1.0-SNAPSHOT</version>
10    <packaging>war</packaging>
11
12    <properties>
13        <maven.compiler.source>1.8</maven.compiler.source>
14        <maven.compiler.target>1.8</maven.compiler.target>
15        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
16        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
17        <!-- Liberty configuration -->
18        <liberty.var.default.http.port>9080</liberty.var.default.http.port>
19        <liberty.var.default.https.port>9443</liberty.var.default.https.port>
20    </properties>
21
22    <dependencies>
23        <!-- Provided dependencies -->
24        <dependency>
25            <groupId>jakarta.platform</groupId>
26            <artifactId>jakarta.jakartaee-api</artifactId>
27            <version>8.0.0</version>
28            <scope>provided</scope>
29        </dependency>
30        <dependency>
31            <groupId>org.eclipse.microprofile</groupId>
32            <artifactId>microprofile</artifactId>
33            <version>4.0.1</version>
34            <type>pom</type>
35            <scope>provided</scope>
36        </dependency>
37        <!-- For tests -->
38        <dependency>
39            <groupId>org.junit.jupiter</groupId>
40            <artifactId>junit-jupiter</artifactId>
41            <version>5.7.1</version>
42            <scope>test</scope>
43        </dependency>
44        <dependency>
45            <groupId>org.apache.cxf</groupId>
46            <artifactId>cxf-rt-rs-client</artifactId>
47            <version>3.4.3</version>
48            <scope>test</scope>
49        </dependency>
50        <dependency>
51            <groupId>org.apache.cxf</groupId>
52            <artifactId>cxf-rt-rs-extension-providers</artifactId>
53            <version>3.4.3</version>
54            <scope>test</scope>
55        </dependency>
56        <dependency>
57            <groupId>org.glassfish</groupId>
58            <artifactId>javax.json</artifactId>
59            <version>1.1.4</version>
60            <scope>test</scope>
61        </dependency>
62    </dependencies>
63
64    <build>
65        <finalName>${project.artifactId}</finalName>
66        <plugins>
67            <!-- Enable liberty-maven plugin -->
68            <plugin>
69                <groupId>io.openliberty.tools</groupId>
70                <artifactId>liberty-maven-plugin</artifactId>
71                <version>3.3.4</version>
72            </plugin>
73            <plugin>
74                <groupId>org.apache.maven.plugins</groupId>
75                <artifactId>maven-war-plugin</artifactId>
76                <version>3.3.1</version>
77            </plugin>
78            <plugin>
79                <groupId>org.apache.maven.plugins</groupId>
80                <artifactId>maven-surefire-plugin</artifactId>
81                <version>2.22.2</version>
82            </plugin>
83            <!-- Plugin to run functional tests -->
84            <plugin>
85                <groupId>org.apache.maven.plugins</groupId>
86                <artifactId>maven-failsafe-plugin</artifactId>
87                <version>2.22.2</version>
88                <configuration>
89                    <systemPropertyVariables>
90                        <http.port>${liberty.var.default.http.port}</http.port>
91                        <context.root>/dev</context.root>
92                    </systemPropertyVariables>
93                </configuration>
94            </plugin>
95        </plugins>
96    </build>
97</project>

Try updating the source code while the server is running in dev mode. At the moment, the /health endpoint reports whether the server is running, but the endpoint doesn’t provide any details on the microservices that are running inside of the server.

MicroProfile Health offers health checks for both readiness and liveness. A readiness check allows third-party services, such as Kubernetes, to know if the microservice is ready to process requests. A liveness check allows third-party services to determine if the microservice is running.

Create the SystemReadinessCheck class.
src/main/java/io/openliberty/sample/system/SystemReadinessCheck.java

SystemReadinessCheck.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2018, 2021 IBM Corporation and others.
 4 * All rights reserved. This program and the accompanying materials
 5 * are made available under the terms of the Eclipse Public License v1.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-v10.html
 8 *
 9 * Contributors:
10 *     IBM Corporation - Initial implementation
11 *******************************************************************************/
12// end::copyright[]
13package io.openliberty.sample.system;
14
15import javax.enterprise.context.ApplicationScoped;
16
17import javax.inject.Inject;
18import javax.inject.Provider;
19
20import org.eclipse.microprofile.config.inject.ConfigProperty;
21import org.eclipse.microprofile.health.Readiness;
22import org.eclipse.microprofile.health.HealthCheck;
23import org.eclipse.microprofile.health.HealthCheckResponse;
24
25@Readiness
26@ApplicationScoped
27// tag::systemReadinessCheck[]
28public class SystemReadinessCheck implements HealthCheck {
29
30    private static final String READINESS_CHECK = SystemResource.class.getSimpleName()
31                                                 + " Readiness Check";
32
33    @Inject
34    @ConfigProperty(name = "io_openliberty_guides_system_inMaintenance")
35    Provider<String> inMaintenance;
36
37    @Override
38    public HealthCheckResponse call() {
39        if (inMaintenance != null && inMaintenance.get().equalsIgnoreCase("true")) {
40            return HealthCheckResponse.down(READINESS_CHECK);
41        }
42        return HealthCheckResponse.up(READINESS_CHECK);
43    }
44
45}
46// end::systemReadinessCheck[]

The SystemReadinessCheck class verifies that the system microservice is not in maintenance by checking a config property.

Create the SystemLivenessCheck class.
src/main/java/io/openliberty/sample/system/SystemLivenessCheck.java

SystemLivenessCheck.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2019, 2021 IBM Corporation and others.
 4 * All rights reserved. This program and the accompanying materials
 5 * are made available under the terms of the Eclipse Public License v1.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-v10.html
 8 *
 9 * Contributors:
10 *     IBM Corporation - Initial implementation
11 *******************************************************************************/
12// end::copyright[]
13package io.openliberty.sample.system;
14
15import javax.enterprise.context.ApplicationScoped;
16
17import java.lang.management.MemoryMXBean;
18import java.lang.management.ManagementFactory;
19
20import org.eclipse.microprofile.health.Liveness;
21import org.eclipse.microprofile.health.HealthCheck;
22import org.eclipse.microprofile.health.HealthCheckResponse;
23
24@Liveness
25@ApplicationScoped
26// tag::systemLivenessCheck[]
27public class SystemLivenessCheck implements HealthCheck {
28
29    @Override
30    public HealthCheckResponse call() {
31        MemoryMXBean memBean = ManagementFactory.getMemoryMXBean();
32        long memUsed = memBean.getHeapMemoryUsage().getUsed();
33        long memMax = memBean.getHeapMemoryUsage().getMax();
34
35        return HealthCheckResponse.named(
36            SystemResource.class.getSimpleName() + " Liveness Check")
37                                  .withData("memory used", memUsed)
38                                  .withData("memory max", memMax)
39                                  .status(memUsed < memMax * 0.9).build();
40    }
41
42}
43// end::systemLivenessCheck[]

The SystemLivenessCheck class reports a status of DOWN if the microservice uses over 90% of the maximum amount of memory.

After you make the file changes, Open Liberty automatically reloads its configuration and the system application.

The following messages display in your first command-line session:

[INFO] [AUDIT] CWWKT0017I: Web application removed (default_host): http://foo:9080/
[INFO] [AUDIT] CWWKZ0009I: The application io.openliberty.guides.getting-started has stopped successfully.
[INFO] [AUDIT] CWWKT0016I: Web application available (default_host): http://foo:9080/
[INFO] [AUDIT] CWWKZ0003I: The application io.openliberty.guides.getting-started updated in 0.136 seconds.

Access the /health endpoint again by going to the http://localhost:9080/health URL. This time you see the overall status of your server and the aggregated data of the liveness and readiness checks for the system microservice:

{
   "checks":[
      {
         "data":{},
         "name":"SystemResource Readiness Check",
         "status":"UP"
      },
      {
         "data":{
            "memory used":40434888,
            "memory max":4294967296
         },
         "name":"SystemResource Liveness Check",
         "status":"UP"
      }
   ],
   "status":"UP"
}

You can also access the /health/ready endpoint by going to the http://localhost:9080/health/ready URL to view the data from the readiness health check. Similarly, access the /health/live endpoint by going to the http://localhost:9080/health/live URL to view the data from the liveness health check.

Making code changes and recompiling is fast and straightforward. Open Liberty dev mode automatically picks up changes in the .class files and artifacts, without needing to be restarted. Alternatively, you can run the run goal and manually repackage or recompile the application by using the mvn package command or the mvn compile command while the server is running. Dev mode was added to further improve the developer experience by minimizing turnaround times.

Checking the Open Liberty server logs

While the server is running in the foreground, it displays various console messages in the command-line session. These messages are also logged to the target/liberty/wlp/usr/servers/defaultServer/logs/console.log file. You can find the complete server logs in the target/liberty/wlp/usr/servers/defaultServer/logs directory. The console.log and messages.log files are the primary log files that contain console output of the running application and the server. More logs are created when runtime errors occur or whenever tracing is enabled. You can find the error logs in the ffdc directory and the tracing logs in the trace.log file.

In addition to the log files that are generated automatically, you can enable logging of specific Java packages or classes by using the logging element:

<logging traceSpecification="<component_1>=<level>:<component_2>=<level>:..."/>

The component element is a Java package or class, and the level element is one of the following logging levels: off, fatal, severe, warning, audit, info, config, detail, fine, finer, finest, all.

Try enabling detailed logging of the MicroProfile Health feature by adding the logging element to your configuration file.

Replace the server configuration file.
src/main/liberty/config/server.xml

server.xml

 1<server description="Sample Liberty server">
 2    <!-- tag::features[] -->
 3    <featureManager>
 4        <feature>jaxrs-2.1</feature>
 5        <feature>jsonp-1.1</feature>
 6        <feature>cdi-2.0</feature>
 7        <feature>mpMetrics-3.0</feature>
 8        <!-- tag::mpHealth[] -->
 9        <feature>mpHealth-3.0</feature>
10        <!-- end::mpHealth[] -->
11        <feature>mpConfig-2.0</feature>
12    </featureManager>
13    <!-- end::features[] -->
14
15    <variable name="default.http.port" defaultValue="9080"/>
16    <variable name="default.https.port" defaultValue="9443"/>
17
18    <webApplication location="guide-getting-started.war" contextRoot="/" />
19    
20    <mpMetrics authentication="false"/>
21
22    <!-- tag::logging[] -->
23    <logging traceSpecification="com.ibm.ws.microprofile.health.*=all" />
24    <!-- end::logging[] -->
25
26    <httpEndpoint host="*" httpPort="${default.http.port}" 
27        httpsPort="${default.https.port}" id="defaultHttpEndpoint"/>
28
29    <variable name="io_openliberty_guides_system_inMaintenance" value="false"/>
30</server>

After you change the file, Open Liberty automatically reloads its configuration.

Now, when you visit the /health endpoint, additional traces are logged in the trace.log file.

When you are done checking out the service, exit dev mode by pressing CTRL+C in the command-line session where you ran the server, or by typing q and then pressing the enter/return key.

Running the application in a Docker container

To run the application in a container, Docker needs to be installed. For installation instructions, see the Official Docker Docs.

Make sure to start your Docker daemon before you proceed.

To containerize the application, you need a Dockerfile. This file contains a collection of instructions that define how a Docker image is built, what files are packaged into it, what commands run when the image runs as a container, and other information. You can find a complete Dockerfile in the start directory. This Dockerfile copies the .war file into a Docker image that contains the Java runtime and a preconfigured Open Liberty server.

Run the mvn package command from the start directory so that the .war file resides in the target directory.

mvn package

Run the following command to download or update to the latest Open Liberty Docker image:

docker pull openliberty/open-liberty:full-java11-openj9-ubi

To build and containerize the application, run the following Docker build command in the start directory:

docker build -t openliberty-getting-started:1.0-SNAPSHOT .

The Docker openliberty-getting-started:1.0-SNAPSHOT image is also built from the Dockerfile. To verify that the image is built, run the docker images command to list all local Docker images:

docker images

Your image should appear in the list of all Docker images:

REPOSITORY                     TAG             IMAGE ID        CREATED         SIZE
openliberty-getting-started    1.0-SNAPSHOT    85085141269b    21 hours ago    487MB

Next, run the image as a container:

docker run -d --name gettingstarted-app -p 9080:9080 openliberty-getting-started:1.0-SNAPSHOT

There is a bit going on here, so here’s a breakdown of the command:

Flag Description

-d

Runs the container in the background.

--name

Specifies a name for the container.

-p

Maps the container ports to the host ports.

The final argument in the docker run command is the Docker image name.

Next, run the docker ps command to verify that your container started:

docker ps

Make sure that your container is running and does not have Exited as its status:

CONTAINER ID    IMAGE                         CREATED          STATUS           NAMES
4294a6bdf41b    openliberty-getting-started   9 seconds ago    Up 11 seconds    gettingstarted-app

To access the application, go to the http://localhost:9080/system/properties URL.

To stop and remove the container, run the following commands:

docker stop gettingstarted-app && docker rm gettingstarted-app

To remove the image, run the following command:

docker rmi openliberty-getting-started:1.0-SNAPSHOT

Developing the application in a Docker container

The Open Liberty Maven plug-in includes a devc goal that simplifies developing your application in a Docker 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.

Build and run the container by running the devc goal from the start directory:

mvn liberty:devc

When you see the following message, Open Liberty is ready to run in dev mode:

**************************************************************
*    Liberty is running in dev mode.

Open another command-line session and run the docker ps command to verify that your container started:

docker ps

Your container should be running and have Up as its status:

CONTAINER ID        IMAGE                                 COMMAND                  CREATED             STATUS                         PORTS                                                                    NAMES
17af26af0539        guide-getting-started-dev-mode        "/opt/ol/helpers/run…"   3 minutes ago       Up 3 minutes                   0.0.0.0:7777->7777/tcp, 0.0.0.0:9080->9080/tcp, 0.0.0.0:9443->9443/tcp   liberty-dev

To access the application, go to the http://localhost:9080/system/properties URL.

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

Update the server.xml file to change the context root from / to /dev.

Replace the server configuration file.
src/main/liberty/config/server.xml

server.xml

 1<server description="Sample Liberty server">
 2    <!-- tag::features[] -->
 3    <featureManager>
 4        <feature>jaxrs-2.1</feature>
 5        <feature>jsonp-1.1</feature>
 6        <feature>cdi-2.0</feature>
 7        <feature>mpMetrics-3.0</feature>
 8        <!-- tag::mpHealth[] -->
 9        <feature>mpHealth-3.0</feature>
10        <!-- end::mpHealth[] -->
11        <feature>mpConfig-2.0</feature>
12    </featureManager>
13    <!-- end::features[] -->
14
15    <variable name="default.http.port" defaultValue="9080"/>
16    <variable name="default.https.port" defaultValue="9443"/>
17
18    <!-- tag::webApplication[] -->
19    <webApplication location="guide-getting-started.war" contextRoot="/dev" />
20    <!-- end::webApplication[] -->    
21    <mpMetrics authentication="false"/>
22
23    <!-- tag::logging[] -->
24    <logging traceSpecification="com.ibm.ws.microprofile.health.*=all" />
25    <!-- end::logging[] -->
26
27    <httpEndpoint host="*" httpPort="${default.http.port}" 
28        httpsPort="${default.https.port}" id="defaultHttpEndpoint"/>
29
30    <variable name="io_openliberty_guides_system_inMaintenance" value="false"/>
31</server>

Update the mpData.js file to change the url in the getSystemPropertiesRequest method to reflect the new context root.

Replace the mpData.js file.
src/main/webapp/js/mpData.js

mpData.js

  1// tag::copyright[]
  2/*******************************************************************************
  3* Copyright (c) 2018, 2021 IBM Corporation and others.
  4* All rights reserved. This program and the accompanying materials
  5* are made available under the terms of the Eclipse Public License v1.0
  6* which accompanies this distribution, and is available at
  7* http://www.eclipse.org/legal/epl-v10.html
  8*
  9* Contributors:
 10*     IBM Corporation - initial API and implementation
 11*******************************************************************************/
 12// end::copyright[]
 13function displayMetrics() {
 14    getSystemMetrics();
 15}
 16
 17function getSystemMetrics() {
 18    var url = "http://localhost:9080/metrics";
 19    var req = new XMLHttpRequest();
 20
 21    var metricToDisplay = {};
 22    metricToDisplay["application_getProperties_total"] = "Request Count";
 23    metricToDisplay["application_io_openliberty_sample_system_SystemResource_getPropertiesTime_one_min_rate_per_second"] = "Min Request Time (ms)";
 24    metricToDisplay["application_io_openliberty_sample_system_SystemResource_getPropertiesTime_mean_seconds"] = "Mean Request Time (ms)";
 25    metricToDisplay["application_io_openliberty_sample_system_SystemResource_getPropertiesTime_max_seconds"] = "Max Request Time (ms)";
 26    metricToDisplay["base_cpu_processCpuLoad_percent"] = "System CPU Usage (%)";
 27    metricToDisplay["base_memory_usedHeap_bytes"] = "System Heap Usage (MB)";
 28
 29    var metricToMatch = "^(";
 30    for (var metricKey in metricToDisplay) {
 31        metricToMatch += metricKey + "|"
 32    }
 33    // remove the last |
 34    metricToMatch = metricToMatch.substring(0, metricToMatch.length-1);
 35    metricToMatch += ")\\s*(\\S*)$"
 36
 37    req.onreadystatechange = function() {
 38        if (req.readyState != 4) return; // Not there yet
 39        if (req.status != 200) {
 40            document.getElementById("metricsText").innerHTML = req.statusText;
 41            return;
 42        }
 43
 44        var resp = req.responseText;
 45        var regexpToMatch = new RegExp(metricToMatch, "gm");
 46        var matchMetrics = resp.match(regexpToMatch);
 47
 48        var keyValPairs = {};
 49        for (var metricKey in metricToDisplay) {
 50            matchMetrics.forEach(function(line) {
 51                var keyToMatch = metricKey + " (.*)";
 52                var keyVal = line.match(new RegExp(keyToMatch));
 53                if (keyVal) {
 54                    var val = keyVal[1];
 55                    if (metricKey.indexOf("application_io_openliberty_sample_system_SystemResource_getPropertiesTime") === 0) {
 56                        val = val * 1000;
 57                    } else if (metricKey.indexOf("base_memory_usedHeap_bytes") === 0) {
 58                        val = val / 1000000;
 59                    }
 60                    keyValPairs[metricToDisplay[metricKey]] = val;
 61                }
 62            })
 63        }
 64
 65        var table = document.getElementById("metricsTableBody");
 66        for (key in keyValPairs) {
 67            var row = document.createElement("tr");
 68            var keyData = document.createElement("td");
 69            keyData.innerText = key;
 70            var valueData = document.createElement("td");
 71            valueData.innerText = keyValPairs[key];
 72            row.appendChild(keyData);
 73            row.appendChild(valueData);
 74            table.appendChild(row);
 75        }
 76
 77        addSourceRow(table, url);
 78    };
 79
 80    req.open("GET", url, true);
 81    req.send();
 82}
 83
 84function displaySystemProperties() {
 85    getSystemPropertiesRequest();
 86}
 87
 88// tag::getSystemPropertiesRequest[]
 89function getSystemPropertiesRequest() {
 90// end::getSystemPropertiesRequest[]
 91    var propToDisplay = ["java.vendor", "java.version", "user.name",
 92                         "os.name", "wlp.install.dir", "wlp.server.name" ];
 93    // tag::jsUrl[]
 94    var url = "http://localhost:9080/dev/system/properties";
 95    // end::jsUrl[]
 96    var req = new XMLHttpRequest();
 97    var table = document.getElementById("systemPropertiesTable");
 98    // Create the callback:
 99    req.onreadystatechange = function () {
100        if (req.readyState != 4) return; // Not there yet
101        displayMetrics();
102        if (req.status != 200) {
103            table.innerHTML = "";
104            var row = document.createElement("tr");
105            var th = document.createElement("th");
106            th.innerText = req.statusText;
107            row.appendChild(th);
108            table.appendChild(row);
109
110            addSourceRow(table, url);
111            return;
112        }
113        // Request successful, read the response
114        var resp = JSON.parse(req.responseText);
115        for (var i = 0; i < propToDisplay.length; i++) {
116            var key = propToDisplay[i];
117            if (resp.hasOwnProperty(key)) {
118                var row = document.createElement("tr");
119                var keyData = document.createElement("td");
120                keyData.innerText = key;
121                var valueData = document.createElement("td");
122                valueData.innerText = resp[key];
123                row.appendChild(keyData);
124                row.appendChild(valueData);
125                table.appendChild(row);
126            }
127        }
128
129        addSourceRow(table, url);
130    };
131    req.open("GET", url, true);
132    req.send();
133}
134
135function displayHealth() {
136    getHealth();
137}
138
139function getHealth() {
140    var url = "http://localhost:9080/health";
141    var req = new XMLHttpRequest();
142
143    var healthBox = document.getElementById("healthBox");
144    var serviceName = document.getElementById("serviceName");
145    var healthStatus = document.getElementById("serviceStatus");
146    var healthIcon = document.getElementById("healthStatusIconImage");
147
148    req.onreadystatechange = function () {
149        if (req.readyState != 4) return; // Not there yet
150
151        // Request successful, read the response
152        if (req.responseText) {
153            var resp = JSON.parse(req.responseText);
154            var service = resp.checks[0]; //TODO: use for loop for multiple services
155
156            resp.checks.forEach(function (service) {
157                serviceName.innerText = service.name;
158                healthStatus.innerText = service.status;
159
160                if (service.status === "UP") {
161                    healthBox.style.backgroundColor = "#f0f7e1";
162                    healthIcon.setAttribute("src", "img/systemUp.svg");
163                } else {
164                    healthBox.style.backgroundColor = "#fef7f2";
165                    healthIcon.setAttribute("src", "img/systemDown.svg");
166                }
167            });
168        }
169        var table = document.getElementById("healthTable");
170
171        addSourceRow(table, url);
172    };
173    req.open("GET", url, true);
174    req.send();
175}
176
177function displayConfigProperties() {
178    getConfigPropertiesRequest();
179}
180
181function getConfigPropertiesRequest() {
182    var url = "http://localhost:9080/config";
183    var req = new XMLHttpRequest();
184
185    var configToDisplay = {};
186    configToDisplay["io_openliberty_sample_system_inMaintenance"] = "System In Maintenance";
187    configToDisplay["io_openliberty_sample_testConfigOverwrite"] = "Test Config Overwrite";
188    configToDisplay["io_openliberty_sample_port_number"] = "Port Number";
189    // Create the callback:
190    req.onreadystatechange = function () {
191        if (req.readyState != 4) return; // Not there yet
192        if (req.status != 200) {
193            return;
194        }
195
196        // Request successful, read the response
197        var resp = JSON.parse(req.responseText);
198        var configProps = resp["ConfigProperties"];
199        var table = document.getElementById("configTableBody");
200        for (key in configProps) {
201            var row = document.createElement("tr");
202            var keyData = document.createElement("td");
203            keyData.innerText = configToDisplay[key];
204            var valueData = document.createElement("td");
205            valueData.innerText = configProps[key];
206            row.appendChild(keyData);
207            row.appendChild(valueData);
208            table.appendChild(row);
209        }
210
211        addSourceRow(table, url);
212    }
213    req.open("GET", url, true);
214    req.send();
215}
216
217function toggle(e) {
218    var callerElement;
219    if (!e) {
220        if (window.event) {
221            e = window.event;
222            callerElement = e.currentTarget;
223        } else {
224            callerElement = window.toggle.caller.arguments[0].currentTarget; // for firefox
225        }
226    }
227
228    var classes = callerElement.parentElement.classList;
229    var collapsed = classes.contains("collapsed");
230    var caretImg = callerElement.getElementsByClassName("caret")[0];
231    var caretImgSrc = caretImg.getAttribute("src");
232    if (collapsed) { // expand the section
233        classes.replace("collapsed", "expanded");
234        caretImg.setAttribute("src", caretImgSrc.replace("down", "up"));
235    } else { // collapse the section
236        classes.replace("expanded", "collapsed");
237        caretImg.setAttribute("src", caretImgSrc.replace("up", "down"));
238    }
239}
240
241function addSourceRow(table, url) {
242    var sourceRow = document.createElement("tr");
243    sourceRow.classList.add("sourceRow");
244    var sourceText = document.createElement("td");
245    sourceText.setAttribute("colspan", "100%");
246    sourceText.innerHTML = "API Source\: <a href='"+url+"'>"+url+"</a>";
247    sourceRow.appendChild(sourceText);
248    table.appendChild(sourceRow);
249}

After you make the file changes, Open Liberty automatically reloads its configuration. You can access the application at the http://localhost:9080/dev/system/properties URL. Notice that context root is now /dev.

When you are finished, exit dev mode by pressing CTRL+C in the command-line session that the container was started from, or by typing q and then pressing the enter/return key. Either of these options stops and removes the container. To check that the container was stopped, run the docker ps command.

Running the application from a minimal runnable JAR

So far, Open Liberty was running out of the target/liberty/wlp directory, which effectively contains an Open Liberty server installation and the deployed application. The final product of the Maven build is a server package for use in a continuous integration pipeline and, ultimately, a production deployment.

Open Liberty supports a number of different server packages. The sample application currently generates a usr package that contains the servers and application to be extracted onto an Open Liberty installation.

Instead of creating a server package, you can generate a runnable JAR file that contains the application along with a server runtime. This JAR file can then be run anywhere and deploy your application and server at the same time. To generate a runnable JAR file, override the include property:

mvn liberty:package -Dinclude=runnable

The packaging type is overridden from the usr package to the runnable package. This property then propagates to the liberty-maven-plugin plug-in, which generates the server package based on the openliberty-kernel package.

When the build completes, you can find the minimal runnable guide-getting-started.jar file in the target directory. This JAR file contains only the features that you explicitly enabled in your server.xml file. As a result, the generated JAR file is only about 50 MB.

To run the JAR file, first stop the server if it’s running. Then, navigate to the target directory and run the java -jar command:

java -jar guide-getting-started.jar

When the server starts, go to the http://localhost:9080/dev/system/properties URL to access your application that is now running out of the minimal runnable JAR file.

You can stop the server by pressing CTRL+C in the command-line session that the server runs in.

pom.xml

  1<?xml version='1.0' encoding='utf-8'?>
  2<project xmlns="http://maven.apache.org/POM/4.0.0"
  3         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5    <modelVersion>4.0.0</modelVersion>
  6
  7    <groupId>io.openliberty.guides</groupId>
  8    <artifactId>guide-getting-started</artifactId>
  9    <version>1.0-SNAPSHOT</version>
 10    <packaging>war</packaging>
 11
 12    <properties>
 13        <maven.compiler.source>1.8</maven.compiler.source>
 14        <maven.compiler.target>1.8</maven.compiler.target>
 15        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 16        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 17        <!-- Liberty configuration -->
 18        <liberty.var.default.http.port>9080</liberty.var.default.http.port>
 19        <liberty.var.default.https.port>9443</liberty.var.default.https.port>
 20    </properties>
 21
 22    <dependencies>
 23        <!-- Provided dependencies -->
 24        <dependency>
 25            <groupId>jakarta.platform</groupId>
 26            <artifactId>jakarta.jakartaee-api</artifactId>
 27            <version>8.0.0</version>
 28            <scope>provided</scope>
 29        </dependency>
 30        <dependency>
 31            <groupId>org.eclipse.microprofile</groupId>
 32            <artifactId>microprofile</artifactId>
 33            <version>4.0.1</version>
 34            <type>pom</type>
 35            <scope>provided</scope>
 36        </dependency>
 37        <!-- For tests -->
 38        <dependency>
 39            <groupId>org.junit.jupiter</groupId>
 40            <artifactId>junit-jupiter</artifactId>
 41            <version>5.7.1</version>
 42            <scope>test</scope>
 43        </dependency>
 44        <dependency>
 45            <groupId>org.apache.cxf</groupId>
 46            <artifactId>cxf-rt-rs-client</artifactId>
 47            <version>3.4.3</version>
 48            <scope>test</scope>
 49        </dependency>
 50        <dependency>
 51            <groupId>org.apache.cxf</groupId>
 52            <artifactId>cxf-rt-rs-extension-providers</artifactId>
 53            <version>3.4.3</version>
 54            <scope>test</scope>
 55        </dependency>
 56        <dependency>
 57            <groupId>org.glassfish</groupId>
 58            <artifactId>javax.json</artifactId>
 59            <version>1.1.4</version>
 60            <scope>test</scope>
 61        </dependency>
 62    </dependencies>
 63
 64    <build>
 65        <finalName>${project.artifactId}</finalName>
 66        <plugins>
 67            <!-- Enable liberty-maven plugin -->
 68            <!-- tag::libertyMavenPlugin[] -->
 69            <plugin>
 70                <groupId>io.openliberty.tools</groupId>
 71                <artifactId>liberty-maven-plugin</artifactId>
 72                <version>3.3.4</version>
 73            </plugin>
 74            <!-- end::libertyMavenPlugin[] -->
 75            <plugin>
 76                <groupId>org.apache.maven.plugins</groupId>
 77                <artifactId>maven-war-plugin</artifactId>
 78                <version>3.3.1</version>
 79            </plugin>
 80            <plugin>
 81                <groupId>org.apache.maven.plugins</groupId>
 82                <artifactId>maven-surefire-plugin</artifactId>
 83                <version>2.22.2</version>
 84            </plugin>
 85            <!-- Plugin to run functional tests -->
 86            <plugin>
 87                <groupId>org.apache.maven.plugins</groupId>
 88                <artifactId>maven-failsafe-plugin</artifactId>
 89                <version>2.22.2</version>
 90                <configuration>
 91                    <systemPropertyVariables>
 92                        <http.port>${liberty.var.default.http.port}</http.port>
 93                        <context.root>/dev</context.root>
 94                    </systemPropertyVariables>
 95                </configuration>
 96            </plugin>
 97        </plugins>
 98    </build>
 99</project>

server.xml

 1<server description="Sample Liberty server">
 2    <!-- tag::features[] -->
 3    <featureManager>
 4        <feature>jaxrs-2.1</feature>
 5        <feature>jsonp-1.1</feature>
 6        <feature>cdi-2.0</feature>
 7        <feature>mpMetrics-3.0</feature>
 8        <!-- tag::mpHealth[] -->
 9        <feature>mpHealth-3.0</feature>
10        <!-- end::mpHealth[] -->
11        <feature>mpConfig-2.0</feature>
12    </featureManager>
13    <!-- end::features[] -->
14
15    <variable name="default.http.port" defaultValue="9080"/>
16    <variable name="default.https.port" defaultValue="9443"/>
17
18    <!-- tag::webApplication[] -->
19    <webApplication location="guide-getting-started.war" contextRoot="/dev" />
20    <!-- end::webApplication[] -->    
21    <mpMetrics authentication="false"/>
22
23    <!-- tag::logging[] -->
24    <logging traceSpecification="com.ibm.ws.microprofile.health.*=all" />
25    <!-- end::logging[] -->
26
27    <httpEndpoint host="*" httpPort="${default.http.port}" 
28        httpsPort="${default.https.port}" id="defaultHttpEndpoint"/>
29
30    <variable name="io_openliberty_guides_system_inMaintenance" value="false"/>
31</server>

Great work! You’re done!

You’ve learned the basics of deploying and updating an application on an Open Liberty server.

Guide Attribution

Getting started with Open Liberty by Open Liberty is licensed under CC BY-ND 4.0

Copied to clipboard
Copy code block
Copy file contents

Prerequisites:

Nice work! Where to next?

What did you think of this guide?

Extreme Dislike Dislike Like Extreme Like

What could make this guide better?

Raise an issue to share feedback

Create a pull request to contribute to this guide

Need help?

Ask a question on Stack Overflow

Like Open Liberty? Star our repo on GitHub.

Star