Hej alle sammen i denne blog, og her er det fjerde indlæg i serien om Quarkus!

handlede om, hvordan Quarkus kombinerer MicroProfile og Spring. Lad os huske, at det positioneres som "ultra-hurtig subatomisk Java", også kaldet "Kubernetes-orienteret Java-stak designet til GraalVM og OpenJDK HotSpot og bygget med de bedste biblioteker og standarder". I dag vil vi vise, hvordan man moderniserer eksisterende Java-applikationer ved at udnytte Quarkus' muligheder, ved eksempel fra , som benytter Quarkus-understøttede teknologier CDI og Servlet 3.
Det er vigtigt at bemærke, at både Quarkus og JBoss EAP fokuserer på at bruge værktøjer, der er så meget som muligt bygget på standarder. Har du ikke en applikation, der kører på JBoss EAP? Ikke noget problem, den kan nemt overføres fra den nuværende applikationsserver til JBoss EAP ved hjælp af . Herefter vil den endelige og fungerende version af den moderniserede kode være tilgængelig i depotet , i modulet .
I skrivelsen af dette indlæg blev der brugt , hovedsageligt og Building a .
Lad os få koden
Først opretter vi et lokalt klon af depotet :
$ git clone https://github.com/jboss-developer/jboss-eap-quickstarts.git
Kloning ind i 'jboss-eap-quickstarts'...
remote: Tæller objekter: 148133, færdig.
remote: I alt 148133 (delta 0), genbrugt 0 (delta 0), pakke-genbrugt 148133
Modtage objekter: 100% (148133/148133), 59.90 MiB | 7.62 MiB/s, færdig.
Løser deltas: 100% (66476/66476), færdig.
$ cd jboss-eap-quickstarts/helloworld/
Lad os se, hvordan den oprindelige helloworld virker
Selve essensen af denne applikation kan forstås ud fra navnet, men vi vil modernisere dens kode strengt videnskabeligt. Så lad os først se på denne applikation i dens oprindelige form.
Køre helloworld
1. Åbn terminalen og navigér til rodmappen for JBoss EAP (den kan downloades ), dvs. mappen EAP_HOME.
2. Start JBoss EAP-serveren med standardprofilen:
$ EAP_HOME/bin/standalone.sh
Bemærk: på Windows bruges scriptet EAP_HOMEbinstandalone.bat til at starte.
Efter et par sekunder skulle loggen vise noget som følgende:
[org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.2.0.GA (WildFly Core 6.0.11.Final-redhat-00001) startet på 3315ms - Startede 306 af 527 tjenester (321 tjenester er late, passive eller on-demand)
3. Åbn i browseren :8080 og se dette:

Figur 1. Hjemmesiden for JBoss EAP.
4. Følg instruktionerne i vejledningen : vi starterprogrammet helloworld og kør følgende kommando (fra rodmappen for projektet):
$ mvn clean install wildfly:deploy
Efter en succesfuld udførelse af denne kommando vil vi se noget som følgende i loggen:
[INFO] ------------------------------------------------------------------------
[INFO] BYG SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total tid: 8.224 s
Så den første implementering af applikationen helloworld på JBoss EAP tog lidt over 8 sekunder.
Tester helloworld
Følg guiden nøje , åbner i browseren :8080/helloworld og ser følgende:

Fig. 2. Den originale Hello World fra JBoss EAP.
Foretag ændringer
Ændrer inputparameteren createHelloMessage(String name) fra World til Marco:
writer.println("<h1>" + helloService.createHelloMessage("Marco") + "</h1>");
Kør igen følgende kommando:
$ mvn clean install wildfly:deploy
Derefter opdaterer vi siden i browseren og ser, at teksten er ændret:

Fig. 3. Hello Marco i JBoss EAP.
Rul implementeringen af helloworld tilbage og afslut JBoss EAP
Dette er ikke nødvendigt, men hvis du vil annullere implementeringen, kan du gøre det med følgende kommando:
$ mvn clean install wildfly:undeploy
For at afslutte instansen af JBoss EAP skal du blot trykke Ctrl+C i terminalvinduet.
Modernisere helloworld
Nu vil vi opgradere den oprindelige applikation helloworld.
Opret en ny gren
Opret en ny arbejdsgren, efter at projektet quickstart er færdiggjort:
$ git checkout -b quarkus 7.2.0.GA
Ændre pom.xml-filen
Vi begynder at ændre applikationen med pom.xml-filen. For at Quarkus kan indsætte XML-blokke i den, kører vi følgende kommando i mappen helloworld:
$ mvn io.quarkus:quarkus-maven-plugin:0.23.2:create
Version 0.23.2 blev brugt til at skrive denne artikel. Quarkus får ofte nye versioner; du kan finde den nyeste version på siden .
Den ovenstående kommando vil indsætte følgende elementer i pom.xml:
- Egenskaben , som angiver den version af Quarkus, der anvendes.
- Blokken til at importere Quarkus BOM (bill of materials), så du ikke skal tilføje versionen for hver Quarkus-afhængighed.
- Plugin-en quarkus-maven-plugin, som håndterer pakningen af applikationen og giver udviklingstilstand.
- Nativeprofil til oprettelse af eksekverbare filer af applikationen.
Desuden foretager vi manuelt følgende ændringer i pom.xml:
- Vi flytter -tagget fra blokken og placerer det over -tagget. Da vi på næste trin vil fjerne blokken , skal vi bevare .
- Fjerner blokken , da denne applikation ikke længere har brug for en forældre-pom fra JBoss, når den arbejder med Quarkus.
- Tilføjer tagget og placerer det under tagget . Versionsnummeret kan angives, som du ønsker.
- Fjerner tagget , da denne applikation ikke længere er en WAR, men en almindelig JAR.
- Modificerer følgende afhængigheder:
- Ændrer afhængigheden javax.enterprise:cdi-api til io.quarkus:quarkus-arc ved at fjerne provided, da (ifølge dokumentationen) dette Quarkus-udvidelse sikrer CDI afhængighedsinjektion.
- Ændrer afhængigheden org.jboss.spec.javax.servlet:jboss-servlet-api_4.0_spec til io.quarkus:quarkus-undertow ved at fjerne provided, da (ifølge dokumentationen) dette Quarkus-udvidelse sikrer servlet-understøttelse.
- Fjerner afhængigheden org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec, da de medfølger de afhængigheder, vi netop har ændret.
Versionen af pom.xml-filen med alle ændringerne kan findes på .
Bemærk, at den ovenfor nævnte kommando mvn io.quarkus:quarkus-maven-plugin:0.23.2:create ikke kun ændrer pom.xml-filen, men også tilføjer en række komponenter til projektet, nemlig følgende filer og mapper:
- Filene mvnw og mvnw.cmd samt mappen .mvn: Maven Wrapper gør det muligt at køre Maven-projekter med en specificeret version af Maven uden at skulle installere denne version.
- Mappen docker (i kataloget src/main/): her ligger eksempler på Dockerfile-filer til native og jvm-tilstande (sammen med filen .dockerignore).
- Mappen resources (i kataloget src/main/): her ligger en tom application.properties-fil og et eksempel på Quarkus startside index.html (se nærmere i afsnittet Kør den moderniserede helloworld).
Kører helloworld
For at teste applikationen bruger vi quarkus:dev, som kører Quarkus i udviklingstilstand (se nærmere i dette afsnit i vejledningen om ).
Bemærk: dette trin vil forventeligt føre til en fejl, da vi ikke har foretaget alle nødvendige ændringer endnu.
Nu kører vi kommandoen for at se, hvordan det fungerer:
$ ./mvnw compile quarkus:dev
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------------
[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(Native Method)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Delegating MethodAccessorImpl.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
Jamen, det virker ikke... Hvorfor?
Undtagelsen UnsatisfiedResolutionException peger på klassen HelloService, som er et medlem af klassen HelloWorldServlet (java medlem: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService). Problemet er, at HelloWorldServlet har brug for en injiceret instans af HelloService, og den kan ikke findes (selvom begge klasser er i samme pakke).
Det er tid til at vende tilbage til og læse om, hvordan , og dermed også (CDI). Vi åbner derfor vejledningen til Contexts and Dependency Injection og læser i afsnittet : «Bean-klassen, som ikke har en definerende bean-annotation, bliver ikke søgt».
Vi ser på klassen HelloService – den har faktisk ikke sådan en annotation. Derfor skal vi tilføje den, så Quarkus kan søge og finde bean'en. Og da dette er en stateless-objekt, kan vi uden videre tilføje annoteringen @ApplicationScoped som følger:
@ApplicationScoped
public class HelloService {
Bemærk: her kan udviklingsmiljøet bede dig om at tilføje den nødvendige pakke (se linjen nedenfor), og det skal gøres manuelt, sådan her:
import javax.enterprise.context.ApplicationScoped;
Hvis du er i tvivl om, hvilken scope der skal bruges, når den oprindelige bean ikke er angivet overhovedet, så studer dokumentationen .
Nu prøver vi igen at starte applikationen med kommandoen .\/mvnw compile quarkus:dev:
$ .\/mvnw compile quarkus:dev
[INFO] Scanner efter projekter...
[INFO]
[INFO] ----------------< org.jboss.eap.quickstarts:helloworld >----------------
[INFO] Bygger Quickstart: helloworld quarkus
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ helloworld ---
[INFO] Bruger 'UTF-8' encoding til at kopiere filtrerede ressourcer.
[INFO] Kopierer 2 ressourcer
[INFO]
[INFO] --- maven-compiler-plugin:3.1:compile (default-compile) @ helloworld ---
[INFO] Ændringer opdaget - kompilerer modulet igen!
[INFO] Kompilerer 2 kildefiler til \/home\/mrizzi\/git\/forked\/jboss-eap-quickstarts\/helloworld\/target\/classes
[INFO]
[INFO] --- quarkus-maven-plugin:0.23.2:dev (default-cli) @ helloworld ---
Lytter efter transport dt_socket på adresse: 5005
INFO [io.qua.dep.QuarkusAugmentor] (main) Begynder quarkus augmentation
INFO [io.qua.dep.QuarkusAugmentor] (main) Quarkus augmentation fuldført på 576ms
INFO [io.quarkus] (main) Quarkus 0.23.2 startet på 1.083s. Lytter på: http:\/\/0.0.0.0:8080
INFO [io.quarkus] (main) Profil dev aktiveret. Live Coding aktiveret.
INFO [io.quarkus] (main) Installerede funktioner: [cdi]
Nu kører alt uden fejl.
Starter den opdaterede helloworld
Som nævnt i loggen, åbner vi i browseren :8080 (Quarkus standard startside) og ser følgende:

Fig. 4. Quarkus dev startside.
I annoteringen WebServlet for denne applikation er følgende kontekstdefinerende anført:
@WebServlet("\/HelloWorld")
public class HelloWorldServlet extends HttpServlet {
Så lad os åbne browseren og navigere til :8080/HelloWorld og se følgende:

Fig. 5: The Quarkus dev-side for Hello World-applikationen.
Der, nu fungerer det hele.
Og nu foretager vi ændringer i koden. Bemærk, at kommandoen ./mvnw compile quarkus:dev stadig kører, og vi har ikke tænkt os at stoppe den. Lad os nu prøve at anvende de samme - meget trivielle - ændringer til selve koden og se, hvordan Quarkus gør livet lettere for udvikleren:
writer.println("<h1>" + helloService.createHelloMessage("Marco") + "</h1>");
Vi gemmer filen og opdaterer derefter web-siden for at se teksten Hello Marco som vist på skærmbilledet nedenfor:

Fig. 6. Hello Marco-siden i Quarkus dev.
Lad os nu tjekke output i terminalen:
INFO [io.qua.dev] (vert.x-worker-thread-3) Ændrede kildefiler opdaget, genkompilerer [/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 stoppet på 0.003s
INFO [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Begynder quarkus-forstærkning
INFO [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Quarkus-forstærkning gennemført på 232ms
INFO [io.quarkus] (vert.x-worker-thread-3) Quarkus 0.23.2 startet på 0.257s. Lytter på: http://0.0.0.0:8080
INFO [io.quarkus] (vert.x-worker-thread-3) Profil dev aktiveret. Live Coding aktiveret.
INFO [io.quarkus] (vert.x-worker-thread-3) Installerede funktioner: [cdi]
INFO [io.qua.dev] (vert.x-worker-thread-3) Hot replace total tid: 0.371s
Opdatering af siden udløste registreringen af ændringer i kildekoden, og Quarkus fuldførte automatisk "stop-start"-proceduren. Og alt dette blev udført på kun 0.371 sekunder (her har vi "den ultrahurtige subatomare Java").
Bygger helloworld til en JAR-pakke
Nu hvor koden fungerer som den skal, pakker vi den med følgende kommando:
$ ./mvnw clean package
Denne kommando opretter to JAR-filer i mappen /target: filen helloworld-.jar, der repræsenterer det standardartefakt, der er bygget af Maven-kommandoen sammen med klasserne og ressourcerne i projektet. Og filen helloworld—runner.jar, som repræsenterer en eksekverbar JAR.
Bemærk, at dette ikke er en uber-jar, da alle afhængigheder blot kopieres til mappen /target/lib (og ikke pakkes i JAR-filen). Derfor, for at køre denne JAR fra en anden mappe eller på en anden vært, skal både JAR-filen og mappen /lib kopieres over, idet man skal tage højde for, at Class-Path-elementet i MANIFEST.MF-filen i JAR-pakken indeholder en eksplicit opregning af JAR’er fra lib-mappen.
For at finde ud af, hvordan man lager uber-jar-applikationer, se venligst vejledningen .
Starter helloworld pakket i JAR
Nu kan vi starte vores JAR med den standard kommando java:
$ java -jar ./target/helloworld--runner.jar
INFO [io.quarkus] (main) Quarkus 0.23.2 startet på 0.673s. Lytter på: http://0.0.0.0:8080
INFO [io.quarkus] (main) Profil prod aktiveret.
INFO [io.quarkus] (main) Installerede funktioner: [cdi]
Når alt dette er udført, skal du åbne en browser og gå til :8080 og kontrollere, at alt fungerer som det skal.
Vi samler helloworld til en nativer eksekverbar fil
Så vores helloworld fungerer som en standalone Java-applikation ved hjælp af Quarkus-afhængigheder. Men vi kan tage det et skridt videre og konvertere det til en nativer eksekverbar fil.
Installerer GraalVM
Først skal vi installere de nødvendige værktøjer:
1. Download GraalVM 19.2.0.1 fra .
2. Udpak den downloadede arkiv:
$ tar xvzf graalvm-ce-linux-amd64-19.2.0.1.tar.gz
3. Gå til mappen, der blev udpakket.
4. Kør følgende kommando for at downloade og tilføje den nativer billede:
$ ./bin/gu install native-image
5. Sæt mappen, der blev oprettet i trin 2, til miljøvariablen GRAALVM_HOME:
$ export GRAALVM_HOME={untar-folder}/graalvm-ce-19.2.0.1)
Yderligere oplysninger og installationsvejledninger til andre operativsystemer kan findes i manualen .
Vi bygger helloworld til en nativer eksekverbar fil
Læs manualen : «Nu opretter vi en nativer eksekverbar fil til vores applikation for at reducere opstartstiden og diskpladsen. Den eksekverbare fil vil have alt, hvad der er nødvendigt for at køre applikationen, inklusive JVM-maskinen (eller rettere en forkortet version, der kun indeholder det, der er nødvendigt for at køre applikationen) og selve vores applikation».
For at oprette en nativer eksekverbar fil skal du inkludere native-profilen i Maven:
$ ./mvnw package -Pnative
Vores samling tog et minut og 10 sekunder, og den endelige fil helloworld-runner blev oprettet i mappen /target.
Vi kører den nativer eksekverbare fil helloworld
I det forrige trin fik vi den eksekverbare fil /target/helloworld-runner. Lad os nu køre den:
$ ./target/helloworld--runner
INFO [io.quarkus] (main) Quarkus 0.23.2 startet på 0.006s. Lytter på: http://0.0.0.0:8080
INFO [io.quarkus] (main) Profil prod aktiveret.
INFO [io.quarkus] (main) Installerede funktioner: [cdi]
Vi åbner igen i browseren :8080 og kontrollerer, at alt fungerer som det skal.
Fortsættes!
Vi mener, at den metode til modernisering af Java-applikationer med brug af Quarkus’ funktioner, som vi har diskuteret i dette indlæg (omend med et simpelt eksempel) , bør anvendes aktivt i det virkelige liv. Du vil sandsynligvis støde på en række problemer, hvis løsninger delvist vil blive behandlet i det næste indlæg, hvor vi vil diskutere, hvordan man måler hukommelsesforbrug for at vurdere forbedringer i ydeevnen, som er en vigtig del af hele moderniseringsprocessen for applikationer.
Kilde: habr.com
