Quarkus: application upgrades with helloworld from JBoss EAP Quickstart

Hello everyone on this blog, and with you the fourth post from the Quarkus series!

The previous post was about how Quarkus combines MicroProfile and Spring. Recall that Quarkus It is positioned as “ultra-fast subatomic Java”, it is also “Kubernetes-oriented Java-stack, sharpened by GraalVM and OpenJDK HotSpot and compiled from the best libraries and standards.” Today we will show how to upgrade existing Java applications using the capabilities of Quarkus, using an example helloworld applications from the Red Hat JBoss Enterprise Application Platform (JBoss EAP) Quickstart repositorywhich uses the CDI and Servlet 3 technologies supported in Quarkus.

It is important to note here that both Quarkus and JBoss EAP focus on using tools that are built to the maximum standards. Don’t have an app running on JBoss EAP? Not a problem, it can be easily migrated from the current application server to JBoss EAP using Red Hat Application Migration Toolkit. After that, the final and working version of the upgraded code will be available in the repository github.com/mrizzi/jboss-eap-quickstarts/tree/quarkusin the module helloworld.

When writing this post were used Quarkus Guides, primarily Creating your first application and building a Native executable.

We get a code

First, create a local repository clone JBoss EAP quickstarts:

$ git clone https://github.com/jboss-developer/jboss-eap-quickstarts.git
Cloning into 'jboss-eap-quickstarts'...
remote: Enumerating objects: 148133, done.
remote: Total 148133 (delta 0), reused 0 (delta 0), pack-reused 148133
Receiving objects: 100% (148133/148133), 59.90 MiB | 7.62 MiB/s, done.
Resolving deltas: 100% (66476/66476), done.
$ cd jboss-eap-quickstarts/helloworld/

See how the original helloworld works

Actually, the essence of this application is clear from the name, but we will upgrade its code strictly scientific. Therefore, to begin with, look at this application in its original form.

Expand helloworld

1. Open the terminal and go to the root of the JBoss EAP folder (you can download it here), i.e. to the EAP_HOME folder.

2. Start the JBoss EAP server with the default profile:

$ EAP_HOME/bin/standalone.sh

Note: on Windows, the EAP_HOME bin standalone.bat script is used to run.

After a couple of seconds, the following should appear in the log:

