Quarkus: modernizace aplikací na příkladu helloworld z JBoss EAP Quickstart

Ahoj všem na tomto blogu, a toto je čtvrtý příspěvek ze série o Quarkusu!

Quarkus: modernizace aplikací na příkladu helloworld z JBoss EAP Quickstart

Předchozí příspěvek byl o tom, jak Quarkus kombinuje MicroProfile a Spring. Připomeňme, že Quarkus se představuje jako "superrychlá subatomová Java", což je "Kubernetes-orientovaný Java stack, zaměřený na GraalVM a OpenJDK HotSpot, sestavený z nejlepších knihoven a standardů". Dnes ukážeme, jak modernizovat již existující Java aplikace a aktivovat možnosti Quarkusu na příkladu aplikace helloworld z repozitáře Red Hat JBoss Enterprise Application Platform (JBoss EAP) Quickstart, kde jsou použity technologie podporované v Quarkusu, CDI a Servlet 3.

Je důležité zmínit, že jak Quarkus, tak JBoss EAP se zaměřují na používání nástrojů, které jsou maximálně postavené na standardech. Nemáte aplikaci běžící na JBoss EAP? Nevadí, lze ji snadno přenést z aktuálního aplikačního serveru na JBoss EAP pomocí Red Hat Application Migration Toolkit. Po tom bude finální a funkční verze modernizovaného kódu dostupná v repozitáři github.com/mrizzi/jboss-eap-quickstarts/tree/quarkus, v modulu helloworld.

Při psaní tohoto příspěvku byly použity příručky k Quarkusu, především Creating Your First Application a Building a Native Executable.

Získáváme kód

Nejprve vytvoříme lokální klon repozitáře JBoss EAP quickstarts:

$ git clone https://github.com/jboss-developer/jboss-eap-quickstarts.git
Klone do 'jboss-eap-quickstarts'...
remote: Enumerování objektů: 148133, hotovo.
remote: Celkem 148133 (delta 0), znovu použito 0 (delta 0), pack-reused 148133
Přijímání objektů: 100% (148133/148133), 59.90 MiB | 7.62 MiB/s, hotovo.
Řešení deltas: 100% (66476/66476), hotovo.
$ cd jboss-eap-quickstarts/helloworld/

Podíváme se, jak funguje původní helloworld

Podstata této aplikace je z názvu jasná, ale kód budeme modernizovat vědecky. Takže nejprve se podíváme na tuto aplikaci v jejím původním stavu.

Spouštíme helloworld

1. Otevřete terminál a přejděte do kořenového adresáře JBoss EAP (můžete si ho stáhnout zde), tedy do složky EAP_HOME.

2. Spusťte server JBoss EAP s výchozím profilem:

$ EAP_HOME/bin/standalone.sh

Poznámka: na Windows se použije skript EAP_HOMEbinstandalone.bat.

Za pár sekund by se v logu mělo objevit přibližně následující:

