Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

OpenLiberty.io: Java EE Microservices Done Right

DZone's Guide to

OpenLiberty.io: Java EE Microservices Done Right

This deep dive into OpenLiberty examines its capabilities and speed when developing enterprise-grade microservices, as well as how it compares to competitors.

· Java Zone
Free Resource

Build vs Buy a Data Quality Solution: Which is Best for You? Gain insights on a hybrid approach. Download white paper now!

The sample application built as a part of this article can be found on GitHub.

It's the year 2017 and we’re all building microservices, where shared a runtime environment represented by a single application server hosting many applications is passé. As a result, rightsizing application environments makes sense! The functionality is still required, but not all at once by a single application, if ever.

Therefore, the packaging and distribution changed, as mentioned in one of my previous posts. However, the obvious and most simple way to just pack everything into one Fatjar comes with long redeployment times and developers stuck waiting for something to reload, when an application server provides the environment already running and redeploying the business logic takes just a few milliseconds. Java rockstar Adam Bien has been pointing this fact out for a long time, and if you’d like to know more, watch his video on Thin WARs, Java EE 7, Docker, and Productivity. Java EE has offered many solutions so far.

And now, one more player appears. It’s called OpenLiberty, by IBM. OpenLiberty.io is completely open source and, in their own words, is used to build cloud-native apps and microservices while running only what you need. At the moment, it provides a full Java EE 7 stack complemented with the mighty MicroProfile. Java EE 8 support is being added constantly. The new JAX-RS and Servlet 4.0 are already supported.

OpenLiberty.io addresses a common fear of many developers — long reload times. It is fast to start and provides dynamic code update capabilities.

Building the Sample Application

I believe I’ve written enough theoretical articles on packaging, fast redeployments, and good engineering practices in Java application packaging. Time to offer something practical. Commercial break: If you want to read more on both theory and practice, our new book Java EE 8 MicroServices is available for pre-order. Spring Boot is covered in the book.

It is absolutely easy to create a Java EE microservice running on OpenLiberty. The application described is ready to git clone and run on GitHub.

  1. Create a common Java EE application.
  2. Add OpenLiberty configuration to Maven’s pom.xml
  3. Configure server properties with OpenLiberty’s server.xml

Two additional steps needed to be taken. OpenLiberty, as any other solution on the market, needs to attach to the microservice build process in order to produce the rightsized artifact. In order for OpenLiberty to download all the necessary parts, attach on proper ports and instantiate the right services, a simple configuration has to be added. There is nothing more.

Creating the Maven Project

First, a Java EE Maven project has to be created.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>cool.pavel</groupId>
    <artifactId>openliberty-jaxrs-example</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>


    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <compilerArguments>
                        <endorseddirs>${endorsed.dir}</endorseddirs>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                    <packagingExcludes>pom.xml</packagingExcludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>


There is only one provided dependency referencing Java EE 7 functionality. Nothing more. The resulting application/microservice in the form of a Java Web ARchive (WAR) only contains business logic and is very thin so far. On top of this, OpenLiberty.io functionality is added. There are two things to enable OpenLiberty in a Maven Java EE project.

  1. OpenLiberty parent project
  2. OpenLiberty plugin

Including the OpenLiberty parent project in Maven is as simple as adding five lines into your pom.xml.

    <parent>
        <groupId>net.wasdev.wlp.maven.parent</groupId>
        <artifactId>liberty-maven-app-parent</artifactId>
        <version>2.0</version>
    </parent>


As a last step, OpenLiberty’s Maven plugin must be added. As seen from the following example, there are several values like OpenLiberty runtime version or port configurations. Following good manners, these variables are externalized into Maven’s properties section.

            <plugin>
                <groupId>net.wasdev.wlp.maven.plugins</groupId>
                <artifactId>liberty-maven-plugin</artifactId>
                <version>2.0</version>
                <configuration>
                    <assemblyArtifact>
                        <groupId>io.openliberty</groupId>
                        <artifactId>openliberty-runtime</artifactId>
                        <version>17.0.0.3</version>
                        <type>zip</type>
                    </assemblyArtifact>
                    <serverName>${project.artifactId}Server</serverName>
                    <stripVersion>true</stripVersion>
                    <configFile>src/main/liberty/config/server.xml</configFile>
                    <packageFile>${package.file}</packageFile>
                    <include>${packaging.type}</include>
                    <bootstrapProperties>
                        <default.http.port>${testServerHttpPort}</default.http.port>
                        <default.https.port>${testServerHttpsPort}</default.https.port>
                        <app.context.root>${project.artifactId}</app.context.root>
                    </bootstrapProperties>
                </configuration>
                <executions>
                    <execution>
                        <id>package-server</id>
                        <phase>package</phase>
                        <goals>
                            <goal>package-server</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>target/wlp-package</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>


