Creare un’applicazione basata su Spring Boot, JSF (JavaServer Faces) e PrimeFaces

In questo articolo descriverò come creare un’applicazione basata su Spring Boot, JSF (JavaServer Faces) e PrimeFaces. L’esempio che vedremo utilizzerà una piccola variante rispetto alla configurazione standard di una web application basata su Spring Boot, questa variante è necessaria per la corretta integrazione dei diversi framework adottati.

Iniziamo con una brevissima introduzione su Spring Boot, rimandando ai prossimi articoli l’approfondimento delle sue caratteristiche.

Il progetto Spring Boot (http://projects.spring.io/spring-boot/) permette di semplificare, e di molto, lo sviluppo di applicazioni basate sul framework Spring. 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:

  1. Creazione di un progetto basato su Spring Boot
  2. Importazione del progetto in Eclipse
  3. Configurazione del progetto per l’utilizzo di JavaServer Faces e PrimeFaces
  4. Generazione del file .war 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

starter

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 Spring Boot 1.2.7

infatti lo scopo è quello di generare un progetto Maven con la versione di Spring Boot 1.2.7

Si devono poi riempire i campi : Group, Artifact, Name, Description e Package Name con i valori che si preferisce, scegliere il valore War per il campo Packaging, perché vogliamo che Maven generi un file .war, cioè una web application che contenga il nostro progetto.

Per questo esempio ho utilizzato i seguenti valori:

Group: com.theserverside
Artifact: demoJsf
Name: demoJsf
Description: Esempio configurazione Spring Boot, JSF e PrimeFaces
Package Name: com.theserverside.demoJsf
Packaging: War

ho lasciato i seguenti valori di default per i seguenti campi:

Java Version: 1.8
Language: Java

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 demoJsf.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 demoJsf.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:

ImportEclipse

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:

importazione

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:

ImportEclipse2

Al termine di questa operazione, Eclipse importerà il nostro progetto che risulterà visibile nella sua finestra Package Explorer, come riportato nella figura seguente:

struttura

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>demoJsf</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>demoJsf</name>
  <description>
        Esempio configurazione Spring Boot, JSF e PrimeFaces
  </description>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.2.7.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <scope>provided</scope>
    </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 30 a 39, 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-web – per importare le librerie necessarie per una web application
  • spring-boot-starter-tomcat – per importare le librerie necessarie per l’utilizzo del server Tomcat embedded

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 dei moduli starter è disponibile all’indirizzo: Spring Boot Reference Guide

Vediamo adesso il contenuto della classe DemoJsfApplication.java:

package com.theserverside.demoJsf;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DemoJsfApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoJsfApplication.class, args);
    }
}

La classe DemoJsfApplication è 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.demoJsf, 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 DemoJsfApplication, nel metodo main (righe 9-11), si effettua il bootstrap di Spring Boot passando la classe DemoJsfApplication in input al metodo run di SpringApplication

Per verificare che l’importazione sia andata a buon fine è possibile eseguire l’applicazione cliccando con il tasto destro del mouse sul nome della classe DemoJsfApplication.java e scegliere Run As -> Java Application. Si osservi che, pur trattandosi di una web application, è possibile eseguirla come un’applicazione stand-alone, grazie alla presenza del server Tomcat embedded che Spring Boot inserisce nella nostra applicazione.

Al termine della fase di avvio, nella finestra Console di Eclipse, possiamo verificare che è stato lanciato il web server Tomcat incluso nell’applicazione e che la nostra applicazione è configurata sulla porta 8080:

run1

E’ possibile testare il corretto funzionamento dell’applicazione utilizzando l’url: http://localhost:8080/, la pagina visualizzata sarà la seguente:

primorun

Come si può osservare, l’applicazione funziona correttamente ma restituisce una pagina di errore 404- Not Found: questo perché non abbiamo impostato una pagina iniziale.

Si osservi che nell’url manca l’application context (il nome dell’applicazione), questo accade perché l’applicazione utilizza il server Tomcat embedded che utilizza la context di ROOT

Ricapitoliamo quanto fatto fino ad ora: abbiamo creato un progetto basato su Spring Boot, l’abbiamo importato in Eclipse e, infine, l’abbiamo eseguito per verificare che non si siano verificati errori. Il passo successivo è configurare il progetto in modo da poter utilizzare JavaServer FacesPrimeFaces

Passo 3 – Configurazione del progetto per l’utilizzo di JavaServer Faces e PrimeFaces

