Kubernetes with OpenShift World Tour: Get hands-on experience and build applications fast! Find a workshop!

Composing microservices with the Appsody Quarkus experimental collection

Java microservices with atomic speed and no YAML files make for a winning combination. Using Quarkus along with Appsody, a new open source project, enables you to benefit from the speed of Java microservices without the pain of YAML files.

This tutorial assumes you have a general understanding of Appsody. Appsody makes it natural to develop applications that are ready for the cloud, abstracting away tiresome interactions with Docker builds and Kubernetes deployment files. Even though Appsody has had support for the Java language from the start, there was no masking the inherent heft of Java applications in a cloud world. Until now.

Enter Quarkus, a milestone evolution for the Java platform, engineered for the cloud with near-instantaneous bootstrap times and significantly reduced disk and memory footprints.

In this tutorial, I cover the recently released Appsody experimental collection for Quarkus and show you how to quickly develop a Quarkus-based application that can interact with a real database.

Prerequisites

Complete the following steps to build and test applications on your local workstation:

Estimated time

With the prerequisites in place, you should be able to complete this tutorial in less than 30 minutes.

Steps

Following along to this tutorial, you will perform the following steps:

  1. Create the application
  2. Run the application
  3. Run the application in test mode
  4. Add a Quarkus extension
  5. Connect the application to a remote service

At the end of the tutorial, you should have the following system running in your computer:

Application architecture

1. Create the application

Begin by using the Appsody command-line interface (CLI) to create the application.

Open a terminal window and run the following commands to create the application directory and prime it with the template of a Quarkus application:

mkdir quarkus-microservice
cd quarkus-microservice
appsody init experimental/quarkus

This step creates all the source files and a build pom.xml file inside the directory.

All projects are created with the same Maven group and artifact identifiers, which may cause clashes in the Maven repository. To avoid these potential conflicts, you must modify the <artifactId>getting-started</artifactId> line in the pom.xml file to:

<artifactId>quarkus-microservice</artifactId>

Note that the pom.xml file references a parent project that is not visible to you:

  <parent>
    <groupId>dev.appsody</groupId>
    <artifactId>quarkus</artifactId>
    <version>0.1.5</version>
  </parent>

You will, however, find that dependency inside the default Maven repository in your workstation, using the appropriate command for your platform, as listed below.

  • On macOS or Linux, enter the following command:
find ~/.m2/repository/dev/appsody/quarkus
  • On Windows, enter the following command:
dir /s /b /og %USERPROFILE%\.m2\repository\dev\appsody\quarkus

Unless you’re hitting the issue listed in the sub-section Special note about Appsody Issue #448, you should see output similar to the one below, with possibly different versions of Quarkus due to new releases of Appsody stacks:

.../repository/dev/appsody/quarkus/0.1.5/quarkus-0.1.5.pom
.../repository/dev/appsody/quarkus/0.1.5/_remote.repositories
.../repository/dev/appsody/quarkus/maven-metadata-local.xml

Special note about Appsody Issue #448

If you are not seeing the .m2 directory in the user home directory or the maven-metadata-local.xml file in the output above, that means you are hitting Appsody Issue #448, so you need to execute the following workaround steps before proceeding with the tutorial.

You must execute the workaround steps from the same directory where the application was created:

On macOS or Linux, run the following commands:

rm -rf ~/.m2/repository/dev/appsody/quarkus
appsody extract --target-dir tmp
./tmp/user-app/mvnw install -Denforcer.skip=true -f tmp/pom.xml
rm -rf tmp

On Windows, run the following commands:

rmdir /q /s %USERPROFILE%\.m2\repository\dev\appsody\quarkus
appsody extract --target-dir tmp
.\tmp\user-app\mvnw install -Denforcer.skip=true -f tmp\pom.xml
rmdir /q /s tmp

2. Run the application

With the application created, it’s time to run it for the first time. Note that the Quarkus collection is based on Maven builds, so that the very first run may trigger a large download of Maven dependencies.

Still in the same terminal and directory where you created the application, execute the following command: appsody run.

After Appsody finishes downloading the application dependencies, it will immediately start the application and indicate that it is listening on port 8080:

[Container] Listening for transport dt_socket at address: 5005
[Container] 2019-10-31 20:43:44,780 INFO  [io.qua.dep.QuarkusAugmentor] (main) Beginning quarkus augmentation
[Container] 2019-10-31 20:43:47,119 INFO  [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed in 2339ms
[Container] 2019-10-31 20:43:48,530 INFO  [io.quarkus] (main) Quarkus 0.18.0 started in 4.694s. Listening on: http://0.0.0.0:8080
[Container] 2019-10-31 20:43:48,536 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]