The resulting <properties>...</properties> section with externalized variables ends up as follows. Theruntime version, OpenLiberty server ports, and package path are specified in addition to common Maven properties.

    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <testServerHttpPort>9080</testServerHttpPort>
        <testServerHttpsPort>9443</testServerHttpsPort>
        <package.file>${project.build.directory}/${project.artifactId}.zip</package.file>
        <packaging.type>usr</packaging.type>
        <openliberty.runtime.version>17.0.0.3</openliberty.runtime.version>
    </properties>


Final Pom.xml

The final and complete pom.xml, with OpenLiberty set up and Java EE 7 functionality referenced, is below. When copy/pasting this file, do not be surprised that the artifact groupId is cool.pavel and the artifactId is liberty-maven-app-parent.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>net.wasdev.wlp.maven.parent</groupId>
        <artifactId>liberty-maven-app-parent</artifactId>
        <version>2.0</version>
    </parent>

    <groupId>cool.pavel</groupId>
    <artifactId>openliberty-jaxrs-example</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <testServerHttpPort>9080</testServerHttpPort>
        <testServerHttpsPort>9443</testServerHttpsPort>
        <package.file>${project.build.directory}/${project.artifactId}.zip</package.file>
        <packaging.type>usr</packaging.type>
        <openliberty.runtime.version>17.0.0.3</openliberty.runtime.version>
    </properties>


    <dependencies>
        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <compilerArguments>
                        <endorseddirs>${endorsed.dir}</endorseddirs>
                    </compilerArguments>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                    <packagingExcludes>pom.xml</packagingExcludes>
                </configuration>
            </plugin>
            <plugin>
                <groupId>net.wasdev.wlp.maven.plugins</groupId>
                <artifactId>liberty-maven-plugin</artifactId>
                <version>2.0</version>
                <configuration>
                    <assemblyArtifact>
                        <groupId>io.openliberty</groupId>
                        <artifactId>openliberty-runtime</artifactId>
                        <version>${openliberty.runtime.version}</version>
                        <type>zip</type>
                    </assemblyArtifact>
                    <serverName>${project.artifactId}Server</serverName>
                    <stripVersion>true</stripVersion>
                    <configFile>src/main/liberty/config/server.xml</configFile>
                    <packageFile>${package.file}</packageFile>
                    <include>${packaging.type}</include>
                    <bootstrapProperties>
                        <default.http.port>${testServerHttpPort}</default.http.port>
                        <default.https.port>${testServerHttpsPort}</default.https.port>
                        <app.context.root>${project.artifactId}</app.context.root>
                    </bootstrapProperties>
                </configuration>
                <executions>
                    <execution>
                        <id>package-server</id>
                        <phase>package</phase>
                        <goals>
                            <goal>package-server</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>target/wlp-package</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>


</project>


Sample JAX-RS Endpoint

In order to test OpenLiberty is up&running, a simple RESTful resource using JAX-RS is just right. Create a class Resea

package cool.pavel.openliberty.api;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;

@Path("/ping")
public class PingPong {

    @GET
    public Response ping() {
        return Response.ok("Pong")
                .build();
    }
}


Since JAX-RS is used, the RESTful API path is configured by creating a class extending javax.ws.rs.core.Application.

package cool.pavel.openliberty.api;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("")
public class MicroserviceConfiguration extends Application {

}


The previous two classes are placed in the cool.pavel.openliberty.api package.

main
│   ├── java
│   │   └── cool
│   │       └── pavel
│   │           └── openliberty
│   │               └── api
│   │                   ├── PingPong.java
│   │                   └── MicroserviceConfiguration.java


Configuring OpenLiberty Server

In order to configure the OpenLiberty server, create a server.xml file in {project-root}/src/main/liberty/config.

<server description="Sample Liberty server">

  <featureManager>
      <feature>jaxrs-2.0</feature>
  </featureManager>

  <httpEndpoint httpPort="${default.http.port}" httpsPort="${default.https.port}"
      id="defaultHttpEndpoint" host="*" />

  <webApplication location="openliberty-jaxrs-example-1.0-SNAPSHOT.jar" contextRoot="${app.context.root}"/>
</server>


The <featureManager> part contains a list of features to be included in the created runtime. The basic configuration also contains default host and port bindings and path to applications to be contained in the resulting package. Besides the <webApplication ...> tag, it is also possible to use the <enterpriseApplication ...> tag to deploy Enterprise Archives, if required. If you’re looking for a complete list of features available, there is no official list yet. But a list of features can be found directly in the /dev folder on GitHub.

With the server.xml file, the main folder’s content looks as follows. Nothing more is added to the project in order to run it. In this state, the application/microservice is fully functional.

