Tempo fa ho scritto un articolo su come configurare un’applicazione basata su Spring Boot per utilizzare JavaServer Faces e in particolare PrimeFaces: si trattava di un’operazione un po’ ostica che richiedeva l’importazione di alcune librerie e l’utilizzo di opportune configurazioni affinché tutto funzionasse correttamente.
Oggi è disponibile un progetto molto interessante, chiamato JoinFaces, che semplifica di molto l’integrazione di Spring Boot con PrimeFaces e con molti altri progetti legati al mondo JavaServer Faces, come per esempio PrimeFaces Extensions, BootsFaces, ButterFaces, RichFaces, OmniFaces e AngularFaces.
In questo articolo descriverò come utilizzare JoinFaces in un’applicazione basata su Spring Boot.
Ricordo brevemente che Spring Boot (http://projects.spring.io/spring-boot/) permette di semplificare di molto lo sviluppo di applicazioni basate sul framework Spring infatti, quando si crea un nuovo progetto basato su Spring, si presentano immediatamente alcuni punti da risolvere: il primo riguarda quali librerie di Spring è necessario importare nel progetto per poter utilizzare le diverse funzionalità offerte dal framework come, per esempio, Spring Data, Spring Integration, Spring Social, ecc. Il secondo punto, strettamente legato al primo, è quali versioni delle librerie devono essere utilizzate. Il terzo aspetto fondamentale da affrontare è la scrittura del codice necessario per effettuare il bootstrap di Spring e l’individuazione e configurazione di tutti i bean che saranno gestiti dal framework e necessari alla nostra applicazione. Tutti questi punti, come vedremo, sono efficacemente risolti dal progetto Spring Boot.
I passi che seguirò in questo articolo sono i seguenti:
- Creazione di un progetto basato su Spring Boot
- Importazione del progetto in Eclipse
- Configurazione del progetto per l’utilizzo di JoinFaces
- Aggiunta dei componenti di presentazione (pagine jsf e controller)
- Generazione del file .jar e suo utilizzo
Passo 1 – Creazione di un progetto basato su Spring Boot
E’ possibile creare facilmente un’applicazione basata sul progetto Spring Boot utilizzando l’applicazione Spring Initializr presente all’indirizzo https://start.spring.io
Una volta caricata questa pagina nel browser, conviene selezionare il link Switch to the full version, presente in basso alla pagina, per visualizzare la sua versione completa.
Per configurare la nostra applicazione possiamo lasciare i valori di default:
Generate a Maven Project with java and Spring Boot 1.5.4
infatti lo scopo è quello di generare un progetto Maven che utilizza il linguaggio java e la versione di Spring Boot 1.5.4
Si devono poi riempire i campi : Group, Artifact, Name, Description e Package Name con i valori che si preferisce, scegliere il valore Jar per il campo Packaging, perché vogliamo che Maven generi un file .jar che contenga il nostro progetto.
Per questo esempio ho utilizzato i seguenti valori:
Group: com.theserverside
Artifact: demoJoinFaces
Name: demoJoinFaces
Description: Esempio configurazione Spring Boot e JoinFaces
Package Name: com.theserverside.demoJoinFaces
ho lasciato i seguenti valori di default per i seguenti campi:
Packaging: Jar
Java Version: 1.8
Una volta inseriti questi valori è possibile premere bottone verde “Generate Project” per far sì che l’applicazione Spring Initializr prosegua con la creazione del progetto. Il risultato è la generazione di un progetto Maven con i parametri che abbiamo selezionato e il download automatico del file demoJoinFaces.zip: questo file contiene il nostro progetto che possiamo salvare in una directory di appoggio.
Passo 2 – Importazione del progetto in Eclipse
Ora dobbiamo decomprimere il file demoJoinFaces.zip nella directory in cui è stato salvato e poi importare questo progetto in Eclipse.
Per importare il progetto in Eclipse è possibile selezionare la voce di menu File -> Import…, Eclipse visualizza la seguente finestra di dialogo:
In questa finestra si deve selezionare il folder Maven e, al suo interno, ‘Existing Maven Projects’ e poi premere ‘Next’, Eclipse visualizza quindi la seguente finestra:
Selezionando il bottone ‘Browse…’ è possibile scegliere la directory di appoggio in cui abbiamo decompresso i file del progetto e poi premere ‘Finish’ per importare il progetto in Eclipse, come nella seguente figura:
Al termine di questa operazione, Eclipse importerà il nostro progetto che risulterà visibile nella sua finestra Package Explorer, come riportato nella figura seguente:
Il file pom.xml generato è il seguente,
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.theserverside</groupId> <artifactId>demoJoinFaces</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>demoJoinFaces</name> <description>Esempio configurazione Spring Boot e JoinFaces</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.4.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
Se si osservano le righe da 27 a 36, si può notare che durante la generazione del progetto sono stati introdotti due moduli starter, cioè due pacchetti di librerie che Spring Boot mette a disposizione per configurare specifiche funzionalità dell’applicazione, in particolare:
- spring-boot-starter – per importare le librerie per la configurazione base di spring
- spring-boot-starter-test – per importare le librerie necessarie per il testing dell’applicazione
Sono proprio questi moduli starter che permettono di semplificare la configurazione delle librerie a cui accennavo all’inizio dell’articolo, con il loro utilizzo non è più necessario elencare tutti i singoli .jar e la loro versione.
Spring Boot mette a disposizione numerosi moduli starter, ciascuno utile per includere nell’applicazione pacchetti di librerie necessarie per implementare specifiche funzionalità; un elenco completo di questi moduli è disponibile all’indirizzo: Spring Boot Reference Guide
Vediamo adesso il contenuto della classe DemoJoinFacesApplication.java:
package com.theserverside.demoJoinFaces; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoJoinFacesApplication { public static void main(String[] args) { SpringApplication.run(DemoJoinFacesApplication.class, args); } }
La classe DemoJoinFacesApplication è stata annotata (riga 6) con @SpringBootApplication: questa annotazione include nella sua definizione le seguenti 3 annotazioni, ciascuna con il seguente significato:
- @Configuration questa annotazione permette alla classe di definire dei metodi di creazione di bean gestiti da Spring: questo significa che un metodo di questa classe annotato con @Bean restituisce un oggetto che sarà automaticamente gestito dal container di Spring
- @ComponentScan questa annotazione fa sì che Spring cerchi le classi annotate con @Component, @Service, @Controller, @Repository ecc. nel package in cui è definita questa classe, nel nostro caso com.theserverside.demoJoinFaces, e le inserisca nel proprio container
- @EnableAutoConfiguration questa annotazione abilita il meccanismo di autoconfigurazione che è alla base di Spring Boot: questo meccanismo fa sì che in base alle librerie presenti nel classpath dell’applicazione, cioè quelle dichiarate nel file pom.xml, Spring Boot inserisca automaticamente nel proprio container un insieme di bean, opportunamente configurati, semplificando enormemente la configurazione dell’applicazione.
Sempre nella classe DemoJoinFacesApplication, nel metodo main (righe 9-11), si effettua il bootstrap di Spring Boot passando la classe DemoJoinFacesApplication in input al metodo statico run della classe SpringApplication
Il passo successivo è configurare il progetto in modo da poter utilizzare il progetto JoinFaces
Passo 3 – Configurazione del progetto per l’utilizzo di JoinFaces
Modifichiamo il file pom.xml, presente nella root del progetto, in modo da sostituire l’elemento <parent> e aggiungere l’elemento <dependency> riportati di seguito:
<parent> <groupId>org.joinfaces</groupId> <artifactId>jsf-spring-boot-parent</artifactId> <version>2.4.0</version> <relativePath /> </parent>
<dependency> <groupId>org.joinfaces</groupId> <artifactId>jsf-spring-boot-starter</artifactId> </dependency>
dopo questa modifica, il contenuto del file pom.xml sarà il seguente:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.theserverside</groupId> <artifactId>demoJoinFaces</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>demoJoinFaces</name> <description>Esempio configurazione Spring Boot e JoinFaces</description> <parent> <groupId>org.joinfaces</groupId> <artifactId>jsf-spring-boot-parent</artifactId> <version>2.4.0</version> <relativePath /> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.joinfaces</groupId> <artifactId>jsf-spring-boot-starter</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
In generale, quando si modifica il file pom.xml conviene sempre eseguire il comando Maven Update Project (tasto destro del mouse sul nome del progetto e poi scegliere Maven -> Update Project…) questo per essere sicuri che Eclipse aggiorni le librerie del progetto in base a quanto riportato nel file pom.xml.
Abbiamo terminato la configurazione della nostra applicazione! Il progetto è pronto per ospitare le nostre pagine JSF. Si osservi come l’utilizzo del progetto JoinFaces abbia ridotto la configurazione alla sola modifica del file pom.xml
Passo 4 – Aggiunta dei componenti di presentazione (pagine jsf e controller)
Aggiungiamo alla nostra applicazione una pagina saluti.xhtml che utilizza alcuni tag JSF e PrimeFaces in modo da verificare che la configurazione dell’applicazione sia corretta.
Poiché il nostro obiettivo è quello di creare un file jar contenente al suo interno sia la nostra applicazione web, sia il server Tormcat embedded (seguiamo la famosa regola di Spring Boot: “Make jar, not war”) dobbiamo fare attenzione ad inserire le pagine jsf nella seguente directory (e non in una directory WEB-INF come nel caso di file war):
src/main/resources/META-INF/resources
Il progetto che abbiamo generato e importato in eclipse ha soltanto la directory src/main/resources, quindi dobbiamo creare (tasto desto del mouse sulla directory e poi New -> Folder) le due nuove directory META-INF e, al suo interno, resources.
In quest’ultima directory creiamo il nuovo file saluti.xhtml (tasto desto del mouse sulla directory e poi New -> File) il cui sorgente è il seguente:
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:p="http://primefaces.org/ui"> <f:view contentType="text/html"> <h:head /> <h:body> <h:form> <p:panel id="panel1" header="DemoJsf"> <h:outputText value="#{helloController.hello}" /> <h:outputText value="Orario: " /> <h:outputText value="#{helloController.timeStamp}" /><br/><br/> <p:commandButton update="panel1" action="#{helloController.updateOrario}" value="Aggiorna orario" /> </p:panel> </h:form> </h:body> </f:view> </html>
La pagina saluti.xhtml è quella che vogliamo visualizzare come pagina iniziale del progetto, a tale scopo dobbiamo inserire una classe controller che in corrispondenza dell’url iniziale dell’applicazione (la sua context root) la faccia visualizzare.
Creiamo dunque un nuovo package com.theserverside.demoJoinFaces.controller, per farlo basta cliccare con il tasto destro del mouse sul package com.theserverside.demoJoinFaces e poi scegliere New -> Package e inserire come nome del nuovo package com.theserverside.demoJoinFaces.controller
In questo nuovo package creiamo una classe controller chiamata HomeController. Per creare la nuova classe basta cliccare con il tasto destro del mouse sul nome del package com.theserverside.demoJoinFaces.controller e scegliere New -> Class
Il sorgente della classe HomeController è il seguente:
package com.theserverside.demoJoinFaces.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; @Controller public class HomeController { @RequestMapping("/") public String getfirstPage() { return "saluti.xhtml"; } }
Si osservi l’annotazione @Controller presente alla linea 6: in questo modo si contrassegna la classe HomeController come un componente gestito da Spring, in particolare un componente di tipo controller, cioè un componente che appartiene allo strato di presentazione dell’applicazione (presentation layer) e che è quindi in grado di gestire richieste HTTP provenienti dall’esterno. Al momento dello startup dell’applicazione, Spring crea un’istanza di questa classe e la inserisce nel suo container.
Il metodo getfirstPage() è invece annotato con @RequestMapping(“/”): questa annotazione fa sì che questo metodo venga invocato ogni volta che l’applicazione riceve una richiesta http GET che abbia come url la sua context root (il nome dell’applicazione).
Il metodo getfirstPage() non fa nient’altro che restituire il nome della pagina che deve essere visualizzata, quindi invocando nel browser la context root dell’applicazione si ottiene come risultato la visualizzazione della pagina saluti.xhtml
Ritornando alla pagina saluti.xhtml. si può osservare dal suo sorgente che utilizza un ManagedBean HelloController che deve essere creato nel progetto (tasto destro del mouse sul nome del package com.theserverside.demoJoinFaces.controller e poi scegliere New -> Class) e la cui implementazione è la seguente:
package com.theserverside.demoJoinFaces.controller; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import javax.faces.bean.ManagedBean; import javax.faces.bean.RequestScoped; @ManagedBean(name="helloController") @RequestScoped public class HelloController { private String timeStamp; public HelloController() { LocalDateTime date = LocalDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss"); timeStamp = date.format(formatter); } private String hello = "Saluti dall'applicazione demoJoinFaces"; public String getHello() { return hello; } public void setHello(String hello) { this.hello = hello; } public String getTimeStamp() { return timeStamp; } public void setTimeStamp(String timeStamp) { this.timeStamp = timeStamp; } public String updateOrario() { LocalDateTime date = LocalDateTime.now(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm:ss"); timeStamp = date.format(formatter); return null; } }
La classe HelloController inizializza nel costruttore il suo campo timeStamp con la data corrente, per tale campo si utilizza la nuova classe LocalDateTime introdotta in java 8. Inoltre è presente un metodo updateOrario() che permette di aggiornare l’orario ogni volta che questo metodo è invocato.
A questo punto la configurazione del progetto è terminata, nella figura seguente è riportata la sua struttura finale:
Possiamo lanciare l’applicazione cliccando con il tasto destro del mouse sulla classe DemoJoinFacesApplication.java e scegliere la voce di menù Run As -> Java Application
Al termine della fase di avvio, nella finestra Console di Eclipse comparirà il seguente output:
Leggendo le ultime righe è possibile verificare che il web server Tomcat embedded che Spring Boot ha integrato nella nostra applicazione è in ascolto sulla porta 8080, è quindi possibile visualizzare l’applicazione utilizzando l’url:
Il risultato è il seguente:
La pagina visualizza un semplice tag panel di PrimeFaces con un messaggio di saluto e la data corrente. E’ possibile aggiornare la data visualizzata premendo il bottone “Aggiorna orario”.
Il funzionamento dell’applicazione è molto semplice: quando nel browser si digita l’url http://localhost:8080, il browser invia una richiesta HTTP di tipo GET alla nostra applicazione richiedendo la risorsa “/”. L’uri (Uniform Resource Identifier) richiesta “/” è mappata sul metodo getfirstPage() della classe HomeController tramite l’annotazione @RequestMapping(“/”), quindi Spring invoca il metodo getfirstPage() che restituisce il nome della pagina da visualizzare ed il browser mostra il contenuto di saluti.xhtml
Nella pagina saluti.xhtml sono presenti i seguenti tag jsf che visualizzano il messaggio di saluto con la data e l’ora:
<h:outputText value="#{helloController.hello}" /> <h:outputText value="Orario: " /> <h:outputText value="#{helloController.timeStamp}" />
i valori sono letti dai campi hello e timeStamp del managed bean helloController
Nella stessa pagina è anche presente il seguente tag jsf che visualizza un bottone contenente il testo “Aggiorna orario”:
<p:commandButton update="panel1" action="#{helloController.updateOrario}" value="Aggiorna orario" />
La presenza in questo tag dell’attributo action=”#{helloController.updateOrario}” fa sì che quando l’utente preme il bottone “Aggiorna orario”, il framework JSF invoca il metodo updateOrario() della classe HelloController
Il metodo updateOrario() non fa nient’altro che aggiornare il campo timeStamp e, al termine della sua esecuzione, poiché nell’elemento <p:commandButton> è presente l’attributo update=”panel1″, il framework jsf aggiorna immediatamente il panel con id panel1 contenente i campi campi hello e timeStamp che quindi risultano aggiornati senza che l’intera pagina debba essere ricaricata. Si osservi come il framework PrimeFaces utilizzi richieste ajax senza che l’utente debba esplicitamente programmarle in Javascript.
Passo 5 – Generazione del file .jar e suo utilizzo
Per generare il file .jar contenente la nostra applicazione è possibile cliccare con il tasto destro del mouse sul nome del progetto e scegliere la voce di menù Run As -> Maven build…
Nella finestra che Eclipse visualizza si deve scrivere la stringa package nel campo Goal e poi premere Run, come mostrato nella figura seguente:
Eclipse genererà il file demoJoinFaces-0.0.1-SNAPSHOT.jar nella directory target presente all’interno del progetto.
Quello che abbiamo appena generato è un file .jar eseguibile, nel senso che è possibile lanciare l’applicazione con il comando:
java -jar demoJoinFaces-0.0.1-SNAPSHOT.jar
Il file .jar è eseguibile perché Spring Boot l’ha generato con un server Tomcat embedded al suo interno. L’unico particolare da tener presente è che il server embedded assegna all’applicazione la root context e quindi l’url da utilizzare per visualizzarla è: http://localhost:8080/ senza inserire il nome dell’applicazione.
Il codice sorgente dell’applicazione è disponibile su GitHub all’indirizzo:
https://github.com/thespringside/demoJoinFaces.git
Nei prossimi articoli continuerò ad approfondire le enormi potenzialità offerte dal framework Spring e dai progetti Spring Boot e Spring Cloud
A presto
@Copyright Roberto Venanzi – www.thespringside.com