[org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.2.0.GA (WildFly Core 6.0.11.Final-redhat-00001) spuštěn za 3315 ms - Spuštěno 306 ze 527 služeb (321 služeb je neaktivních, pasivních nebo na vyžádání)

3. Otevřete v prohlížeči 127.0.0.1:8080 a vidíte toto:

Quarkus: modernizace aplikací na příkladu helloworld z JBoss EAP Quickstart

Obr. 1. Domovská stránka JBoss EAP.

4. Postupujte podle pokynů v příručce Build and Deploy the Quickstart: rozbalujeme helloworld a vykonáváme (z kořenového adresáře projektu) následující příkaz:

$ mvn clean install wildfly:deploy

Po úspěšném vykonání tohoto příkazu uvidíme v logu přibližně následující:

[INFO] ------------------------------------------------------------------------ 
[INFO] BUILD SUCCESS 
[INFO] ------------------------------------------------------------------------ 
[INFO] Celkový čas: 8.224 s

Takže první nasazení aplikace helloworld na JBoss EAP trvalo něco málo přes 8 sekund.

Testujeme helloworld

Postupujeme přesně podle příručky Přistupujeme k aplikaci, otevřeme v prohlížeči 127.0.0.1:8080/helloworld a vidíme toto:

Quarkus: modernizace aplikací na příkladu helloworld z JBoss EAP Quickstart

Obr. 2. Původní Hello World z JBoss EAP.

Provádíme změny

Změňte vstupní parametr createHelloMessage(String name) z World na Marco:

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

Opět vykonáváme následující příkaz:

$ mvn clean install wildfly:deploy

Pak obnovíme stránku v prohlížeči a vidíme, že text se změnil:

Quarkus: modernizace aplikací na příkladu helloworld z JBoss EAP Quickstart

Obr. 3. Hello Marco v JBoss EAP.

Vracíme nasazení helloworld a ukončujeme práci JBoss EAP

Není to nutné, ale pokud chcete zrušit nasazení, můžete použít následující příkaz:

$ mvn clean install wildfly:undeploy

Abychom ukončili instanci JBoss EAP, jednoduše stiskněte Ctrl+C v okně terminálu.

Modernizujeme helloworld

Nyní se zaměříme na modernizaci původní aplikace helloworld.

Vytváříme novou větev

Vytváříme novou pracovní větev poté, co projekt quickstart skončí:

$ git checkout -b quarkus 7.2.0.GA

Změňte soubor pom.xml

S aplikací začneme měnit soubor pom.xml. Aby mohl Quarkus vkládat XML bloky, vykonáme následující příkaz v adresáři helloworld:

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

Při psaní tohoto článku byla použita verze 0.23.2. Quarkus často vydává nové verze, můžete zjistit, která verze je nejnovější, na stránkách github.com/quarkusio/quarkus/releases/latest.

Výše uvedený příkaz vloží do pom.xml následující prvky:

  • Vlastnost , která určuje používanou verzi Quarkus.
  • Blok pro import Quarkus BOM (bill of materials), aby nebylo nutné přidávat verzi pro každou závislost Quarkus.
  • Plugin quarkus-maven-plugin, který zodpovídá za balení aplikace a poskytuje režim pro vývoj.
  • Profil native pro vytváření spustitelných souborů aplikace.

Kromě toho v pom.xml ručně provádíme následující změny:

  1. Vyjmeme tag z bloku a umístíme ho nad tag . Jelikož v dalším kroku odstraníme blok , je nutné uchovat.
  2. Odstraňujeme blok , protože při práci s Quarkus tato aplikace již nepotřebuje rodičovský pom od JBoss.
  3. Přidáváme tag a umisťujeme jej pod tag . Číslo verze můžete uvést libovolné.
  4. Odstraňujeme tag , protože tato aplikace již není WAR, ale běžný JAR.
  5. Modifikujeme následující závislosti:
    1. Měníme závislost javax.enterprise:cdi-api na io.quarkus:quarkus-arc, přičemž odstraňujeme provided, protože (podle dokumentace) toto Quarkus rozšíření poskytuje injection závislostí CDI.
    2. Měníme závislost org.jboss.spec.javax.servlet:jboss-servlet-api_4.0_spec na io.quarkus:quarkus-undertow, přičemž odstraňujeme provided, protože (podle dokumentace) toto Quarkus rozšíření zajišťuje podporu servletů.
    3. Odstraňujeme závislost org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec, protože přichází v balíčku se závislostmi, které jsme právě změnili.

Verze souboru pom.xml se všemi změnami je k nalezení na adrese github.com/mrizzi/jboss-eap-quickstarts/blob/quarkus/helloworld/pom.xml.

Všimněte si, že výše uvedený příkaz mvn io.quarkus:quarkus-maven-plugin:0.23.2:create nejenže mění soubor pom.xml, ale také do projektu přidává řadu komponent, a to konkrétně následující soubory a složky:

  • Soubor mvnw a mvnw.cmd a složka .mvn: Maven Wrapper umožňuje spouštět projekty Maven v určité verzi Maven bez této verze instalované.
  • Složka docker (v adresáři src/main/): zde naleznete příklady souborů Dockerfile pro režimy native a jvm (spolu se souborem .dockerignore).
  • Složka resources (v adresáři src/main/): zde se nachází prázdný soubor application.properties a vzorová úvodní stránka Quarkus index.html (podrobněji viz Spuštění modernizovaného helloworld).

Spouštíme helloworld
Pro testování aplikace používáme quarkus:dev, který spouští Quarkus v režimu vývoje (podrobněji viz tato část v příručce k Development Mode).

Poznámka: tento krok očekávaně povede k chybě, protože jsme dosud neprovedli všechny potřebné změny.

Nyní spouštíme příkaz, abychom zkontrolovali, jak to funguje:

$ ./mvnw compile quarkus:dev
[INFO] Skenuji projekty...
[INFO]
[INFO] ----------------< org.jboss.eap.quickstarts:helloworld >----------------
[INFO] Vytváření rychlého startu: helloworld quarkus
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ helloworld ---
[INFO] Používání kódování 'UTF-8' pro kopírování filtrovaných zdrojů.
[INFO] Kopírování 2 zdrojů
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ helloworld ---
[INFO] Není co kompilovat - všechny třídy jsou aktuální
[INFO]
[INFO] --- quarkus-maven-plugin:0.23.2:dev (default-cli) @ helloworld ---
Naslouchání pro transport dt_socket na adrese: 5005
INFO  [io.qua.dep.QuarkusAugmentor] Začíná augmentace quarkusu
INFO  [org.jbo.threads] Verze JBoss Threads 3.0.0.Final
ERROR [io.qua.dev.DevModeMain] Nepodařilo se spustit quarkus: java.lang.RuntimeException: io.quarkus.builder.BuildException: Chyba při sestavení: Sestavení selhalo z důvodu chyb
	[error]: Krok sestavení io.quarkus.arc.deployment.ArcProcessor#validate vyvolal výjimku: javax.enterprise.inject.spi.DeploymentException: javax.enterprise.inject.UnsatisfiedResolutionException: Nevyřešená závislost pro typ org.jboss.as.quickstarts.helloworld.HelloService a kvalifikátory [@Default]
	- člen java: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService
	- deklarováno v tříde 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)
Způsobeno: javax.enterprise.inject.UnsatisfiedResolutionException: Nevyřešená závislost pro typ org.jboss.as.quickstarts.helloworld.HelloService a kvalifikátory [@Default]
	- člen java: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService
	- deklarováno v třídě 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

Tak to nefunguje… A proč?

Výjimka UnsatisfiedResolutionException se odkazuje na třídu HelloService, která je členem třídy HelloWorldServlet (java member: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService). Problém spočívá v tom, že HelloWorldServlet potřebuje injektovanou instanci HelloService, kterou se nedaří najít (i když obě třídy jsou ve stejném balíčku).

Je nejvyšší čas se vrátit k dokumentaci a přečíst si, jak funguje v Quarkus Injekce, a tedy i Kontexty a injekce závislostí (CDI). Otevřeme příručku Kontexty a injekce závislostí a v sekci Objevování beanů si přečteme: „Bean třída, která nemá určující bean anotaci, není vyhledávána.“

Podíváme se na třídu HelloService – v ní skutečně tato anotace není. Je tedy třeba ji přidat, aby Quarkus mohl bean vyhledávat a nalézt. A jelikož se jedná o stateless objekt, můžeme v klidu přidat anotaci @ApplicationScoped následujícím způsobem:

@ApplicationScoped
public class HelloService {

Poznámka: zde vás může vývojové prostředí požádat o přidání požadovaného balíčku (viz níže uvedený řádek), což je třeba provést ručně, takto:

import javax.enterprise.context.ApplicationScoped;

Pokud si nejste jisti, jakou oblast scope použít, když pro původní bean není vůbec zadána, prostudujte dokumentaci JSR 365: Kontexty a injekce závislostí pro Java 2.0—Výchozí scope.

Nyní znovu zkoušíme spustit aplikaci příkazem .\/mvnw compile quarkus:dev:

$ .\/mvnw compile quarkus:dev
[INFO] Skenuji projekty...
[INFO]
[INFO] ----------------< org.jboss.eap.quickstarts:helloworld >----------------
[INFO] Budování Quickstartu: helloworld quarkus
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ helloworld ---
[INFO] Používám kódování 'UTF-8' pro kopírování filtrovaných zdrojů.
[INFO] Kopíruji 2 zdroje
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ helloworld ---
[INFO] Zjistily se změny - překládám modul znovu!
[INFO] Překládám 2 zdrojové soubory do \/home\/mrizzi\/git\/forked\/jboss-eap-quickstarts\/helloworld\/target\/classes
[INFO]
[INFO] --- quarkus-maven-plugin:0.23.2:dev (default-cli) @ helloworld ---
Naslouchám transportu dt_socket na adrese: 5005
INFO  [io.qua.dep.QuarkusAugmentor] (main) Začínám augmentaci quarkus
INFO  [io.qua.dep.QuarkusAugmentor] (main) Augmentace quarkus byla dokončena za 576ms
INFO  [io.quarkus] (main) Quarkus 0.23.2 spuštěn za 1.083s. Naslouchá na: http:\/\/0.0.0.0:8080
INFO  [io.quarkus] (main) Aktivován profil dev. Aktivováno živé kódování.
INFO  [io.quarkus] (main) Nainstalované funkce: [cdi]

Nyní vše probíhá bez chyb.

Spouštíme modernizovaný helloworld
Jak je uvedeno v logu, otevřeme v prohlížeči 0.0.0.0:8080 (výchozí stránka Quarkus) a vidíme toto:

Quarkus: modernizace aplikací na příkladu helloworld z JBoss EAP Quickstart

Obr. 4. Výchozí stránka Quarkus dev.

V anotaci WebServlet má tato aplikace následující definici kontextu:

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

Proto jdeme v prohlížeči na 0.0.0.0:8080/HelloWorld a vidíme následující:

Quarkus: modernizace aplikací na příkladu helloworld z JBoss EAP Quickstart

Obr. 5: Stránka The Quarkus dev pro aplikaci Hello World.

No, funguje to.

Teď provedeme změny v kódu. Všimněte si, že příkaz ./mvnw compile quarkus:dev stále funguje a nemáme v plánu jej zastavit. Teď se pokusíme aplikovat stejnou – triviální – změnu na samotný kód a podíváme se, jak Quarkus usnadňuje život vývojářovi:

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

Uložíme soubor a pak aktualizujeme webovou stránku, abychom viděli nápis Hello Marco, jak je ukázáno na obrázku níže:

Quarkus: modernizace aplikací na příkladu helloworld z JBoss EAP Quickstart

Obr. 6. Stránka Hello Marco v Quarkus dev.

Teď zkontrolujeme výstup v terminálu:

INFO  [io.qua.dev] (vert.x-worker-thread-3) Zjaveny změny v zdrojových souborech, kompilace [\/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 zastaveno za 0.003s
INFO  [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Začátek augmentace quarkus
INFO  [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Augmentace quarkus dokončena za 232ms
INFO  [io.quarkus] (vert.x-worker-thread-3) Quarkus 0.23.2 spuštěn za 0.257s. Naslouchá na: http:\/\/0.0.0.0:8080
INFO  [io.quarkus] (vert.x-worker-thread-3) Aktivován profil dev. Aktivováno živé kódování.
INFO  [io.quarkus] (vert.x-worker-thread-3) Nainstalované funkce: [cdi]
INFO  [io.qua.dev] (vert.x-worker-thread-3) Celkový čas horké výměny: 0.371s

Aktualizace stránky spustila detekci změn v zdrojovém kódu, a Quarkus automaticky provedl proceduru „stop-start“. A to vše proběhlo za pouhých 0.371 sekundy (tady je, ta super rychlá subatomová Java).

Vytváříme helloworld do balíčku JAR
Teď, když kód funguje jak má, zabalíme jej následujícím příkazem:

$ ./mvnw clean package

Tento příkaz vytvoří dva JAR soubory ve složce \/target: soubor helloworld-.jar, který představuje standardní artefakt, vytvořený příkazem Maven spolu s třídami a zdroji projektu. A soubor helloworld—runner.jar, který představuje spustitelný JAR.

Všimněte si, že se nejedná o uber-jar, protože všechny závislosti jsou prostě zkopírovány do složky \/target\/lib (a nejsou zabaleny do souboru JAR). Proto, aby bylo možné tento JAR spustit z jiné složky nebo na jiném hostiteli, je třeba zkopírovat jak samotný JAR soubor, tak složku \/lib, přičemž prvek Class-Path v souboru MANIFEST.MF v balíčku JAR obsahuje explicitní seznam JARů ze složky lib.
Chcete-li se dozvědět, jak vytvářet aplikace uber-jar, podívejte se do příručky Vytváření Uber-Jar.

Spouštíme helloworld, zabaleno v JAR

Nyní můžeme spustit náš JAR pomocí standardního příkazu java:

$ java -jar ./target/helloworld--runner.jar
INFO  [io.quarkus] (main) Quarkus 0.23.2 spuštěn za 0.673s. Naslouchá na: http://0.0.0.0:8080
INFO  [io.quarkus] (main) Aktivován profil prod.
INFO  [io.quarkus] (main) Nainstalované funkce: [cdi]

Po dokončení všech těchto kroků přejděte do prohlížeče na 0.0.0.0:8080 a zkontrolujte, že to funguje, jak má.

Sestavujeme helloworld do nativního spustitelného souboru

Takže náš helloworld funguje jako samostatná Java aplikace s využitím závislostí Quarkus. Ale můžeme jít dál a přetvořit jej na nativní spustitelný soubor.

Instalujeme GraalVM
Nejprve je třeba nainstalovat potřebné nástroje:

1. Stáhněte GraalVM 19.2.0.1 z github.com/oracle/graal/releases/tag/vm-19.2.0.1.

2. Rozbalte stažený archiv:

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

3. Přejděte do rozbalené složky.

4. Spusťte níže uvedený příkaz pro stažení a přidání nativního obrazu:

$ ./bin/gu install native-image

5. Nastavte složku vytvořenou v kroku 2 do proměnné prostředí GRAALVM_HOME:

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

Další informace a pokyny k instalaci na jiných operačních systémech naleznete v průvodci Building a Native Executable—Prerequisites.

Sestavujeme helloworld do nativního spustitelného souboru
Přečtěte si průvodce Building a Native Executable—Producing a native executable: „A nyní vytvořme nativní spustitelný soubor pro naši aplikaci, abychom zkrátili čas jejího spuštění a velikost na disku. Spustitelný soubor bude obsahovat vše potřebné k spuštění aplikace, včetně JVM (přesněji řečeno, její zkrácené verze, která obsahuje pouze to, co je potřeba pro běh aplikace) a samotnou naši aplikaci.“

Abychom vytvořili nativní spustitelný soubor, musíme zahrnout nativní profil Maven:

$ ./mvnw package -Pnative

Naše sestavení trvalo jednu minutu a 10 sekund, a výstupní soubor helloworld-runner byl vytvořen ve složce /target.

Spouštíme nativní spustitelný soubor helloworld

V předchozím kroku jsme získali spustitelný soubor /target/helloworld-runner. Nyní jej spusťme:

$ ./target/helloworld--runner
INFO  [io.quarkus] (main) Quarkus 0.23.2 spuštěn za 0.006s. Naslouchá na: http://0.0.0.0:8080
INFO  [io.quarkus] (main) Aktivován profil prod.
INFO  [io.quarkus] (main) Nainstalované funkce: [cdi]

Opět otevřete v prohlížeči 0.0.0.0:8080 a zkontrolujte, že to funguje, jak má.

Pokračování příště!

Domníváme se, že metoda modernizace Java aplikací využívající schopnosti Quarkus, jak byla probrána v tomto příspěvku (i když na velmi jednoduchém příkladu), by měla být aktivně aplikována v reálném životě. Při tom se pravděpodobně setkáte s řadou problémů, derenž řešení částečně probereme v dalším příspěvku, kde se zaměříme na to, jak měřit spotřebu paměti k vyhodnocení zlepšení výkonu, což je důležitá součást celého procesu modernizace aplikací.

Zdroj: habr.com

Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery 🔥 Koupit spolehlivý hosting pro webové stránky s ochranou proti DDoS, VPS VDS servery | ProHoster