.
├── main
│   ├── java
│   │   └── cool
│   │       └── pavel
│   │           └── openliberty
│   │               └── api
│   │                   ├── PingPong.java
│   │                   └── MicroserviceConfiguration.java
│   ├── liberty
│   │   └── config
│   │       └── server.xml


Invoking and Fast Redeployment

In order to run the application, execute the mvn package liberty:run-server command. OpenLiberty is attached to Maven’s package goal and automatically downloads the dependencies necessary to package the final application. The liberty:run-server goal starts the OpenLiberty server with the configuration defined in server.xml and deploys all applications defined in the very same server.xml file.

As OpenLiberty starts, important information appears on stdout.

...
[INFO] [AUDIT   ] CWWKT0016I: Web application available (default_host): http://localhost:9080/openliberty-jaxrs-example/
[INFO] [AUDIT   ] CWWKZ0001I: Application openliberty-jaxrs-example started in 0,222 seconds.
...


First, OpenLiberty displays the URL under which the application is reachable. In the case of this sample application, it is http://localhost:9080/openliberty-jaxrs-example/. Secondly, it tells the time required for an application to start. In this case, it's 0.222 seconds. 200 milliseconds for a complete application startup is a great result.

When invoking HTTP GET on http://localhost:9080/openliberty-jaxrs-example/ping, an HTTP 200 OK response arrives with a “Pong” message in the body.

Redeploy

Now comes the fun part. Let’s change the endpoint and see how fast OpenLiberty updates the application with the latest changes. A new method producing a response message is introduced to distinguish OpenLiberty from simple HotSwap solutions. Also, the response changes from HTTP 200 OK to HTTP 202 Accepted. The final shape of the PingPong class can be found in the figure below.

package cool.pavel.openliberty.api;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;

@Path("/ping")
public class PingPong {

    @GET
    public Response ping() {
        return Response.accepted(getMessage())
                .build();
    }

    private String getMessage(){
        return "A request for ping has been accepted.";
    }
}


Now, simply recompile the application. This can be easily done by invoking mvn compile. OpenLiberty takes care of everything else. The result can be seen in OpenLiberty’s console.

[INFO] [WARNING ] CWWKZ0014W: The application openliberty-jaxrs-example-1.0-SNAPSHOT could not be started as it could not be found at location openliberty-jaxrs-example-1.0-SNAPSHOT.jar.
[INFO] [AUDIT   ] CWWKT0016I: Web application available (default_host): http://10.0.0.13:9080/openliberty-jaxrs-example/
[INFO] [AUDIT   ] CWWKZ0001I: Application openliberty-jaxrs-example started in 0,222 seconds.
[INFO] [AUDIT   ] CWWKF0012I: The server installed the following features: [servlet-3.1, json-1.0, jaxrs-2.0, jaxrsClient-2.0].
[INFO] [AUDIT   ] CWWKF0011I: The server openliberty-jaxrs-exampleServer is ready to run a smarter planet.
[INFO] [AUDIT   ] CWWKT0017I: Web application removed (default_host): http://10.0.0.13:9080/openliberty-jaxrs-example/
[INFO] [AUDIT   ] CWWKZ0009I: The application openliberty-jaxrs-example has stopped successfully.
[INFO] [AUDIT   ] CWWKT0016I: Web application available (default_host): http://10.0.0.13:9080/openliberty-jaxrs-example/
[INFO] [AUDIT   ] CWWKZ0003I: The application openliberty-jaxrs-example updated in 0,035 seconds.


It took only 35 milliseconds to redeploy the changes. After invoking HTTP GET on http://localhost:9080/openliberty-jaxrs-example/ping, an HTTP 202 Accepted response arrives with the message “A request for ping has been accepted” in the body. No need to redeploy manually and no need for external functionality or incorporating third-party tools.

The results may vary from machine to machine, however, there is no technology able to reach comparable results on my machine so far. Fast redeployments are varying from 200 ms to one second. Spring Loaded is slightly slower and the technologically is different/limited.

Final Thoughts

OpenLiberty.io is an impressive offer. It has the whole Java EE 7 stack with growing support for Java EE 8 and, most importantly, Eclipse MicroProfile 1.2. Startup and automatic updates are a fresh breeze. Spring Boot developers should especially try it. And no, Spring Loaded does not even remotely match the capabilities and speed of OpenLiberty. OpenLiberty makes the usual advantage of fast redeployments to traditional application servers disappear. WildFly's hollow JARs represent an excellent alternative not only performance-wise, but with OpenLiberty, everything just happens automatically.

Build vs Buy a Data Quality Solution: Which is Best for You? Maintaining high quality data is essential for operational efficiency, meaningful analytics and good long-term customer relationships. But, when dealing with multiple sources of data, data quality becomes complex, so you need to know when you should build a custom data quality tools effort over canned solutions. Download our whitepaper for more insights into a hybrid approach.

Topics:
java ,java ee ,java microservices ,openliberty ,tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}