[org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.2.0.GA (WildFly Core 6.0.11.Final-redhat-00001) started in 3315ms - Started 306 of 527 services (321 services are lazy, passive or on-demand)

3. Open in the browser 127.0.0.1: 8080 and see this:

Fig. 1. JBoss EAP Homepage.

4. Follow the instructions in the manual Build and deploy the quickstart: deploy helloworld and execute (from the project root folder) the following command:

$ mvn clean install wildfly:deploy

After successful execution of this command in the log, we will see something like the following:

[INFO] ------------------------------------------------------------------------ 
[INFO] BUILD SUCCESS 
[INFO] ------------------------------------------------------------------------ 
[INFO] Total time: 8.224 s

So, the first deployment of the helloworld application on JBoss EAP took just over 8 seconds.

Testing helloworld

Acting strictly on leadership Access the applicationopen in browser 127.0.0.1: 8080 / helloworld and see this:

Fig. 2. Source Hello World from JBoss EAP.

Make changes

Change the input parameter createHelloMessage (String name) from World to Marco:

writer.println("

" + helloService.createHelloMessage("Marco") + "

");

Again, run the following command:

$ mvn clean install wildfly:deploy

Then we refresh the page in the browser and see that the text has changed:

Fig. 3. Hello Marco at JBoss EAP.

Roll back helloworld deployment and shut down JBoss EAP

This is optional, but if you want to cancel the deployment, you can do this with the following command:

$ mvn clean install wildfly:undeploy

To shut down the JBoss EAP instance, simply press Ctrl + C in the terminal window.

Upgrading helloworld

Now let’s upgrade the original helloworld application.

Create a new branch

Create a new working branch after the quickstart project finishes:

$ git checkout -b quarkus 7.2.0.GA

Change the pom.xml file

We will start changing the application from the pom.xml file. So that Quarkus can insert XML blocks into it, execute the following command in the helloworld folder:

$ mvn io.quarkus:quarkus-maven-plugin:0.23.2:create

When writing this article, version 0.23.2 was used. Quarkus often has new versions, you can find out which version is the latest on the website github.com/quarkusio/quarkus/releases/latest.

The above command will insert the following elements into pom.xml:

  • Property specifying the version of Quarkus to use.
  • Block to import Quarkus BOM (bill of materials) so as not to add a version for each Quarkus dependency.
  • The quarkus-maven-plugin plugin, which is responsible for packaging the application and providing development mode.
  • A native profile for creating application executables.

In addition, we manually make the following changes to pom.xml:

  1. Pull out the tag out of block and place it above the tag . Since in the next step we will remove the block then you need to save .
  2. Delete block , since when working with Quarkus this application will no longer need the parent pom from JBoss.
  3. Add tag and place it under the tag . The version number can be specified as you want.
  4. Delete tag , since this application is no longer a WAR, but a regular JAR.
  5. We modify the following dependencies:
    1. Change javax.enterprise: cdi-api dependency to io.quarkus: quarkus-arc, removing providedbecause (according to the docks) this Quarkus extension provides injection dependencies of CDI.
    2. Change the org.jboss.spec.javax.servlet: jboss-servlet-api_4.0_spec dependency to io.quarkus: quarkus-undertow, removing providedbecause (according to the docks) this Quarkus extension provides support for servlets.
    3. We remove the org.jboss.spec.javax.annotation: jboss-annotations-api_1.3_spec dependency, since they come bundled with the dependencies we just changed.

The version of the pom.xml file with all changes is located at github.com/mrizzi/jboss-eap-quickstarts/blob/quarkus/helloworld/pom.xml.

Please note that the above mvn io.quarkus: quarkus-maven-plugin: 0.23.2: create command not only changes the pom.xml file, but also adds a number of components to the project, namely the following files and folders:

  • The mvnw and mvnw.cmd file and the .mvn: Maven Wrapper folder allow you to run Maven projects of a given version of Maven without installing this version itself.
  • Docker folder (in the src / main / directory): here are examples of Dockerfiles for native and jvm modes (along with the .dockerignore file).
  • Resources folder (in the src / main / directory): here lies an empty application.properties file and a sample Quarkus index.html start page (for details, see Run the modernized helloworld).

Launch helloworld

To test the application, we use quarkus: dev, which launches Quarkus in development mode (for more details see this section in the manual Development mode)

Note: this step is expected to lead to an error, because we have not yet made all the necessary changes.

Now run the command to check how it works:

$ ./mvnw compile quarkus:dev
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------< org.jboss.eap.quickstarts:helloworld >----------------
[INFO] Building Quickstart: helloworld quarkus
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ helloworld ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ helloworld ---
[INFO] Nothing to compile - all classes are up to date
[INFO]
[INFO] --- quarkus-maven-plugin:0.23.2:dev (default-cli) @ helloworld ---
Listening for transport dt_socket at address: 5005
INFO  [io.qua.dep.QuarkusAugmentor] Beginning quarkus augmentation
INFO  [org.jbo.threads] JBoss Threads version 3.0.0.Final
ERROR [io.qua.dev.DevModeMain] Failed to start quarkus: java.lang.RuntimeException: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
	[error]: Build step io.quarkus.arc.deployment.ArcProcessor#validate threw an exception: javax.enterprise.inject.spi.DeploymentException: javax.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type org.jboss.as.quickstarts.helloworld.HelloService and qualifiers [@Default]
	- java member: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService
	- declared on CLASS bean [types=[javax.servlet.ServletConfig, java.io.Serializable, org.jboss.as.quickstarts.helloworld.HelloWorldServlet, javax.servlet.GenericServlet, javax.servlet.Servlet, java.lang.Object, javax.servlet.http.HttpServlet], qualifiers=[@Default, @Any], target=org.jboss.as.quickstarts.helloworld.HelloWorldServlet]
	at io.quarkus.arc.processor.BeanDeployment.processErrors(BeanDeployment.java:841)
	at io.quarkus.arc.processor.BeanDeployment.init(BeanDeployment.java:214)
	at io.quarkus.arc.processor.BeanProcessor.initialize(BeanProcessor.java:106)
	at io.quarkus.arc.deployment.ArcProcessor.validate(ArcProcessor.java:249)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at io.quarkus.deployment.ExtensionLoader$1.execute(ExtensionLoader.java:780)
	at io.quarkus.builder.BuildContext.run(BuildContext.java:415)
	at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
	at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2011)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1535)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1426)
	at java.lang.Thread.run(Thread.java:748)
	at org.jboss.threads.JBossThread.run(JBossThread.java:479)
