Quarkus: modernizarea aplicațiilor pe exemplul helloworld din JBoss EAP Quickstart

Bună tuturor în acest blog, și vă prezentăm a patra postare din seria dedicată lui Quarkus!

Quarkus: modernizarea aplicațiilor pe exemplul helloworld din JBoss EAP Quickstart

Postarea anterioară a fost despre cum Quarkus îmbină MicroProfile și Spring. Să reamintim că Quarkus este poziționat ca fiind „Java subatomică extrem de rapidă”, un „stivă Java orientată spre Kubernetes, optimizată pentru GraalVM și OpenJDK HotSpot și construită din cele mai bune biblioteci și standarde”. Astăzi vom arăta cum să modernizăm aplicațiile Java existente, utilizând capacitățile lui Quarkus, prin exemplul aplicației helloworld din repositoariul Red Hat JBoss Enterprise Application Platform (JBoss EAP) Quickstart, care utilizează tehnologiile CDI și Servlet 3 suportate de Quarkus.

Este important de menționat că atât Quarkus cât și JBoss EAP se concentrează pe utilizarea instrumentelor construite pe baza standardelor. Nu aveți o aplicație care funcționează pe JBoss EAP? Nu e nicio problemă, poate fi ușor migrată de pe serverul de aplicații existent pe JBoss EAP folosind Red Hat Application Migration Toolkit. După care versiunea finală și funcțională a codului modernizat va fi disponibilă în repositoariul github.com/mrizzi/jboss-eap-quickstarts/tree/quarkus, în modulul helloworld.

La scrierea acestei postări au fost utilizate ghiduri pentru Quarkus, în principal Creating Your First Application și Building a Native Executable.

Obțineți codul

În primul rând, să creăm un clon local al repositoariului JBoss EAP quickstarts:

$ git clone https://github.com/jboss-developer/jboss-eap-quickstarts.git
Clonare în '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/

Să vedem cum funcționează codul sursă helloworld

Practic, esența acestei aplicații este clară din nume, dar vom moderniza codul său în mod strict științific. Așadar, mai întâi să privim această aplicație în forma ei inițială.

Desfășurăm helloworld

1. Deschidem terminalul și mergem în directorul rădăcină al folderului JBoss EAP (acesta poate fi descărcat aici), adică în folderul EAP_HOME.

2. Pornim serverul JBoss EAP cu profilul implicit:

$ EAP_HOME/bin/standalone.sh

Notă: pe Windows se folosește scriptul EAP_HOME/bin/standalone.bat.

După câteva secunde, în jurnal ar trebui să apară ceva de genul:

[org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.2.0.GA (WildFly Core 6.0.11.Final-redhat-00001) a pornit în 3315ms - Au fost pornite 306 din 527 de servicii (321 de servicii sunt lazy, pasive sau la cerere)

3. Deschidem în browser 127.0.0.1:8080 și vedem asta:

Quarkus: modernizarea aplicațiilor pe exemplul helloworld din JBoss EAP Quickstart

Fig. 1. Pagina principală JBoss EAP.

4. Urmăm instrucțiunile din ghidul Build and Deploy the Quickstart: desfășurăm helloworld și executăm (din folderul de bază al proiectului) următoarea comandă:

$ mvn clean install wildfly:deploy

După ce această comandă a fost executată cu succes, vom vedea în log ceva asemănător:

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

Așadar, prima desfășurare a aplicației helloworld pe JBoss EAP a durat puțin mai mult de 8 secunde.

Testăm helloworld

Acționând strict conform ghidului Accesați aplicația, deschidem în browser 127.0.0.1:8080/helloworld și vedem următoarele:

Quarkus: modernizarea aplicațiilor pe exemplul helloworld din JBoss EAP Quickstart

Fig. 2. Hello World original din JBoss EAP.

Facem modificări

Schimbăm parametrul de intrare createHelloMessage(String name) de la World la Marco:

writer.println("<h1>" + helloService.createHelloMessage("Marco") + "</h1>");

Din nou executăm următoarea comandă:

$ mvn clean install wildfly:deploy

Apoi actualizăm pagina în browser și vedem că textul s-a schimbat:

Quarkus: modernizarea aplicațiilor pe exemplul helloworld din JBoss EAP Quickstart

Fig. 3. Hello Marco în JBoss EAP.

Anulăm desfășurarea helloworld și încheiem activitatea JBoss EAP

Acest lucru nu este obligatoriu, dar dacă doriți să anulați desfășurarea, o puteți face cu următoarea comandă:

$ mvn clean install wildfly:undeploy

Pentru a încheia activitatea instanței JBoss EAP, pur și simplu apăsați Ctrl+C în fereastra terminalului.

Modernizăm helloworld

Acum ne vom ocupa de modernizarea aplicației originale helloworld.

Creăm o nouă ramură

Creăm o nouă ramură de lucru după ce se termină execuția proiectului quickstart:

$ git checkout -b quarkus 7.2.0.GA

Schimbăm fișierul pom.xml

Vom începe să modificăm aplicația cu fișierul pom.xml. Pentru ca Quarkus să poată insera blocuri XML în acesta, vom executa următoarea comandă în folderul helloworld:

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

La scrierea acestui articol s-a utilizat versiunea 0.23.2. Quarkus lansează frecvent versiuni noi, iar pentru a afla care este cea mai recentă versiune, vizitați site-ul github.com/quarkusio/quarkus/releases/latest.

Comanda de mai sus va insera în pom.xml următoarele elemente:

  • Proprietatea , care definește versiunea utilizată a Quarkus.
  • Blocul pentru a importa Quarkus BOM (bill of materials), astfel încât să nu adăugăm versiunea pentru fiecare dependență Quarkus.
  • Pluginul quarkus-maven-plugin, responsabil pentru ambalarea aplicației și oferind modul de dezvoltare.
  • Profilul native pentru a crea fișiere executabile ale aplicației.

De asemenea, în pom.xml facem manual următoarele modificări:

  1. Extragem eticheta din blocul și o plasăm deasupra etichetei . Deoarece la pasul următor vom elimina blocul , trebuie să păstrăm .
  2. Îndepărtăm blocul , deoarece în timpul lucrului cu Quarkus, această aplicație nu va mai necesita pom-ul părinte de la JBoss.
  3. Adăugăm eticheta și o plasăm sub eticheta . Puteți specifica orice număr de versiune doriți.
  4. Îndepărtăm eticheta , deoarece această aplicație nu mai este un WAR, ci un JAR obișnuit.
  5. Modificăm următoarele dependențe:
    1. Schimbăm dependența javax.enterprise:cdi-api cu io.quarkus:quarkus-arc, eliminând provided, deoarece (conform documentației) această extensie Quarkus oferă injecția dependenței CDI.
    2. Schimbăm dependența org.jboss.spec.javax.servlet:jboss-servlet-api_4.0_spec cu io.quarkus:quarkus-undertow, eliminând provided, deoarece (conform documentației) această extensie Quarkus oferă suport pentru servlet-uri.
    3. Îndepărtăm dependența org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec, deoarece aceasta vine împreună cu dependențele pe care tocmai le-am modificat.

Versiunea fișierului pom.xml cu toate modificările este disponibilă la adresa github.com/mrizzi/jboss-eap-quickstarts/blob/quarkus/helloworld/pom.xml.

Rețineți că comanda mvn io.quarkus:quarkus-maven-plugin:0.23.2:create nu modifică doar fișierul pom.xml, ci adaugă la proiect o serie de componente, și anume, următoarele fișiere și foldere:

  • Fișierul mvnw și mvnw.cmd și folderul .mvn: Maven Wrapper permite rularea proiectelor Maven cu o versiune specificată de Maven fără a instala acea versiune.
  • Folderul docker (în directorul src/main/): aici se află exemple de fișiere Dockerfile pentru modurile native și jvm (împreună cu fișierul .dockerignore).
  • Folderul resources (în directorul src/main/): aici se află un fișier gol application.properties și un exemplu de pagină de start Quarkus index.html (pentru detalii, consultați Rulați helloworld modernizat).

Rulăm helloworld
Pentru a testa aplicația, folosim quarkus:dev, care pornește Quarkus în modul de dezvoltare (pentru detalii, consultați această secțiune din manualul de Modul de Dezvoltare).

Notă: această etapă va duce, așa cum era de așteptat, la o eroare, deoarece nu am făcut încă toate modificările necesare.

Acum rulăm comanda pentru a verifica cum va funcționa:

$ .\/mvnw compile quarkus:dev
[INFO] Scanare pentru proiecte...
[INFO]
[INFO] ----------------< org.jboss.eap.quickstarts:helloworld >----------------
[INFO] Construirea Quickstart: helloworld quarkus
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ helloworld ---
[INFO] Folosind codificarea 'UTF-8' pentru a copia resurse filtrate.
[INFO] Copierea a 2 resurse
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ helloworld ---
[INFO] Nimic de compilat - toate clasele sunt actualizate
[INFO]
[INFO] --- quarkus-maven-plugin:0.23.2:dev (default-cli) @ helloworld ---
Ascultând pentru transport dt_socket la adresa: 5005
INFO  [io.qua.dep.QuarkusAugmentor] Începerea augmentării quarkus
INFO  [org.jbo.threads] Versiunea JBoss Threads 3.0.0.Final
ERROR [io.qua.dev.DevModeMain] Eșec la pornirea quarkus: java.lang.RuntimeException: io.quarkus.builder.BuildException: Eșec la construcție: Eșec al construcției din cauza erorilor
	[eroare]: Pasul de construcție io.quarkus.arc.deployment.ArcProcessor#validate a generat o excepție: javax.enterprise.inject.spi.DeploymentException: javax.enterprise.inject.UnsatisfiedResolutionException: Dependență nesatisfăcută pentru tipul org.jboss.as.quickstarts.helloworld.HelloService și calificatori [@Default]
	- membru java: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService
	- declarat pe bean-ul CLASS [tipuri=[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], calificatori=[@Default, @Any], țintă=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(Native Method)
	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)
Cauzat de: javax.enterprise.inject.UnsatisfiedResolutionException: Dependență nesatisfăcută pentru tipul org.jboss.as.quickstarts.helloworld.HelloService și calificatori [@Default]
	- membru java: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService
	- declarat pe bean-ul CLASS [tipuri=[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], calificatori=[@Default, @Any], țintă=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 mai multe

Deci, nu funcționează… De ce?

Excepția UnsatisfiedResolutionException indică către clasa HelloService, care este un membru al clasei HelloWorldServlet (membru java: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService). Problema este că HelloWorldServlet are nevoie de o instanță injectată a HelloService, iar aceasta nu poate fi găsită (deși ambele clase se află în același pachet).

Este timpul să ne întoarcem la documentation și să citim despre cum funcționează în Quarkus Inject, și prin urmare, și Contexts and Dependency Injection (CDI). Așadar, deschidem ghidul Contexts and Dependency Injection și în secțiunea Bean Discovery citit: „Clasa bean care nu are o anotație definită pentru bean, nu este căutată.”

Privind clasa HelloService – aceasta nu are cu adevărat o astfel de anotație. Prin urmare, aceasta trebuie adăugată pentru ca Quarkus să poată căuta și găsi bean. Și deoarece este un obiect stateless, putem adăuga anotația @ApplicationScoped astfel:

@ApplicationScoped
public class HelloService {

Notă: aici, mediul de dezvoltare poate solicita să adăugați pachetul necesar (vezi linia de mai jos), și va trebui să o faceți manual, astfel:

import javax.enterprise.context.ApplicationScoped;

Dacă sunteți în dubiu cu privire la ce domeniu scope ar trebui să folosiți în cazul în care pentru bean-ul original nu este specificat deloc, studiați documentația JSR 365: Contexts and Dependency Injection for Java 2.0—Default scope.

Acum încercăm din nou să lansăm aplicația cu comanda .\/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]

Acum totul se desfășoară fără erori.

Lansăm helloworld modernizat
Așa cum este menționat în jurnal, deschidem în browser 0.0.0.0:8080 (pagina de start Quarkus implicită) și vedem următoarele:

Quarkus: modernizarea aplicațiilor pe exemplul helloworld din JBoss EAP Quickstart

Fig. 4. Pagina de start a Quarkus dev.

În anotația WebServlet, această aplicație are următoarea definiție a contextului:

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

Prin urmare, mergem în browser la 0.0.0.0:8080/HelloWorld și vedem următoarele:

Quarkus: modernizarea aplicațiilor pe exemplul helloworld din JBoss EAP Quickstart

Fig. 5: Pagina The Quarkus dev pentru aplicația Hello World.

Ei bine, totul funcționează.

Acum facem modificări în cod. Observați că comanda ./mvnw compile quarkus:dev funcționează încă, și nu avem de gând să o oprim. Acum să încercăm să aplicăm aceleași - cele mai banale - modificări codului și să vedem cum Quarkus le facilitează viața dezvoltatorului:

writer.println("<h1>" + helloService.createHelloMessage("Marco") + "</h1>");

Salvăm fișierul și apoi actualizăm pagina web pentru a vedea inscripția Hello Marco, așa cum este arătat în captura de ecran de mai jos:

Quarkus: modernizarea aplicațiilor pe exemplul helloworld din JBoss EAP Quickstart

Fig. 6. Pagina Hello Marco în Quarkus dev.

Acum să verificăm ieșirea în terminal:

INFO  [io.qua.dev] (vert.x-worker-thread-3) Fișiere sursă modificate detectate, recompilând [/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 oprit în 0.003s
INFO  [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Începere augmentare quarkus
INFO  [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Augmentarea quarkus finalizată în 232ms
INFO  [io.quarkus] (vert.x-worker-thread-3) Quarkus 0.23.2 pornit în 0.257s. Ascultă pe: http://0.0.0.0:8080
INFO  [io.quarkus] (vert.x-worker-thread-3) Profil dev activat. Live Coding activat.
INFO  [io.quarkus] (vert.x-worker-thread-3) Funcționalități instalate: [cdi]
INFO  [io.qua.dev] (vert.x-worker-thread-3) Timp total de înlocuire rapidă: 0.371s

Actualizarea paginii a declanșat detectarea modificărilor în codul sursă, iar Quarkus a executat automat procedura "stop-start". Și toate acestea s-au întâmplat în doar 0.371 secunde (iată-o, celebra „Java subatomică super rapidă”).

Generăm pachetul JAR pentru helloworld
Acum, când codul funcționează corect, îl împachetăm cu următoarea comandă:

$ ./mvnw clean package

Această comandă creează două fișiere JAR în folderul /target: fișierul helloworld-.jar, care reprezintă artefactul standard, compilat de comanda Maven împreună cu clasele și resursele proiectului. Și fișierul helloworld—runner.jar, care este un JAR executabil.

Rețineți că acesta nu este un uber-jar, deoarece toate dependențele sunt pur și simplu copiate în folderul /target/lib (nu sunt împachetate în fișierul JAR). Prin urmare, pentru a rula acest JAR dintr-un alt folder sau pe un alt server, trebuie să copiați atât fișierul JAR, cât și folderul /lib, având în vedere că elementul Class-Path din fișierul MANIFEST.MF din pachetul JAR conține o enumerare explicită a JAR-urilor din folderul lib.
Pentru a învăța cum să creați aplicații uber-jar, consultați ghidul Crearea Uber-Jar.

Rulăm helloworld, împachetat în JAR

Acum putem rula JAR-ul nostru folosind comanda standard java:

$ java -jar ./target/helloworld--runner.jar
INFO  [io.quarkus] (main) Quarkus 0.23.2 a început în 0.673s. Ascultând pe: http://0.0.0.0:8080
INFO  [io.quarkus] (main) Profilul prod activat.
INFO  [io.quarkus] (main) Caracteristici instalate: [cdi]

După ce totul s-a finalizat, accesați în browser 0.0.0.0:8080 și verificați că totul funcționează corect.

Compilăm helloworld într-un fișier executabil nativ

Așadar, helloworld-ul nostru funcționează ca o aplicație standalone Java, folosind dependențele Quarkus. Dar putem merge mai departe și să-l transformăm într-un fișier executabil nativ.

Instalăm GraalVM
În primul rând, trebuie să instalăm uneltele necesare:

1. Descărcăm GraalVM 19.2.0.1 de la github.com/oracle/graal/releases/tag/vm-19.2.0.1.

2. Deschidem arhiva descărcată:

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

3. Mergem în folderul untar.

4. Executăm comanda de mai jos pentru a descărca și a adăuga imaginea nativă:

$ ./bin/gu install native-image

5. Adăugăm folderul creat la pasul 2 în variabila de mediu GRAALVM_HOME:

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

Informații suplimentare și instrucțiuni de instalare pe alte sisteme de operare pot fi găsite în ghidul Building a Native Executable—Prerequisites.

Executăm compilarea helloworld într-un fișier executabil nativ
Citim ghidul Building a Native Executable—Producing a native executable: „Și acum să creăm un fișier executabil nativ pentru aplicația noastră, pentru a reduce timpul de pornire și dimensiunea pe disc. Fișierul executabil va avea tot ce este necesar pentru a rula aplicația, inclusiv mașina JVM (mai exact, o versiune restrânsă, care conține doar ceea ce este necesar pentru a rula aplicația) și aplicația noastră.”

Pentru a crea un fișier executabil nativ, trebuie să activăm profilul native în Maven:

$ ./mvnw package -Pnative

Compilarea noastră a durat un minut și 10 secunde, iar fișierul final helloworld-runner a fost creat în folderul /target.

Executăm fișierul executabil nativ helloworld

În pasul anterior am obținut fișierul executabil /target/helloworld-runner. Acum să-l lansăm:

$ ./target/helloworld--runner
INFO  [io.quarkus] (main) Quarkus 0.23.2 a început în 0.006s. Ascultând pe: http://0.0.0.0:8080
INFO  [io.quarkus] (main) Profilul prod activat.
INFO  [io.quarkus] (main) Caracteristici instalate: [cdi]

Deschidem din nou în browser 0.0.0.0:8080 și verificăm că totul funcționează corect.

Continuarea urmează!

Considerăm că metoda de modernizare a aplicațiilor Java discutată în această postare (chiar și printr-un exemplu simplu) prin utilizarea capacităților Quarkus ar trebui aplicată activ în viața reală. În acest proces, este probabil să vă confruntați cu o serie de probleme, a căror soluție le vom discuta parțial în următoarea postare, unde vom aborda modul de măsurare a consumului de memorie pentru estimarea îmbunătățirii performanței, o parte esențială a întregului proces de modernizare a aplicațiilor.

Sursa: habr.com

Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS 🔥 Cumpără un hosting fiabil pentru site-uri cu protecție DDoS, servere VPS VDS | ProHoster