{"id":123247,"date":"2022-01-26T08:00:00","date_gmt":"2022-01-26T08:00:00","guid":{"rendered":"https:\/\/fedoramagazine.org\/?p=35869"},"modified":"2022-01-26T08:00:00","modified_gmt":"2022-01-26T08:00:00","slug":"quarkus-and-mutiny","status":"publish","type":"post","link":"https:\/\/sickgaming.net\/blog\/2022\/01\/26\/quarkus-and-mutiny\/","title":{"rendered":"Quarkus and Mutiny"},"content":{"rendered":"<p>Quarkus is a foundation for building Java based applications; whether for the desktop, server or cloud. An excellent write up on usage can be found at <a href=\"https:\/\/fedoramagazine.org\/using-the-quarkus-framework-on-fedora-silverblue-just-a-quick-look\/\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/fedoramagazine.org\/using-the-quarkus-framework-on-fedora-silverblue-just-a-quick-look\/<\/a>. This article is primer for coding asynchronous processes using Quarkus and Mutiny.<\/p>\n<p> <span id=\"more-35869\"><\/span> <\/p>\n<p>So what is Mutiny? Mutiny allows streaming of objects in an event driven flow. The stream might originate from a local process or something remote like a database. Mutiny streaming is accomplished by either a <em>Uni<\/em> or a <em>Multi<\/em> object. We are using the Uni to stream one object &#8212; a <em>List<\/em> containing many integers. A subscribe pattern initiates the stream.<\/p>\n<p>A traditional program is executed and results are returned before continuing. Mutiny can easily support non-blocking code to run processes concurrently. RxJava, ReactiveX and even native Java are alternatives. Mutiny is easy to use (the exposed API is minimal) and it is the default in many of the Quarkus extensions. The two extensions used are <em>quarkus-mutiny<\/em> and <em>quarkus-vertx<\/em>. Vert.x is the underlying framework wrapped by Quarkus. The Promise classes are supplied by quarkus-vertx. A <em>promise<\/em> returns a Uni stream when the process is complete. To get started, install a Java JDK and Maven.<\/p>\n<h2>Bootstrap<\/h2>\n<p>The minimum requirement is either Java-11 <em>or<\/em> Java-17 with Maven.<\/p>\n<div class=\"wp-container-623d625b58d4c wp-block-group\">\n<div class=\"wp-block-group__inner-container\">\n<p><strong>With Java-11<\/strong>:<\/p>\n<pre class=\"wp-block-preformatted\">$ sudo dnf install -y java-11-openjdk-devel maven<\/pre>\n<\/div>\n<\/div>\n<div class=\"wp-container-623d625b59166 wp-block-group\">\n<div class=\"wp-block-group__inner-container\">\n<p><strong>With Java-17<\/strong>:<\/p>\n<pre class=\"wp-block-preformatted\">$ sudo dnf install -y java-17-openjdk-devel maven<\/pre>\n<\/div>\n<\/div>\n<p>Bootstrap<em> <\/em>Quarkus<em> <\/em>and Mutiny with the Maven call below. The extension <em>quarkus-vertx<\/em> is not included to demonstrate how to add additional extensions. Locate an appropriate directory before executing. The directory <em>mutiny-demo<\/em> will be created with the initial application.<\/p>\n<div class=\"wp-container-623d625b5956d wp-block-group\">\n<div class=\"wp-block-group__inner-container\">\n<pre class=\"wp-block-preformatted\">$ mvn io.quarkus.platform:quarkus-maven-plugin:2.6.2.Final:create \\ -DprojectGroupId=fedoramag \\ -DprojectArtifactId=mutiny-demo \\ -DprojectVersion=1.0.0 \\ -DclassName=\"org.demo.mag.Startup\" \\ -Dextensions=\"mutiny\" \\ -DbuildTool=gradle<\/pre>\n<\/div>\n<\/div>\n<p>Now that Gradle is bootstrapped, other extensions can be added. In the <em>mutiny-demo<\/em> directory execute:<\/p>\n<pre class=\"wp-block-preformatted\">$ .\/gradlew addExtension --extensions='quarkus-vertx'<\/pre>\n<p>To view all available extensions execute:<\/p>\n<pre class=\"wp-block-preformatted\">$ .\/gradlew listExtensions<\/pre>\n<p>To get all of the defined Gradle tasks execute:<\/p>\n<pre class=\"wp-block-preformatted\">$ .\/gradlew tasks<\/pre>\n<h2>Mutiny Code<\/h2>\n<p>The<strong> <\/strong><em>className<\/em> entry on the Quarkus bootstrap is<em> org.demo.mag.Startup<\/em> which creates the file <em>src\/main\/java\/org\/demo\/map\/Startup.java<\/em>. Replace the contents with the following code:<\/p>\n<pre class=\"wp-block-preformatted\">package org.demo.mag; import java.util.List;\nimport java.util.concurrent.ExecutionException;\nimport java.util.function.IntSupplier;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream; import io.quarkus.runtime.Quarkus;\nimport io.quarkus.runtime.QuarkusApplication;\nimport io.quarkus.runtime.annotations.QuarkusMain;\nimport io.smallrye.mutiny.Uni;\nimport io.smallrye.mutiny.tuples.Tuple2;\nimport io.vertx.mutiny.core.Promise; @QuarkusMain\npublic class Startup implements QuarkusApplication { public static void main(String... args) { Quarkus.run(Startup.class, args); } @Override public int run(String... args) throws InterruptedException, ExecutionException { final Promise&lt;String&gt; finalMessage = Promise.promise(); final String elapsedTime = \"Elapsed time for asynchronous method: %d milliseconds\"; final int[] syncResults = {0}; Application.runTraditionalMethod(); final Long millis = System.currentTimeMillis(); Promise&lt;List&lt;Integer&gt;&gt; promiseRange = Application.getRange(115000); Promise&lt;Tuple2&lt;Promise&lt;List&lt;Integer&gt;&gt;, Promise&lt;List&lt;Integer&gt;&gt;&gt;&gt; promiseCombined = Application.getCombined(10000, 15000); Promise&lt;List&lt;Integer&gt;&gt; promiseReverse = Application.getReverse(24000); \/* * Retrieve the Uni stream and on the complete event obtain the List&lt;Integer&gt; *\/ promiseRange.future().onItem().invoke(list -&gt; { System.out.println(\"Primes Range: \" + list.size()); if(syncResults[0] == 1) { finalMessage.complete(String.format(elapsedTime, System.currentTimeMillis() - millis)); } { syncResults[0] = 2; } return; }).subscribeAsCompletionStage(); promiseReverse.future().onItem().invoke(list -&gt; { System.out.println(\"Primes Reverse: \" + list.size()); return; }).subscribeAsCompletionStage(); \/* * Notice that this finishes before the other two prime generators(smaller lists). *\/ promiseCombined.future().onItem().invoke(p -&gt; { \/* * Notice that \"Combined Range\" displays first *\/ p.getItem2().future().invoke(reverse -&gt; { System.out.println(\"Combined Reverse: \" + reverse.size()); return; }).subscribeAsCompletionStage(); p.getItem1().future().invoke(range -&gt; { System.out.println(\"Combined Range: \" + range.size()); \/* * Nesting promises to get multple results together *\/ p.getItem2().future().invoke(reverse -&gt; { System.out.println(String.format(\"Asserting that expected primes are equal: %d -- %d\", range.get(0), reverse.get(reverse.size() - 1))); assert range.get(0) == reverse.get(reverse.size() - 1) : \"Generated primes incorrect\"; if(syncResults[0] == 2) { finalMessage.complete(String.format(elapsedTime, System.currentTimeMillis() - millis)); } else { syncResults[0] = 1; } return; }).subscribeAsCompletionStage(); return; }).subscribeAsCompletionStage(); return; }).subscribeAsCompletionStage(); \/\/ Note: on very fast machines this may not display first. System.out.println(\"This should display first - indicating asynchronous code.\"); \/\/ blocking for final message String elapsedMessage = finalMessage.futureAndAwait(); System.out.println(elapsedMessage); return 0; } public static class Application { public static Promise&lt;List&lt;Integer&gt;&gt; getRange(int n) { final Promise&lt;List&lt;Integer&gt;&gt; promise = Promise.promise(); \/\/ non-blocking - this is only for demonstration(emulating some remote call) new Thread(() -&gt; { try { \/* * RangeGeneratedPrimes.primes is blocking, only returns when done *\/ promise.complete(RangeGeneratedPrimes.primes(n)); } catch (Exception exception) { Thread.currentThread().interrupt(); } }).start(); return promise; } public static Promise&lt;List&lt;Integer&gt;&gt; getReverse(int n) { final Promise&lt;List&lt;Integer&gt;&gt; promise = Promise.promise(); new Thread(() -&gt; { try { \/\/ Generating a new object stream promise.complete(ReverseGeneratedPrimes.primes(n)); } catch (Exception exception) { Thread.currentThread().interrupt(); } }).start(); return promise; } public static Promise&lt;Tuple2&lt;Promise&lt;List&lt;Integer&gt;&gt;, Promise&lt;List&lt;Integer&gt;&gt;&gt;&gt; getCombined(int ran, int rev) { final Promise&lt;Tuple2&lt;Promise&lt;List&lt;Integer&gt;&gt;, Promise&lt;List&lt;Integer&gt;&gt;&gt;&gt; promise = Promise.promise(); new Thread(() -&gt; { try { Uni.combine().all() \/* * Notice that these are running concurrently *\/ .unis(Uni.createFrom().item(Application.getRange(ran)), Uni.createFrom().item(Application.getReverse(rev))) .asTuple().onItem().call(tuple -&gt; { promise.complete(tuple); return Uni.createFrom().nullItem(); }) .onFailure().invoke(Throwable::printStackTrace) .subscribeAsCompletionStage(); } catch (Exception exception) { Thread.currentThread().interrupt(); } }).start(); return promise; } public static void runTraditionalMethod() { Long millis = System.currentTimeMillis(); System.out.println(\"Traditiona1-1: \" + RangeGeneratedPrimes.primes(115000).size()); System.out.println(\"Traditiona1-2: \" + RangeGeneratedPrimes.primes(10000).size()); System.out.println(\"Traditiona1-3: \" + ReverseGeneratedPrimes.primes(15000).size()); System.out.println(\"Traditiona1-4: \" + ReverseGeneratedPrimes.primes(24000).size()); System.out.println(String.format(\"Elapsed time for traditional method: %d milliseconds\\n\", System.currentTimeMillis() - millis)); } } public interface Primes { static List&lt;Integer&gt; primes(int n) { return null; }; } public abstract static class PrimeBase { static boolean isPrime(int number) { return IntStream.rangeClosed(2, (int) (Math.sqrt(number))) .allMatch(n -&gt; number % n != 0); } } public static class RangeGeneratedPrimes extends PrimeBase implements Primes { public static List&lt;Integer&gt; primes(int n) { return IntStream.rangeClosed(2, n) .filter(x -&gt; isPrime(x)).boxed() .collect(Collectors.toList()); } } public static class ReverseGeneratedPrimes extends PrimeBase implements Primes { public static List&lt;Integer&gt; primes(int n) { List&lt;Integer&gt; list = IntStream.generate(getReverseList(n)).limit(n - 1) .filter(x -&gt; isPrime(x)).boxed() .collect(Collectors.toList()); return list; } private static IntSupplier getReverseList(int startValue) { IntSupplier reverse = new IntSupplier() { private int start = startValue; public int getAsInt() { return this.start--; } }; return reverse; } }\n}\n<\/pre>\n<h2>Testing<\/h2>\n<p>The Quarkus install showcases the <em>quarkus-resteasy<\/em> extension by default. We are not using it, replace the contents of <em>src\/test\/java\/org\/demo\/mag\/StartupTest.java<\/em> with:<\/p>\n<pre class=\"wp-block-preformatted\">package org.demo.mag; import io.quarkus.test.junit.QuarkusTest;\nimport io.vertx.mutiny.core.Promise; import java.util.List; import org.demo.mag.Startup;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Tag;\nimport org.junit.jupiter.api.Test; @QuarkusTest\npublic class StartupTest { Promise&lt;List&lt;Integer&gt;&gt; promise = Promise.promise(); Promise&lt;Void&gt; promiseAndAwait = Promise.promise(); List&lt;Integer&gt; testValue; @Tag(\"DEV\") @Test public void testVerifyAsync() { Assertions.assertEquals( null , testValue); promise.future().onItem().invoke(list -&gt; { testValue = list; promiseAndAwait.complete(); }).subscribeAsCompletionStage(); Assertions.assertEquals(null, testValue); promise.complete(Startup.ReverseGeneratedPrimes.primes(100)); promiseAndAwait.futureAndAwait(); Assertions.assertNotNull(testValue); Assertions.assertEquals(2, testValue.get(testValue.size()-1)); }\n}\n<\/pre>\n<h2>Optional<\/h2>\n<p>To reduce download volume, remove the following entries from<strong> <\/strong>the <em>build.gradle<\/em> file.<\/p>\n<pre class=\"wp-block-preformatted\">\timplementation 'io.quarkus:quarkus-resteasy' testImplementation 'io.rest-assured:rest-assured'<\/pre>\n<h2>Installation and Execution<\/h2>\n<p>The next step is to build the project. This includes downloading all dependencies as well as compiling and executing the Startup.java program. Everything is included in one file for brevity.<\/p>\n<pre class=\"wp-block-preformatted\">$ .\/gradlew quarkusDev<\/pre>\n<p>The above command produces a banner and console output from Quarkus and the program.<\/p>\n<p>This is development mode. Notice the prompt: &#8220;Press [space] to restart&#8221;. To review edits hit the space-bar and enter-key to re-compile and execute. Enter <strong>q<\/strong> to quit.<\/p>\n<p>To build an Uber jar (all dependencies included) execute:<\/p>\n<pre class=\"wp-block-preformatted\">$ .\/gradlew quarkusBuild -Dquarkus.package.type=uber-jar<\/pre>\n<p>This creates a jar in the <em>build<\/em> directory named mutiny-<em>demo-1.0.0-runner.jar<\/em>. To run the jar file, enter the following command.<\/p>\n<pre class=\"wp-block-preformatted\">$ java -jar .\/build\/mutiny-demo-1.0.0-runner.jar<\/pre>\n<p>To remove the banner and console logs, add the following lines to the <em>src\/main\/resources\/application.properties<\/em> file.<\/p>\n<pre class=\"wp-block-preformatted\">%prod.quarkus.log.console.enable=false\n%prod.quarkus.banner.enabled=false<\/pre>\n<p>The output might look similar to the following.<\/p>\n<pre class=\"wp-block-preformatted\">\tTraditional-1: 9592 Traditional-2: 1229 Traditional-3: 2262 Traditional-4: 2762 Elapsed time for traditional method: 67 milliseconds Combined Range: 1229 This should display first - indicating asynchronous code. Combined Reverse: 2262 Primes Reverse: 2762 Asserting that expected primes are equal: 2 -- 2 Primes Range: 9592 Elapsed time for asynchronous method: 52 milliseconds<\/pre>\n<p>You will still get the banner and logs in development mode.<\/p>\n<p>To go one step further, Quarkus can generate an executable out of the box using GraalVM.<\/p>\n<pre class=\"wp-block-preformatted\">$ .\/gradlew build -Dquarkus.package.type=native<\/pre>\n<p>The executable generated by the above command will be <em>.\/build\/mutiny-demo-1.0.0-runner<\/em>.<\/p>\n<p>The default GraalVM is a downloaded container. To override this, set the environment variable <em>GRAALVM_HOME<\/em> to your local install. Don&#8217;t forget to install the <em>native-image<\/em> with the following command.<\/p>\n<pre class=\"wp-block-preformatted\">$ ${GRAALVM_HOME}\/bin\/gu install native-image<\/pre>\n<h2>The Code<\/h2>\n<p>The code, generates prime numbers for a range, reversed on a limit and a combination of the two. For example, consider the range: &#8220;Promise&lt;List&lt;Integer&gt;&gt; promiseRange = Application.getRange(115000);&#8221;.<\/p>\n<p>This generates all primes between 1 and 115000 and displays the number of primes in the range. It is executed first but displays its results last. The code near the end of the main method &#8212; System.out.println (&#8220;This should display first &#8211; indicating asynchronous code.&#8221;);<strong> <\/strong>&#8212; displays first. This is an example of asynchronous code. We can run multiple processes concurrently. However, the order of completion is unpredictable. The traditional calls are orderly and the results can be collected when completed.<\/p>\n<p>Execution can be blocked until a result is returned. The code does exactly that to display the asynchronous elapsed time message. At the end of the main method we have: &#8220;String elapsedMessage = finalMessage.futureAndAwait();&#8221;. The message arrives from either <em>promiseRange<\/em> or <em>promiseCombined<\/em> &#8212; the two longest running processes. But even this is not guaranteed. The state of the underling OS is unknown. One of the other processes might finish last. Normally, asynchronous calls are nested to co-ordinate results. This is demonstrated in the <em>promiseCombined<\/em> promise to evaluate the results of range and reversed primes.<\/p>\n<h2>Conclusion<\/h2>\n<p>The comparison between the traditional method and asynchronous method suggests that the asynchronous method can be up to 25% faster on a modern computer. An older CPU that does not have the resources and computing power produces results faster with the traditional method. If a computer has many cores, why not use them\u203d<\/p>\n<p>More documentation can be found on the following web sites.<\/p>\n<ul>\n<li><a href=\"https:\/\/quarkus.io\">https:\/\/<\/a><a href=\"https:\/\/quarkus.io\" target=\"_blank\" rel=\"noreferrer noopener\">quarkus.io<\/a><\/li>\n<li><a href=\"https:\/\/smallrye.io\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/smallrye.io<\/a><\/li>\n<li><a href=\"https:\/\/vertx.io\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/vertx.io<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Quarkus is a foundation for building Java based applications; whether for the desktop, server or cloud. An excellent write up on usage can be found at https:\/\/fedoramagazine.org\/using-the-quarkus-framework-on-fedora-silverblue-just-a-quick-look\/. This article is primer for coding asynchronous processes using Quarkus and Mutiny. So what is Mutiny? Mutiny allows streaming of objects in an event driven flow. The stream [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[48],"tags":[1295,45,42,859,1296,46,1297,47,872],"class_list":["post-123247","post","type-post","status-publish","format-standard","hentry","category-fedora-os","tag-asynchronous","tag-fedora","tag-for-developers","tag-java","tag-jdk","tag-magazine","tag-mutiny","tag-news","tag-quarkus"],"_links":{"self":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/posts\/123247","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/comments?post=123247"}],"version-history":[{"count":0,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/posts\/123247\/revisions"}],"wp:attachment":[{"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/media?parent=123247"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/categories?post=123247"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sickgaming.net\/blog\/wp-json\/wp\/v2\/tags?post=123247"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}