Ciao a tutti in questo blog, e benvenuti al quarto post della serie su Quarkus!

parlava di come Quarkus integri MicroProfile e Spring. Ricordiamo che è posizionato come «Java subatomica superveloce», noto anche come «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 capacità di Quarkus, usando come esempio , che utilizza le tecnologie CDI e Servlet 3 supportate in Quarkus.
È importante notare che sia Quarkus che JBoss EAP si concentrano sull'utilizzo di strumenti costruiti il più possibile su basi standard. Non hai un'applicazione che funziona su JBoss EAP? Nessun problema, può essere facilmente migrata dall'attuale server di applicazioni a JBoss EAP utilizzando . Dopodiché, la versione finale e funzionante del codice modernizzato sarà disponibile nel repository , nel modulo .
Per la redazione di questo post sono stati utilizzati , in particolare e Building a .
Acquisire il codice
Per prima cosa creiamo un clone locale del repository :
$ git clone https://github.com/jboss-developer/jboss-eap-quickstarts.git
Clonazione in 'jboss-eap-quickstarts'...
remote: Enumerating objects: 148133, done.
remote: Total 148133 (delta 0), reused 0 (delta 0), pack-reused 148133
Ricezione oggetti: 100% (148133/148133), 59.90 MiB | 7.62 MiB/s, done.
Risoluzione dei deltas: 100% (66476/66476), done.
$ cd jboss-eap-quickstarts/helloworld/
Vediamo come funziona l'originale helloworld
In effetti, il senso di questa applicazione è chiaro dal nome, ma modernizzeremo il suo codice in modo scientifico. Quindi iniziamo guardando questa applicazione nella sua forma originale.
Avviamo helloworld
1. Apri il terminale e vai alla radice della cartella JBoss EAP (che puoi scaricare ), ovvero alla cartella EAP_HOME.
2. Avvia il server JBoss EAP con il profilo predefinito:
$ EAP_HOME/bin/standalone.sh
Nota: su Windows per avviare si utilizza lo script EAP_HOME/bin/standalone.bat.
Dopo pochi secondi nel log dovrebbe apparire circa il seguente messaggio:
[org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.2.0.GA (WildFly Core 6.0.11.Final-redhat-00001) avviato in 3315ms - Avviati 306 dei 527 servizi (321 servizi sono lazy, passivi o su richiesta)
3. Apri nel browser :8080 e vedrai questo:

Fig. 1. Homepage di JBoss EAP.
4. Segui le istruzioni nella guida : deployiamo helloworld ed eseguiamo (dalla cartella principale del progetto) il seguente comando:
$ mvn clean install wildfly:deploy
Dopo l'esecuzione con successo di questo comando, nel log vedremo circa il seguente:
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 8.224 s
Quindi, il primo deploy dell'applicazione helloworld su JBoss EAP ha impiegato poco più di 8 secondi.
Testiamo helloworld
Agendo rigorosamente secondo la guida , apriamo nel browser :8080/helloworld e vediamo questo:

Fig. 2. Hello World originale da JBoss EAP.
Apportiamo delle modifiche
Cambiando il parametro d'ingresso createHelloMessage(String name) da World a Marco:
writer.println("<h1>" + helloService.createHelloMessage("Marco") + "</h1>");
Eseguiamo ancora 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 il deploy di helloworld e chiudiamo JBoss EAP
Non è obbligatorio, ma se vuoi annullare il deploy, puoi farlo con il seguente comando:
$ mvn clean install wildfly:undeploy
Per chiudere l'istanza di JBoss EAP, basta premere Ctrl+C nella finestra del terminale.
Modernizziamo helloworld
Ora ci occuperemo della modernizzazione dell'applicazione originale helloworld.
Creiamo un nuovo ramo
Creiamo un nuovo ramo di lavoro dopo che il progetto quickstart sarà completato:
$ git checkout -b quarkus 7.2.0.GA
Modifichiamo il file pom.xml
Inizieremo a modificare l'applicazione dal file pom.xml. Per permettere a Quarkus di inserire blocchi XML, eseguiremo il seguente comando nella cartella helloworld:
$ mvn io.quarkus:quarkus-maven-plugin:0.23.2:create
Nella stesura di questo articolo è stata utilizzata la versione 0.23.2. Quarkus riceve spesso nuove versioni, è possibile scoprire quale è l'ultima visitando il sito .
Il comando sopra inserirà nel pom.xml i seguenti elementi:
- La proprietà che definisce la versione di Quarkus utilizzata.
- Il blocco per importare il BOM (bill of materials) di Quarkus, così da non dover aggiungere la versione per ogni dipendenza di Quarkus.
- Il plugin quarkus-maven-plugin, responsabile dell'imballaggio dell'applicazione e che fornisce la modalità di sviluppo.
- Il profilo nativo per la creazione di file eseguibili dell'applicazione.
Inoltre, nel pom.xml apportiamo manualmente le seguenti modifiche:
- Estraiamo il tag dal blocco e lo posizioniamo sopra il tag . Poiché nel passo successivo rimuoveremo il blocco , è necessario mantenere .
- Rimuoviamo il blocco , poiché durante l'utilizzo di Quarkus questa applicazione non avrà più bisogno del pom genitore da JBoss.
- Aggiungiamo il tag e lo posizioniamo sotto il tag . Puoi specificare il numero di versione che desideri.
- Rimuoviamo il tag , poiché questa applicazione non è più un WAR, ma un normale JAR.
- Modifichiamo le seguenti dipendenze:
- Sostituiamo la dipendenza javax.enterprise:cdi-api con io.quarkus:quarkus-arc, rimuovendo provided, poiché (secondo la documentazione) questa estensione Quarkus fornisce l'iniezione delle dipendenze CDI.
- Sostituiamo la dipendenza org.jboss.spec.javax.servlet:jboss-servlet-api_4.0_spec con io.quarkus:quarkus-undertow, rimuovendo provided, poiché (secondo la documentazione) questa estensione Quarkus fornisce supporto per i servlet.
- Rimuoviamo la dipendenza org.jboss.spec.javax.annotation:jboss-annotations-api_1.3_spec, poiché è inclusa con le dipendenze che abbiamo appena modificato.
La versione del file pom.xml con tutte le modifiche si trova all'indirizzo .
Nota che il comando mvn io.quarkus:quarkus-maven-plugin:0.23.2:create non solo modifica il file pom.xml, ma aggiunge al progetto vari componenti, 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 della versione specificata senza installare quella versione.
- La cartella docker (nella directory src/main/): qui si trovano esempi di file Dockerfile per le modalità native e jvm (insieme al file .dockerignore).
- La cartella resources (nella directory src/main/): qui si trova un file application.properties vuoto e un esempio della pagina di avvio 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 verificare come funzionerà:
$ .\/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... E 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 di HelloService iniettata, ma non riesce a trovarla (anche se entrambe le classi si trovano nel stesso pacchetto).
È tempo di tornare a e leggere come funziona in Quarkus , e quindi anche (CDI). Pertanto, apriamo il manuale Contexts and Dependency Injection e nella sezione leggiamo: «Una classe bean senza un'annotazione bean definente non viene cercata».
Esaminiamo la classe HelloService: non ha effettivamente tale annotazione. Quindi dobbiamo aggiungerla affinché Quarkus possa cercare e trovare il bean. E poiché si tratta di un oggetto stateless, possiamo tranquillamente aggiungere l'annotazione @ApplicationScoped in questo modo:
@ApplicationScoped
public class HelloService {
Nota: qui l'ambiente di sviluppo potrebbe chiederti di aggiungere il pacchetto necessario (vedi la riga qui sotto), e dovrai farlo manualmente, in questo modo:
import javax.enterprise.context.ApplicationScoped;
Se hai dubbi su quale area di scope utilizzare quando per il bean di origine non è specificata affatto, consulta la documentazione .
Ora proviamo di nuovo a lanciare l'applicazione con il comando .\/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]
Ora tutto procede senza errori.
Avviamo il helloworld modernizzato
Come scritto 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 è specificata la seguente definizione di contesto:
@WebServlet("\/HelloWorld")
public class HelloWorldServlet extends HttpServlet {
Quindi andiamo nel browser a :8080/HelloWorld e vediamo quanto segue:

Fig. 5: Pagina The Quarkus dev per l'applicazione Hello World.
Ecco, tutto funziona.
Ora apportiamo delle modifiche al codice. Si noti che il comando ./mvnw compile quarkus:dev è ancora attivo, e non abbiamo intenzione di fermarlo. Proviamo ora ad applicare le stesse – molto semplici – modifiche al codice stesso e vediamo come Quarkus semplifica la vita agli sviluppatori:
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 controlliamo l'output nel terminale:
INFO [io.qua.dev] (vert.x-worker-thread-3) 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 fermato in 0.003s
INFO [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Inizio dell'implementazione di Quarkus
INFO [io.qua.dep.QuarkusAugmentor] (vert.x-worker-thread-3) Implementazione 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 di sostituzione rapida: 0.371s
L'aggiornamento della pagina ha innescato il rilevamento delle modifiche nel codice sorgente, e Quarkus ha automaticamente eseguito la procedura "stop-start". E tutto questo è avvenuto in soli 0.371 secondi (eccola, la famosa "Java subatomica super veloce").
Compiliamo helloworld in un pacchetto JAR
Ora che il codice funziona correttamente, imballiamolo con il seguente comando:
$ ./mvnw clean package
Questo comando crea due file JAR nella cartella /target: il file helloworld-.jar, che rappresenta un artefatto standard, creato dal comando Maven insieme alle classi e alle risorse del progetto. E il file helloworld—runner.jar, che rappresenta un JAR eseguibile.
Si noti 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 che la cartella /lib, considerando che l'elemento Class-Path nel file MANIFEST.MF nel pacchetto JAR contiene un'esplicita enumerazione dei JAR nella cartella lib.
Per sapere come creare applicazioni uber-jar, consultare la guida .
Eseguiamo helloworld, imballato in JAR
Ora possiamo avviare il nostro JAR usando il comando standard java:
$ java -jar ./target/helloworld--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 questo sarà completato, aprite il browser su :8080 e verificate che tutto funzioni correttamente.
Compilazione di helloworld in un file eseguibile nativo
Quindi, il nostro helloworld funziona come un'applicazione standalone Java, 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 necessari:
1. Scaricare GraalVM 19.2.0.1 da .
2. Estrarre l'archivio scaricato:
$ tar xvzf graalvm-ce-linux-amd64-19.2.0.1.tar.gz
3. Andare nella cartella untar.
4. Eseguire il comando riportato di seguito per scaricare e aggiungere l'immagine nativa:
$ ./bin/gu install native-image
5. Impostare la cartella creata al passo 2 nella variabile di ambiente GRAALVM_HOME:
$ export GRAALVM_HOME={untar-folder}/graalvm-ce-19.2.0.1)
Ulteriori informazioni e istruzioni per l'installazione su altri OS possono essere trovate nella guida .
Compilazione di helloworld in un file eseguibile nativo
Leggere la guida : «E ora creiamo un file eseguibile nativo per la nostra applicazione, per ridurre il tempo di avvio e lo spazio su disco. Il file eseguibile avrà tutto il necessario per eseguire l'applicazione, compresa la JVM (ossia, 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
Nel passaggio precedente abbiamo ottenuto il file eseguibile /target/helloworld—runner. Ora eseguiamolo:
$ ./target/helloworld--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]
Apriamo di nuovo nel browser :8080 e controlliamo che tutto funzioni correttamente.
Continua!
Crediamo che il metodo di modernizzazione delle applicazioni Java tramite le funzionalità di Quarkus, esaminato in questo post (anche se con un esempio semplice), debba essere adottato attivamente nella vita reale. In questo contesto, è probabile che tu possa incontrare diverse problematiche, la cui soluzione esamineremo parzialmente nel prossimo post, dove discuteremo di come misurare il consumo di memoria per valutare il miglioramento delle prestazioni, una parte fondamentale dell'intero processo di modernizzazione delle applicazioni.
Fonte: habr.com
