
Le estensioni sono il cuore pulsante di Quarkus, rappresentando un meccanismo potente per integrare librerie e framework nel suo nucleo. Queste componenti aggiuntive permettono agli sviluppatori di personalizzare le proprie applicazioni, scegliendo solo le funzionalità necessarie e ottimizzando le risorse.
Questo approccio modulare non solo riduce l’ingombro del codice, ma porta anche a tempi di avvio più rapidi e un utilizzo più efficiente della memoria.
Prima di entrare nel vivo delle estensioni, è importante capire il processo di bootstrap di un’applicazione Quarkus, suddiviso in tre fasi distinte, ognuna con un ruolo specifico nell’ottimizzazione e avvio dell’applicazione.
- Augmentation (Aumento): Questa fase avviene durante il build time e rappresenta il cuore dell’ottimizzazione di Quarkus. In questa fase, le estensioni di Quarkus vengono caricate ed esaminano il bytecode dell’applicazione, incluse le dipendenze e la configurazione. Le estensioni possono leggere file di configurazione, analizzare le annotazioni nelle classi e raccogliere metadati. Una volta raccolte tutte queste informazioni, le estensioni possono pre-processare le azioni di bootstrap delle librerie, come ad esempio la configurazione di ORM, DI (Dependency Injection) o controller REST. Il risultato di questa fase è una serie di istruzioni in bytecode, che saranno parte del pacchetto finale dell’applicazione. È importante notare che durante la fase di Augmentation, non vengono caricate classi dell’applicazione, ma si analizzano metadati e annotazioni. In questa fase si fa uso di Build Step Processors che hanno accesso alle informazioni Jandex e possono parsare descrittori e annotazioni. L’output di questa fase è il cosiddetto bytecode registrato.
- Static Init (Inizializzazione Statica): Durante la fase di runtime, Quarkus esegue per prima cosa un metodo di inizializzazione statica. Questo metodo contiene azioni e configurazioni delle estensioni. Quando l’applicazione viene pacchettizzata in modo nativo, questo metodo viene pre-processato durante il build time e gli oggetti che genera sono serializzati nell’eseguibile nativo finale. In questo modo, il codice di inizializzazione non viene eseguito in modalità nativa, ma il risultato della computazione viene direttamente incluso nell’eseguibile. In modalità JVM, questa fase viene eseguita all’avvio dell’applicazione.
- Runtime: Questa è la fase in cui l’applicazione vera e propria viene eseguita, utilizzando il bytecode pre-elaborato nelle fasi precedenti. In questa fase si eseguono azioni che devono essere fatte in fase di runtime, come l’apertura di porte o l’avvio di servizi.
Questi tre passaggi evidenziano la filosofia di Quarkus di spostare quanta più elaborazione possibile al build time per ottimizzare le performance e ridurre il consumo di risorse al runtime. Questo approccio rende le applicazioni Quarkus più veloci, più leggere e più adatte agli ambienti cloud-native.
È importante sottolineare che le estensioni di Quarkus sono progettate per integrarsi perfettamente con queste fasi di bootstrap. Le estensioni hanno un ruolo cruciale nel processo di ottimizzazione, poiché eseguono operazioni di pre-elaborazione, come la generazione di bytecode e la configurazione delle dipendenze durante la fase di Augmentation.
Come funzionano le estensioni
Ogni estensione è composta da due moduli principali: uno di runtime, che rende disponibili le funzionalità all’applicazione, e uno di deployment, che entra in azione durante la fase di build per ottimizzare l’applicazione secondo i principi di Quarkus (vedi Building my first extension).
Le estensioni si integrano con l’architettura di Quarkus sfruttando i suoi paradigmi, come ad esempio la generazione di bytecode in fase di build, al posto di aspettare la valutazione del codice a runtime. Questo significa che Quarkus può fare molte ottimizzazioni durante la compilazione, rendendo le applicazioni più veloci.
Quarkus utilizza il concetto di “capability” per definire le funzionalità di un’estensione. Ogni estensione può richiedere o fornire “capability” e solo un provider di una determinata capability è permesso in un’applicazione. Le capabilities possono anche essere condizionali, ovvero, un’estensione può fornire o richiedere una capability solo se una certa condizione è soddisfatta.
Il sistema di capabilities di Quarkus serve per:
- Dichiarare le funzionalità che un’estensione offre (capabilities fornite).
- Definire le dipendenze da altre funzionalità (capabilities richieste).
- Prevenire conflitti tra estensioni diverse che forniscono la stessa funzionalità.
- Consentire l’attivazione condizionale di alcune capabilities.
Le estensioni possono essere facilmente aggiunte o rimosse dai progetti Quarkus e possono anche essere incluse implicitamente come dipendenze di altre estensioni. Sono gestite come dipendenze Maven, semplificando la loro gestione e integrazione nei progetti.
Cosa possono fare le estensioni
Le estensioni di Quarkus offrono un’ampia gamma di funzionalità, aprendo un mondo di possibilità per gli sviluppatori.
Le estensioni possono fornire integrazioni con database di vario tipo, semplificando l’accesso e la gestione dei dati. Inoltre, permettono di creare API REST in modo facile e veloce, grazie a modelli basati su annotazioni. Le estensioni possono anche abilitare il supporto per GraphQL e gRPC, offrendo alternative a REST per la comunicazione tra servizi.
Un aspetto fondamentale delle estensioni è la loro capacità di esporre componenti tramite CDI (Contexts and Dependency Injection), consentendo agli sviluppatori di utilizzare questi componenti nelle loro applicazioni in modo semplice. Questo permette di creare applicazioni ben strutturate e di riutilizzare facilmente le componenti. Si integrano perfettamente con il sistema di configurazione unificata di Quarkus, offrendo un’esperienza coerente e intuitiva.
Le estensioni sono progettate per ottenere le massime prestazioni possibili, adottando un modello non bloccante per le operazioni interne e offrendo API basate su Mutiny per la massima scalabilità. Questo significa che le applicazioni create con Quarkus sono in grado di gestire un gran numero di richieste senza rallentamenti. Inoltre, sono pensate per l’integrazione con Kubernetes e container, essendo Quarkus un framework cloud-native.
Oltre a queste funzionalità, le estensioni possono fornire integrazioni con i servizi di sviluppo, come i Dev Services, e anche strumenti per il testing, il monitoraggio della salute (health checks), e l’esposizione di metriche; quest’ultimo aspetto è un elemento fondamentale per costruire, gestire e scalare applicazioni cloud-native in modo efficace. Sistemi di osservabilità come Prometheus, Grafana, Datadog e New Relic sono diventati indispensabili per sfruttare al meglio questa capacità.
Possono anche essere utilizzate per la gestione della sicurezza delle applicazioni, offrendo integrazioni con sistemi di autenticazione e autorizzazione. Un esempio di progetto che fa uso di queste estensioni è disponibile su GitHub Tutorial Project for Quarkus Mutual TLS (mTLS) Authentication.
Analizziamo l’estensione Quarkus Info
L’estensione Quarkus Info (che fare parte del core) fornisce informazioni sull’applicazione Quarkus in esecuzione. Questa estensione, come altre, sfrutta i meccanismi di Quarkus per eseguire la maggior parte del lavoro di elaborazione al build time, ottimizzando le prestazioni a runtime. Il codice sorgente dell’estensione è disponibile sul repository GitHub di Quarkus.
L’estensione quarkus-info
segue la convenzione di Quarkus per le estensioni, con un modulo runtime e un modulo deployment. Il modulo runtime contiene le classi e le risorse necessarie per il funzionamento dell’estensione durante l’esecuzione dell’applicazione, mentre il modulo deployment contiene le classi che eseguono la logica di build-time, inclusi i build step processor.
A seguire le principali funzionalità.
- L’estensione Info espone un endpoint HTTP che fornisce un documento JSON contenente metadati sull’applicazione.
- Questi metadati includono informazioni come il nome dell’applicazione, la versione e le dipendenze runtime. Questa funzionalità è utile per il monitoraggio, il debug e la gestione dell’applicazione.
- L’estensione Info mette a disposizione dei bean a cui poter fare riferimento tramite l’annotazione
@Inject
.
Augmentation (Build Time). Durante la fase di augmentation, l’estensione Info utilizza i build step processors per raccogliere metadati sull’applicazione. Questi processori analizzano le informazioni disponibili, come il file pom.xml, le annotazioni e altre risorse del progetto.
- Le informazioni sull’applicazione (nome e versione) vengono ottenute tramite un BuildItem chiamato ApplicationInfoBuildItem.
- Le dipendenze runtime vengono recuperate tramite AppModelProviderBuildItem.
- Questi dati vengono elaborati per creare un documento JSON che sarà esposto dall’endpoint.
Static Init (Inizializzazione Statica): L’estensione Info sfrutta la fase di static init. Il documento JSON viene generato nella fase di build e serializzato, quindi incluso nell’eseguibile nativo. In modalità JVM, l’inizializzazione statica viene eseguita all’avvio. Questo approccio assicura che le informazioni non vengano ricalcolate ogni volta che l’applicazione viene avviata.
Runtime: Durante la fase runtime, l’estensione Info espone un endpoint HTTP che restituisce il documento JSON contenente le informazioni raccolte durante la fase di build. Questo endpoint è tipicamente accessibile tramite /q/info. L’implementazione dell’endpoint HTTP può basarsi su Vert.x.
Le classi principali di questa estensione sono: InfoProcessor
, InfoRecorder
e InfoHandler
. Nel flusso di esecuzione accade quanto segue.
- Build Time: Durante la fase di build di un’applicazione Quarkus, il build step processor InfoProcessor viene eseguito.
- Raccolta Metadati: Il InfoProcessor utilizza i metodi annotati con @BuildStep per raccogliere metadati sull’applicazione, consumando build item come ApplicationInfoBuildItem e AppModelProviderBuildItem.
- Creazione JSON: Il InfoProcessor crea un JsonObject contenente le informazioni raccolte, inclusi nome, versione e dipendenze.
- Registrazione Handler: Il InfoProcessor utilizza InfoRecorder per registrare il bytecode necessario per creare un’istanza dell’handler HTTP (InfoHandler). Questo codice sarà eseguito durante l’inizializzazione dell’applicazione.
- Creazione Endpoint: Il InfoProcessor crea un RouteBuildItem associando l’handler all’endpoint /q/info.
- Runtime: Quando l’applicazione viene avviata, il codice registrato da InfoRecorder crea l’InfoHandler, che risponde alle richieste HTTP all’endpoint /q/info. L’InfoHandler utilizza il JsonObject creato a build time per rispondere alla richiesta con il JSON contenente le informazioni.
Usiamo l’estensione Quarkus Info
Adesso proviamo a usare l’estensione quarkus-info creando una semplice applicazione Quarkus. In questo esempio faremo uso della versione 3.17.5 usando Maven e il plugin Quarkus per la creazione del progetto. Consiglio di consultare il documento Creating Your First Application per verificare i pre-requisiti.
Per creare il progetto di esempio quarkus-info-app che includa l’estensione info usando la versione di Quarkus 3.17.5, occorre lanciare il comando indicato a seguire.
mvn io.quarkus.platform:quarkus-maven-plugin:3.17.5:create \
-DprojectGroupId=it.dontesta.quarkus.lab \
-DprojectArtifactId=quarkus-info-app \
-DprojectVersion=1.0.0-SNAPSHOT \
-Dextensions="info"
Code language: JavaScript (javascript)
Una volta eseguito il comando, dobbiamo spostarci all’interno della directory quarkus-info-app e da qui eseguire l’applicazione Quarkus con il comando mvn quarkus:dev
.
Quando l’applicazione sarà attiva, chiamando l’endpoint http://localhost:8080/q/info vedremo un JSON come quello mostrato a seguire.
{
"os" : {
"name" : "Mac OS X",
"version" : "15.2",
"arch" : "aarch64"
},
"build" : {
"group" : "it.dontesta.quarkus.lab",
"artifact" : "quarkus-info-app",
"version" : "1.0.0-SNAPSHOT",
"time" : "2024-12-29T14:21:24.453256+01:00",
"quarkusVersion" : "3.17.5",
"enabled" : "true"
},
"java" : {
"version" : "23.0.1"
}
}
Code language: JSON / JSON with Comments (json)
Nel caso in cui il progetto fosse sottoposto a versioning tramite git, chiamando l’endpoint http://localhost:8080/q/info vedremo un JSON come quello mostrato a seguire, dov’è possibile notare le informazioni base su git, come il nome del branch e l’ultimo commit.
{
"git" : {
"branch" : "main",
"commit" : {
"id" : "9b0dbf2777a9d2e955b6b18d2dff71af956ad838",
"time" : "2024-12-29T14:42:04+01:00"
}
},
"java" : {
"version" : "23.0.1"
},
"os" : {
"name" : "Mac OS X",
"version" : "15.2",
"arch" : "aarch64"
},
"build" : {
"group" : "it.dontesta.quarkus.lab",
"artifact" : "quarkus-info-app",
"version" : "1.0.0-SNAPSHOT",
"time" : "2024-12-29T14:43:50.451164+01:00",
"quarkusVersion" : "3.17.5",
"enabled" : "true"
}
}
Code language: JSON / JSON with Comments (json)
Agendo sulla configurazione quarkus.info.git.mode
dell’estensione quarkus-info, impostando il valore FULL, chiamando nuovamente l’endpoint http://localhost:8080/q/info vedremo un JSON come quello mostrato a seguire, dov’è possibile notare, oltre alle informazioni standard, i tags e maggiori dettagli sull’ultimo commit.
{
"git" : {
"branch" : "main",
"tags" : [ "v1.0.0" ],
"commit" : {
"id" : {
"full" : "9b0dbf2777a9d2e955b6b18d2dff71af956ad838",
"abbrev" : "9b0dbf2777a9d",
"message" : {
"full" : "Initial project import to git",
"short" : "Initial project import to git"
}
},
"time" : "2024-12-29T14:42:04+01:00",
"author" : {
"time" : "2024-12-29T14:42:04+01:00"
},
"committer" : {
"time" : "2024-12-29T14:42:04+01:00"
},
"user" : {
"email" : "antonio.musarra@gmail.com",
"name" : "Antonio Musarra"
}
},
"build" : {
"user" : {
"email" : "antonio.musarra@gmail.com",
"name" : "Antonio Musarra"
},
"version" : "1.0.0-SNAPSHOT",
"host" : "amusarra-macbook-pro.local"
}
},
"java" : {
"version" : "23.0.1"
},
"os" : {
"name" : "Mac OS X",
"version" : "15.2",
"arch" : "aarch64"
},
"build" : {
"group" : "it.dontesta.quarkus.lab",
"artifact" : "quarkus-info-app",
"version" : "1.0.0-SNAPSHOT",
"time" : "2024-12-29T14:50:01.092601+01:00",
"quarkusVersion" : "3.17.5",
"enabled" : "true"
}
}
Code language: JSON / JSON with Comments (json)
Queste informazioni sono anche accessibili tramite la DevUI di Quarkus attraverso la URL http://localhost:8080/q/dev-ui/io.quarkus.quarkus-info/information
L’estensione quarkus-info è un modo semplice per esporre le informazioni sull’applicazione, rendendo più facile il monitoraggio e la diagnostica. Con pochi passaggi, puoi aggiungere questa estensione a qualsiasi applicazione Quarkus e iniziare a usufruire delle sue funzionalità senza la necessità di “inventare la ruota”.
Conclusioni
Comprendere la struttura delle estensioni di Quarkus permette di creare componenti personalizzati potenti, che integrano librerie o strumenti di terze parti. Le estensioni sono un elemento fondamentale per l’ecosistema di Quarkus, che consente di creare applicazioni flessibili, efficienti e adatte alle esigenze di ogni progetto.