Caused by: javax.enterprise.inject.UnsatisfiedResolutionException: Unsatisfied dependency for type org.jboss.as.quickstarts.helloworld.HelloService and qualifiers [@Default]
	- java member: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService
	- declared on CLASS bean [types=[javax.servlet.ServletConfig, java.io.Serializable, org.jboss.as.quickstarts.helloworld.HelloWorldServlet, javax.servlet.GenericServlet, javax.servlet.Servlet, java.lang.Object, javax.servlet.http.HttpServlet], qualifiers=[@Default, @Any], target=org.jboss.as.quickstarts.helloworld.HelloWorldServlet]
	at io.quarkus.arc.processor.Beans.resolveInjectionPoint(Beans.java:428)
	at io.quarkus.arc.processor.BeanInfo.init(BeanInfo.java:371)
	at io.quarkus.arc.processor.BeanDeployment.init(BeanDeployment.java:206)
	... 14 more

So, it doesn’t work … And why?

The UnsatisfiedResolutionException exception points to the HelloService class, which is a member of the HelloWorldServlet class (java member: org.jboss.as.quickstarts.helloworld.HelloWorldServlet # helloService). The problem is that HelloWorldServlet needs an injected instance of HelloService, but it cannot be found (although both of these classes are in the same package).

It’s time to get back to documentation and read how Inject works in Quarkus, and therefore Contexts and Dependency Injection (CDI). Therefore, we open the Contexts and Dependency Injection guide in the section Bean discovery we read: “A bean class that does not have a defining bean annotation is not searched.”

We look at the HelloService class – it really does not have such an annotation. Therefore, it must be added so that Quarkus can search and find a bean. And since this is a stateless object, we can well add the @ApplicationScoped annotation as follows:

@ApplicationScoped
public class HelloService {

Note: here the development environment may ask you to add the required package (see the line below), and this will have to be done manually, like this:

import javax.enterprise.context.ApplicationScoped;

If in doubt, which scope should be used when the source bean’s is not set at all, study the documentation JSR 365: Contexts and Dependency Injection for Java 2.0 — Default scope.

Now again, we try to run the application with the command ./mvnw compile quarkus: dev:

$ ./mvnw compile quarkus:dev
[INFO] Scanning for projects...
[INFO]
[INFO] ----------------< org.jboss.eap.quickstarts:helloworld >----------------
[INFO] Building Quickstart: helloworld quarkus
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ helloworld ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 2 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ helloworld ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 2 source files to /home/mrizzi/git/forked/jboss-eap-quickstarts/helloworld/target/classes
[INFO]
[INFO] --- quarkus-maven-plugin:0.23.2:dev (default-cli) @ helloworld ---
Listening for transport dt_socket at address: 5005
INFO  [io.qua.dep.QuarkusAugmentor] (main) Beginning quarkus augmentation
INFO  [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation completed in 576ms
INFO  [io.quarkus] (main) Quarkus 0.23.2 started in 1.083s. Listening on: http://0.0.0.0:8080
INFO  [io.quarkus] (main) Profile dev activated. Live Coding activated.
INFO  [io.quarkus] (main) Installed features: [cdi]

Now everything goes without errors.

We launch the upgraded helloworld

As written in the log, open in the browser 0.0.0.0: 8080 (Quarkus start page by default) and see this:

Fig. 4. Start page of Quarkus dev.

The WebServlet annotation for this application has the following context definition:

@WebServlet("/HelloWorld")
public class HelloWorldServlet extends HttpServlet {

Therefore, go to the browser on 0.0.0.0: 8080 / HelloWorldto and see the following:

Fig. 5: The Quarkus dev page for the Hello World application.

Well, everything works.

And now we are making changes to the code. Note that the ./mvnw compile quarkus: dev command is still working and we are not going to stop it. Now let’s try to apply the same – trivial – changes to the code itself and see how Quarkus makes life easier for the developer:

writer.println("

" + helloService.createHelloMessage("Marco") + "

");

Save the file and then refresh the web page to see Hello Marco, as shown in the screenshot below:

Fig. 6. Hello Marco page in Quarkus dev.

Now check the output in the terminal:

INFO  [io.qua.dev] (vert.x-worker-thread-3) Changed source files detected, recompiling [/home/mrizzi/git/forked/jboss-eap-quickstarts/helloworld/src/main/java/org/jboss/as/quickstarts/helloworld/HelloWorldServlet.java]
INFO  [io.quarkus] (vert.x-worker-thread-3) Quarkus stopped in 0.003s
INFO  [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Beginning quarkus augmentation
INFO  [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Quarkus augmentation completed in 232ms
INFO  [io.quarkus] (vert.x-worker-thread-3) Quarkus 0.23.2 started in 0.257s. Listening on: http://0.0.0.0:8080
INFO  [io.quarkus] (vert.x-worker-thread-3) Profile dev activated. Live Coding activated.
INFO  [io.quarkus] (vert.x-worker-thread-3) Installed features: [cdi]
INFO  [io.qua.dev] (vert.x-worker-thread-3) Hot replace total time: 0.371s

The page refresh triggered the detection of changes in the source code, and Quarkus automatically performed the stop-start procedure. And all this was completed in just 0.371 seconds (here it is, that very “ultrafast subatomic Java”).

Build helloworld into a JAR package

Now that the code works as it should, we pack it with the following command:

$ ./mvnw clean package

This command creates two JAR files in the / target folder: the helloworld-.jar file, which is a standard artifact assembled by the Maven team together with the project classes and resources. And the helloworld file is runner.jar, which is an executable JAR.

Please note that this is not an uber-jar, since all dependencies are simply copied to the / target / lib folder (and not packaged into a JAR file). Therefore, to run this JAR from another folder or on a different host, you need to copy both the JAR file and the / lib folder there, given that the Class-Path element in the MANIFEST.MF file in the JAR package contains an explicit listing of the JARs from lib folders.
To learn how to create uber-jar applications, refer to the manual Uber-jar creation.

Launch helloworld packed in JAR

Now you can run our JAR using the standard java command:

$ java -jar ./target/helloworld--runner.jar
INFO  [io.quarkus] (main) Quarkus 0.23.2 started in 0.673s. Listening on: http://0.0.0.0:8080
INFO  [io.quarkus] (main) Profile prod activated.
INFO  [io.quarkus] (main) Installed features: [cdi]

After all this is done, go to the browser on 0.0.0.0: 8080 and check that everything is working as it should.

Putting helloworld into a native executable

So, our helloworld works as a standalone Java application using Quarkus dependencies. But you can go further and turn it into a native executable file.

Install GraalVM

First of all, for this you need to install the necessary tools:

1. Download GraalVM 19.2.0.1 from github.com/oracle/graal/releases/tag/vm-19.2.0.1.

2. Expand the downloaded archive:

$ tar xvzf graalvm-ce-linux-amd64-19.2.0.1.tar.gz

3. Go to the untar folder.

4. Run the command below to download and add a native image:

$ ./bin/gu install native-image

5. We register the folder created in step 2 into the environment variable GRAALVM_HOME:

$ export GRAALVM_HOME={untar-folder}/graalvm-ce-19.2.0.1)

For more information and installation instructions on other OSs, see the manual. Building a Native Executable — Prerequisites.

Build helloworld into a native executable

Read the manual Building a Native Executable — Producing a native executable: “And now we will create a native executable file for our application to reduce its launch time and disk size. The executable file will have everything necessary to run the application, including the JVM machine (or rather, its truncated version, containing only what is required to run the application) and our application itself. ”

To create a native executable, you must enable the Maven native profile:

$ ./mvnw package -Pnative

Our assembly took one minute and 10 seconds, and the final helloworld file – runner f was created in the / target folder.

Run the native executable file helloworld

In the previous step, we got the executable / target / helloworld – runner. Now run it:

$ ./target/helloworld--runner
INFO  [io.quarkus] (main) Quarkus 0.23.2 started in 0.006s. Listening on: http://0.0.0.0:8080
INFO  [io.quarkus] (main) Profile prod activated.
INFO  [io.quarkus] (main) Installed features: [cdi]

Open again in the browser 0.0.0.0: 8080 and check that everything works as it should.

To be continued!

We believe that the method of modernizing Java applications using the capabilities of Quarkus considered in this post (albeit with the simplest example) should be actively applied in real life. In this case, you are likely to encounter a number of problems, the solution of which we will partially consider in the next post, which will focus on how to measure memory consumption in order to evaluate performance improvements, an important part of the whole process of application modernization.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *