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

byl o tom, jak Quarkus kombinuje MicroProfile a Spring. Připomeňme, že 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 , 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í . Po tom bude finální a funkční verze modernizovaného kódu dostupná v repozitáři , v modulu .
Při psaní tohoto příspěvku byly použity , především a Building a .
Získáváme kód
Nejprve vytvoříme lokální klon repozitáře :
$ 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 ), 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 :8080 a vidíte toto:

Obr. 1. Domovská stránka JBoss EAP.
4. Postupujte podle pokynů v příručce : 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 , otevřeme v prohlížeči :8080/helloworld a vidíme toto:

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:

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 .
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:
- Vyjmeme tag z bloku a umístíme ho nad tag . Jelikož v dalším kroku odstraníme blok , je nutné uchovat.
- Odstraňujeme blok , protože při práci s Quarkus tato aplikace již nepotřebuje rodičovský pom od JBoss.
- Přidáváme tag a umisťujeme jej pod tag . Číslo verze můžete uvést libovolné.
- Odstraňujeme tag , protože tato aplikace již není WAR, ale běžný JAR.
- Modifikujeme následující závislosti:
- 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.
- 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ů.
- 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 .
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 ).
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 a přečíst si, jak funguje v Quarkus , a tedy i (CDI). Otevřeme příručku Kontexty a injekce závislostí a v sekci 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 .
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 :8080 (výchozí stránka Quarkus) a vidíme toto:

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 :8080/HelloWorld a vidíme následující:

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:

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 .
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 :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 .
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 .
Sestavujeme helloworld do nativního spustitelného souboru
Přečtěte si průvodce : „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 :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