Modifichiamo il file pom.xml, presente nella root del progetto, in modo da aggiungere le seguenti librerie:

  
<dependency>
  <groupId>org.apache.tomcat.embed</groupId>
  <artifactId>tomcat-embed-jasper</artifactId>
  <scope>provided</scope>
</dependency>
    
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>jstl</artifactId>
</dependency>

<dependency>
  <groupId>com.sun.faces</groupId>
  <artifactId>jsf-api</artifactId>
  <version>2.2.7</version>
  <scope>compile</scope>
</dependency>

<dependency>
  <groupId>com.sun.faces</groupId>
  <artifactId>jsf-impl</artifactId>
  <version>2.2.7</version>
  <scope>compile</scope>
</dependency>

<dependency>
  <groupId>org.primefaces</groupId>
  <artifactId>primefaces</artifactId>
  <version>5.2</version>
</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.thespringside</groupId>
  <artifactId>demoJsf</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>demoJsf</name>
  <description>Demo project for Spring Boot</description>

  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.2.7.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
  </parent>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.version>1.8</java.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      <scope>provided</scope>
    </dependency>
    
    <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-jasper</artifactId>
      <scope>provided</scope>
    </dependency>
    
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>jstl</artifactId>
    </dependency>

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-api</artifactId>
      <version>2.2.7</version>
      <scope>compile</scope>
    </dependency>

    <dependency>
      <groupId>com.sun.faces</groupId>
      <artifactId>jsf-impl</artifactId>
      <version>2.2.7</version>
      <scope>compile</scope>
    </dependency>

    <dependency>
      <groupId>org.primefaces</groupId>
      <artifactId>primefaces</artifactId>
      <version>5.2</version>
    </dependency>

  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>

</project>

Eliminiamo, nel package com.theserverside.demoJsf, la classe ServletInitializer.java che è stata introdotta durante la generazione del progetto.

Modifichiamo la classe DemoJsfApplication  in modo da farle estendere la classe SpringBootServletInitializer e aggiungiamo le righe da 23 a 41 evidenziate nella figura seguente:

package com.thespringside.demoJsf;

import javax.faces.application.Application;
import javax.faces.webapp.FacesServlet;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.ServletListenerRegistrationBean;
import org.springframework.boot.context.embedded.ServletRegistrationBean;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.context.annotation.Bean;

import com.sun.faces.config.ConfigureListener;

@SpringBootApplication
public class DemoJsfApplication extends SpringBootServletInitializer {

    public static void main(String[] args) {
        SpringApplication.run(DemoJsfApplication.class, args);
    }
    
    @Override
    protected SpringApplicationBuilder 
        configure(SpringApplicationBuilder application) {
            return application.sources(DemoJsfApplication.class);
    }
    
    @Bean
    public FacesServlet facesServlet() {
        return new FacesServlet();
    }
    

    @Bean
    public ServletRegistrationBean facesServletRegistration() {
      ServletRegistrationBean registration = 
          new ServletRegistrationBean(facesServlet(), "*.xhtml");
      registration.setName("FacesServlet");
      return registration;
    }
  
}

Il primo metodo configure(SpringApplicationBuilder application), di cui effettuiamo l’override (per questo abbiamo esteso la classe SpringBootServletInitializer), permette di effettuare il bootstrap di Spring Boot quando l’applicazione è utilizzata con un web server esterno.

Il secondo metodo facesServlet() permette di creare la servlet FacesServlet che gestisce le pagine JavaServer Faces

Il terzo metodo permette di registrare nella nostra applicazione la servlet FacesServlet e di configurarla in modo da farle gestire le pagine JSF aventi l’estensione .xhtml

A questo punto dobbiamo creare la directory WEB-INF all’interno della directory src/main/webapp, per farlo basta cliccare con il tasto destro del mouse sulla directory webapp, scegliere new -> Folder nel menù contestuale che è visualizzato, e assegnare il nome WEB-INF alla nuova directory creata

In questa nuova directory WEB-INF creiamo i due nuovi file web.xml e faces-config.xml, il cui contenuto è il seguente:

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  
    <servlet>
        <servlet-name>FacesServlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>FacesServlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
    </servlet-mapping>    
</web-app>

Nel file web.xml è stata definita la servlet FacesServlet che permette di gestire le pagine JSF: potrebbe sembrare ridondante dover definire nuovamente questa servlet, si ricordi infatti che l’abbiamo già definita nella classe DemoJsfApplication.java, il problema è che, senza ripetere questa definizione nel file web.xml, Tomcat va in errore e non riesce ad utilizzare JavaServer Faces e PrimeFaces

<faces-config xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_2.xsd" version="2.2">
  
  <application>
    <el-resolver>org.springframework.web.jsf.el.SpringBeanFacesELResolver</el-resolver>
  </application>
 
