Quarkus – Supersonic Subatomic Java, trying out some Quarkus code guides (part1)

Marc Lameriks

In my previous article, I already shared with you the Quarkus code guide “Quarkus – Creating Your First Application”, I tried out, including the hot deployment with background compilation.
[ https://technology.amis.nl/2020/08/21/using-vagrant-and-shell-scripts-to-further-automate-setting-up-my-quarkus-demo-environment-from-scratch-and-trying-out-a-quarkus-code-guide/]

In this article, you can read more about other Quarkus code guides I tried out, related to the following topics:

  • Configuring Your Application
  • Compiling the application to a native executable by using a local GraalVM installation or by leveraging a container runtime such as Docker and using a Docker container that embeds GraalVM

In a next article, you can read more about other Quarkus code guides I tried out, related to the following topics:

  • Packaging the native executable in a container
  • The ability to automatically generate Kubernetes resources by Quarkus

Quarkus Guides

On the “QUARKUS – GET STARTED” page, I clicked on the link “Get More Guides”.
[https://quarkus.io/guides/]

On this page, you can find lots of guides in several categories.

I will leave it up to you to follow these guides. I will only share with you some of the steps of some of the guides I followed.

Quarkus guide “Configuring Your Application”

On the “QUARKUS – GUIDES” page, I clicked on the link “Configuring Your Application”. But instead of creating a new Maven project, I applied some of the steps in my existing “getting-started” Maven project, described in my previous article.
[https://technology.amis.nl/2020/08/21/using-vagrant-and-shell-scripts-to-further-automate-setting-up-my-quarkus-demo-environment-from-scratch-and-trying-out-a-quarkus-code-guide/]

Injecting configuration value

Quarkus uses MicroProfile Config to inject the configuration in the application. The injection uses the @ConfigProperty annotation.
[https://quarkus.io/guides/config#injecting-configuration-value]

I wanted to try out configuring my application and therefor I changed the code of the hello method to the following:
[in bold, I highlighted the changes]

package org.acme.getting.started;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.eclipse.microprofile.config.inject.ConfigProperty;
import org.jboss.resteasy.annotations.jaxrs.PathParam;

import java.util.Optional;

@Path("/hello")
public class GreetingResource {

    @Inject
    GreetingService service;

    @ConfigProperty(name = "greeting.message")
    String message;

    @ConfigProperty(name = "greeting.suffix", defaultValue = "!")
    String suffix;

    @ConfigProperty(name = "greeting.name")
    Optional<String> name;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    @Path("/greeting/{name}")
    public String greeting(@PathParam String name) {
        return service.greeting(name);
    }

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return message + " " + name.orElse("world") + suffix;
    }
}

Remark:
The default value is injected if the configuration does not provide a value for greeting.suffix.

By default, Quarkus reads application.properties. Following, the guide, I changed the code of src/main/resources/application.properties to the following:
[in bold, I highlighted the changes]

# Configuration file
# key = value

# Your configuration properties
greeting.message = hello
greeting.name = quarkus

# The path of the banner (path relative to root of classpath)
# which could be provided by user
#
#quarkus.banner.path=default_banner.txt
quarkus.banner.path=my_banner.txt

I used vagrant ssh to connect into the running VM. Next, in order to compile the code , I used the following command on the Linux Command Prompt:

cd /vagrant/applications/getting-started
./mvnw compile quarkus:dev

Then, in the Web Browser on my Windows laptop, I entered the URL: http://localhost:8080/hello

And I got the following result:

So, using the configuration properties worked.

I also updated the functional test to reflect the changes made to the endpoint.

In order to test the Quarkus project, I used the following command on the Linux Command Prompt:

./mvnw test

Configuring Quarkus

Quarkus itself is configured via the same mechanism as your application. Quarkus reserves the quarkus. namespace for its own configuration. For example, to configure the HTTP server port you can set quarkus.http.port in application.properties.

Remark:
As mentioned above, properties prefixed with quarkus. are effectively reserved for configuring Quarkus itself and therefore quarkus. should never be used as prefix for application specific properties.
[https://quarkus.io/guides/config#configuring-quarkus]

List of all configuration properties

All the Quarkus configuration properties are documented and searcheable.
[https://quarkus.io/guides/config#list-of-all-configuration-properties]

Generating configuration for your application

It is also possible to generate an example application.properties with all known configuration properties, to make it easy to see what Quarkus configuration options are available.
[https://quarkus.io/guides/config#generating-configuration-for-your-application]

In order to generate an example application.properties, I used the following command on the Linux Command Prompt:

./mvnw quarkus:generate-config

With the following output:


[INFO] Scanning for projects…
[INFO]
[INFO] ———————-< org.acme:getting-started >———————-
[INFO] Building getting-started 1.0-SNAPSHOT
[INFO] ——————————–[ jar ]———————————
[INFO]
[INFO] — quarkus-maven-plugin:1.7.0.Final:generate-config (default-cli) @ getting-started —
[INFO] [org.jboss.threads] JBoss Threads version 3.1.1.Final
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time:  16.017 s
[INFO] Finished at: 2020-08-23T17:18:30Z
[INFO] ————————————————————————
vagrant@ubuntu-bionic:/vagrant/applications/getting-started$

This will create a src/main/resources/application.properties.example file that contains all the config options exposed via the extensions you currently have installed. These options are commented out, and have their default value when applicable.
[https://quarkus.io/guides/config#generating-configuration-for-your-application]

Following, the guide, in order to change the HTTP port, I changed the code of src/main/resources/application.properties to the following:
[in bold, I highlighted the changes]

# Configuration file
# key = value

# Your configuration properties
greeting.message = hello
greeting.name = quarkus

#
# The HTTP port
#
#quarkus.http.port=8080
quarkus.http.port=8090

# The path of the banner (path relative to root of classpath)
# which could be provided by user
#
#quarkus.banner.path=default_banner.txt
quarkus.banner.path=my_banner.txt

Next, in order to compile the code , I used the following command on the Linux Command Prompt:

./mvnw compile quarkus:dev

With the following output (only showing the part about the port):


2020-08-23 17:30:47,170 INFO  [io.quarkus] (Quarkus Main Thread) getting-started 1.0-SNAPSHOT on JVM (powered by Quarkus 1.7.0.Final) started in 7.610s. Listening on: http://0.0.0.0:8090

Then, in the Web Browser on my Windows laptop, I entered the URL: http://localhost:8080/hello

And I got the following result:

Then, in the Web Browser on my Windows laptop, I entered the URL: http://localhost:8090/hello

And I got the following result:

Remark:
Remember that on my Windows laptop, I could call port 8090 of my guest operating system because via port forwarding this was made possible.

I advise you to have a look at this code guide if you want to learn more with regard to configuration.

In my previous article, I shared with you the Quarkus guide “Quarkus – Creating Your First Application”.
[https://technology.amis.nl/2020/08/21/using-vagrant-and-shell-scripts-to-further-automate-setting-up-my-quarkus-demo-environment-from-scratch-and-trying-out-a-quarkus-code-guide/]

At the end of that guide, it was recommend continuing the journey with the building a native executable guide, where you learn about creating a native executable and packaging it in a container.
[https://quarkus.io/guides/getting-started#whats-next]

So, I continued with that guide.

Quarkus guide “Building a Native Executable”

From the Quarkus guide “Creating Your First Application, part “What’s next?”, I clicked on the link “building a native executable guide”.
[https://quarkus.io/guides/getting-started#whats-next]

Building a native executable requires using a distribution of GraalVM.

Because of the way I setup my demo environment, almost all the prerequisites for this guide were already met, except the “Supporting native compilation in C” part.
[https://quarkus.io/guides/building-native-image#prerequisites]

On Linux, I needed GCC, and the glibc and zlib headers.

I used the following command on the Linux Command Prompt:

cd /home/vagrant
sudo apt-get install -y build-essential libz-dev zlib1g-dev

With the following output:


Reading package lists… Done
Building dependency tree
Reading state information… Done
Note, selecting ‘zlib1g-dev’ instead of ‘libz-dev’
build-essential is already the newest version (12.4ubuntu1).
build-essential set to manually installed.
The following NEW packages will be installed:
  zlib1g-dev
0 upgraded, 1 newly installed, 0 to remove and 1 not upgraded.
Need to get 176 kB of archives.
After this operation, 457 kB of additional disk space will be used.
Get:1 http://archive.ubuntu.com/ubuntu bionic/main amd64 zlib1g-dev amd64 1:1.2.11.dfsg-0ubuntu2 [176 kB]
Fetched 176 kB in 5s (32.1 kB/s)
Selecting previously unselected package zlib1g-dev:amd64.
(Reading database … 66534 files and directories currently installed.)
Preparing to unpack …/zlib1g-dev_1%3a1.2.11.dfsg-0ubuntu2_amd64.deb …
Unpacking zlib1g-dev:amd64 (1:1.2.11.dfsg-0ubuntu2) …
Setting up zlib1g-dev:amd64 (1:1.2.11.dfsg-0ubuntu2) …
Processing triggers for man-db (2.8.3-2ubuntu0.1) …
vagrant@ubuntu-bionic:~$

By the way, I also added this step in the file quarkus.sh (mentioned in my previous article), in the scripts directory on my Windows laptop.
[https://technology.amis.nl/2020/08/21/using-vagrant-and-shell-scripts-to-further-automate-setting-up-my-quarkus-demo-environment-from-scratch-and-trying-out-a-quarkus-code-guide/]

This guide continues with the code in the existing “getting-started” Maven project.

Creating a native executable with GraalVM installed locally

In order to produce a native executable, I used the following command on the Linux Command Prompt:

cd /vagrant/applications/getting-started
./mvnw package -Pnative

With the following output:


vagrant@ubuntu-bionic:/vagrant/applications/getting-started$ ./mvnw package -Pnative
[INFO] Scanning for projects…
[INFO]
[INFO] ———————-< org.acme:getting-started >———————-
[INFO] Building getting-started 1.0-SNAPSHOT
[INFO] ——————————–[ jar ]———————————
[INFO]
[INFO] — quarkus-maven-plugin:1.7.0.Final:prepare (default) @ getting-started —
[INFO]
[INFO] — maven-resources-plugin:2.6:resources (default-resources) @ getting-started —
[INFO] Using ‘UTF-8’ encoding to copy filtered resources.
[INFO] Copying 4 resources
[INFO]
[INFO] — maven-compiler-plugin:3.8.1:compile (default-compile) @ getting-started —
[INFO] Nothing to compile – all classes are up to date
[INFO]
[INFO] — quarkus-maven-plugin:1.7.0.Final:prepare-tests (default) @ getting-started —
[INFO]
[INFO] — maven-resources-plugin:2.6:testResources (default-testResources) @ getting-started —
[INFO] Using ‘UTF-8’ encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /vagrant/applications/getting-started/src/test/resources
[INFO]
[INFO] — maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ getting-started —
[INFO] Nothing to compile – all classes are up to date
[INFO]
[INFO] — maven-surefire-plugin:3.0.0-M5:test (default-test) @ getting-started —
[INFO]
[INFO] ——————————————————-
[INFO]  T E S T S
[INFO] ——————————————————-
[INFO] Running org.acme.getting.started.GreetingResourceTest
2020-08-23 18:46:16,198 INFO  [io.quarkus] (main) Quarkus 1.7.0.Final on JVM started in 12.220s. Listening on: http://0.0.0.0:8081
2020-08-23 18:46:16,375 INFO  [io.quarkus] (main) Profile test activated.
2020-08-23 18:46:16,376 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 23.815 s – in org.acme.getting.started.GreetingResourceTest
2020-08-23 18:46:25,516 INFO  [io.quarkus] (main) Quarkus stopped in 0.112s
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] — maven-jar-plugin:2.4:jar (default-jar) @ getting-started —
[INFO]
[INFO] — quarkus-maven-plugin:1.7.0.Final:build (default) @ getting-started —
[INFO] [org.jboss.threads] JBoss Threads version 3.1.1.Final
[INFO] [io.quarkus.deployment.pkg.steps.JarResultBuildStep] Building native image source jar: /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-native-image-source-jar/getting-started-1.0-SNAPSHOT-runner.jar
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Building native image from /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-native-image-source-jar/getting-started-1.0-SNAPSHOT-runner.jar
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Running Quarkus native-image plugin on GraalVM Version 20.1.0 (Java Version 11.0.7)
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] /opt/graalvm-ce-java11-20.1.0/bin/native-image -J-Dsun.nio.ch.maxUpdateArraySize=100 -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager -J-Dvertx.logger-delegate-factory-class-name=io.quarkus.vertx.core.runtime.VertxLogDelegateFactory -J-Dvertx.disableDnsResolver=true -J-Dio.netty.leakDetection.level=DISABLED -J-Dio.netty.allocator.maxOrder=1 -J-Duser.language=en -J-Dfile.encoding=UTF-8 –initialize-at-build-time= -H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy$BySpaceAndTime -H:+JNI -jar getting-started-1.0-SNAPSHOT-runner.jar -H:FallbackThreshold=0 -H:+ReportExceptionStackTraces -H:-AddAllCharsets -H:EnableURLProtocols=http -H:NativeLinkerOption=-no-pie –no-server -H:-UseServiceLoaderFeature -H:+StackTrace getting-started-1.0-SNAPSHOT-runner
[getting-started-1.0-SNAPSHOT-runner:12893]    classlist:  34,723.00 ms,  0.96 GB
[getting-started-1.0-SNAPSHOT-runner:12893]        (cap):   4,778.24 ms,  0.94 GB
[getting-started-1.0-SNAPSHOT-runner:12893]        setup:  18,105.54 ms,  0.94 GB
18:50:33,508 INFO  [org.jbo.threads] JBoss Threads version 3.1.1.Final
[getting-started-1.0-SNAPSHOT-runner:12893]     (clinit):   3,235.27 ms,  2.69 GB
[getting-started-1.0-SNAPSHOT-runner:12893]   (typeflow): 145,004.01 ms,  2.69 GB
[getting-started-1.0-SNAPSHOT-runner:12893]    (objects): 265,715.47 ms,  2.69 GB
[getting-started-1.0-SNAPSHOT-runner:12893]   (features):   3,582.22 ms,  2.69 GB
[getting-started-1.0-SNAPSHOT-runner:12893]     analysis: 422,646.79 ms,  2.69 GB
[getting-started-1.0-SNAPSHOT-runner:12893]     universe:   9,135.39 ms,  2.69 GB
[getting-started-1.0-SNAPSHOT-runner:12893]      (parse):  62,059.00 ms,  2.75 GB
[getting-started-1.0-SNAPSHOT-runner:12893]     (inline):  28,026.29 ms,  2.53 GB
[getting-started-1.0-SNAPSHOT-runner:12893]    (compile): 271,676.91 ms,  3.28 GB
[getting-started-1.0-SNAPSHOT-runner:12893]      compile: 371,979.59 ms,  3.27 GB
[getting-started-1.0-SNAPSHOT-runner:12893]        image:  20,944.50 ms,  3.27 GB
[getting-started-1.0-SNAPSHOT-runner:12893]        write:   3,051.83 ms,  3.27 GB
[getting-started-1.0-SNAPSHOT-runner:12893]      [total]: 882,942.44 ms,  3.27 GB
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Execute [objcopy, –strip-debug, /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-runner]
[INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 903901ms
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time:  15:54 min
[INFO] Finished at: 2020-08-23T19:01:31Z
[INFO] ————————————————————————
vagrant@ubuntu-bionic:/vagrant/applications/getting-started$

Remark:
It produces 2 jar files in /target:

You can run the application using: java -jar target/getting-started-1.0-SNAPSHOT-runner.jar

In addition to the regular files, the build also produces target/getting-started-1.0-SNAPSHOT-runner. You can run this native executable using: ./target/getting-started-1.0-SNAPSHOT-runner.
[https://quarkus.io/guides/building-native-image#producing-a-native-executable]

The native executable for this application will contain the application code, required libraries, Java APIs, and a reduced version of a VM. The smaller VM base improves the startup time of the application and produces a minimal disk footprint.
[https://quarkus.io/guides/building-native-image#producing-a-native-executable]

For your and my convenience, in the table below I summarized the Quarkus deployment steps (io.quarkus.deployment.pkg.steps) from the output above:

Step Output
io.quarkus.deployment.pkg.steps.JarResultBuildStep Building native image source jar: /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-native-image-source-jar/getting-started-1.0-SNAPSHOT-runner.jar
io.quarkus.deployment.pkg.steps.NativeImageBuildStep Building native image from /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-native-image-source-jar/getting-started-1.0-SNAPSHOT-runner.jar
Running Quarkus native-image plugin on GraalVM Version 20.1.0 (Java Version 11.0.7)
/opt/graalvm-ce-java11-20.1.0/bin/native-image -J-Dsun.nio.ch.maxUpdateArraySize=100 -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager -J-Dvertx.logger-delegate-factory-class-name=io.quarkus.vertx.core.runtime.VertxLogDelegateFactory -J-Dvertx.disableDnsResolver=true -J-Dio.netty.leakDetection.level=DISABLED -J-Dio.netty.allocator.maxOrder=1 -J-Duser.language=en -J-Dfile.encoding=UTF-8 --initialize-at-build-time= -H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy$BySpaceAndTime -H:+JNI -jar getting-started-1.0-SNAPSHOT-runner.jar -H:FallbackThreshold=0 -H:+ReportExceptionStackTraces -H:-AddAllCharsets -H:EnableURLProtocols=http -H:NativeLinkerOption=-no-pie --no-server -H:-UseServiceLoaderFeature -H:+StackTrace getting-started-1.0-SNAPSHOT-runner
Execute [objcopy, --strip-debug, /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-runner]

As you can see in the output above, for creating the native executable, the locally installed GraalVM (/opt/graalvm-ce-java11-20.1.0/bin/native-image) is used.

Remark:

As you may have noticed, the command I used, contains a profile named native.

In the pom.xml you can find that profile:

    <profile>
      <id>native</id>
      <activation>
        <property>
          <name>native</name>
        </property>
      </activation>
      <build>
        <plugins>
          <plugin>
            <artifactId>maven-failsafe-plugin</artifactId>
            <version>${surefire-plugin.version}</version>
            <executions>
              <execution>
                <goals>
                  <goal>integration-test</goal>
                  <goal>verify</goal>
                </goals>
                <configuration>
                  <systemPropertyVariables>
                    <native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
                    <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
                    <maven.home>${maven.home}</maven.home>
                  </systemPropertyVariables>
                </configuration>
              </execution>
            </executions>
          </plugin>
        </plugins>
      </build>
      <properties>
        <quarkus.package.type>native</quarkus.package.type>
      </properties>
    </profile>

Remark:
In the pom.xml you can find the location of the produced native executable:

<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>

As is mentioned in the guide, the use of a profile is recommended because, as you could see in the output shown above, packaging the native executable takes a few minutes. You could just pass -Dquarkus.package.type=native as a property on the command line, however it is better to use a profile as this allows native image tests to also be run.

You can provide custom options for the native-image command using the <quarkus.native.additional-build-args> property. Multiple options may be separated by a comma.

Another possibility is to include the quarkus.native.additional-build-args configuration property in your application.properties.

You can find more information about how to configure the native image building process in the Configuring the Native Executable section of the code guide.
[https://quarkus.io/guides/building-native-image#producing-a-native-executable]

Testing the native executable

Producing a native executable can lead to a few issues, and so it’s also a good idea to run some tests against the application running in the native file.
[https://quarkus.io/guides/building-native-image#testing-the-native-executable]

In order to test the native executable, I used the following command on the Linux Command Prompt:

./mvnw verify -Pnative

With the following output:


[INFO] Scanning for projects…
[INFO]
[INFO] ———————-< org.acme:getting-started >———————-
[INFO] Building getting-started 1.0-SNAPSHOT
[INFO] ——————————–[ jar ]———————————
[INFO]
[INFO] — quarkus-maven-plugin:1.7.0.Final:prepare (default) @ getting-started —
[INFO]
[INFO] — maven-resources-plugin:2.6:resources (default-resources) @ getting-started —
[INFO] Using ‘UTF-8’ encoding to copy filtered resources.
[INFO] Copying 4 resources
[INFO]
[INFO] — maven-compiler-plugin:3.8.1:compile (default-compile) @ getting-started —
[INFO] Nothing to compile – all classes are up to date
[INFO]
[INFO] — quarkus-maven-plugin:1.7.0.Final:prepare-tests (default) @ getting-started —
[INFO]
[INFO] — maven-resources-plugin:2.6:testResources (default-testResources) @ getting-started —
[INFO] Using ‘UTF-8’ encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /vagrant/applications/getting-started/src/test/resources
[INFO]
[INFO] — maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ getting-started —
[INFO] Nothing to compile – all classes are up to date
[INFO]
[INFO] — maven-surefire-plugin:3.0.0-M5:test (default-test) @ getting-started —
[INFO]
[INFO] ——————————————————-
[INFO]  T E S T S
[INFO] ——————————————————-
[INFO] Running org.acme.getting.started.GreetingResourceTest
2020-08-28 18:30:30,484 INFO  [io.quarkus] (main) Quarkus 1.7.0.Final on JVM started in 11.837s. Listening on: http://0.0.0.0:8081
2020-08-28 18:30:30,658 INFO  [io.quarkus] (main) Profile test activated.
2020-08-28 18:30:30,659 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 23.508 s – in org.acme.getting.started.GreetingResourceTest
2020-08-28 18:30:39,982 INFO  [io.quarkus] (main) Quarkus stopped in 0.099s
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] — maven-jar-plugin:2.4:jar (default-jar) @ getting-started —
[INFO]
[INFO] — quarkus-maven-plugin:1.7.0.Final:build (default) @ getting-started —
[INFO] [org.jboss.threads] JBoss Threads version 3.1.1.Final
[INFO] [io.quarkus.deployment.pkg.steps.JarResultBuildStep] Building native image source jar: /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-native-image-source-jar/getting-started-1.0-SNAPSHOT-runner.jar
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Building native image from /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-native-image-source-jar/getting-started-1.0-SNAPSHOT-runner.jar
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Running Quarkus native-image plugin on GraalVM Version 20.1.0 (Java Version 11.0.7)
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] /opt/graalvm-ce-java11-20.1.0/bin/native-image -J-Dsun.nio.ch.maxUpdateArraySize=100 -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager -J-Dvertx.logger-delegate-factory-class-name=io.quarkus.vertx.core.runtime.VertxLogDelegateFactory -J-Dvertx.disableDnsResolver=true -J-Dio.netty.leakDetection.level=DISABLED -J-Dio.netty.allocator.maxOrder=1 -J-Duser.language=en -J-Dfile.encoding=UTF-8 –initialize-at-build-time= -H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy$BySpaceAndTime -H:+JNI -jar getting-started-1.0-SNAPSHOT-runner.jar -H:FallbackThreshold=0 -H:+ReportExceptionStackTraces -H:-AddAllCharsets -H:EnableURLProtocols=http -H:NativeLinkerOption=-no-pie –no-server -H:-UseServiceLoaderFeature -H:+StackTrace getting-started-1.0-SNAPSHOT-runner
[getting-started-1.0-SNAPSHOT-runner:25626]    classlist:  30,254.24 ms,  0.96 GB
[getting-started-1.0-SNAPSHOT-runner:25626]        (cap):   4,655.15 ms,  0.94 GB
[getting-started-1.0-SNAPSHOT-runner:25626]        setup:  17,233.53 ms,  0.94 GB
18:34:42,643 INFO  [org.jbo.threads] JBoss Threads version 3.1.1.Final
[getting-started-1.0-SNAPSHOT-runner:25626]     (clinit):   3,177.69 ms,  2.69 GB
[getting-started-1.0-SNAPSHOT-runner:25626]   (typeflow): 141,500.85 ms,  2.69 GB
[getting-started-1.0-SNAPSHOT-runner:25626]    (objects): 246,163.33 ms,  2.69 GB
[getting-started-1.0-SNAPSHOT-runner:25626]   (features):   3,666.14 ms,  2.69 GB
[getting-started-1.0-SNAPSHOT-runner:25626]     analysis: 399,595.91 ms,  2.69 GB
[getting-started-1.0-SNAPSHOT-runner:25626]     universe:   9,180.69 ms,  2.69 GB
[getting-started-1.0-SNAPSHOT-runner:25626]      (parse):  51,668.67 ms,  2.69 GB
[getting-started-1.0-SNAPSHOT-runner:25626]     (inline):  42,712.69 ms,  2.99 GB
[getting-started-1.0-SNAPSHOT-runner:25626]    (compile): 256,520.90 ms,  2.83 GB
[getting-started-1.0-SNAPSHOT-runner:25626]      compile: 360,053.30 ms,  2.83 GB
[getting-started-1.0-SNAPSHOT-runner:25626]        image:  20,236.10 ms,  2.83 GB
[getting-started-1.0-SNAPSHOT-runner:25626]        write:   3,058.62 ms,  2.83 GB
[getting-started-1.0-SNAPSHOT-runner:25626]      [total]: 841,992.78 ms,  2.83 GB
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Execute [objcopy, –strip-debug, /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-runner]
[INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 863284ms
[INFO]
[INFO] — maven-failsafe-plugin:3.0.0-M5:integration-test (default) @ getting-started —
[INFO]
[INFO] ——————————————————-
[INFO]  T E S T S
[INFO] ——————————————————-
[INFO] Running org.acme.getting.started.NativeGreetingResourceIT
Executing [/vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-runner, -Dquarkus.http.port=8081, -Dquarkus.http.ssl-port=8444, -Dtest.url=http://localhost:8081, -Dquarkus.log.file.path=target/target/quarkus.log]

Powered by Quarkus 1.7.0.Final
2020-08-28 18:45:17,012 INFO  [io.quarkus] (main) getting-started 1.0-SNAPSHOT native (powered by Quarkus 1.7.0.Final) started in 0.205s. Listening on: http://0.0.0.0:8081
2020-08-28 18:45:17,020 INFO  [io.quarkus] (main) Profile prod activated.
2020-08-28 18:45:17,020 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 11.452 s – in org.acme.getting.started.NativeGreetingResourceIT
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] — maven-failsafe-plugin:3.0.0-M5:verify (default) @ getting-started —
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time:  15:34 min
[INFO] Finished at: 2020-08-28T18:45:28Z
[INFO] ————————————————————————
vagrant@ubuntu-bionic:/vagrant/applications/getting-started$

NativeGreetingResourceIT.java

Below, you can see the test file that runs against the native executable. The executable is retrieved using the native.image.path system property configured in the Failsafe Maven Plugin (see pom.xml).

For more information about testing, debugging and configuring the native executable please see the Quarkus documentation and the code guide.
[https://quarkus.io/guides/building-native-image#testing-the-native-executable]

Creating a Linux native executable without GraalVM installed locally

Quite often one only needs to create a native Linux executable for their Quarkus application (for example in order to run in a containerized environment) and would like to avoid the trouble of installing the proper GraalVM version in order to accomplish this task (for example, in CI environments it’s common practice to install as little software as possible).

To this end, Quarkus provides a very convenient way of creating a native Linux executable by leveraging a container runtime such as Docker or podman.
[https://quarkus.io/guides/building-native-image#container-runtime]

So, if you don’t have GraalVM installed locally or your local operating system is not Linux, you can still create a Linux native executable.

In order to try this out, I used the following command on the Linux Command Prompt, being the easiest way of accomplishing this task:

./mvnw package -Pnative -Dquarkus.native.container-build=true

Remark:
The build is done using a container runtime. If the Quarkus config property quarkus.native.container-build is set, Docker will be used by default, unless container-runtime (by using property quarkus.native.container-runtime) is also set. In my demo environment I had Docker installed, so this is fine by me.

For more information, please see the code guide.
[https://quarkus.io/guides/building-native-image#container-runtime]

With the following output:


[INFO] Scanning for projects…
[INFO]
[INFO] ———————-< org.acme:getting-started >———————-
[INFO] Building getting-started 1.0-SNAPSHOT
[INFO] ——————————–[ jar ]———————————
[INFO]
[INFO] — quarkus-maven-plugin:1.7.0.Final:prepare (default) @ getting-started —
[INFO]
[INFO] — maven-resources-plugin:2.6:resources (default-resources) @ getting-started —
[INFO] Using ‘UTF-8’ encoding to copy filtered resources.
[INFO] Copying 4 resources
[INFO]
[INFO] — maven-compiler-plugin:3.8.1:compile (default-compile) @ getting-started —
[INFO] Nothing to compile – all classes are up to date
[INFO]
[INFO] — quarkus-maven-plugin:1.7.0.Final:prepare-tests (default) @ getting-started —
[INFO]
[INFO] — maven-resources-plugin:2.6:testResources (default-testResources) @ getting-started —
[INFO] Using ‘UTF-8’ encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /vagrant/applications/getting-started/src/test/resources
[INFO]
[INFO] — maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ getting-started —
[INFO] Nothing to compile – all classes are up to date
[INFO]
[INFO] — maven-surefire-plugin:3.0.0-M5:test (default-test) @ getting-started —
[INFO]
[INFO] ——————————————————-
[INFO]  T E S T S
[INFO] ——————————————————-
[INFO] Running org.acme.getting.started.GreetingResourceTest
2020-08-29 09:37:00,943 INFO  [io.quarkus] (main) Quarkus 1.7.0.Final on JVM started in 12.027s. Listening on: http://0.0.0.0:8081
2020-08-29 09:37:01,129 INFO  [io.quarkus] (main) Profile test activated.
2020-08-29 09:37:01,131 INFO  [io.quarkus] (main) Installed features: [cdi, resteasy]
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 23.853 s – in org.acme.getting.started.GreetingResourceTest
2020-08-29 09:37:10,804 INFO  [io.quarkus] (main) Quarkus stopped in 0.126s
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO]
[INFO] — maven-jar-plugin:2.4:jar (default-jar) @ getting-started —
[INFO]
[INFO] — quarkus-maven-plugin:1.7.0.Final:build (default) @ getting-started —
[INFO] [org.jboss.threads] JBoss Threads version 3.1.1.Final
[INFO] [io.quarkus.deployment.pkg.steps.JarResultBuildStep] Building native image source jar: /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-native-image-source-jar/getting-started-1.0-SNAPSHOT-runner.jar
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Building native image from /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-native-image-source-jar/getting-started-1.0-SNAPSHOT-runner.jar
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Checking image status quay.io/quarkus/ubi-quarkus-native-image:20.1.0-java11
20.1.0-java11: Pulling from quarkus/ubi-quarkus-native-image
57de4da701b5: Pull complete
Digest: sha256:ebdd479c0cb465ef513a645dd055999aee5a59d6828f9e57495c4d5d3882c56d
Status: Downloaded newer image for quay.io/quarkus/ubi-quarkus-native-image:20.1.0-java11
quay.io/quarkus/ubi-quarkus-native-image:20.1.0-java11
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Running Quarkus native-image plugin on GraalVM Version 20.1.0 (Java Version 11.0.7)
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] docker run -v /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-native-image-source-jar:/project:z –env LANG=C –user 1000:1000 –rm quay.io/quarkus/ubi-quarkus-native-image:20.1.0-java11 -J-Dsun.nio.ch.maxUpdateArraySize=100 -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager -J-Dvertx.logger-delegate-factory-class-name=io.quarkus.vertx.core.runtime.VertxLogDelegateFactory -J-Dvertx.disableDnsResolver=true -J-Dio.netty.leakDetection.level=DISABLED -J-Dio.netty.allocator.maxOrder=1 -J-Duser.language=en -J-Dfile.encoding=UTF-8 –initialize-at-build-time= -H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy$BySpaceAndTime -H:+JNI -jar getting-started-1.0-SNAPSHOT-runner.jar -H:FallbackThreshold=0 -H:+ReportExceptionStackTraces -H:-AddAllCharsets -H:EnableURLProtocols=http –no-server -H:-UseServiceLoaderFeature -H:+StackTrace getting-started-1.0-SNAPSHOT-runner
[getting-started-1.0-SNAPSHOT-runner:19]    classlist:  12,790.08 ms,  0.96 GB
[getting-started-1.0-SNAPSHOT-runner:19]        (cap):   1,831.81 ms,  0.96 GB
[getting-started-1.0-SNAPSHOT-runner:19]        setup:   6,935.07 ms,  0.96 GB
09:49:46,882 INFO  [org.jbo.threads] JBoss Threads version 3.1.1.Final
[getting-started-1.0-SNAPSHOT-runner:19]     (clinit):   1,181.84 ms,  2.68 GB
[getting-started-1.0-SNAPSHOT-runner:19]   (typeflow):  57,891.48 ms,  2.68 GB
[getting-started-1.0-SNAPSHOT-runner:19]    (objects):  90,120.46 ms,  2.68 GB
[getting-started-1.0-SNAPSHOT-runner:19]   (features):   1,482.72 ms,  2.68 GB
[getting-started-1.0-SNAPSHOT-runner:19]     analysis: 152,945.06 ms,  2.68 GB
[getting-started-1.0-SNAPSHOT-runner:19]     universe:   3,639.09 ms,  2.68 GB
[getting-started-1.0-SNAPSHOT-runner:19]      (parse):  24,118.92 ms,  2.79 GB
[getting-started-1.0-SNAPSHOT-runner:19]     (inline):  11,436.18 ms,  2.58 GB
[getting-started-1.0-SNAPSHOT-runner:19]    (compile): 108,086.55 ms,  3.29 GB
[getting-started-1.0-SNAPSHOT-runner:19]      compile: 147,548.59 ms,  3.29 GB
[getting-started-1.0-SNAPSHOT-runner:19]        image:   7,754.27 ms,  3.29 GB
[getting-started-1.0-SNAPSHOT-runner:19]        write:   1,359.51 ms,  3.29 GB
[getting-started-1.0-SNAPSHOT-runner:19]      [total]: 333,980.04 ms,  3.29 GB
[INFO] [io.quarkus.deployment.pkg.steps.NativeImageBuildStep] Execute [objcopy, –strip-debug, /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-runner]
[INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 1010110ms
[INFO] ————————————————————————
[INFO] BUILD SUCCESS
[INFO] ————————————————————————
[INFO] Total time:  17:39 min
[INFO] Finished at: 2020-08-29T09:54:03Z
[INFO] ————————————————————————
vagrant@ubuntu-bionic:/vagrant/applications/getting-started$

For your and my convenience, in the table below I summarized the Quarkus deployment steps (io.quarkus.deployment.pkg.steps) from the output above:
[in bold, I highlighted the changes against the previous summarization]

Step Output
io.quarkus.deployment.pkg.steps.JarResultBuildStep Building native image source jar: /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-native-image-source-jar/getting-started-1.0-SNAPSHOT-runner.jar
io.quarkus.deployment.pkg.steps.NativeImageBuildStep Building native image from /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-native-image-source-jar/getting-started-1.0-SNAPSHOT-runner.jar
Checking image status quay.io/quarkus/ubi-quarkus-native-image:20.1.0-java11 20.1.0-java11: Pulling from quarkus/ubi-quarkus-native-image 57de4da701b5: Pull complete cf0f3ebe9f53: Pull complete 4c48107dcc7b: Pull complete Digest: sha256:ebdd479c0cb465ef513a645dd055999aee5a59d6828f9e57495c4d5d3882c56d Status: Downloaded newer image for quay.io/quarkus/ubi-quarkus-native-image:20.1.0-java11 quay.io/quarkus/ubi-quarkus-native-image:20.1.0-java11
Running Quarkus native-image plugin on GraalVM Version 20.1.0 (Java Version 11.0.7)
docker run -v /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-native-image-source-jar:/project:z --env LANG=C --user 1000:1000 --rm quay.io/quarkus/ubi-quarkus-native-image:20.1.0-java11 -J-Dsun.nio.ch.maxUpdateArraySize=100 -J-Djava.util.logging.manager=org.jboss.logmanager.LogManager -J-Dvertx.logger-delegate-factory-class-name=io.quarkus.vertx.core.runtime.VertxLogDelegateFactory -J-Dvertx.disableDnsResolver=true -J-Dio.netty.leakDetection.level=DISABLED -J-Dio.netty.allocator.maxOrder=1 -J-Duser.language=en -J-Dfile.encoding=UTF-8 --initialize-at-build-time= -H:InitialCollectionPolicy=com.oracle.svm.core.genscavenge.CollectionPolicy$BySpaceAndTime -H:+JNI -jar getting-started-1.0-SNAPSHOT-runner.jar -H:FallbackThreshold=0 -H:+ReportExceptionStackTraces -H:-AddAllCharsets -H:EnableURLProtocols=http --no-server -H:-UseServiceLoaderFeature -H:+StackTrace getting-started-1.0-SNAPSHOT-runner
Execute [objcopy, --strip-debug, /vagrant/applications/getting-started/target/getting-started-1.0-SNAPSHOT-runner]

As you can see in the output above, for creating the native executable, the following docker image is used:

quay.io/quarkus/ubi-quarkus-native-image:20.1.0-java11

If you want to use a different one, you can set Quarkus config property quarkus.native.builder-image.
[https://quarkus.io/guides/building-native-image#configuration-reference]

Remark:
When using the docker run –rm flag, Docker automatically cleans up the container and removes the file system when the container exits.
[https://docs.docker.com/engine/reference/run/]

In order to get a list of all the docker images, I used the following command on the Linux Command Prompt:

docker image ls

With the following output:

Here you can see the quay.io/quarkus/ubi-quarkus-native-image docker image that was used. Although the code guide has some more topics, for now I will stop and continue later on writing about them.

So, I conclude this article. I shared with you the steps I took trying out the Quarkus code guides, related to the following topics:

  • Configuring Your Application
  • Compiling the application to a native executable by using a local GraalVM installation or by leveraging a container runtime such as Docker and using a Docker container that embeds GraalVM

In a next article, you can read more about other Quarkus code guides I tried out, related to the following topics:

  • Packaging the native executable in a container
  • The ability to automatically generate Kubernetes resources by Quarkus

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.