You can open the application URL from a web-browser, which contains a greeting web page indicating your application is running correctly.

You will also note that the template application is essentially the same as the sample application in the Quarkus “Getting Started” guide.

You can launch example RESTful URLs as well, both for the basic “hello service” at http://localhost:8080/hello endpoint and for the injected service endpoint at http://localhost:8080/hello/greeting/some-text-param.

After you finish inspecting the responses from the various endpoints, let’s make modifications to the response messages in the source code and then test the endpoint results again.

Notice how the code changes get reflected in the application output nearly instantly.

First, replace the line return "hello"; in src/main/java/org/acme/quickstart/GreetingResource.java with:

return "modified hello";

Now check the modified response at the http://localhost:8080/hello endpoint.

3. Run the application in test mode

Appsody can run an application in test mode, where it also runs all test cases bundled with the application.

For the Quarkus stack, these tests are written using the JUnit 5 framework and are located under ./src/test/java:

./src/test/java/org/acme/quickstart/NativeGreetingResourceIT.java
./src/test/java/org/acme/quickstart/GreetingResourceTest.java

Now stop the application with the combination of "Ctrl+C" keys and then run: appsody stop.

Restart the application in test mode, with: appsody test.

Notice that after Appsody completes the download of dependencies for running in test mode, the output shows a test case failure due to the code change made in the previous steps:

[Container] [ERROR]   GreetingResourceTest.testHelloEndpoint:20 1 expectation failed.
[Container] Response body doesn't match expectation.
[Container] Expected: is "hello"
[Container]   Actual: modified hello
[Container]
[Container] [INFO] 
[Container] [ERROR] Tests run: 2, Failures: 1, Errors: 0, Skipped: 0
[Container] [INFO] 
[Container] [INFO] ------------------------------------------------------------------------
[Container] [INFO] BUILD FAILURE
[Container] [INFO] ------------------------------------------------------------------------

To fix the test case, replace the line: .body(is("hello")); in ./src/test/java/org/acme/quickstart/GreetingResourceTest.java with:

.body(is("modified hello"));

In a couple of seconds, you should see Appsody detecting the change and running the test cases again, this time showing successful results:

[Container] [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 9.094 s - in org.acme.quickstart.GreetingResourceTest
[Container] 2019-11-01 00:52:53,692 INFO  [io.quarkus] (main) Quarkus stopped in 0.015s
[Container] [INFO] 
[Container] [INFO] Results:
[Container] [INFO] 
[Container] [INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[Container] [INFO] 
[Container] [INFO] ------------------------------------------------------------------------
[Container] [INFO] BUILD SUCCESS
[Container] [INFO] ------------------------------------------------------------------------

Once you are done with all the steps, terminate the run with the combination of "Ctrl+C" keys, and then run: appsody stop.

4. Add a Quarkus extension to the application

Quarkus makes it easier for developers to include new capabilities to an application, using Quarkus Extensions.

Using the extension mechanism creates the required modifications to the pom.xml file instead of forcing developers to mine the correct dependencies from Maven repositories.

For this part of the tutorial, I show you how to add the PostgreSQL extension as an example of how to extend the Quarkus project created by Appsody, then make modifications to the application to connect to a running instance of PostgreSQL.

The Quarkus extensions work almost identically with Appsody, but there is one key difference: You must use the mvnw Maven wrapper created by Appsody in the application directory instead of using the Maven mvn command-line utility directly.

Before adding the extension, run the following command from the application directory to see all available extensions:

  • On macOS or Linux, run:
./mvnw quarkus:list-extensions
  • On Windows, run:
.\mvnw quarkus:list-extensions

The list of extensions should contain a line with the identifier for the PostgreSQL extension:

JDBC Driver - PostgreSQL                           quarkus-jdbc-postgresql

With the name of the extension in hand, you can now add the extension to the application:

  • On macOS or Linux, use the following command:
./mvnw quarkus:add-extension -Dextensions="quarkus-jdbc-postgresql"
  • On Windows, use the following command:
.\mvnw quarkus:add-extension -Dextensions="quarkus-jdbc-postgresql"

At this point, you should be able to see the corresponding modification made to the pom.xml file:

    <dependency>
      <groupId>io.quarkus</groupId>
      <artifactId>quarkus-jdbc-postgresql</artifactId>
      <version>${quarkus.version}</version>
    </dependency>

With the application pom.xml file augmented with the dependency for PostgreSQL connections, we can create the REST endpoint that connects to the database.

Copy the DatabaseResource.java file in this tutorial resources to the src/main/java/org/acme/quickstart directory of the application.

Inspect the content of the new file and notice that it introduces a ‘/database’ endpoint that establishes a simple connection to a PostgreSQL database and returns the connection metadata:

@Path("/database")
public class DatabaseResource {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String databaseMetadata() {

Upon further inspection, you will see that the new endpoint can read the connection parameters from environment variables:

        String databaseHostname = getEnv("WKS_PSQL_HOST", "workshop-postgres");
        String databasePort = getEnv("WKS_PSQL_PORT", "5432");
        String jdbcUrl = "jdbc:postgresql://" + databaseHostname + ":" + databasePort + "/";
        ...
        String databaseUser = getEnv("WKS_PSQL_USER", "postgres");
        String databasePass = getEnv("WKS_PSQL_PWD", "mysecretpassword");

For simplicity, the code uses default values matching the examples in this tutorial, but you could provide the environment variables to the application by creating a file with those properties and then passing them to appsody run with the --docker-options="--env-file=postgresql.properties" option.

An example of this file is available in the resources for this tutorial: postgres.properties

In a Kubernetes cluster, environment variables can be mapped from Kubernetes ConfigMap objects or by using Secrets as Environment Variables, so that the same source in this tutorial can run inside a cluster without modifications.

5. Connect the application to a remote service

As a final step before running the application, let’s instantiate a local PostgreSQL database. To do so, you use a custom Docker network for both the PostgreSQL database container and the application container, which makes it possible for the application to locate the database by container name instead of IP address.

docker network create workshop_nw

Now you can launch the PostgreSQL database connecting to that network. Name the container as workshop-postgres:

docker run --rm -it --name workshop-postgres --hostname psqldb --network workshop_nw -e POSTGRES_PASSWORD=mysecretpassword -d postgres

Ensure the database container is running, by running the following command:

docker ps

b66c53a3be0f        postgres                                                  "docker-entrypoint.s…"   22 seconds ago      Up 21 seconds       5432/tcp                    workshop-postgres

Now run the application, this time instructing Appsody to connect the resulting container to the newly created network:

appsody run --network workshop_nw

Accessing the endpoint at http://localhost:8080/database should return results similar to this:

client.info.ApplicationName=PostgreSQL JDBC Driver; 
db.product.name=PostgreSQL; 
db.product.version=12.0 (Debian 12.0-2.pgdg100+1); 
db.major.version=12; db.minor.version=0; 
db.driver.version=42.2.5; 
db.jdbc.major.version=4; 
db.jdbc.minor.version=2

Unwinding the development environment

Once you are done with all the tests, stop the containers created in this tutorial and dispose of the custom network.

End the application with the Ctrl+C combination of keys and execute the following commands:

appsody stop
docker stop workshop-postgres
docker network rm workshop_nw

Next steps

After completing this tutorial, you should feel comfortable using Appsody to develop new applications and taken your first steps to create a real Java application using the Quarkus framework. Here are a few next steps you can do with your new knowledge:

  • Extend your application with Codewind: To understand the larger developer experience for Appsody, read this tutorial which covers Codewind extensions to popular IDEs. You can use Codewind to import the application you created in this tutorial into your IDE and iterate more fluidly through code changes.
  • Deploy your application to Kubernetes: To see how Appsody handles Kubernetes deployments, check out this tutorial and jump straight to the appsody deploy references, from where you could quickly adapt the instructions to deploy the Quarkus sample here into a Kubernetes cluster.
  • Create new Appsody stacks or templates: If you are ready to create a stack of your own or maybe add a new application template to the Quarkus collection in Appsody, read the article about Customizing Appsody which explains the concept of Appsody collections and then dive into this tutorial which shows you how to create your own Appsody stack.

As a final suggestion, check out the Kabanero project, which brings together Appsody, Codewind, and other open source projects, to deliver an integrated DevOps experience from application developers to application deployment and operations.

Acknowledgments

Special thanks to Robert Barron and Tim Robinson for the review of instructions and the thoughtful suggestions that contributed to the final draft of this tutorial.

Denilson Nastacio