Ciao a tutti nel blog, ecco il quarto post della serie su Quarkus!

parlava di come Quarkus unisce MicroProfile e Spring. Ricordiamo che è posizionato come «Java subatomico super veloce», un «stack Java orientato a Kubernetes, progettato per GraalVM e OpenJDK HotSpot e costruito con le migliori librerie e standard». Oggi mostreremo come modernizzare le applicazioni Java esistenti sfruttando le potenzialità di Quarkus, prendendo come esempio , che utilizza le tecnologie supportate in Quarkus come CDI e Servlet 3.
È importante sottolineare che sia Quarkus che JBoss EAP puntano a utilizzare strumenti prevalentemente basati su standard. Non hai un'applicazione che gira su JBoss EAP? Nessun problema, può essere facilmente migrata dal tuo attuale server applicazioni a JBoss EAP utilizzando . Dopo di che, la versione finale e funzionante del codice modernizzato sarà disponibile nel repository , nel modulo .
Per la scrittura di questo post sono stati utilizzati , principalmente e Costruire un .
Iniziamo con il codice
Per prima cosa, creiamo una copia locale del repository :
$ git clone https://github.com/jboss-developer/jboss-eap-quickstarts.git
Cloning into '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/
Diamo un'occhiata al funzionamento del progetto helloworld
In effetti, il senso di questa applicazione è chiaro dal titolo, ma procederemo a modernizzare il suo codice in modo scientifico. Quindi, iniziamo esaminando questa applicazione nella sua forma originale.
Avviamo helloworld
1. Apriamo il terminale e navighiamo fino alla cartella principale di JBoss EAP (puoi scaricarlo ), ovvero nella cartella EAP_HOME.
2. Avviamo il server JBoss EAP con il profilo predefinito:
$ EAP_HOME/bin/standalone.sh
Nota: su Windows usare lo script EAP_HOMEbinstandalone.bat.
Dopo qualche secondo, nel log dovrebbe apparire più o meno quanto segue:
[org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.2.0.GA (WildFly Core 6.0.11.Final-redhat-00001) avviato in 3315 ms - Avviati 306 di 527 servizi (321 servizi sono pigri, passivi o su richiesta)
3. Apriamo nel browser :8080 e vediamo questo:

Fig. 1. Homepage di JBoss EAP.
4. Seguiamo le istruzioni nel manuale : distribuiamo helloworld e eseguiamo (dalla cartella radice del progetto) il seguente comando:
$ mvn clean install wildfly:deploy
Dopo l'esecuzione riuscita di questo comando, nel log vedremo circa quanto segue:
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8.224 s
Quindi, la prima distribuzione dell'applicazione helloworld su JBoss EAP ha impiegato poco più di 8 secondi.
Testiamo helloworld
Seguendo rigorosamente il manuale , apriamo nel browser :8080/helloworld e vediamo questo:

Fig. 2. Hello World originale da JBoss EAP.
Apportiamo modifiche
Modifichiamo il parametro di ingresso createHelloMessage(String name) da World a Marco:
writer.println("<h1>" + helloService.createHelloMessage("Marco") + "</h1>");
Eseguiamo nuovamente il seguente comando:
$ mvn clean install wildfly:deploy
Poi aggiorniamo la pagina nel browser e vediamo che il testo è cambiato:

Fig. 3. Hello Marco in JBoss EAP.
Annulliamo la distribuzione di helloworld e fermiamo JBoss EAP
Questo non è obbligatorio, ma se desideri annullare la distribuzione, puoi farlo con il seguente comando:
$ mvn clean install wildfly:undeploy
Per fermare l'istanza di JBoss EAP, basta premere Ctrl+C nella finestra del terminale.
Modernizziamo helloworld
Adesso ci occupiamo dell'aggiornamento dell'applicazione di origine helloworld.
Creiamo un nuovo ramo
Creiamo un nuovo ramo di lavoro dopo il completamento del progetto quickstart:
$ git checkout -b quarkus 7.2.0.GA
Modifichiamo il file pom.xml
Inizieremo a modificare l'applicazione dal file pom.xml. Affinché Quarkus possa inserire i blocchi XML, eseguiamo il seguente comando nella cartella helloworld:
$ mvn io.quarkus:quarkus-maven-plugin:0.23.2:create
Per la stesura di questo articolo è stata utilizzata la versione 0.23.2. Quarkus rilascia frequentemente nuove versioni, puoi scoprire qual è l'ultima versione disponibile sul sito .
Il comando sopra riportato inserirà nel pom.xml i seguenti elementi:
- La proprietà , che definisce la versione di Quarkus da utilizzare.
- Il blocco per importare il BOM di Quarkus (bill of materials), così da non dover aggiungere la versione per ciascuna dipendenza di Quarkus.
- Il plugin quarkus-maven-plugin, responsabile per l'imballaggio dell'applicazione e che fornisce la modalità di sviluppo.
- Il profilo nativo per creare file eseguibili dell'applicazione.
Inoltre, apportiamo manualmente le seguenti modifiche al pom.xml:
- Estraiamo il tag dal blocco e lo poniamo sopra il tag . Dato che nel passaggio successivo rimuoveremo il blocco , è necessario mantenere .
- Rimuoviamo il blocco , poiché a questo punto l'applicazione non avrà più bisogno del pom genitore di JBoss per lavorare con Quarkus.
- Aggiungiamo il tag e lo poniamo sotto il tag . Puoi specificare il numero di versione che vuoi.
- Rimuoviamo il tag , poiché questa applicazione non è più un WAR, ma un normale JAR.
- Modifichiamo le seguenti dipendenze:
- Cambiamo la dipendenza javax.enterprise:cdi-api in io.quarkus:quarkus-arc, rimuovendo provided, poiché (secondo la documentazione) questa estensione di Quarkus fornisce l'injection della dipendenza CDI.
- Cambiamo la dipendenza org.jboss.spec.javax.servlet:jboss-servlet-api_4.0_spec in io.quarkus:quarkus-undertow, rimuovendo provided, poiché (secondo la documentazione) questa estensione di Quarkus fornisce supporto per i servlet.
- Rimuoviamo la dipendenza org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec, poiché è inclusa nelle dipendenze che abbiamo appena modificato.
La versione del file pom.xml con tutte le modifiche è disponibile all'indirizzo .
Si prega di notare che il comando sopra riportato mvn io.quarkus:quarkus-maven-plugin:0.23.2:create non modifica solo il file pom.xml, ma aggiunge anche una serie di componenti al progetto, in particolare i seguenti file e cartelle:
- Il file mvnw e mvnw.cmd e la cartella .mvn: il Maven Wrapper consente di eseguire progetti Maven con una specifica versione di Maven senza la necessità di installare tale versione.
- La cartella docker (nella directory src/main/): qui si trovano esempi di file Dockerfile per le modalità native e jvm (assieme al file .dockerignore).
- La cartella resources (nella directory src/main/): qui si trova un file vuoto application.properties e un esempio di pagina iniziale Quarkus index.html (per ulteriori dettagli, vedere Esegui il modernized helloworld).
Avviamo helloworld
Per testare l'applicazione, utilizziamo quarkus:dev, che avvia Quarkus in modalità di sviluppo (per ulteriori dettagli, vedere questa sezione nella guida su ).
Nota: questo passaggio porterà prevedibilmente a un errore, poiché non abbiamo ancora apportato tutte le modifiche necessarie.
Ora eseguiamo il comando per vedere come funziona:
$ ./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(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)
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
Quindi, non funziona... Perché?
L'eccezione UnsatisfiedResolutionException indica la classe HelloService, che è un membro della classe HelloWorldServlet (membro Java: org.jboss.as.quickstarts.helloworld.HelloWorldServlet#helloService). Il problema è che HelloWorldServlet ha bisogno di un'istanza iniettata di HelloService, e non riesce a trovarla (anche se entrambe le classi si trovano nello stesso pacchetto).
È il momento di tornare a e leggere come funziona in Quarkus , e quindi anche (CDI). Pertanto, apriamo la guida su Contexts and Dependency Injection e nella sezione leggiamo: «Una classe bean che non ha un'annotazione bean definita non viene cercata».
Guardiamo la classe HelloService: in essa effettivamente non c'è un'annotazione di questo tipo. Quindi, bisogna aggiungerla affinché Quarkus possa cercare e trovare il bean. E poiché questo è un oggetto stateless, possiamo tranquillamente aggiungere l'annotazione @ApplicationScoped nel seguente modo:
@ApplicationScoped
public class HelloService {
Nota: qui l'ambiente di sviluppo potrebbe chiederti di aggiungere il pacchetto richiesto (vedi la riga sottostante), e dovrai farlo manualmente, in questo modo:
import javax.enterprise.context.ApplicationScoped;
Se non sei sicuro di quale ambito utilizzare quando non è assegnato per il bean originale, consulta la documentazione. .
Adesso riproviamo a lanciare l'applicazione con il comando ./mvnw compile quarkus:dev:
$ ./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] 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]
Ora tutto funziona senza errori.
Avviamo il helloworld modernizzato.
Come indicato nel log, apriamo nel browser :8080 (pagina iniziale predefinita di Quarkus) e vediamo questo:

Fig. 4. Pagina iniziale di Quarkus dev.
Nell'annotazione WebServlet di questa applicazione è definito il seguente contesto:
@WebServlet("/HelloWorld")
public class HelloWorldServlet extends HttpServlet {
Quindi andiamo nel browser su :8080/HelloWorld e vediamo quanto segue:

Fig. 5: Pagina The Quarkus dev per l'applicazione Hello World.
Ecco, tutto funziona.
Ora apportiamo modifiche al codice. Si noti che il comando ./mvnw compile quarkus:dev è ancora attivo e non intendiamo interromperlo. Proviamo ora ad applicare gli stessi – molto semplici – cambiamenti al codice stesso e vediamo come Quarkus facilita la vita dello sviluppatore:
writer.println("<h1>" + helloService.createHelloMessage("Marco") + "</h1>");
Salviamo il file e poi aggiorniamo la pagina web per vedere la scritta Hello Marco, come mostrato nello screenshot qui sotto:

Fig. 6. Pagina Hello Marco in Quarkus dev.
Ora verifichiamo l'output nel terminale:
INFO [io.qua.dev] (vert.x-worker-thread-3) Rilevati file sorgente modificati, ricompilazione [/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 arrestato in 0.003s
INFO [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Inizio dell'augmentazione di quarkus
INFO [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Augmentazione di quarkus completata in 232ms
INFO [io.quarkus] (vert.x-worker-thread-3) Quarkus 0.23.2 avviato in 0.257s. In ascolto su: http://0.0.0.0:8080
INFO [io.quarkus] (vert.x-worker-thread-3) Profilo dev attivato. Live Coding attivato.
INFO [io.quarkus] (vert.x-worker-thread-3) Funzionalità installate: [cdi]
INFO [io.qua.dev] (vert.x-worker-thread-3) Tempo totale per la sostituzione attiva: 0.371s
L'aggiornamento della pagina ha attivato il rilevamento delle modifiche nel codice sorgente, e Quarkus ha eseguito automaticamente la procedura di «stop-start». E tutto questo è avvenuto in soli 0.371 secondi (eccola, quella «Java subatomica super veloce»).
Stiamo costruendo helloworld in un pacchetto JAR
Ora che il codice funziona come dovrebbe, impacchettiamolo con il seguente comando:
$ ./mvnw clean package
Questo comando crea due file JAR nella cartella /target: il file helloworld-.jar, che rappresenta il normale artefatto creato dal comando Maven insieme con le classi e le risorse del progetto. E il file helloworld—runner.jar, che rappresenta un JAR eseguibile.
Si prega di notare che questo non è un uber-jar, poiché tutte le dipendenze sono semplicemente copiate nella cartella /target/lib (e non imballate nel file JAR). Pertanto, per eseguire questo JAR da un'altra cartella o su un altro host, è necessario copiare sia il file JAR stesso che la cartella /lib, considerando che l'elemento Class-Path nel file MANIFEST.MF nel pacchetto JAR contiene un elenco esplicito dei JAR dalla cartella lib.
Per scoprire come creare applicazioni uber-jar, consultare la guida .
Esecuzione di helloworld, impacchettato in JAR
Ora possiamo eseguire il nostro JAR utilizzando il comando standard java:
$ java -jar ./target/helloworld-<version>-runner.jar
INFO [io.quarkus] (main) Quarkus 0.23.2 avviato in 0.673s. In ascolto su: http://0.0.0.0:8080
INFO [io.quarkus] (main) Profilo prod attivato.
INFO [io.quarkus] (main) Funzionalità installate: [cdi]
Dopo che tutto ciò è stato eseguito, vai nel browser su :8080 e verifica che tutto funzioni come previsto.
Compilazione di helloworld in un file eseguibile nativo
Quindi, il nostro helloworld funziona come un'applicazione Java standalone, utilizzando le dipendenze di Quarkus. Ma possiamo andare oltre e trasformarlo in un file eseguibile nativo.
Installazione di GraalVM
Prima di tutto, è necessario installare gli strumenti richiesti:
1. Scarica GraalVM 19.2.0.1 da .
2. Estraiamo l'archivio caricato:
$ tar xvzf graalvm-ce-linux-amd64-19.2.0.1.tar.gz
3. Accediamo alla cartella untar.
4. Eseguiamo il comando sottostante per scaricare e aggiungere l'immagine nativa:
$ ./bin/gu install native-image
5. Impostiamo la cartella creata al passo 2 nella variabile d'ambiente GRAALVM_HOME:
$ export GRAALVM_HOME={untar-folder}/graalvm-ce-19.2.0.1)
Ulteriori informazioni e istruzioni per l'installazione su altri sistemi operativi possono essere trovate nella guida .
Compiliamo helloworld in un file eseguibile nativo
Consultiamo la guida : «Ora creiamo un file eseguibile nativo per la nostra applicazione, per ridurre il tempo di avvio e la dimensione su disco. Il file eseguibile conterrà tutto il necessario per eseguire l'applicazione, inclusa la JVM (o meglio, una versione ridotta che contiene solo ciò che è necessario per l'esecuzione dell'applicazione) e la nostra applicazione stessa».
Per creare un file eseguibile nativo, è necessario attivare il profilo nativo di Maven:
$ ./mvnw package -Pnative
La nostra compilazione ha impiegato un minuto e 10 secondi, e il file finale helloworld-runner è stato creato nella cartella /target.
Eseguiamo il file eseguibile nativo helloworld
Nella fase precedente abbiamo ottenuto il file eseguibile /target/helloworld—runner. Ora lo avviamo:
$ ./target/helloworld-<version>-runner
INFO [io.quarkus] (main) Quarkus 0.23.2 avviato in 0.006s. In ascolto su: http://0.0.0.0:8080
INFO [io.quarkus] (main) Profilo prod attivato.
INFO [io.quarkus] (main) Funzionalità installate: [cdi]
Apertura di nuovo nel browser :8080 e controlliamo che tutto funzioni come previsto.
Continua a seguire!
Riteniamo che il metodo di modernizzazione delle applicazioni Java utilizzando le funzionalità di Quarkus, esaminato in questo post (anche se con un esempio molto semplice), debba essere applicato attivamente nella vita reale. In questo processo, probabilmente ti imbatterai in una serie di problemi, la cui soluzione esamineremo parzialmente nel prossimo post, dove parleremo di come misurare il consumo di memoria per valutare il miglioramento delle prestazioni, una parte importante dell'intero processo di modernizzazione delle applicazioni.
Fonte: habr.com