</faces-config>

Nel file faces-config.xml è stato dichiarato l’elemento SpringBeanFacesELResolver che permette alle pagine JSF e ai suoi Managed Bean di avere accesso, tramite l’utilizzo dell’expression language, ai bean gestiti dal container di Spring (Spring Bean): è in questo modo che è possibile integrare i framework JavaServer Faces e Spring.

Come ultimo passo aggiungiamo alla nostra applicazione, nella directory webapp, una pagina html index.html e un’altra pagina saluti.xhtml che utilizza alcuni tag JSFPrimeFaces in modo da verificare che la configurazione dell’applicazione sia funzionante.

<!DOCTYPE html>
<html>
  <head>
    <title>Titolo pagina</title>
  </head>
  <body>
    Saluti dall'applicazione DemoJsf!!! 
  </body>
</html> 

 
<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>
        <p:panel id="panel1" header="DemoJsf">
          <h:outputText value="Saluti dall'applicazione DemoJsf" />
        </p:panel>
      </h:body>
  </f:view>
</html>

A questo punto la configurazione del progetto è terminata, nella figura seguente è riportata la sua struttura finale:

struttura

Possiamo lanciare l’applicazione cliccando con il tasto destro del mouse sulla classe DemoJsFApplicaztion.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:

run2

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:

http://localhost:8080/saluti.xhtml

Il risultato è il seguente:

paginafinale

la pagina visualizza un semplice tag panel di PrimeFaces con un messaggio di saluto.

Passo 4 – Generazione del file .war e suo utilizzo

Per generare il file .war contenente la nostra applicazione è possible 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:

 generawar1

Eclipse genererà il file demoJsf-0.0.1-SNAPSHOT.war nella directory target presente all’interno del progetto.

Quello che abbiamo appena generato è un file .war eseguibile, nel senso che è possibile lanciare l’applicazione con il comando:

java  -jar  demoJsf-0.0.1-SNAPSHOT.war

Il file .war è 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 context di ROOT e quindi l’url da utilizzare è:  http://localhost:8080/ senza inserire il nome dell’applicazione.

Lo stesso file può essere utilizzato, senza nessuna modifica, con un server Tomcat esterno: basta copiarlo nella sua directoty webapps. In questo caso, per accedere all’applicazione si dovrà utilizzare, come context dell’applicazione, il nome del suo file .war, quindi l’url sarà: http://localhost:8080/demoJsf-0.0.1-SNAPSHOT/

Alla base di questo duplice comportamento del file .war, cioè come un’applicazione stand-alone eseguibile oppure come una web application da deploiare in un web server esterno, c’è il plugin Spring Maven Boot Plugin che è stato configurato nel file pom.xml del progetto:

<build>
  <plugins>
    <plugin>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-maven-plugin</artifactId>
    </plugin>
  </plugins>
</build>

Questo plugin, durante la fase di creazione del file .war, verifica se le librerie relative al server Tomcat configurate nel file pom.xml hanno come scope il valore provided e, in caso affermativo, le copia in una directory di nome lib-provided presente all’interno del file .war stesso. E’ proprio per questo motivo che sono stati aggiunti i seguenti elementi nel file pom.xml:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-tomcat</artifactId>
  <scope>provided</scope>
</dependency>
    
<dependency>
  <groupId>org.apache.tomcat.embed</groupId>
  <artifactId>tomcat-embed-jasper</artifactId>
  <scope>provided</scope>
</dependency>

Lo stesso plugin rende anche eseguibile il file .war inserendo nel suo file MANIFEST.MF l’attributo:

Start-Class: com.thespringside.demoJsf.DemoJsfApplication

cioè il nome completo della classe contenente il metodo main. In questo modo, eseguendo il file .war tramite il comando  java -jar, viene invocato il metodo main che a sua volta effettua lo startup di Spring Boot.

Il file .war è poi utilizzabile anche con un server Tomcat esterno grazie al fatto che la classe java DemoJsfApplication estende la classe SpringBootServletInitializer ed effettua l’override del metodo configure. In fase di avvio dell’applicazione, il web server invoca questo metodo configure il quale, a sua volta, effettua lo startup di Spring Boot

Il codice sorgente dell’applicazione è disponibile su GitHub all’indirizzo:

https://github.com/thespringside/demoJsf.git

Nei prossimi articoli continuerò ad approfondire le enormi potenzialità offerte dal framework Spring e dal progetto Spring Boot

A presto

@Copyright Roberto Venanzi – www.thespringside.com