2. Java Server Faces
Presentiamo ora il framework Java Server Faces. Verrà utilizzata la versione 2, ma gli esempi illustrano principalmente le caratteristiche della versione 1. Della versione 2 presenteremo solo le caratteristiche necessarie all’applicazione di esempio che seguirà.
2.1. Il ruolo di JSF in un’applicazione web
Innanzitutto, collochiamo JSF nel contesto dello sviluppo di un’applicazione web. Nella maggior parte dei casi, questa sarà costruita su un’architettura a più livelli come la seguente:
![]() |
- il livello [web] è il livello a contatto con l’utente dell’applicazione web. Quest’ultimo interagisce con l’applicazione web attraverso pagine web visualizzate da un browser. È in questo livello che si trova JSF e solo in questo livello,
- il livello [métier] implementa le regole di gestione dell’applicazione, come il calcolo di uno stipendio o di una fattura. Questo livello utilizza i dati provenienti dall’utente tramite il livello [web] e dal Sgbd tramite il livello [DAO],
- il livello [DAO] (Data Access Objects), il livello [jpa] (Java Persistence API) e il driver JDBC gestiscono l’accesso ai dati del DBMS. Il livello [jpa] funge da ORM (Object Relational Mapper). Funge da ponte tra gli oggetti gestiti dal livello [DAO] e le righe e le colonne dei dati di un database relazionale,
- l’integrazione dei livelli può essere realizzata tramite un contenitore Spring o EJB3 (Enterprise Java Bean).
Gli esempi riportati di seguito per illustrare JSF utilizzeranno un solo livello, ovvero il livello [web]:
![]() |
Una volta acquisite le nozioni di base su JSF, realizzeremo applicazioni Java EE multistrato.
2.2. Il modello di sviluppo MVC di JSF
JSF implementa il modello di architettura denominato MVC (Modello – Vista – Controller) nel modo seguente:
![]() |
Questa architettura implementa il Design Pattern MVC (Modello, Vista, Controller). L’elaborazione di una richiesta da parte di un client si svolge secondo le quattro fasi seguenti:
- richiesta – il browser del cliente invia una richiesta al controller [Faces Servlet]. Quest’ultimo gestisce tutte le richieste dei clienti. È il punto di ingresso dell’applicazione. È la C di MVC,
- elaborazione - il controller C elabora questa richiesta. A tal fine, si avvale di gestori di eventi specifici dell’applicazione [2a]. Questi gestori possono necessitare dell’aiuto del livello di business [2b]. Una volta elaborata la richiesta del cliente, questa può generare diverse risposte. Un esempio classico è:
- una pagina di errore se la richiesta non è stata elaborata correttamente;
- una pagina di conferma in caso contrario,
- la navigazione: il controller sceglie la risposta (= vista) da inviare al cliente. La scelta della risposta da inviare al cliente richiede diverse fasi:
- scegliere il Facelet che genererà la risposta. È ciò che viene chiamato la vista V, la V di MVC. Questa scelta dipende in genere dal risultato dell’esecuzione dell’azione richiesta dall’utente;
- fornire a questo Facelet i dati di cui ha bisogno per generare tale risposta. Infatti, questa contiene molto spesso informazioni calcolate dal controller. Queste informazioni costituiscono ciò che viene chiamato il modello M della vista, la M di MVC,
La fase 3 consiste quindi nella scelta di una vista V e nella costruzione del modello M necessario a tale vista.
- Risposta: il controller C richiede al Facelet selezionato di visualizzarsi. Quest’ultimo utilizza il modello M preparato dal controller C per inizializzare le parti dinamiche della risposta che deve inviare al client. La forma esatta di quest’ultima può variare: può trattarsi di un flusso HTML, PDF, Excel, ...
In un progetto JSF:
- il controller C è il servlet [javax.faces.webapp.FacesServlet]. Questo si trova nella libreria [javaee.jar],
- le viste V sono implementate da pagine che utilizzano la tecnologia Facelets,
- i modelli M e i gestori di eventi sono implementati da classi Java spesso denominate "backing beans" o, più semplicemente, beans.
Ora precisiamo il legame tra l’architettura web MVC e l’architettura a livelli. Si tratta di due concetti diversi che talvolta vengono confusi. Prendiamo un’applicazione web JSF a un livello:
![]() |
Se implementiamo il livello [web] con JSF, avremo sì un’architettura web MVC, ma non un’architettura a più livelli. In questo caso, il livello [web] si occuperà di tutto: presentazione, logica di business, accesso ai dati. Con JSF, saranno i bean a svolgere questo lavoro.
Ora consideriamo un'architettura web multistrato:
![]() |
Il livello [web] può essere implementato senza framework e senza seguire il modello MVC. Si ha quindi effettivamente un’architettura multilivello, ma il livello web non implementa il modello MVC.
In MVC abbiamo affermato che il modello M è quello della vista V, c.a.d, ovvero l’insieme dei dati visualizzati dalla vista V. Spesso viene fornita un’altra definizione del modello M di MVC:
![]() |
Molti autori ritengono che ciò che si trova a destra del livello [web] costituisca il modello M del MVC. Per evitare ambiguità, si parlerà:
- del modello di dominio quando si indica tutto ciò che si trova a destra del livello [web],
- del modello della vista quando si indicano i dati visualizzati da una vista V.
Di seguito, il termine «modello M» indicherà esclusivamente il modello di una vista V.
2.3. Esempio mv-jsf2-01: gli elementi di un progetto JSF
I primi esempi si limiteranno al solo livello web implementato con JSF 2:
![]() |
Una volta acquisite le nozioni di base, analizzeremo esempi più complessi con architetture multistrato.
2.3.1. Generazione del progetto
Generiamo il nostro primo progetto JSF2 con NetBeans 7.
![]() |
- in [1], creare un nuovo progetto,
- in [2], scegliamo la categoria [Maven] e il tipo di progetto [Web Application],
![]() |
- in [3], specificare la cartella principale della cartella del nuovo progetto,
- in [4], assegnare un nome al progetto,
- in [5], scegliere un server. Con NetBeans 7, è possibile scegliere tra i server Apache Tomcat e GlassFish. La differenza tra i due è che GlassFish supporta i EJB (Enterprise Java Bean) mentre Tomcat no. I nostri esempi JSF non utilizzeranno EJB. Pertanto, in questo caso è possibile scegliere qualsiasi server,
- in [6], si sceglie la versione Java EE 6 Web,
- in [7], il progetto generato.
Esaminiamo gli elementi del progetto e spieghiamo il ruolo di ciascuno.
![]() |
- in [1]: i diversi rami del progetto:
- [Web Pages]: conterrà le pagine web (.xhtml, .jsp, .html), le risorse (immagini, documenti vari), la configurazione del livello web e quella del framework JSF;
- [Source packages]: le classi Java del progetto;
- [Dependencies]: gli archivi .jar necessari al progetto e gestiti dal framework Maven;
- [Java Dependencies]: gli archivi .jar necessari al progetto e non gestiti dal framework Maven;
- [Project Files]: file di configurazione di Maven e NetBeans,
![]() |
- in [2]: il ramo [Web Pages],
Contiene la seguente pagina [index.jsp]:
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/HTML4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>
Si tratta di una pagina web che visualizza la stringa di caratteri 'Hello World' in caratteri grandi.
Il file [META-INF/context.xml] è il seguente:
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/mv-jsf2-01"/>
La riga 2 indica che il contesto dell'applicazione (o il suo nome) è /mv-jsf2-01. Ciò significa che le pagine web del progetto saranno richieste tramite un URL della forma http://machine:port/mv-jsf2-01/page. Il contesto è, per impostazione predefinita, il nome del progetto. Non sarà necessario modificare questo file.
![]() |
- in [3], il ramo [Source Packages],
Questo ramo contiene il codice sorgente delle classi Java del progetto. Qui non sono presenti classi. NetBeans ha generato un pacchetto predefinito che può essere eliminato: [4].
![]() |
- in [5], il ramo [Dependencies],
Questo ramo mostra tutte le librerie necessarie al progetto e gestite da Maven. Tutte le librerie elencate qui verranno scaricate automaticamente da Maven. Ecco perché un progetto Maven necessita di una connessione a Internet. Le librerie scaricate verranno archiviate in locale. Se un altro progetto necessita di una libreria già presente in locale, questa non verrà scaricata. Vedremo che questo elenco di librerie, così come i repository in cui sono disponibili, sono definiti nel file di configurazione del progetto Maven.
![]() |
- in [6], le librerie necessarie al progetto e non gestite da Maven,
![]() |
- in [7], i file di configurazione del progetto Maven:
- [nb-configuration.xml] è il file di configurazione di NetBeans. Non ci occuperemo di questo.
- [pom.xml]: il file di configurazione di Maven. POM sta per Project Object Model. A volte sarà necessario intervenire direttamente su questo file.
Il file [pom.xml] generato è il seguente:
<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>istia.st</groupId>
<artifactId>mv-jsf2-01</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>mv-jsf2-01</name>
<properties>
<endorsed.dir>${project.build.directory}/endorsed</endorsed.dir>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
<compilerArguments>
<endorseddirs>${endorsed.dir}</endorseddirs>
</compilerArguments>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.1.1</version>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.1</version>
<executions>
<execution>
<phase>validate</phase>
<goals>
<goal>copy</goal>
</goals>
<configuration>
<outputDirectory>${endorsed.dir}</outputDirectory>
<silent>true</silent>
<artifactItems>
<artifactItem>
<groupId>javax</groupId>
<artifactId>javaee-endorsed-api</artifactId>
<version>6.0</version>
<type>jar</type>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
- le righe 5-8 definiscono l’oggetto (artefatto) Java che verrà creato dal progetto Maven. Queste informazioni provengono dalla procedura guidata utilizzata durante la creazione del progetto:
![]() |
Un oggetto Maven è definito da quattro proprietà:
- [groupId]: un'informazione che assomiglia a un nome di pacchetto. Pertanto, le librerie del framework Spring hanno groupId=org.springframework, quelle del framework JSF hanno groupId=javax.faces,
- [artifactId]: il nome dell’oggetto Maven. Nel gruppo [org.springframework] si trovano quindi i seguenti artifactId: spring-context, spring-core, spring-beans, ... Nel gruppo [javax.faces] si trovano artifactId e jsf-api,
- [version]: numero di versione dell’artifact Maven. Pertanto, l'artifact org.springframework.spring-core presenta le seguenti versioni: 2.5.4, 2.5.5, 2.5.6, 2.5.6.SECO1, ...
- [packaging]: il formato dell'artifact, solitamente war o jar.
Il nostro progetto Maven genererà quindi un [war] (riga 8) nel gruppo [istia.st] (riga 5), denominato [mv-jsf2-01] (riga 6) e con versione [1.0-SNAPSHOT] (riga 7). Queste quattro informazioni devono definire in modo univoco un artefatto Maven.
Le righe 17-24 elencano le dipendenze del progetto Maven, ovvero l’elenco delle librerie necessarie al progetto. Ogni libreria è definita da quattro informazioni (groupId, artifactId, versione, packaging). Quando l’informazione packaging è assente, come in questo caso, viene utilizzato il file jar packaging. Si aggiunge un’altra informazione, scope, che stabilisce in quali fasi del ciclo di vita del progetto è necessaria la libreria. Il valore predefinito è compile, che indica che la libreria è necessaria sia per la compilazione che per l’esecuzione. Il valore provided significa che la libreria è necessaria durante la compilazione ma non durante l’esecuzione. In questo caso, durante l’esecuzione, sarà fornita dal server Tomcat 7.
2.3.2. Esecuzione del progetto
Eseguiamo il progetto:
![]() |
In [1], il progetto Maven viene eseguito. Il server Tomcat viene quindi avviato, se non lo era già. Viene avviato anche un browser e viene richiesta la pagina URL del contesto del progetto [2]. Poiché non viene richiesto alcun documento, viene utilizzata la pagina index.html, index.jsp, index.xhtml, se presente. In questo caso, si tratterà della pagina [index.jsp].
2.3.3. Il sistema di file di un progetto Maven
![]() |
- [1]: il sistema di file del progetto si trova nella scheda [Files],
- [2]: i sorgenti Java si trovano nella cartella [src / main / java],
- [3]: le pagine web si trovano nella cartella [src / main / webapp],
- [4]: la cartella [target] viene creata durante la compilazione (build) del progetto,
- [5]: in questo caso, la compilazione del progetto ha creato un archivio denominato [mv-jsf2-01-1.0-SNAPSHOT.war]. È proprio questo archivio che è stato eseguito dal server Tomcat.
2.3.4. Configurare un progetto per JSF
Il nostro progetto attuale non è un progetto JSF. Mancano le librerie del framework JSF. Per trasformare il progetto corrente in un progetto JSF, procedere come segue:
![]() |
- in [1], si accede alle proprietà del progetto,
- in [2], si seleziona la categoria [Frameworks],
- in [3], si aggiunge un framework,
![]() |
- In [4], si seleziona Java Server Faces,
- in [5], NetBeans ci propone la versione 2.1 del framework. La accettiamo,
- in [6], il progetto viene quindi arricchito con nuove dipendenze.
Il file [pom.xml] è stato aggiornato per riflettere questa nuova configurazione:
<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>istia.st</groupId>
<artifactId>mv-jsf2-01</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>mv-jsf2-01</name>
...
<dependencies>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1.1-b04</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.1.1-b04</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
...
</build>
<repositories>
<repository>
<URL>http://download.java.net/maven/2/</URL>
<id>jsf20</id>
<layout>default</layout>
<name>Repository for library Library[jsf20]</name>
</repository>
<repository>
<URL>http://repo1.maven.org/maven2/</URL>
<id>jstl11</id>
<layout>default</layout>
<name>Repository for library Library[jstl11]</name>
</repository>
</repositories>
</project>
Righe 14-33: sono state aggiunte nuove dipendenze. Maven le scarica automaticamente. Le recupera da quelli che vengono chiamati repository. Viene utilizzato automaticamente il repository centrale (Central Repository). È possibile aggiungere altri repository tramite il tag <repository>. In questo caso sono stati aggiunti due repository:
- righe 46-51: un repository per la libreria JSF 2,
- righe 52-57: un repository per la libreria JSTL 1.1.
Il progetto è stato inoltre arricchito da una nuova pagina web:
![]() |
La pagina [index.HTML] è la seguente:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
Hello from Facelets
</h:body>
</html>
Qui abbiamo un file XML (riga 1). Vi si ritrovano i tag del HTML ma nel formato XML. Questo viene chiamato XHTML. La tecnologia utilizzata per creare pagine web con JSF 2 si chiama Facelets. Pertanto, la pagina XHTML viene talvolta definita una pagina Facelet.
Le righe 3-4 definiscono il tag <html> con gli spazi dei nomi XML (xmlns=XML Name Space).
- la riga 3 definisce lo spazio dei nomi principale http://www.w3.org/1999/xhtml,
- la riga 4 definisce lo spazio dei nomi http://java.sun.com/jsf/html dei tag HTML. Questi saranno preceduti dal prefisso h: come indicato da xmlns:h. Questi tag si trovano alle righe 5, 7, 8 e 10.
All'incontro con la dichiarazione di uno spazio dei nomi, il server web esplorerà le cartelle [META-INF] e Classpath dell'applicazione, alla ricerca di file con il suffisso .tld (TagLib Definition). In questo caso, li troverà nell’archivio [jsf-impl.jar] [1,2]:
![]() |
Esaminiamo il file [3] e il file [HTML_basic.tld]:
- alla riga 19, l'URI della libreria dei tag,
- alla riga 16, il suo nome abbreviato.
Le definizioni dei vari tag <h:xx> si trovano in questo file. Questi tag sono gestiti da classi Java che si trovano anche nell’artefatto [jsf-impl.jar].
Torniamo al nostro progetto JSF. È stato arricchito con un nuovo ramo:
![]() |
Il ramo [Other Sources] [1] contiene i file che devono trovarsi nel Classpath del progetto e che non sono codice Java. È il caso dei file di messaggi in JSF. Abbiamo visto che, se non si aggiunge il framework JSF al progetto, questo ramo non è presente. Per crearlo, è sufficiente creare la cartella [src / main / resources] [3] nella scheda [Files] [2].
Infine, è apparsa una nuova cartella nel ramo [Web Pages]:
![]() |
È stata creata la cartella [WEB-INF] contenente il file [web.xml] . Questo file configura l’applicazione web:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<URL-pattern>/faces/*</URL-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file>
</welcome-file-list>
</web-app>
- le righe 7-10 definiscono un servlet, c.a.d, una classe Java in grado di elaborare le richieste dei client. Un'applicazione JSF funziona nel modo seguente:
![]() |
Questa architettura implementa il Design Pattern MVC (Modello, Vista, Controller). Ricordiamo quanto già scritto in precedenza. L’elaborazione di una richiesta da parte di un client si svolge secondo le quattro fasi seguenti:
1 - richiesta - il browser del cliente invia una richiesta al controller [Faces Servlet]. Quest’ultimo riceve tutte le richieste dei clienti. È il punto di ingresso dell’applicazione. È la C di MVC,
2 - elaborazione - il controller C elabora questa richiesta. A tal fine, si avvale di gestori di eventi specifici dell’applicazione [2a]. Questi gestori possono aver bisogno dell’aiuto del livello di business [2b]. Una volta elaborata la richiesta del cliente, questa può generare diverse risposte. Un esempio classico è:
- una pagina di errore se la richiesta non è stata elaborata correttamente;
- una pagina di conferma in caso contrario,
3 - navigazione - il controller sceglie la risposta (= vista) da inviare al cliente. La scelta della risposta da inviare al cliente richiede diverse fasi:
- scegliere il Facelet che genererà la risposta. È ciò che viene chiamato la vista V, la V di MVC. Questa scelta dipende in genere dal risultato dell’esecuzione dell’azione richiesta dall’utente;
- fornire a questo Facelet i dati di cui ha bisogno per generare tale risposta. Infatti, questa contiene molto spesso informazioni calcolate dal controller. Queste informazioni costituiscono ciò che viene chiamato il modello M della vista, la M di MVC,
La fase 3 consiste quindi nella scelta di una vista V e nella costruzione del modello M necessario a tale vista.
4 - risposta - il controller C richiede alla Facelet selezionata di visualizzarsi. Quest’ultima utilizza il modello M preparato dal controller C per inizializzare le parti dinamiche della risposta che deve inviare al client. La forma esatta di quest’ultima può variare: può trattarsi di un flusso HTML, PDF, Excel, ...
In un progetto JSF:
- il controller C è il servlet [javax.faces.webapp.FacesServlet],
- le viste V sono implementate da pagine che utilizzano la tecnologia Facelets,
- i modelli M e i gestori di eventi sono implementati da classi Java spesso denominate "backing beans" o, più semplicemente, Beans.
Torniamo al contenuto del file [web.xml):
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<URL-pattern>/faces/*</URL-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file>
</welcome-file-list>
</web-app>
- righe 12-15: il tag <servlet-mapping> serve ad associare un servlet a un URL richiesto dal browser client. Qui si specifica che i URL della forma [/faces/*] devono essere elaborati dal servlet denominato [Faces Servlet]. Quest’ultimo è definito alle righe 7-10. Poiché nel file non è presente nessun altro tag <servlet-mapping>, ciò significa che il servlet [Faces Servlet] elaborerà solo i URL della forma [/faces/*]. Abbiamo visto che il contesto dell’applicazione si chiama [/mv-jsf2-01]. I URL dei clienti elaborati dal servlet [Faces Servlet] avranno quindi la forma [http://machine:port/mv-jsf2-01/faces/*]. Le pagine .html e .jsp saranno gestite per impostazione predefinita dal contenitore di servlet stesso, e non da una servlet specifica. Infatti, il contenitore di servlet sa come gestirle,
- righe 7-10: definiscono la servlet [Faces Servlet]. Poiché tutte le richieste URL accettate vengono indirizzate a essa, essa funge da controller C del modello MVC,
- riga 10: indica che la servlet deve essere caricata in memoria all’avvio del server web. Per impostazione predefinita, una servlet viene caricata solo al ricevimento della prima richiesta che le viene inviata,
- righe 3-6: definiscono un parametro destinato al servlet [Faces Servlet]. Il parametro javax.faces.PROJECT_STAGE definisce la fase in cui si trova il progetto in esecuzione. Nella fase di sviluppo (Development), la servlet [Faces Servlet] visualizza messaggi di errore utili per il debug. Nella fase di produzione (Production) questi messaggi non vengono più visualizzati,
- righe 17-19: durata in minuti di una sessione. Un client interagisce con l’applicazione tramite una serie di cicli richiesta/risposta. Ogni ciclo utilizza una connessione TCP-IP a sé stante, nuova ad ogni nuovo ciclo. Pertanto, se un cliente C effettua due richieste D1 e D2, il server S non ha modo di sapere che entrambe le richieste appartengono allo stesso cliente C. Il server S non dispone della memoria del cliente. È il protocollo HTTP utilizzato (HyperText Transport Protocol) a determinarlo: il cliente dialoga con il server attraverso una successione di cicli richiesta-cliente/risposta-server, utilizzando ogni volta una nuova connessione TCP-IP. Si parla di protocollo senza stato. In altri protocolli, come ad esempio il FTP (File Transfer Protocol), il client C utilizza la stessa connessione per tutta la durata del suo dialogo con il server S. Una connessione è quindi associata a un cliente specifico. Il server S sa sempre con chi ha a che fare. Per poter riconoscere che una richiesta appartiene a un determinato cliente, il server web può utilizzare la tecnica della sessione:
- alla prima richiesta di un client, il server S gli invia la risposta prevista più un token, una sequenza di caratteri casuale, unica per quel client;
- ad ogni richiesta successiva, il cliente C rinvia al server S il token che ha ricevuto, consentendo così al server S di riconoscerlo.
L’applicazione ha ora la possibilità di chiedere al server di memorizzare informazioni associate a un determinato cliente. Si parla in questo caso di sessione cliente. La riga 18 indica che la durata di una sessione è di 30 minuti. Ciò significa che se un cliente C non effettua una nuova richiesta entro 30 minuti, la sua sessione viene interrotta e le informazioni in essa contenute vanno perse. Alla sua prossima richiesta, tutto avverrà come se fosse un nuovo cliente e verrà avviata una nuova sessione,
- righe 21-23: l’elenco delle pagine da visualizzare quando l’utente richiede il contesto senza specificare una pagina, ad esempio in questo caso [http://machine:port/mv-jsf2-01]. In questo caso, il server web (non il servlet) verifica se l’applicazione ha definito un tag <welcome-file-list>. In caso affermativo, visualizza la prima pagina trovata nell’elenco. Se questa non esiste, visualizza la seconda pagina e così via fino a trovare una pagina esistente. In questo caso, quando il client richiede URL [http://machine:port/mv-jsf2-01], gli verrà fornita URL [http://machine:port/mv-jsf2-01/index.xhtml].
2.3.5. Eseguire il progetto
Quando si esegue il nuovo progetto, il risultato ottenuto nel browser è il seguente:
![]() |
- in [1], il contesto è stato richiesto senza specificare il documento,
- in [2], come spiegato, viene quindi fornita la pagina iniziale (welcome-file) [index.xhtml].
Si può essere curiosi di dare un'occhiata al codice sorgente ricevuto [3]:
Abbiamo ricevuto HTML. Tutti i tag <h:xx> di index.xhtml sono stati tradotti nei loro corrispondenti in HTML.
2.3.6. Il repository Maven locale
Abbiamo detto che Maven scarica le dipendenze necessarie al progetto e le memorizza localmente. È possibile esplorare questo repository locale:
![]() |
- in [1], si seleziona l’opzione [Window / Other / Maven Repository Browser],
- in [2], si apre una scheda [Maven Repositories],
- in [3], contiene due rami, uno per il repository locale e l’altro per quello centrale. Quest’ultimo è enorme. Per visualizzarne il contenuto, è necessario aggiornare il relativo indice [4]. Questo aggiornamento richiede diverse decine di minuti.
![]() |
- in [5], le librerie del repository locale,
- in [6], vi si trova un ramo [istia.st] che corrisponde al [groupId] del nostro progetto,
- In [7] si accede alle proprietà del repository locale,
- in [8] si trova il percorso del repository locale. È utile conoscerlo perché a volte (raramente) Maven non utilizza più l'ultima versione del progetto. Si apportano delle modifiche e si nota che non vengono prese in considerazione. È quindi possibile eliminare manualmente il ramo del repository locale corrispondente al nostro [groupId]. Questo costringe Maven a ricreare il ramo a partire dall’ultima versione del progetto.
2.3.7. Cercare un artefatto con Maven
Impariamo ora a cercare un artefatto con Maven. Partiamo dall’elenco delle dipendenze attuali del file [pom.xml]:
<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>istia.st</groupId>
<artifactId>mv-jsf2-01</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>mv-jsf2-01</name>
...
<dependencies>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1.1-b04</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.1.1-b04</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
...
</build>
<repositories>
<repository>
<url>http://download.java.net/maven/2/</url>
<id>jsf20</id>
<layout>default</layout>
<name>Repository for library Library[jsf20]</name>
</repository>
<repository>
<url>http://repo1.maven.org/maven2/</url>
<id>jstl11</id>
<layout>default</layout>
<name>Repository for library Library[jstl11]</name>
</repository>
</repositories>
</project>
Le righe 13-40 definiscono le dipendenze e le righe 45-58 i repository in cui è possibile trovarle, oltre al repository centrale, che viene sempre utilizzato. Modificheremo le dipendenze per utilizzare le librerie nella loro versione più recente.
![]() |
Per prima cosa, eliminiamo le dipendenze attuali [1]. Il file [pom.xml] viene quindi modificato:
<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>
...
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
...
<repositories>
<repository>
<url>http://download.java.net/maven/2/</url>
<id>jsf20</id>
<layout>default</layout>
<name>Repository for library Library[jsf20]</name>
</repository>
<repository>
<url>http://repo1.maven.org/maven2/</url>
<id>jstl11</id>
<layout>default</layout>
<name>Repository for library Library[jstl11]</name>
</repository>
</repositories>
</project>
Righe 5-12: le dipendenze rimosse non compaiono più in [pom.xml]. Ora cerchiamole nei repository Maven.
![]() |
- in [1], si aggiunge una dipendenza al progetto,
- in [2], è necessario specificare le informazioni relative all’artefatto ricercato (groupId, artifactId, versione, packaging (Tipo) e ambito). Iniziamo specificando [groupId] [3],
- in [4], digitiamo [espace] per visualizzare l'elenco degli artefatti possibili. In questo caso [jsf-api] e [jsf-impl]. Scegliamo [jsf-api],
- poi [5]; procedendo allo stesso modo, scegliamo la versione più recente. Il tipo di pacchetto è jar.
Procediamo in questo modo per tutti gli artefatti:
![]() | ![]() | ![]() | ![]() |
![]() |
In [6], le dipendenze aggiunte compaiono nel progetto. Il file [pom.xml] riflette queste modifiche:
<dependencies>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1.7</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.1.7</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
<type>jar</type>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
Supponiamo ora di non conoscere il [groupId] dell'artefatto desiderato. Ad esempio, vogliamo utilizzare Hibernate come ORM (Object Relational Mapper) ed è tutto ciò che sappiamo. Possiamo quindi visitare il sito [http://mvnrepository.com/]:
![]() |
In [1] è possibile digitare delle parole chiave. Digitiamo hibernate e avviamo la ricerca.
![]() |
- in [2], scegliamo [groupId], org.hibernate e [artifactId], hibernate-core,
- in [3], scegliamo la versione 4.1.2-Final,
- in [4], otteniamo il codice Maven da incollare nel file [pom.xml]. Procediamo.
<dependencies>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>4.1.2.Final</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1.7</version>
<type>jar</type>
</dependency>
...
</dependencies>
Salviamo il file [pom.xml]. Maven avvia quindi il download delle nuove dipendenze. Il progetto si evolve come segue:
![]() |
- in [5], la dipendenza [hibernate-core-4.1.2-Final]. Nel repository in cui è stato trovato, anche questo [artifactId] è descritto da un file [pom.xml]. Questo file è stato letto e Maven ha rilevato che il file [artifactId] presentava delle dipendenze. Le scarica anch'esse. Lo farà per ogni file [artifactId] scaricato. Alla fine, nel file [6] si trovano delle dipendenze che non erano state richieste direttamente. Queste sono contrassegnate da un'icona diversa da quella del file principale [artifactId].
In questo documento utilizziamo Maven principalmente per questa caratteristica. Ciò ci evita di dover conoscere tutte le dipendenze di una libreria che vogliamo utilizzare. Lasciamo che sia Maven a gestirle. Inoltre, condividendo un file [pom.xml] tra gli sviluppatori, si ha la certezza che ogni sviluppatore utilizzi effettivamente le stesse librerie.
Negli esempi che seguiranno, ci limiteremo a fornire il file [pom.xml] utilizzato. Il lettore dovrà semplicemente utilizzarlo per trovarsi nelle stesse condizioni descritte nel documento. Inoltre, i progetti Maven sono supportati dai principali ambienti di sviluppo Java (Eclipse, NetBeans, IntelliJ, JDeveloper). Il lettore potrà quindi utilizzare il proprio ambiente preferito per testare gli esempi.
2.4. Esempio mv-jsf2-02: gestore di eventi – internazionalizzazione – navigazione tra le pagine
2.4.1. L'applicazione
L’applicazione è la seguente:
![]() |
- in [1], la pagina iniziale dell'applicazione,
- in [2], due link per cambiare la lingua delle pagine dell'applicazione,
- in [3], un link di navigazione verso un'altra pagina,
- quando si clicca su [3], viene visualizzata la pagina [4],
- il link [5] consente di tornare alla pagina iniziale.
![]() |
- nella pagina iniziale [1], i link [2] consentono di cambiare lingua,
- in [3], la pagina iniziale in inglese.
2.4.2. Il progetto NetBeans
Si genererà un nuovo progetto web come spiegato al paragrafo 2.3.1. Lo si chiamerà mv-jsf2-02:
![]() |
- in [1], il progetto generato,
- in [2], sono stati eliminati il pacchetto [istia.st.mvjsf202] e il file [index.jsp],
- Nel file [3] sono state aggiunte delle dipendenze Maven tramite il seguente file [pom.xml]:
<dependencies>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
Le dipendenze aggiunte sono quelle del framework JSF. È sufficiente copiare le righe sopra riportate nel file [pom.xml] al posto delle dipendenze precedenti.
![]() |
- in [4, 5]: si crea una cartella [src / main / resources] nella scheda [Files],
- in [6], nella scheda [Projects], è stato creato il ramo [Other Sources].
Ora disponiamo di un progetto JSF. In esso creeremo diversi tipi di file:
- pagine web in formato XHTML,
- classi Java,
- file di messaggi,
- il file di configurazione del progetto JSF.
Vediamo come creare ciascun tipo di file:
![]() |
- in [1], creiamo una pagina JSF
- in [2], creiamo una pagina [index.xhtml] in formato [Facelets] [3],
- in [4], sono stati creati due file: [index.xhtml] e [WEB-INF / web.xml].
Il file [web.xml] configura l’applicazione JSF. È il seguente:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<URL-pattern>/faces/*</URL-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file>
</welcome-file-list>
</web-app>
Abbiamo già commentato questo file nel paragrafo 2.3.4. Ricordiamo le sue proprietà principali:
- tutti i file URL del tipo faces/* vengono elaborati dal servlet [javax.faces.webapp.FacesServlet],
- la pagina [index.xhtml] è la pagina iniziale dell'applicazione.
Il file [index.xhtml] creato è il seguente:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<h:head>
<title>Facelet Title</title>
</h:head>
<h:body>
Hello from Facelets
</h:body>
</html>
Abbiamo già incontrato questo file nel paragrafo 2.3.4.
Creiamo ora una classe Java:
![]() |
- in [1], creiamo una classe Java nel ramo [Source Packages],
- in [2], le si assegna un nome e la si inserisce nel pacchetto [3],
- in [4], la classe creata appare nel progetto.
Il codice della classe creata è uno scheletro di classe:
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package istia.st;
/**
*
* @author Serge Tahé
*/
public class Form {
}
Infine, creiamo un file di messaggi:
- in [1], creazione di un file [Properties],
- in [2], si specifica il nome del file e in [3] la sua cartella,
- in [4], il file [messages.properties] è stato creato.
A volte è necessario creare il file [WEB-INF/faces-config.xml] per configurare il progetto JSF. Questo file era obbligatorio con JSF 1. È facoltativo con JSF 2. È tuttavia necessario se il sito JSF è internazionalizzato. Questo sarà il caso in seguito. Mostriamo quindi ora come creare questo file di configurazione.
![]() |
- in [1], creiamo il file di configurazione JSF,
- in [2], gli assegniamo un nome e in [3] la cartella,
- in [4], il file creato.
Il file [faces-config.xml] creato è il seguente:
<?xml version='1.0' encoding='UTF-8'?>
<!-- =========== FULL CONFIGURATION FILE ================================== -->
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
</faces-config>
Il tag radice è <faces-config>. Il corpo di questo tag è vuoto. Lo completeremo in seguito.
Ora disponiamo di tutti gli elementi necessari per creare un progetto JSF. Negli esempi che seguiranno, presenteremo il progetto JSF completo e ne analizzeremo poi gli elementi uno per uno. Presentiamo ora un progetto per illustrare i concetti:
- gestione degli eventi di un modulo,
- dell’internazionalizzazione delle pagine di un sito JSF,
- della navigazione tra le pagine.
Il progetto [mv-jsf2-02] si presenta come segue. Il lettore può trovarlo sul sito degli esempi (cfr. paragrafo 1.2).
![]() |
- in [1], i file di configurazione del progetto JSF,
- in [2], le pagine JSF del progetto,
- in [3], l'unica classe Java,
- in [4], i file dei messaggi.
2.4.3. La pagina [index.xhtml]
Il file [index.xhtml] [1] invia la pagina [2] al browser del cliente:
![]() |
Il codice che genera questa pagina è il seguente:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<head>
...
</head>
<body>
....
</body>
</f:view>
</html>
- righe 7-9: gli spazi dei nomi / le librerie di tag utilizzati dalla pagina. I tag con prefisso h sono tag HTML, mentre i tag con prefisso f sono tag specifici di JSF,
- riga 10: il tag <f:view> serve a delimitare il codice che il motore JSF deve elaborare, ovvero quello in cui compaiono i tag <f:xx>. L'attributo locale consente di specificare la lingua di visualizzazione della pagina. In questo caso ne useremo due: l'inglese e il francese. Il valore dell’attributo `local` è espresso sotto forma di un’espressione EL (Expression Language) #{espressione}. La forma dell’espressione può variare. La esprimeremo più spesso sotto forma di `bean['clé']` o `bean.champ`. Nei nostri esempi, bean sarà una classe Java o un file di messaggi. Con JSF 1, questi bean dovevano essere dichiarati nel file [faces-config.xml]. Con JSF 2, ciò non è più obbligatorio per le classi Java. Ora è possibile utilizzare delle annotazioni che trasformano una classe Java in un bean riconosciuto da JSF 2. Il file dei messaggi deve invece essere dichiarato nel file di configurazione [faces-config.xml].
2.4.4. Il bean [changeLocale]
Nell'espressione EL #{changeLocale.locale}:
- changeLocale è il nome di un bean, in questo caso la classe Java ChangeLocale,
- locale è un campo della classe ChangeLocale. L'espressione viene valutata da [ChangeLocale].getLocale(). In generale, l'espressione #{bean.champ} viene valutata come [Bean].getChamp(), dove [Bean] è un'istanza della classe Java a cui sono stati assegnati i nomi bean e getChamp, il getter associato al campo champ del bean.
La classe ChangeLocale è la seguente:
package utils;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.enterprise.context.SessionScoped;
@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{
// la versione locale delle pagine
private String locale="fr";
public ChangeLocale() {
}
...
public String getLocale() {
return locale;
}
}
- riga 11: il campo locale,
- riga 17: il suo getter,
- riga 7: l'annotazione ManagedBean rende la classe Java ChangeLocale un bean riconosciuto da JSF. Un bean è identificato da un nome. Questo può essere specificato tramite l'attributo name dell'annotazione: @ManagedBean(name= "xx "). In assenza dell'attributo name, viene utilizzato il nome della classe, con la prima lettera in minuscolo. Il nome del bean ChangeLocale è quindi changeLocale. Si noti che l'annotazione ManagedBean appartiene al pacchetto javax.faces.bean.ManagedBean e non al pacchetto javax.annotations.ManagedBean.
- riga 8: l'annotazione SessionScoped definisce l'ambito del bean. Ne esistono diversi. Utilizzeremo comunemente i seguenti tre:
- RequestScoped: la durata del bean corrisponde a quella del ciclo richiesta del browser / risposta del server. Se, per elaborare una nuova richiesta proveniente dallo stesso browser o da un altro, questo bean dovesse essere nuovamente necessario, verrà istanziato di nuovo,
- SessionScoped: la durata del bean corrisponde a quella della sessione di un determinato cliente. Il bean viene creato inizialmente per soddisfare le esigenze di una delle richieste di quel cliente. Rimarrà quindi in memoria nella sessione di quel cliente. Un bean di questo tipo memorizza in genere dati specifici di un determinato cliente. Verrà distrutto quando verrà chiusa la sessione del cliente,
- ApplicationScoped: la durata del bean corrisponde a quella dell’applicazione stessa. Un bean con questa durata è solitamente condiviso da tutti i clienti dell’applicazione. In genere viene inizializzato all’avvio dell’applicazione.
Queste annotazioni sono presenti in due pacchetti: javax.enterprise.context.SessionScoped (JSF 2) e javax.faces.bean.SessionScoped (JSF 1). In questo caso utilizziamo il pacchetto JSF 2. Ciò ci obbliga a creare il file [WEB-INF / beans.xml]:
![]() |
Questo file viene generato automaticamente da NetBeans quando si importa il pacchetto [javax.enterprise.context.SessionScoped]. Il suo contenuto è il seguente:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
</beans>
A parte il tag radice <beans>, il file è vuoto. È sufficiente così. È necessaria solo la sua presenza.
Si noti infine che la classe [ChangeLocale] implementa l'interfaccia [Serializable]. Ciò è obbligatorio per i bean con ambito Session che il server web potrebbe dover serializzare in file. Torneremo in seguito sul bean [ChangeLocale].
2.4.5. Il file dei messaggi
Torniamo al file [index.xhtml]:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<head>
<title><h:outputText value="#{msg['welcome.titre']}" /></title>
</head>
<body>
...
</body>
</f:view>
</html>
- riga 8: il tag <h:outputText> visualizza il valore di un'espressione EL #{msg['welcome.titre']} della forma #{bean['champ']}. bean è il nome di una classe Java oppure quello di un file di messaggi. In questo caso, si tratta del nome di un file di messaggi. Quest’ultimo deve essere dichiarato nel file di configurazione [faces-config.xml]. Il bean msg è dichiarato come segue:
<?xml version='1.0' encoding='UTF-8'?>
<!-- =========== FULL CONFIGURATION FILE ================================== -->
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<application>
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
</application>
</faces-config>
- righe 11-18: il tag <application> serve a configurare l'applicazione JSF,
- righe 12-17: il tag <resource-bundle> serve a definire le risorse per l’applicazione, in questo caso un file di messaggi,
- righe 13-15: il tag <base-name> definisce il nome del file dei messaggi,
- riga 14: il file si chiamerà messages[_CodeLangue][_CodePays].properties. Il tag <base-name> definisce solo la prima parte del nome. Il resto è implicito. Possono esistere più file di messaggi, uno per ogni lingua:
![]() |
- in [1] si vedono quattro file di messaggi corrispondenti al nome di base "messages" definito in [faces-config.xml],
- messages_fr.properties: contiene i messaggi in francese (codice fr);
- messages_en.properties: contiene i messaggi in inglese (codice en);
- messages_es_ES.properties: contiene i messaggi in spagnolo (codice es) della Spagna (codice ES). Esistono altre varianti dello spagnolo, ad esempio quella della Bolivia (es_BO);
- messages.properties: viene utilizzato dal server quando la lingua del computer su cui è in esecuzione non ha alcun file di messaggi ad essa associato. Verrebbe utilizzato, ad esempio, se l’applicazione fosse in esecuzione su un computer in Germania, dove la lingua predefinita sarebbe il tedesco (de). Poiché non esiste il file [messages_de.properties], l'applicazione utilizzerebbe il file [messages.properties],
- in [2]: i codici delle lingue sono oggetto di uno standard internazionale,
- in [3]: lo stesso vale per i codici dei paesi.
Il nome del file dei messaggi è definito alla riga 14. Verrà cercato nel file Classpath del progetto. Se si trova all’interno di un pacchetto, quest’ultimo deve essere specificato alla riga 14, ad esempio ressources.messages, se il file [messages.properties] si trova nella cartella [ressources] del Classpath. Poiché il nome, alla riga 14, non contiene alcun pacchetto, il file [messages.properties] deve essere collocato nella directory principale della cartella [src / main / resources]:
![]() |
In [1], nella scheda [Projects] del progetto NetBeans, il file [messages.properties] viene presentato come un elenco delle diverse versioni dei messaggi definite. Le versioni sono identificate da una sequenza composta da uno a tre codici [codeLangue_codePays_codeVariante]. In [1] è stato utilizzato solo il codice [codeLangue]: en per l'inglese, fr per il francese. Ogni versione è contenuta in un file separato nel sistema di file.
Nel nostro esempio, il file dei messaggi in francese [messages_fr.properties] conterrà i seguenti elementi:
welcome.titre=Tutoriel JSF (JavaServer Faces)
welcome.langue1=Fran\u00e7ais
welcome.langue2=Anglais
welcome.page1=Page 1
page1.titre=page1
page1.entete=Page 1
page1.welcome=Page d'accueil
Il file [messages_en.properties], invece, sarà il seguente:
welcome.titre=JSF (JavaServer Faces) Tutorial
welcome.langue1=French
welcome.langue2=English
welcome.page1=Page 1
page1.titre=page1
page1.entete=Page 1
page1.welcome=Welcome page
Il file [messages.properties] è identico al file [messages_en.properties]. Alla fine, il browser del cliente potrà scegliere tra pagine in francese e pagine in inglese.
Torniamo al file [faces-config.xml] che dichiara il file dei messaggi:
...
<application>
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
</application>
</faces-config>
La riga 8 indica che una riga del file dei messaggi sarà referenziata dall’identificatore msg nelle pagine JSF. Questo identificatore è utilizzato nel file [index.xhtml] già esaminato:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<head>
<title><h:outputText value="#{msg['welcome.titre']}" /></title>
</head>
<body>
...
</body>
</f:view>
</html>
Il tag <h:outputText> della riga 8 visualizzerà il valore del messaggio (presenza dell'identificatore msg) con chiave welcome.titre. Questo messaggio viene cercato e trovato nel file [messages.properties] della lingua attualmente attiva. Ad esempio, per il francese:
welcome.titre=Tutoriel JSF (JavaServer Faces)
Un messaggio ha la forma chiave=valore. La riga 8 del file [index.xhtml] diventa la seguente dopo la valutazione dell’espressione #{msg['welcome.titre']}:
<title><h:outputText value="Tutoriel JSF (JavaServer Faces)" /></title>
Questo meccanismo dei file di messaggi consente di cambiare facilmente la lingua delle pagine di un progetto JSF. Si parla di internazionalizzazione del progetto o, più spesso, della sua abbreviazione i18n, poiché la parola «internazionalizzazione» inizia con la i e finisce con la n e ci sono 18 lettere tra la i e la n.
2.4.6. Il modulo
Continuiamo ad esplorare il contenuto del file [index.xhtml]:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<head>
<title><h:outputText value="#{msg['welcome.titre']}" /></title>
</head>
<body>
<h:form id="formulaire">
<h:panelGrid columns="2">
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
<h:commandLink value="#{msg['welcome.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
</h:panelGrid>
<h1><h:outputText value="#{msg['welcome.titre']}" /></h1>
<h:commandLink value="#{msg['welcome.page1']}" action="page1"/>
</h:form>
</body>
</f:view>
</html>
- righe 11-18: il tag <h:form> introduce un modulo. Un modulo è generalmente costituito da:
- tag dei campi di immissione (testo, pulsanti di opzione, caselle di controllo, elenchi di elementi, ...);
- tag di convalida del modulo (pulsanti, link). È tramite un pulsante o un link che l’utente invia i dati inseriti al server che li elaborerà,
Ogni tag JSF può essere identificato tramite un attributo id. Il più delle volte è possibile farne a meno ed è proprio ciò che è stato fatto nella maggior parte dei tag JSF utilizzati in questo contesto. Tuttavia, tale attributo risulta utile in alcuni casi. Riga 17: il modulo è identificato dall’ID «formulaire». In questo esempio, l’ID del modulo non verrà utilizzato e avrebbe potuto essere omesso.
- Righe 18-21: il tag <h:panelGrid> definisce qui una tabella HTML a due colonne. Da esso deriva il tag <table>,
- il modulo dispone di tre link che ne attivano l’elaborazione, alle righe 19, 20 e 23. Il tag <h:commandLink> ha almeno due attributi:
- value: il testo del link;
- action: o una stringa di caratteri C, oppure il riferimento a un metodo che, una volta eseguito, restituisce la stringa di caratteri C. Questa stringa di caratteri C può essere:
- il nome di una pagina JSF del progetto,
- oppure un nome definito nelle regole di navigazione del file [faces-config.xml] e associato a una pagina JSF del progetto;
In entrambi i casi, la pagina JSF viene visualizzata una volta eseguita l’azione definita dall’attributo action.
Esaminiamo il funzionamento dell’elaborazione dei moduli con l’esempio del link alla riga 13:
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>}"/>
Innanzitutto, viene utilizzato il file dei messaggi per sostituire l’espressione #{msg['welcome.langue1']} con il suo valore. Dopo la valutazione, il tag diventa:
<h:commandLink value="Français" action="#{changeLocale.setFrenchLocale}"/>}"/>
La traduzione HTML di questo tag JSF sarà la seguente:
<a href="<a href="view-source:http://localhost:8080/mv-jsf2-02/faces/page1.xhtml#">#</a>" onclick="mojarra.jsfcljs(document.getElementById('formulaire'),{'formulaire:j_idt8':'formulaire:j_idt8'},'');return false">Français</a>
il che darà il seguente risultato visivo:
![]() |
Si noti l'attributo onclick del tag HTML <a>. Quando l'utente cliccherà sul link [Français], verrà eseguito del codice JavaScript. Questo è incorporato nella pagina ricevuta dal browser ed è il browser stesso a eseguirlo. Il codice JavaScript è ampiamente utilizzato nelle tecnologie JSF e AJAX (Asynchronous JavaScript and XML). In genere ha lo scopo di migliorare l’usabilità e la reattività delle applicazioni web. Il più delle volte viene generato automaticamente da strumenti software e in tal caso non è necessario comprenderlo. Tuttavia, a volte uno sviluppatore può trovarsi nella necessità di aggiungere codice JavaScript alle proprie pagine JSF. In questi casi è necessaria la conoscenza di JavaScript.
In questo caso non è necessario comprendere il codice JavaScript generato per il tag JSF <h:commandLink>. Si possono tuttavia notare due punti:
- il codice JavaScript utilizza l’identificatore del modulo che abbiamo assegnato al tag JSF <h:form>,
- JSF genera identificatori automatici per tutti i tag in cui l’attributo id non è stato definito. Ne vediamo un esempio qui: j_idt8. Assegnare un identificatore chiaro ai tag permette di comprendere meglio il codice JavaScript generato, qualora ciò si rendesse necessario. Ciò vale in particolare quando lo sviluppatore deve aggiungere autonomamente del codice JavaScript che manipola i componenti della pagina. In tal caso, ha bisogno di conoscere gli identificatori id dei propri componenti.
Cosa accadrà quando l’utente cliccherà sul link [Français] nella pagina sopra riportata? Consideriamo l’architettura di un’applicazione JSF:
![]() |
Il controller [Faces Servlet] riceverà la richiesta dal browser del client nel seguente formato: HTTP:
- righe 1-2: il browser richiede URL [http://localhost:8080/mv-jsf2-02/faces/index.xhtml]. È sempre così: i dati inseriti in un modulo JSF, ottenuto inizialmente tramite URL e URLFormulaire, vengono inviati a questo stesso URL. Il browser dispone di due modalità per inviare i valori inseriti: GET e POST. Con il metodo GET, i valori inseriti vengono inviati dal browser nel URL richiesto. Nell’esempio sopra riportato, il browser avrebbe potuto inviare la seguente prima riga:
GET /mv-jsf2-02/faces/index.xhtml?formulaire=formulaire&javax.faces.ViewState=-9139703055324497810%3A8197824608762605653&formulaire%3Aj_idt8=formulaire%3Aj_idt8 HTTP/1.1
Con il metodo POST qui utilizzato, il browser invia al server i valori inseriti tramite la riga 6.
- riga 3: indica il formato di codifica dei valori del modulo,
- riga 4: indica la dimensione in byte della riga 6,
- riga 5: riga vuota che indica la fine delle intestazioni HTTP e l’inizio dei 126 byte dei valori del modulo,
- riga 6: i valori del modulo nella forma element1=valore1&element2=valore2& ..., secondo il formato di codifica definito dalla riga 3. In questo formato di codifica, alcuni caratteri vengono sostituiti dal loro valore esadecimale. È il caso dell’ultimo elemento:
formulaire=formulaire&javax.faces.ViewState=...&formulaire%3Aj_idt8=formulaire%3Aj_idt8
dove %3A rappresenta il carattere :. È quindi la stringa modulo:j_idt8=modulo:j_idt8 che viene inviata al server. Forse ricorderete che abbiamo già incontrato l’identificatore j_idt8 quando abbiamo esaminato il codice HTML generato per il tag
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
Era stato generato automaticamente da JSF. Ciò che ci interessa in questo contesto è che la presenza di questo identificativo nella stringa dei valori inviati dal browser client permette a JSF di sapere che il link [Français] è stato cliccato. A questo punto utilizzerà l’attributo action sopra indicato per decidere come elaborare la stringa ricevuta. L'attributo action="#{changeLocale.setFrenchLocale}" indica a JSF che la richiesta del client deve essere elaborata dal metodo [setFrenchLocale] di un oggetto denominato changeLocale. Ricordiamo che questo bean è stato definito tramite annotazioni nella classe Java [ChangeLocale]:
@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{
Il nome di un bean è definito dall'attributo name dell'annotazione @ManagedBean. In assenza di tale attributo, come nome del bean viene utilizzato il nome della classe con la prima lettera minuscola.
Torniamo alla richiesta del browser:
![]() |
e al tag <h:commandLink> che ha generato il link [Français] su cui si è cliccato:
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
Il controller trasmetterà la richiesta del browser al gestore di eventi definito dall’attributo action del tag <h:commandLink>. Il gestore di eventi M a cui fa riferimento l’attributo action di un comando <h:commandLink> deve avere la seguente firma:
- non riceve alcun parametro. Vedremo che può comunque avere accesso alla richiesta del client,
- deve restituire un risultato C di tipo String. Questa stringa di caratteri C può essere:
- o il nome di una pagina JSF del progetto;
- oppure un nome definito nelle regole di navigazione del file [faces-config.xml] e associato a una pagina JSF del progetto;
- oppure un puntatore nullo, se il browser client non deve cambiare pagina,
Nell’architettura JSF sopra riportata, il controller [Faces Servlet] utilizzerà la stringa C restituita dal gestore di eventi ed eventualmente il proprio file di configurazione [faces-config.xml] per determinare quale pagina JSF deve inviare in risposta al client [4].
Nel tag
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
il gestore dell’evento «clic sul link» [Français] è il metodo [changeLocale.setFrenchLocale], dove changeLocale è un’istanza della classe [utils.ChangeLocale] già esaminata:
package utils;
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.faces.bean.ManagedBean;
@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{
// impostazioni locali delle pagine
private String locale="fr";
public ChangeLocale() {
}
public String setFrenchLocale(){
locale="fr";
return null;
}
public String setEnglishLocale(){
locale="en";
return null;
}
public String getLocale() {
return locale;
}
}
Il metodo setFrenchLocale presenta effettivamente la firma dei gestori di eventi. Ricordiamo che il gestore di eventi deve elaborare la richiesta del client. Poiché non riceve parametri, come può accedervi? Esistono diversi modi per farlo:
- Il bean B, che contiene il gestore degli eventi della pagina JSF P, è spesso anche quello che contiene il modello M di tale pagina. Ciò significa che il bean B contiene campi che verranno inizializzati con i valori inseriti nella pagina P. Ciò avverrà tramite il controller [Faces Servlet] prima che venga chiamato il gestore di eventi del bean B. Tale gestore avrà quindi accesso, tramite i campi del bean B a cui appartiene, ai valori inseriti dal cliente nel modulo e potrà elaborarli.
- Il metodo statico [FacesContext.getCurrentInstance()] di tipo [FacesContext] consente di accedere al contesto di esecuzione della richiesta corrente JSF, che è un oggetto di tipo [FacesContext]. Il contesto di esecuzione della richiesta così ottenuto consente di accedere ai parametri inviati al server dal browser client con il seguente metodo:
Se i parametri inviati (POST) dal browser client sono i seguenti:
il metodo getRequestParameterMap() restituirà il seguente dizionario:
chiave | valore |
modulo | modulo |
javax.faces.ViewState | ... |
modulo:j_id_id21 | modulo:j_id_id21 |
Nel tag
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
cosa ci si aspetta dal gestore di eventi locale.setFrenchLocale? Si vuole che imposti la lingua utilizzata dall'applicazione. Nel gergo Java, questo processo viene definito «localizzazione» dell’applicazione. Tale localizzazione viene utilizzata dal tag <f:view> della pagina JSF [index.xhtml]:
<f:view locale="#{changeLocale.locale}">
...
</f:view>
Per impostare la pagina in francese, è sufficiente che l’attributo locale abbia il valore fr. Per impostarla in inglese, occorre assegnargli il valore en. Il valore dell’attributo locale è ottenuto tramite l’espressione [ChangeLocale].getLocale(). Questa espressione restituisce il valore del campo locale della classe [ChangeLocale]. Da ciò si deduce il codice del metodo [ChangeLocale].setFrenchLocale() che deve impostare le pagine in francese:
public String setFrenchLocale(){
locale="fr";
return null;
}
Abbiamo spiegato che un gestore di eventi deve restituire una stringa di caratteri C che verrà utilizzata da [Faces Servlet] per individuare la pagina JSF da inviare in risposta al browser client. Se la pagina da restituire è la stessa di quella attualmente in elaborazione, il gestore di eventi può limitarsi a restituire il valore null. È ciò che avviene qui alla riga 3: si desidera restituire la stessa pagina [index.xhtml] ma in una lingua diversa.
Torniamo all’architettura di elaborazione della richiesta:
![]() |
Il gestore di eventi changeLocale.setFrenchLocale è stato eseguito e ha restituito il valore null al controller [Faces Servlet]. Quest’ultimo visualizzerà quindi nuovamente la pagina [index.xhtml]. Rivediamola:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<head>
<title><h:outputText value="#{msg['welcome.titre']}" /></title>
</head>
<body>
<h:form id="formulaire">
<h:panelGrid columns="2">
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
<h:commandLink value="#{msg['welcome.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
</h:panelGrid>
<h1><h:outputText value="#{msg['welcome.titre']}" /></h1>
<h:commandLink value="#{msg['welcome.page1']}" action="page1"/>
</h:form>
</body>
</f:view>
</html>
Ogni volta che viene valutato un valore di tipo #{msg['...']}, viene utilizzato uno dei file dei messaggi [messages.properties]. Quello utilizzato è quello che corrisponde alla “localizzazione” della pagina (riga 6). Poiché il gestore di eventi changeLocale.setFrenchLocale definisce questa localizzazione come fr, verrà utilizzato il file [messages_fr.properties]. Cliccando sul link [Anglais] (riga 14) la localizzazione verrà modificata in en (cfr. metodo changeLocale.setEnglishLocale). Verrà quindi utilizzato il file [messages_en.properties] e la pagina apparirà in inglese:
![]() | ![]() |
Ogni volta che viene visualizzata la pagina [index.xhtml], viene eseguito il tag <f:view>:
<f:view locale="#{changeLocale.locale}">
e quindi il metodo [ChangeLocale].getLocale() viene rieseguito. Poiché abbiamo assegnato l’ambito Session al nostro bean:
@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{
la localizzazione effettuata durante una richiesta viene conservata per le richieste successive.
Rimane da esaminare un ultimo elemento della pagina [index.xhtml]:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<head>
<title><h:outputText value="#{msg['welcome.titre']}" /></title>
</head>
<body>
<h:form id="formulaire">
<h:panelGrid columns="2">
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
<h:commandLink value="#{msg['welcome.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
</h:panelGrid>
<h1><h:outputText value="#{msg['welcome.titre']}" /></h1>
<h:commandLink value="#{msg['welcome.page1']}" action="page1"/>
</h:form>
</body>
</f:view>
</html>
Il tag <h:commandLink> alla riga 17 ha un attributo "action" pari a una stringa di caratteri. In questo caso, non viene chiamato alcun gestore di eventi per elaborare la pagina. Si passa immediatamente alla pagina [page1.xhtml]. Esaminiamo il funzionamento dell'applicazione in questo caso d'uso:
![]() |
L’utente clicca sul link [Page 1]. Il modulo viene inviato al controller [Faces Servlet]. Quest’ultimo riconosce nella richiesta ricevuta che è stato cliccato il link [Page 1]. Esamina il tag corrispondente:
<h:commandLink value="#{msg['welcome.page1']}" action="page1"/>
Non vi è alcun gestore di eventi associato al link. Il controller [Faces Servlet] passa immediatamente alla fase [3] sopra indicata e visualizza la pagina [page1.xhtml]:
![]() | ![]() |
2.4.7. La pagina JSF [page1.xhtml]
La pagina [page1.xhtml] invia il seguente flusso al browser client:
![]() |
Il codice che genera questa pagina è il seguente:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<head>
<title><h:outputText value="#{msg['page1.titre']}"/></title>
</head>
<body>
<h1><h:outputText value="#{msg['page1.entete']}"/></h1>
<h:form>
<h:commandLink value="#{msg['page1.welcome']}" action="index"/>
</h:form>
</body>
</f:view>
</html>
In questa pagina non c’è nulla che non sia già stato spiegato. Il lettore potrà individuare la corrispondenza tra il codice JSF e la pagina inviata al browser client. Il link di ritorno alla pagina iniziale:
<h:commandLink value="#{msg['page1.welcome']}" action="index"/>
farà visualizzare la pagina [index.xhtml].
2.4.8. Esecuzione del progetto
Il nostro progetto è ora completo. Possiamo compilarlo (Clean and Build):
![]() |
- la compilazione del progetto crea, nella scheda [Files], la cartella [target]. Al suo interno si trova l’archivio [mv-jsf2-02-1.0-SNAPSHOT.war] del progetto. È proprio questo archivio che viene distribuito sul server,
- in [WEB-INF / classes] e [2] si trovano le classi compilate della cartella [Source Packages] del progetto, nonché i file che si trovavano nel ramo [Other Sources], in questo caso i file dei messaggi,
- in [WEB-INF / lib] [3] si trovano le librerie del progetto,
- alla radice di [WEB-INF] e [4] si trovano i file di configurazione del progetto,
![]() |
- nella directory principale dell'archivio [5] si trovano le pagine JSF che erano presenti nel ramo [Web Pages] del progetto;
- una volta compilato, il progetto può essere eseguito ([6]). Verrà eseguito in base alla sua configurazione di esecuzione ([7]),
- il server Tomcat verrà avviato se non era già in esecuzione ([8]),
- l'archivio [mv-jsf2-02-1.0-SNAPSHOT.war] verrà caricato sul server. Questo processo è denominato «distribuzione del progetto sul server delle applicazioni»,
- in [9], viene richiesto di avviare un browser all’esecuzione. Questo richiederà il contesto dell’applicazione [10], c.a.d. URL e [http://localhost:8080/mv-jsf2-02]. In base alle regole del file [web.xml] (cfr. pagina 44), sarà il file [faces/index.xhtml] a essere fornito al browser client. Poiché il file URL ha la forma [/faces/*], verrà elaborato dal controller [Faces Servlet] (cfr. [web.xml] a pagina 44). Quest’ultimo elaborerà la pagina e invierà il flusso HTML seguente:
![]() |
- il controller [Faces Servlet] elaborerà quindi gli eventi che si verificheranno a partire da questa pagina.
2.4.9. Il file di configurazione [faces-config.xml]
Abbiamo utilizzato il seguente file [faces-config.xml]:
<?xml version='1.0' encoding='UTF-8'?>
<!-- =========== FULL CONFIGURATION FILE ================================== -->
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<application>
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
</application>
</faces-config>
Si tratta del file minimo necessario per un'applicazione JSF 2 internazionalizzata. In questo caso abbiamo sfruttato le nuove funzionalità di JSF 2 rispetto a JSF 1:
- dichiarare i bean e il loro ambito con le annotazioni @ManagedBean, @RequestScoped, @SessionScoped, @ApplicationScoped,
- navigare tra le pagine utilizzando come chiavi di navigazione i nomi delle pagine XHTML senza il suffisso xhtml.
Si potrebbe preferire non utilizzare queste possibilità e dichiarare questi elementi del progetto JSF in [faces-config.xml] così come in JSF 1. In questo caso, il file [faces-config.xml] potrebbe essere il seguente:
<?xml version='1.0' encoding='UTF-8'?>
<!-- =========== FULL CONFIGURATION FILE ================================== -->
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<!-- applicazione -->
<application>
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
</application>
<!-- bean gestiti -->
<managed-bean>
<managed-bean-name>changeLocale</managed-bean-name>
<managed-bean-class>utils.ChangeLocale</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<!-- navigazione -->
<navigation-rule>
<description/>
<from-view-id>/index.xhtml</from-view-id>
<navigation-case>
<from-outcome>p1</from-outcome>
<to-view-id>/page1.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
<navigation-rule>
<description/>
<from-view-id>/page1.xhtml</from-view-id>
<navigation-case>
<from-outcome>welcome</from-outcome>
<to-view-id>/index.xhtml</to-view-id>
</navigation-case>
</navigation-rule>
</faces-config>
- righe 20-24: dichiarazione del bean changeLocale:
- riga 21: nome del bean;
- riga 22: nome completo della classe associata al bean;
- riga 23: ambito del bean. I valori possibili sono request, session, application,
- righe 27-34: dichiarazione di una regola di navigazione:
- riga 28: è possibile descrivere la regola. In questo caso, non è stato fatto;
- riga 29: la pagina da cui si naviga (punto di partenza);
- righe 30-33: un caso di navigazione. Possono essercene diversi;
- riga 31: la chiave di navigazione;
- riga 32: la pagina verso cui si naviga.
Le regole di navigazione possono essere visualizzate in modo più intuitivo. Quando si modifica il file [faces-config.xml], è possibile utilizzare la scheda [PageFlow]:
![]() |
Supponiamo di utilizzare il file [faces-config.xml] precedente. Come cambierebbe la nostra applicazione?
- nella classe [ChangeLocale], le annotazioni @ManagedBean e @SessionScoped scomparirebbero poiché ora il bean è dichiarato in [faces-config],
- la navigazione da [index.xhtml] a [page1.xhtml] tramite un link diventerebbe:
<h:commandLink value="#{msg['welcome.page1']}" action="p1"/>
All'attributo action viene assegnata la chiave di navigazione p1 definita in [faces-config],
- la navigazione da [page1.xhtml] a [index.xhtml] tramite un link diventerebbe:
<h:commandLink value="#{msg['page1.welcome']}" action="welcome"/>
All'attributo "action" viene assegnata la chiave di navigazione "welcome" definita in [faces-config],
- i metodi setFrenchLocale e setEnglishLocale, che devono restituire una chiave di navigazione, non devono essere modificati poiché restituivano null per indicare che si rimaneva sulla stessa pagina.
2.4.10. Conclusione
Torniamo al progetto NetBeans che abbiamo scritto:
![]() |
Questo progetto presenta la seguente struttura:
![]() |
In ogni progetto JSF troveremo i seguenti elementi:
- pagine JSF [A] che vengono inviate [4] ai browser dei clienti tramite il controller [Faces Servlet] [3],
- i file di messaggi [C] che consentono di cambiare la lingua delle pagine JSF,
- classi Java [B] che gestiscono gli eventi che si verificano sul browser client [2a, 2b] e/o che fungono da modelli per le pagine JSF [3]. Nella maggior parte dei casi, i livelli [métier] e [DAO] vengono sviluppati e testati separatamente. Il livello [web] viene quindi testato con un livello [métier] fittizio. Se i livelli [métier] e [DAO] sono disponibili, si lavora solitamente con i relativi archivi .jar.
- file di configurazione [D] per collegare tra loro questi vari elementi. Il file [web.xml] è stato descritto a pagina 44 e verrà modificato raramente. Lo stesso vale per [faces-config], di cui useremo sempre la versione semplificata.
2.5. Esempio mv-jsf2-03: modulo di inserimento dati - componenti JSF
D'ora in poi non illustreremo più la creazione del progetto. Presenteremo progetti già pronti e ne spiegheremo il funzionamento. Il lettore può scaricare tutti gli esempi dal sito indicato in questo documento (cfr. paragrafo 1.2).
2.5.1. L'applicazione
L’applicazione ha un’unica vista:
![]() |
L’applicazione presenta i principali componenti JSF utilizzabili in un modulo di inserimento dati:
- la colonna [1] indica il nome del tag JSF / HTML utilizzato,
- la colonna [2] presenta un esempio di inserimento per ciascuno dei tag presenti,
- la colonna [3] mostra i valori del bean che funge da modello per la pagina,
- i dati inseriti in [2] vengono convalidati tramite il pulsante [4]. Questa convalida si limita ad aggiornare il bean modello della pagina. La stessa pagina viene quindi rinviata. Pertanto, dopo la convalida, la colonna [3] presenta i nuovi valori del bean modello, consentendo così all’utente di verificare l’impatto dei propri inserimenti sul modello della pagina.
2.5.2. Il progetto NetBeans
Il progetto NetBeans dell’applicazione è il seguente:
![]() |
- in [1], i file di configurazione del progetto JSF,
- in [2], l'unica pagina del progetto: index.xhtml,
- in [3], un foglio di stile [styles.css] per configurare l'aspetto della pagina [index.xhtml]
- in [4], le classi Java del progetto,
- in [5], il file dei messaggi dell'applicazione in due lingue: francese e inglese.
2.5.3. Il file [pom.xml]
Presentiamo solo le dipendenze:
<dependencies>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-api</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>com.sun.faces</groupId>
<artifactId>jsf-impl</artifactId>
<version>2.1.7</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-web-api</artifactId>
<version>6.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
Queste sono le dipendenze necessarie per un progetto JSF. Negli esempi che seguiranno, questo file verrà presentato solo quando subirà delle modifiche.
2.5.4. Il file [web.xml]
Il file [web.xml] è stato configurato in modo che la pagina [index.xhtml] sia la pagina iniziale del progetto:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<context-param>
<param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
<param-value>true</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>faces/index.xhtml</welcome-file>
</welcome-file-list>
</web-app>
- riga 30: la pagina [index.xhtml] è la pagina iniziale,
- righe 11-14: un parametro per il servlet [Faces Servlet]. Richiede che i commenti in un facelet del tipo:
<!-- lingue -->
vengano ignorati. Senza questo parametro, i commenti causano problemi di difficile comprensione,
- righe 3-6: un parametro per il servlet [Faces Servlet] che verrà spiegato più avanti.
2.5.5. Il file [faces-config.xml]
Il file [faces-config.xml] dell’applicazione è il seguente:
<?xml version='1.0' encoding='UTF-8'?>
<!-- =========== FULL CONFIGURATION FILE ================================== -->
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<application>
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
</application>
</faces-config>
- righe 11-16: configurano il file dei messaggi dell'applicazione.
2.5.6. Il file dei messaggi [messages.properties]
I file dei messaggi (cfr. [5] nella schermata del progetto) sono i seguenti:
[messages_fr.properties]
form.langue1=Fran\u00e7ais
form.langue2=Anglais
form.titre=Java Server Faces - les tags
form.headerCol1=Type
form.headerCol2=Champs de saisie
form.headerCol3=Valeurs du modèle de la page
form.loginPrompt=login :
form.passwdPrompt=mot de passe :
form.descPrompt=description :
form.selectOneListBox1Prompt=choix unique :
form.selectOneListBox2Prompt=choix unique :
form.selectManyListBoxPrompt=choix multiple :
form.selectOneMenuPrompt=choix unique :
form.selectManyMenuPrompt=choix multiple :
form.selectBooleanCheckboxPrompt=marié(e) :
form.selectManyCheckboxPrompt=couleurs préférées :
form.selectOneRadioPrompt=moyen de transport préféré :
form.submitText=Valider
form.buttonRazText=Raz
Questi messaggi vengono visualizzati nei seguenti punti della pagina:
![]() |
La versione inglese dei messaggi è la seguente:
[messages_en.properties]
form.langue1=French
form.langue2=English
form.titre=Java Server Faces - the tags
form.headerCol1=Input Type
form.headerCol2=Input Fields
form.headerCol3=Page Model Values
form.loginPrompt=login :
form.passwdPrompt=password :
form.descPrompt=description :
form.selectOneListBox1Prompt=unique choice :
form.selectOneListBox2Prompt=unique choice :
form.selectManyListBoxPrompt=multiple choice :
form.selectOneMenuPrompt=unique choice :
form.selectManyMenuPrompt=multiple choice :
form.selectBooleanCheckboxPrompt=married :
form.selectManyCheckboxPrompt=preferred colors :
form.selectOneRadioPrompt=preferred transport means :
form.submitText=Submit
form.buttonRazText=Reset
2.5.7. Il modello [Form.java] della pagina [index.xhtml]
![]() |
Nel progetto sopra riportato, la classe [Form.java] fungerà da modello o backing bean per la pagina JSF [index.xhtml]. Illustriamo questo concetto di modello con un esempio tratto dalla pagina [index.xhtml]:
<!-- riga 1 -->
<h:outputText value="inputText" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.loginPrompt']}"/>
<h:inputText id="inputText" value="#{form.inputText}"/>
</h:panelGroup>
<h:outputText value="#{form.inputText}"/>
Alla richiesta iniziale della pagina [index.xhtml], il codice sopra riportato genera la riga 2 della tabella di immissione dati:
![]() |
La riga 2 visualizza il campo [1], le righe 3-6: il campo [2], la riga 7: il campo [3].
Le righe 5 e 7 utilizzano un'espressione EL che coinvolge il bean form definito nella classe [Form.java] nel modo seguente:
package forms;
import javax.enterprise.context.RequestScoped;
import javax.faces.bean.ManagedBean;
@ManagedBean
@RequestScoped
public class Form {
- la riga 7 definisce un bean senza nome. Il nome della classe, che inizia con una minuscola, sarà quindi: form,
- il bean ha ambito request. Ciò significa che in un ciclo richiesta client / risposta server, viene istanziato quando la richiesta lo richiede e rimosso una volta restituita la risposta al client.
Nel codice riportato di seguito della pagina [index.xhtml]:
<!-- riga 1 -->
<h:outputText value="inputText" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.loginPrompt']}"/>
<h:inputText id="inputText" value="#{form.inputText}"/>
</h:panelGroup>
<h:outputText value="#{form.inputText}"/>
le righe 5 e 7 utilizzano il valore inputText del bean form. Per comprendere i legami che uniscono una pagina P al suo modello M, è necessario tornare al ciclo richiesta client / risposta server che caratterizza un’applicazione web:
![]() |
Occorre distinguere il caso in cui la pagina P viene inviata in risposta al browser (fase 4), ad esempio durante la richiesta iniziale della pagina, dal caso in cui l’utente, avendo generato un evento sulla pagina P, questo venga elaborato dal controller [Faces Servlet] (fase 1).
È possibile distinguere questi due casi osservandoli dal punto di vista del browser:
- durante la richiesta iniziale della pagina, il browser esegue un’operazione GET sull’URL della pagina,
- al momento dell’invio dei valori inseriti nella pagina, il browser esegue un’operazione POST sull’URL della pagina.
In entrambi i casi, viene richiesta la stessa URL. A seconda della natura della richiesta GET o POST del browser, l’elaborazione della richiesta sarà diversa.
[cas 1 – demande initiale de la page P]
Il browser richiede l’URL della pagina con un GET. Il controller [Faces Servlet] passerà direttamente alla fase [4] di rendering della risposta e la pagina [index.xhtml] verrà inviata al client. Il controller JSF richiederà a ciascun tag della pagina di essere visualizzato. Prendiamo ad esempio la riga 5 del codice di [index.xhtml]:
<h:inputText id="inputText" value="#{form.inputText}"/>
Il tag JSF <h:inputText value="valore"/> genera il tag HTML <input type="text" value="valore"/>. La classe incaricata di elaborare questo tag incontra l'espressione #{form.inputText} che deve valutare:
- se il bean form non esiste ancora, viene creato tramite istanziazione della classe forms.Form,
- l’espressione #{form.inputText} viene valutata richiamando il metodo form.getInputText(),
- il testo <input id="form:inputText" type="text" name="formulaire:inputText" value="testo" /> viene inserito nel flusso HTML che verrà inviato al cliente, supponendo che il metodo form.getInputText() abbia restituito la stringa "testo". JSF assegnerà inoltre un nome (name) al componente HTML inserito nel flusso. Questo nome è costruito a partire dagli identificatori id del componente JSF analizzato e da quelli dei suoi componenti padre, in questo caso il tag <h:form id="formulaire"/>.
Si noti che se in una pagina P si utilizza l’espressione #{M.champ}, dove M è il bean modello della pagina P, quest’ultimo deve disporre del metodo pubblico getChamp(). Il tipo restituito da questo metodo deve poter essere convertito in tipo String. Un modello M possibile e frequente è il seguente:
dove T è un tipo che può essere convertito nel tipo String, eventualmente tramite un metodo toString.
Sempre nel caso della visualizzazione della pagina P, l'elaborazione della riga:
<h:outputText value="#{form.inputText}"/>
sarà analogo e verrà creato il seguente flusso HTML:
Internamente al server, la pagina P è rappresentata come un albero di componenti, che rispecchia l’albero dei tag della pagina inviata al cliente. Chiameremo «vista» o « o stato della pagina» questo albero. Tale stato viene memorizzato. Ciò può avvenire in due modi, a seconda della configurazione definita nel file [web.xml] dell’applicazione:
<web-app ...>
...
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
...
</web-app>
Le righe 7-11 definiscono il controller [Faces Servlet]. Questo può essere configurato tramite diversi tag <context-param>, tra cui quello delle righe 3-6 che indica che lo stato di una pagina deve essere salvato sul client (il browser). L'altro valore possibile, alla riga 5, è «server» per indicare il salvataggio sul server. Questo è il valore predefinito.
Quando lo stato di una pagina viene salvato sul client, il controller JSF aggiunge a ogni pagina HTML che invia un campo nascosto il cui valore è lo stato attuale della pagina. Questo campo nascosto ha la seguente forma:
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="H4sIAAAAAAAAANV...Bnoz8dqAAA=" />
Il suo valore rappresenta, in forma codificata, lo stato della pagina inviata al client. È importante comprendere che questo campo nascosto fa parte del modulo della pagina e sarà quindi incluso tra i valori inviati dal browser al momento della convalida del modulo. A partire da questo campo nascosto, il controller JSF è in grado di ripristinare la vista così come è stata inviata al client.
Quando lo stato di una pagina viene salvato sul server, lo stato della pagina inviata al cliente viene salvato nella sessione di quest’ultimo. Quando il browser del cliente invierà i valori inseriti nel modulo, invierà anche il proprio token di sessione. Sulla base di questo, il controller JSF recupererà lo stato della pagina inviata al client e la ripristinerà.
Lo stato di una pagina JSF può richiedere diverse centinaia di byte per essere codificato. Poiché tale stato viene mantenuto per ogni utente dell’applicazione, si possono verificare problemi di memoria in caso di un numero elevato di utenti. Per questo motivo, abbiamo scelto in questo caso di salvare lo stato della pagina sul client (cfr. [web.xml], paragrafo 2.5.4, pagina 66).
[cas 2 – traitement de la page P]
![]() |
Ci troviamo nella fase [1] sopra descritta, in cui il controller [Faces Servlet] riceverà una richiesta POST dal browser del client a cui aveva precedentemente inviato la pagina [index.xhtml]. Si tratta dell’elaborazione di un evento della pagina. Prima ancora che l’evento possa essere elaborato in [2a], si svolgeranno diverse fasi. Il ciclo di elaborazione di una richiesta POST da parte del controller JSF è il seguente:
FEDCBA

- in [A], grazie al campo nascosto javax.faces.ViewState, viene ricostruita la vista inizialmente inviata al browser del cliente. In questa fase, i componenti della pagina riacquistano il valore che avevano nella pagina inviata. Il nostro componente inputText riprende il suo valore "testo",
- in [B], i valori inviati dal browser del cliente vengono utilizzati per aggiornare i componenti della vista. Pertanto, se nel campo di immissione HTML denominato inputText l’utente ha digitato “jean”, il valore “jean” sostituisce il valore “testo”. A questo punto la vista riflette la pagina così come è stata modificata dall’utente e non più così come è stata inviata al browser,
- in [C], i valori inviati vengono verificati. Supponiamo che il componente precedente inputText sia il campo di immissione dell’età. Il valore immesso dovrà essere un numero intero. I valori inviati dal browser sono sempre di tipo String. Il loro tipo finale nel modello M associato alla pagina P può essere completamente diverso. Si verifica quindi una conversione da un tipo String a un altro tipo T. Questa conversione può fallire. In questo caso, il ciclo richiesta/risposta viene interrotto e la pagina P costruita in [B] viene rinviata al browser client con messaggi di errore, se l’autore della pagina P li ha previsti. Si noti che l’utente visualizza la pagina così come l’ha compilata, senza alcuno sforzo da parte dello sviluppatore. In un’altra tecnologia, come JSP, lo sviluppatore deve ricostruire autonomamente la pagina P con i valori inseriti dall’utente. Il valore di un componente può inoltre essere sottoposto a un processo di convalida. Sempre prendendo come esempio il componente inputText, che è il campo di immissione dell’età, il valore inserito dovrà essere non solo un numero intero, ma un numero intero compreso in un intervallo [1,N]. Se il valore inserito supera la fase di conversione, potrebbe non superare quella di convalida. Anche in questo caso, il ciclo richiesta/risposta è terminato e la pagina P generata in [B] viene restituita al browser del cliente,
- In [D], se tutti i componenti della pagina P superano la fase di conversione e convalida, i loro valori verranno assegnati al modello M della pagina P. Se il valore del campo di immissione generato dal seguente tag:
<h:inputText value="#{form.inputText}"/>
è "jean", allora tale valore verrà assegnato al modello form della pagina tramite l'esecuzione del codice form.setInputText("jean"). Si noti che nel modello M della pagina P, i campi privati di M che memorizzano il valore di un campo di immissione di P devono disporre di un metodo set;
- una volta che il modello M della pagina P è stato aggiornato con i valori inviati, l’evento che ha generato il POST della pagina P può essere elaborato. Si tratta della fase [E]. Si noti che se il gestore di questo evento appartiene al bean M, esso ha accesso ai valori del modulo P che sono stati memorizzati nei campi dello stesso bean.
- La fase [E] restituirà al controller JSF una chiave di navigazione. Nei nostri esempi, si tratterà sempre del nome della pagina XHTML da visualizzare, senza il suffisso .xhtml. Si tratta della fase [F]. Un altro modo di procedere consiste nel restituire una chiave di navigazione che verrà ricercata nel file [faces-config.xml]. Abbiamo descritto questo caso.
Da quanto sopra si ricava che:
- una pagina P visualizza i campi C del proprio modello M tramite i metodi [M].getC(),
- i campi C del modello M di una pagina P vengono inizializzati con i valori inseriti nella pagina P tramite i metodi [M].setC(inserimento). In questa fase possono intervenire processi di conversione e convalida che potrebbero fallire. In tal caso, l’evento che ha generato il POST della pagina P non viene elaborato e la pagina viene rinviata al cliente così come è stata inserita.
Il modello [Form.java] della pagina [index.xhtml] sarà il seguente:
package forms;
import javax.enterprise.context.RequestScoped;
import javax.faces.bean.ManagedBean;
@ManagedBean
@RequestScoped
public class Form {
/** Crea una nuova istanza del modulo */
public Form() {
}
// campi del modulo
private String inputText="texte";
private String inputSecret="secret";
private String inputTextArea="ligne1\nligne2\n";
private String selectOneListBox1="2";
private String selectOneListBox2="3";
private String[] selectManyListBox=new String[]{"1","3"};
private String selectOneMenu="1";
private String[] selectManyMenu=new String[]{"1","2"};
private String inputHidden="initial";
private boolean selectBooleanCheckbox=true;
private String[] selectManyCheckbox=new String[]{"1","3"};
private String selectOneRadio="2";
// eventi
public String submit(){
return null;
}
// getter e setter
...
}
I campi delle righe 16-27 vengono utilizzati nei seguenti punti del modulo:
![]() |
2.5.8. La pagina [index.xhtml]
La pagina [index.xhtml] che genera la vista precedente è la seguente:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h:form id="formulaire">
<!-- lingue -->
<h:panelGrid columns="2">
<h:commandLink value="#{msg['form.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
<h:commandLink value="#{msg['form.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
</h:panelGrid>
<h1><h:outputText value="#{msg['form.titre']}"/></h1>
<h:panelGrid columnClasses="col1,col2,col3" columns="3" border="1">
<!-- intestazioni -->
<h:outputText value="#{msg['form.headerCol1']}" styleClass="entete"/>
<h:outputText value="#{msg['form.headerCol2']}" styleClass="entete"/>
<h:outputText value="#{msg['form.headerCol3']}" styleClass="entete"/>
<!-- riga 1 -->
...
<!-- riga 2 -->
...
<!-- riga 3 -->
...
<!-- riga 4 -->
...
<!-- riga 5 -->
...
<!-- riga 6 -->
...
<!-- riga 7 -->
...
<!-- riga 8 -->
...
<!-- riga 9 -->
...
<!-- riga 10 -->
...
<!-- riga 11 -->
...
<!-- riga 12 -->
...
</h:panelGrid>
<p>
<h:commandButton type="submit" id="submit" value="#{msg['form.submitText']}"/>
</p>
</h:form>
</h:body>
</f:view>
</html>
Esamineremo ora uno dopo l’altro i principali componenti di questa pagina. Si noti la struttura generale di un modulo JSF:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view ...>
<h:head>
...
</h:head>
<h:body ...>
<h:form id="formulaire">
...
<h:commandButton type="submit" id="submit" value="#{msg['form.submitText']}"/>
...
</h:form>
</h:body>
</f:view>
</html>
I componenti di un modulo devono trovarsi all’interno di un tag <h:form> (righe 12-16). Il tag <f:view> (righe 7-18) è necessario se si internazionalizza l’applicazione. Inoltre, un modulo deve disporre di un mezzo per essere inviato (POST), spesso un link o un pulsante come nella riga 14. Può essere inviato anche tramite numerosi eventi (modifica di una selezione in un elenco, cambio del campo attivo, digitazione di un carattere in un campo di immissione, ...).
2.5.9. Lo stile del modulo
Per rendere più leggibili le colonne della tabella del modulo, quest’ultimo è accompagnato da un foglio di stile:
<f:view locale="#{changeLocale.locale}">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
- riga 4: il foglio di stile della pagina è definito all’interno del tag HTML <head>, tramite il tag:
<h:outputStylesheet library="css" name="styles.css"/>
Il foglio di stile verrà cercato nella cartella [resources]:
![]() |
Nel tag:
<h:outputStylesheet library="css" name="styles.css"/>
- library è il nome della cartella contenente il foglio di stile,
- "name" è il nome del foglio di stile.
Vediamo un esempio di utilizzo di questo foglio di stile:
<h:panelGrid columnClasses="col1,col2,col3" columns="3" border="1">
Il tag <h:panelGrid columns="3"/> definisce una tabella a tre colonne. L'attributo columnClasses consente di applicare uno stile a queste colonne. I valori col1, col2, col3 dell’attributo columnClasses indicano i rispettivi stili delle colonne 1, 2 e 3 della tabella. Questi stili vengono ricercati nel foglio di stile della pagina:
.info{
font-family: Arial,Helvetica,sans-serif;
font-size: 14px;
font-weight: bold
}
.col1{
background-color: #ccccff
}
.col2{
background-color: #ffcccc
}
.col3{
background-color: #ffcc66
}
.entete{
font-family: 'Times New Roman',Times,serif;
font-size: 14px;
font-weight: bold
}
- righe 7-9: lo stile denominato col1,
- righe 11-13: lo stile denominato col2,
- righe 15-17: lo stile denominato col3,
Questi tre stili definiscono il colore di sfondo di ciascuna colonna.
- righe 19-23: lo stile «entete» serve a definire lo stile dei testi della prima riga della tabella:
<!-- intestazioni -->
<h:outputText value="#{msg['form.headerCol1']}" styleClass="entete"/>
<h:outputText value="#{msg['form.headerCol2']}" styleClass="entete"/>
<h:outputText value="#{msg['form.headerCol3']}" styleClass="entete"/>
- righe 1-5: lo stile «info» serve a definire lo stile dei testi della prima colonna della tabella:
<!-- riga 1 -->
<h:outputText value="inputText" styleClass="info"/>
Non ci soffermeremo molto sull’uso dei fogli di stile, poiché questi meriterebbero un libro a sé stante e, inoltre, la loro elaborazione è spesso affidata a specialisti. Tuttavia, abbiamo voluto utilizzarne uno, minimalista, per ricordare che il loro impiego è indispensabile.
Vediamo ora come è stata definita l’immagine di sfondo della pagina:
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
L’immagine di sfondo è specificata dall’attributo style del tag <h:body>. Questo attributo consente di definire elementi di stile. L’immagine di sfondo si trova nella cartella [resources/images/standard.jpg]:
![]() |
Questa immagine viene ottenuta tramite URL [/mv-jsf2-03/resources/images/standard.jpg]. Si potrebbe quindi scrivere:
<h:body style="background-image: url('mv-jsf2-03/resources/images/standard.jpg');">
/mv-jsf2-03 è il contesto dell'applicazione. Questo contesto è impostato dall'amministratore del server web e può quindi cambiare. È possibile ottenere questo contesto tramite l'espressione EL ${request.contextPath}. Si preferirà quindi il seguente attributo style:
style="background-image: url('${request.contextPath}/resources/images/standard.jpg');"
che sarà valido indipendentemente dal contesto.
2.5.10. I due cicli richiesta client / risposta server di un modulo
Torniamo a quanto già spiegato nel paragrafo 2.5.7 in un caso generale e applichiamolo al modulo in esame. Questo verrà testato nell’ambiente classico JSF:
![]() |
Qui non ci saranno gestori di eventi né il livello [métier]. Le fasi [2x] non esisteranno quindi. Si distinguerà il caso in cui il modulo F sia richiesto inizialmente dal browser dal caso in cui l’utente, avendo generato un evento nel modulo F, questo venga gestito dal controller [Faces Servlet]. Esistono due cicli richiesta client / risposta server che sono diversi.
- il primo, corrispondente alla richiesta iniziale della pagina, è innescato da un’operazione GET del browser sul controllo URL del modulo,
- il secondo, corrispondente all’invio dei valori inseriti nella pagina, è innescato da un’operazione POST su questo stesso URL.
A seconda della natura della richiesta GET o POST del browser, l’elaborazione della richiesta da parte del controller [Faces Servlet] varia.
[cas 1 – demande initiale du formulaire F]
Il browser richiede l’URL della pagina con un GET. Il controller [Faces Servlet] passerà direttamente alla fase [4] di rendering della risposta. Il modulo [index.xhtml] verrà inizializzato dal suo modello [Form.java] e inviato al client, che riceverà la seguente vista:

In questa occasione, gli scambi HTTP tra client e server sono i seguenti:
Richiesta HTTP da parte del cliente:
Riga 1: si vede il codice GET del browser.
Risposta HTTP del server:
Non mostrato qui, la riga 7 è seguita da una riga vuota e dal codice HTML del modulo. È questo codice che il browser interpreta e visualizza.
[cas 2 – traitement des valeurs saisies dans le formulaire F]
L'utente compila il modulo e lo invia tramite il pulsante [Valider]. Il browser richiede quindi l'URL del modulo con un POST. Il controller [Faces Servlet] elabora questa richiesta, aggiorna il modello [Form.java] del modulo [index.xhtml] e rinvia nuovamente il modulo [index.xhtml] aggiornato con questo nuovo modello. Esaminiamo questo ciclo con un esempio:

Nell’esempio sopra riportato, l’utente ha inserito i dati e li ha convalidati. Come risposta riceve la seguente schermata:

In questa occasione, gli scambi client/server HTTP sono i seguenti:
Richiesta HTTP da parte del client:
Alla riga 1, il POST generato dal browser. Alla riga 14, i valori inseriti dall'utente. Ad esempio, è possibile individuare il testo inserito nel campo di immissione:
Nella riga 14 è stato inviato il campo nascosto javax.faces.ViewState. Questo campo rappresenta, in forma codificata, lo stato del modulo così come è stato inizialmente inviato al browser durante il suo GET iniziale.
Risposta HTTP del server:
Non mostrata qui, la riga 6 è seguita da una riga vuota e dal codice HTML del modulo aggiornato in base al suo nuovo modello derivato da POST.
Esaminiamo ora le diverse componenti di questo modulo.
2.5.11. Tag <h:inputText>
Il tag <h:inputText> genera un tag HTML <input type="text" ...>.
Consideriamo il seguente codice:
<!-- riga 1 -->
<h:outputText value="inputText" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.loginPrompt']}"/>
<h:inputText id="inputText" value="#{form.inputText}"/>
</h:panelGroup>
<h:outputText value="#{form.inputText}"/>
e il suo modello [Form.java]:
private String inputText="texte";
public String getInputText() {
return inputText;
}
public void setInputText(String inputText) {
this.inputText = inputText;
}
Quando la pagina [index.html] viene richiesta per la prima volta, la pagina ottenuta è la seguente:
- la riga 2 del codice XHTML genera [1],
- il tag <h:panelGroup> (righe 3-6) consente di raggruppare più elementi in una stessa cella della tabella generata dal tag <h:panelGrid> della riga 20 del codice completo della pagina (cfr. paragrafo 2.5.8). Il testo [2] è generato dalla riga 4. Il campo di immissione [3] è generato dalla riga [5]. In questo caso, è stato utilizzato il metodo getInputText di [Form.java] (righe 3-5 del codice Java) per generare il testo del campo di immissione,
- la riga 7 del codice XHTML genera [4]. È nuovamente il metodo getInputText di [Form.java] che viene utilizzato per generare il testo [4].
Il flusso HTML generato dalla pagina XHTML è il seguente:
<tr>
<td class="col1"><span class="info">inputText</span></td>
<td class="col2">login : <input id="formulaire:inputText" type="text" name="formulaire:inputText" value="texte" /></td>
<td class="col3">texte</td>
</tr>
I tag HTML <tr> e <td> sono generati dal tag <h:panelGrid> utilizzato per generare la tabella del modulo.
Ora, qui sotto, inseriamo un valore nel campo di immissione [1] e inviamo il modulo tramite il pulsante [Valider] [2]. Come risposta otteniamo la pagina [3, 4]:
![]() |
Il valore del campo [1] viene inviato come segue:
In [2], il modulo viene convalidato con il seguente pulsante:
<h:commandButton id="submit" type="submit" value="#{msg['form.submitText']}"/>
Il tag <h:commandButton> non ha l'attributo action. In questo caso, non viene richiamato alcun gestore di eventi né viene applicata alcuna regola di navigazione. Dopo l'elaborazione, viene restituita la stessa pagina. Rivediamo il suo ciclo di elaborazione:
ABCDEF

- in [A] la pagina P viene ripristinata così come era stata inviata. Ciò significa che il componente con id inputText viene ripristinato con il suo valore iniziale "testo",
- in [B], i valori inviati dal browser (inseriti dall’utente) vengono assegnati ai componenti della pagina P. Qui, il componente con id inputText riceve il valore “un nuovo testo”,
- in [C], avvengono le conversioni e le convalide. In questo caso, non ce ne sono. Nel modello M, il campo associato al componente con id inputText è il seguente:
private String inputText="texte";
Poiché i valori inseriti sono di tipo String, non è necessaria alcuna conversione. Inoltre, non è stata creata alcuna regola di convalida. Ne creeremo alcune in seguito.
- In [D], i valori inseriti vengono assegnati al modello. Il campo inputText di [Form.java] riceve il valore "un nuovo testo",
- in [E] non viene eseguita alcuna operazione poiché nessun gestore di eventi è stato associato al pulsante [Valider].
- In [F], la pagina P viene nuovamente inviata al client poiché il pulsante [Valider] non ha l’attributo action. Vengono quindi eseguite le righe successive di [index.xhtml]:
<!-- riga 1 -->
<h:outputText value="inputText" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.loginPrompt']}"/>
<h:inputText id="inputText" value="#{form.inputText}"/>
</h:panelGroup>
<h:outputText value="#{form.inputText}"/>
Le righe 5 e 7 utilizzano il valore del campo inputText del modello, che ora è "un nuovo testo". Da qui il risultato visualizzato:
![]()
2.5.12. Tag <h:inputSecret>
Il tag <h:inputSecret> genera un tag HTML <input type="password" ...>. Si tratta di un campo di immissione simile a quello del tag JSF <h:inputText>, con la differenza che ogni carattere digitato dall'utente viene sostituito visivamente da un asterisco (*).
Consideriamo il seguente codice:
<!-- riga 2 -->
<h:outputText value="inputSecret" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.passwdPrompt']}"/>
<h:inputSecret id="inputSecret" value="#{form.inputSecret}"/>
</h:panelGroup>
<h:outputText value="#{form.inputSecret}"/>
e il suo modello in [Form.java]:
private String inputSecret="secret";
Quando la pagina [index.xhtml] viene richiesta per la prima volta, la pagina ottenuta è la seguente:
- la riga 2 del codice XHTML genera [1]
- il testo [2] viene generato dalla riga 4. Il campo di immissione [3] viene generato dalla riga [5]. Normalmente, per generare il testo del campo di immissione si sarebbe dovuto utilizzare il metodo getInputSecret di [Form.java]. Si verifica un'eccezione quando il campo è di tipo "password". Il tag <h:inputSecret> serve solo a leggere un valore inserito, non a visualizzarlo.
- La riga 7 del codice XHTML genera [4]. In questo caso è stato utilizzato il metodo getInputSecret di [Form.java] per generare il testo [4] (cfr. riga 1 del codice Java).
Il flusso HTML generato dalla pagina XHTML è il seguente:
<tr>
<td class="col1"><span class="info">inputSecret</span></td>
<td class="col2">mot de passe : <input id="formulaire:inputSecret" type="password" name="formulaire:inputSecret" value="" /></td>
<td class="col3">secret</td>
</tr>
- riga 3: il tag HTML <input type="password" .../> generato dal tag JSF <h:inputSecret>
Ora, qui sotto, inseriamo un valore nel campo di immissione [1] e inviamo il modulo con il pulsante [Valider] [2]. Otteniamo in risposta la pagina [3]:
![]() |
Il valore del campo [1] viene inviato come segue:
La convalida del modulo tramite [2] ha provocato l’aggiornamento del modello [Form.java] tramite l’inserimento di [1]. Il campo inputSecret di [Form.java] ha quindi ricevuto il valore «mdp». Poiché il modulo [index.xhtml] non ha definito alcuna regola di navigazione né alcun gestore di eventi, viene visualizzato nuovamente dopo l’aggiornamento del suo modello. Si torna quindi alla visualizzazione risultante dalla richiesta iniziale della pagina [index.xhtml], in cui semplicemente il campo inputSecret del modello ha cambiato valore, passando a [3].
2.5.13. Tag <h:inputTextArea>
Il tag <h:inputTextArea> genera un tag HTML <textarea ...>testo</textarea>. Si tratta di un campo di immissione simile a quello del tag JSF <h:inputText>, con la differenza che in questo caso è possibile digitare più righe di testo.
Consideriamo il seguente codice:
<!-- riga 3 -->
<h:outputText value="inputTextArea" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.descPrompt']}"/>
<h:inputTextarea id="inputTextArea" value="#{form.inputTextArea}" rows="4"/>
</h:panelGroup>
<h:outputText value="#{form.inputTextArea}"/>
e il relativo modello in [Form.java]:
private String inputTextArea="ligne1\nligne2\n";
Quando la pagina [index.xhtml] viene richiesta per la prima volta, la pagina ottenuta è la seguente:
![]() |
- la riga 2 del codice XHTML genera [1],
- il testo [2] viene generato dalla riga 4. Il campo di immissione [3] viene generato dalla riga [5]. Il suo contenuto è stato generato richiamando il metodo getInputTextArea del modello, che ha restituito il valore definito nella riga 1 del codice Java sopra riportato,
- la riga 7 del codice XHTML genera [4]. Qui è stato nuovamente utilizzato il metodo getInputTextArea di [Form.java]. La stringa "riga1\nriga2" conteneva i caratteri di fine riga \n. Sono ancora presenti. Tuttavia, inseriti in un flusso HTML, vengono visualizzati come spazi dai browser. Il tag HTML <textarea>, che visualizza [3], interpreta correttamente i caratteri di fine riga.
Il flusso HTML generato dalla pagina XHTML è il seguente:
<tr>
<td class="col1"><span class="info">inputTextArea</span></td>
<td class="col2">description : <textarea id="formulaire:inputTextArea" name="formulaire:inputTextArea" rows="4">ligne1
ligne2
</textarea></td>
<td class="col3">ligne1
ligne2
</td>
</tr>
- righe 3-5: il tag HTML <textarea>...</textarea> generata dal tag JSF <h:inputTextArea>
Ora, qui sotto, inseriamo un valore nel campo di immissione [1] e inviamo il modulo tramite il pulsante [Valider] [2]. Come risposta otteniamo la pagina [3]:
![]() |
Il valore del campo [1] inviato è il seguente:
La convalida del modulo tramite [2] ha provocato l’aggiornamento del modello [Form.java] tramite l’inserimento di [1]. Il campo textArea di [Form.java] ha quindi ricevuto il valore "Tutorial JSF\nparte1". La visualizzazione di [index.xhtml] mostra che il campo textArea del modello è stato effettivamente aggiornato a [3].
2.5.14. Tag <h:selectOneListBox>
Il tag <h:selectOneListBox> genera un tag HTML <select>...</select>. Visivamente, genera un elenco a discesa o un elenco con barra di scorrimento.
Consideriamo il seguente codice:
<!-- riga 4 -->
<h:outputText value="selectOneListBox (size=1)" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectOneListBox1Prompt']}"/>
<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
<f:selectItem itemValue="1" itemLabel="un"/>
<f:selectItem itemValue="2" itemLabel="deux"/>
<f:selectItem itemValue="3" itemLabel="trois"/>
</h:selectOneListbox>
</h:panelGroup>
<h:outputText value="#{form.selectOneListBox1}"/>
e il relativo modello in [Form.java]:
private String selectOneListBox1="2";
Quando la pagina [index.xhtml] viene richiesta per la prima volta, la pagina ottenuta è la seguente:
![]() |
- la riga 2 del codice XHTML genera [1]
- il testo [2] viene generato dalla riga 4. L'elenco a discesa [3] viene generato dalle righe [5-9]. È il valore dell'attributo size="1" a far sì che l'elenco visualizzi un solo elemento. Se questo attributo manca, il valore predefinito dell’attributo size è 1. Gli elementi dell’elenco sono stati generati dai tag <f:selectItem> delle righe 6-8. Questi tag hanno la seguente sintassi:
<f:selectItem itemValue="valeur" itemLabel="texte"/>
Il valore dell’attributo itemLabel è ciò che viene visualizzato nell’elenco. Il valore dell’attributo itemValue è il valore dell’elemento. È questo valore che verrà inviato al controller [Faces Servlet] se l’elemento viene selezionato nell’elenco a discesa.
L'elemento visualizzato in [3] è stato determinato tramite la chiamata al metodo getSelectOneListBox1() (riga 5). Il risultato «2» ottenuto (riga 1 del codice Java) ha fatto sì che venisse visualizzato l’elemento della riga 7 dell’elenco a discesa, poiché il suo attributo itemValue è pari a «2»,
- la riga 11 del codice XHTML genera [4]. In questo caso è stato nuovamente utilizzato il metodo getSelectOneListBox1 di [Form.java].
Il flusso HTML generato dalla pagina XHTML è il seguente:
<tr>
<td class="col1"><span class="info">selectOneListBox (size=1)</span></td>
<td class="col2">choix unique : <select id="formulaire:selectOneListBox1" name="formulaire:selectOneListBox1" size="1">
<option value="1">un</option>
<option value="2" selected="selected">deux</option>
<option value="3">trois</option>
</select></td>
<td class="col3">2</td>
</tr>
- righe 3 e 7: il tag HTML <select ...>...</select> generato dal tag JSF <h:selectOneListBox>,
- righe 4-6: i tag HTML <option ...> ... </option> generati dai tag JSF <f:selectItem>,
- riga 5: il fatto che l'elemento con valore="2" sia selezionato nell'elenco si traduce nella presenza dell'attributo selected="selected".
Ora, qui sotto, scegliamo [1] un nuovo valore dall'elenco e inviamo il modulo tramite il pulsante [Valider] [2]. Otteniamo in risposta la pagina [3]:
![]() |
Il valore del campo [1] inviato è il seguente:
La convalida del modulo tramite [2] ha provocato l'aggiornamento del modello [Form.java] tramite l'inserimento di [1]. L'elemento HTML
<option value="3">trois</option>
è stato selezionato. Il browser ha inviato la stringa "3" come valore del componente JSF che ha generato l'elenco a discesa:
<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
Il controller JSF utilizzerà il metodo setSelectOneListBox1("3") per aggiornare il modello dell'elenco a discesa. Inoltre, dopo questo aggiornamento, il campo del modello [Form.java]
private String selectOneListBox1;
conterrà ora il valore "3".
Quando la pagina [index.xhtml] viene visualizzata nuovamente al termine dell’elaborazione, questo valore determina la visualizzazione di [3,4] sopra riportata:
- determina l’elemento dell’elenco a discesa che deve essere visualizzato ([3]),
- il valore del campo selectOneListBox1 viene visualizzato in [4].
Consideriamo una variante del tag <h:selectOneListBox>:
<!-- riga 5 -->
<h:outputText value="selectOneListBox (size=3)" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectOneListBox2Prompt']}"/>
<h:selectOneListbox id="selectOneListBox2" value="#{form.selectOneListBox2}" size="3">
<f:selectItem itemValue="1" itemLabel="un"/>
<f:selectItem itemValue="2" itemLabel="deux"/>
<f:selectItem itemValue="3" itemLabel="trois"/>
<f:selectItem itemValue="4" itemLabel="quatre"/>
<f:selectItem itemValue="5" itemLabel="cinq"/>
</h:selectOneListbox>
</h:panelGroup>
<h:outputText value="#{form.selectOneListBox2}"/>
Il modello in [Form.java] del tag <h:selectOneListBox> della riga 5 è il seguente:
private String selectOneListBox2="3";
Quando la pagina [index.xhtml] viene richiesta per la prima volta, la pagina ottenuta è la seguente:
![]() |
- la riga 2 del codice XHTML genera [1],
- il testo [2] viene generato dalla riga 4. L'elenco con barra di scorrimento [3] viene generato dalle righe [5-11]. È il valore dell’attributo size="3" che determina la presenza di un elenco con barra di scorrimento anziché di un menu a tendina. Gli elementi dell’elenco sono stati generati dai tag <f:selectItem> delle righe 6-8,
L'elemento selezionato in [3] è stato determinato tramite la chiamata al metodo getSelectOneListBox2() (riga 5). Il risultato «3» ottenuto (riga 1 del codice Java) ha fatto sì che venisse visualizzato l'elemento della riga 8 dell'elenco, poiché il suo attributo itemValue è pari a «3»,
- la riga 13 del codice XHTML genera [4]. In questo caso è stato nuovamente utilizzato il metodo getSelectOneListBox2 di [Form.java].
Il flusso HTML generato dalla pagina XHTML è il seguente:
<tr>
<td class="col1"><span class="info">selectOneListBox (size=3)</span></td>
<td class="col2">choix unique : <select id="formulaire:selectOneListBox2" name="formulaire:selectOneListBox2" size="3">
<option value="1">un</option>
<option value="2">deux</option>
<option value="3" selected="selected">trois</option>
<option value="4">quatre</option>
<option value="5">cinq</option>
</select></td>
<td class="col3">3</td>
</tr>
- riga 6: il fatto che l’elemento con valore="3" sia selezionato nell’elenco si traduce nella presenza dell’attributo selected="selected".
Ora, qui sotto, scegliamo [1] un nuovo valore dall’elenco e inviamo il modulo tramite il pulsante [Valider] [2]. Otteniamo in risposta la pagina [3]:
![]() |
Il valore inviato per il campo [1] è il seguente:
La convalida del modulo tramite [2] ha provocato l'aggiornamento del modello [Form.java] tramite l'inserimento di [1]. L'elemento HTML
<option value="5">cinq</option>
è stato selezionato. Il browser ha inviato la stringa "5" come valore del componente JSF che ha generato l'elenco a discesa:
<h:selectOneListbox id="selectOneListBox2" value="#{form.selectOneListBox2}" size="3">
Il controller JSF utilizzerà il metodo setSelectOneListBox2("5") per aggiornare il modello dell'elenco. Pertanto, dopo tale aggiornamento, il campo
private String selectOneListBox2;
conterrà ora il valore "5".
Quando la pagina [index.xhtml] viene visualizzata nuovamente al termine dell’elaborazione, questo valore determina la visualizzazione di [3,4] sopra riportata:
- determina l’elemento dell’elenco che deve essere selezionato ([3]),
- il valore del campo selectOneListBox2 viene visualizzato in [4].
2.5.15. Tag <h:selectManyListBox>
Il tag <h:selectmanyListBox> genera un tag <select multiple="multiple">...</select> che consente all'utente di selezionare più elementi da un elenco.
Consideriamo il seguente codice:
<!-- riga 6 -->
<h:outputText value="selectManyListBox (size=3)" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectManyListBoxPrompt']}"/>
<h:selectManyListbox id="selectManyListBox" value="#{form.selectManyListBox}" size="3">
<f:selectItem itemValue="1" itemLabel="un"/>
<f:selectItem itemValue="2" itemLabel="deux"/>
<f:selectItem itemValue="3" itemLabel="trois"/>
<f:selectItem itemValue="4" itemLabel="quatre"/>
<f:selectItem itemValue="5" itemLabel="cinq"/>
</h:selectManyListbox>
<p><input type="button" value="#{msg['form.buttonRazText']}" onclick="this.form['formulaire:selectManyListBox'].selectedIndex=-1;" /></p>
</h:panelGroup>
<h:outputText value="#{form.selectManyListBoxValue}"/>
e il relativo modello in [Form.java]:
private String[] selectManyListBox=new String[]{"1","3"};
Quando la pagina [index.xhtml] viene richiesta per la prima volta, la pagina ottenuta è la seguente:
![]() |
- la riga 2 del codice XHTML genera [1]
- il testo [2] viene generato dalla riga 4. L'elenco [3] viene generato dalle righe [5-11]. L'attributo size="3" fa sì che l'elenco visualizzi in un dato momento tre di questi elementi. Gli elementi selezionati nell’elenco sono stati determinati tramite la chiamata al metodo getSelectManyListBox() (riga 5) del modello Java. Il risultato {"1","3"} ottenuto (riga 1 del codice Java) è un array di elementi di tipo String. Ciascuno di questi elementi serve a selezionare uno degli elementi dell’elenco. In questo caso, verranno selezionati gli elementi delle righe 6 e 10 il cui attributo itemValue è contenuto nell’array {"1","3"}. È quanto mostra [3].
- La riga 14 del codice XHTML genera [4]. Qui non viene richiamato il metodo getSelectManyListBox del modello Java dell’elenco, bensì il seguente metodo getSelectManyListBoxValue:
private String[] selectManyListBox=new String[]{"1","3"};
...
// getter e setter
public String getSelectManyListBoxValue(){
return getValue(selectManyListBox);
}
private String getValue(String[] chaines){
String value="[";
for(String chaine : chaines){
value+=" "+chaine;
}
return value+"]";
}
Se fosse stato richiamato il metodo getSelectManyListBox, si sarebbe ottenuto un array di String. Per includere questo elemento nel flusso HTML, il controller avrebbe dovuto richiamare il suo metodo toString. Tuttavia, questo metodo, quando applicato a un array, restituisce solo il suo “hashcode” e non l’elenco dei suoi elementi, come desideriamo. Pertanto, utilizziamo il metodo getSelectManyListBoxValue sopra indicato per ottenere una stringa di caratteri che rappresenti il contenuto dell’array,
- la riga 12 del codice XHTML genera il pulsante [5]. Quando si fa clic su questo pulsante, viene eseguito il codice JavaScript dell’attributo onclick. Esso verrà incorporato nella pagina HTML che verrà generata dal codice JSF. Per comprenderlo, dobbiamo conoscere la natura esatta di quest’ultima.
Il flusso HTML generato dalla pagina XHTML è il seguente:
<tr>
<td class="col1"><span class="info">selectManyListBox (size=3)</span></td>
<td class="col2">choix multiple : <select id="formulaire:selectManyListBox" name="formulaire:selectManyListBox" multiple="multiple" size="3">
<option value="1" selected="selected">un</option>
<option value="2">deux</option>
<option value="3" selected="selected">trois</option>
<option value="4">quatre</option>
<option value="5">cinq</option>
</select>
<p><input type="button" value="Raz" onclick="this.form['formulaire:selectManyListBox'].selectedIndex=-1;" /></p>
</td>
<td class="col3">[ 1 3]</td>
</tr>
- righe 3 e 9: il tag HTML <select multiple="multiple"...>...</select> generato dal tag JSF <h:selectManyListBox>. È la presenza dell'attributo multiple a indicare che si tratta di un elenco a selezione multipla,
- il fatto che il modello dell’elenco sia l’array di String {"1","3"} fa sì che gli elementi dell’elenco alle righe 4 (value="1") e 6 (value="3") abbiano l’attributo selected="selected",
- riga 10: quando si fa clic sul pulsante [Raz], viene eseguito il codice JavaScript dell’attributo onclick. La pagina viene rappresentata nel browser da un albero di oggetti spesso denominato DOM (Document Object Model). Ogni oggetto dell’albero è accessibile al codice JavaScript tramite il suo attributo name. L’elemento della riga 3 del codice HTML sopra riportato è denominato form:selectManyListBox. Il modulo stesso può essere indicato in vari modi. In questo caso, è indicato con la notazione this.form, dove «this» indica il pulsante [Raz] e this.form indica il modulo in cui si trova tale pulsante. L'elenco «form:selectManyListBox» si trova all’interno dello stesso modulo. Pertanto, la notazione this.form['formulaire:selectManyListBox'] indica la posizione dell’elenco nell’albero dei componenti del modulo. L'oggetto che rappresenta un elenco possiede un attributo selectedIndex il cui valore corrisponde al numero dell'elemento selezionato nell'elenco. Tale numero parte da 0 per indicare il primo elemento dell'elenco. Il valore -1 indica che nessun elemento è selezionato nell'elenco. Il codice JavaScript che assegna il valore -1 all'attributo selectedIndex ha l'effetto di deselezionare tutti gli elementi dell'elenco, se presenti.
Ora, qui sotto, selezioniamo [1] nuovi valori dall’elenco (per selezionare più elementi nell’elenco, tenere premuto il tasto Ctrl mentre si clicca) e inviamo il modulo con il pulsante [Valider] [2]. Otteniamo in risposta la pagina [3,4]:
![]() |
Il valore del campo [1] inviato è il seguente:
La convalida del modulo tramite [2] ha provocato l’aggiornamento del modello [Form.java] tramite l’inserimento di [1]. Gli elementi HTML
<option value="3">trois</option>
<option value="4">quatre</option>
<option value="5">cinq</option>
sono stati selezionati. Il browser ha inviato le tre stringhe "3", "4", "5" come valori del componente JSF che ha generato l'elenco a discesa:
<h:selectManyListbox id="selectManyListBox" value="#{form.selectManyListBox}" size="3">
Il metodo setSelectManyListBox del modello verrà utilizzato per aggiornare questo modello con i valori inviati dal browser:
private String[] selectManyListBox;
....
public void setSelectManyListBox(String[] selectManyListBox) {
this.selectManyListBox = selectManyListBox;
}
Alla riga 3, si vede che il parametro del metodo è un array di String. In questo caso sarà l’array {"3","4","5"}. Dopo questo aggiornamento, il campo
private String[] selectManyListBox;
contiene ora l’array {"3","4","5"}.
Quando la pagina [index.xhtml] viene visualizzata nuovamente al termine dell’elaborazione, questo valore determina la visualizzazione di [3,4] sopra riportata:
- determina quali elementi dell’elenco devono essere selezionati ([3]),
- il valore del campo selectManyListBox viene visualizzato in [4].
2.5.16. Tag <h:selectOneMenu>
Il tag <h:selectOneMenu> è identico al tag <h:selectOneListBox size="1">. Nell'esempio, il codice JSF eseguito è il seguente:
<!-- riga 7 -->
<h:outputText value="selectOneMenu" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectOneMenuPrompt']}"/>
<h:selectOneMenu id="selectOneMenu" value="#{form.selectOneMenu}">
<f:selectItem itemValue="1" itemLabel="un"/>
<f:selectItem itemValue="2" itemLabel="deux"/>
<f:selectItem itemValue="3" itemLabel="trois"/>
<f:selectItem itemValue="4" itemLabel="quatre"/>
<f:selectItem itemValue="5" itemLabel="cinq"/>
</h:selectOneMenu>
</h:panelGroup>
<h:outputText value="#{form.selectOneMenu}"/>
Il modello del tag <h:selectOneMenu> in [Form.java] è il seguente:
private String selectOneMenu="1";
Alla richiesta iniziale della pagina [index.xhtml], il codice precedente genera la vista:
![]() |
Un esempio di esecuzione potrebbe essere il seguente:
![]() |
Il valore inviato per il campo [1] è il seguente:
2.5.17. Tag <h:selectManyMenu>
Il tag <h:selectManyMenu> è identico al tag <h:selectManyListBox size="1">. Il codice JSF eseguito nell'esempio è il seguente:
<!-- riga 8 -->
<h:outputText value="selectManyMenu" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectManyMenuPrompt']}" styleClass="prompt" />
<h:selectManyMenu id="selectManyMenu" value="#{form.selectManyMenu}" >
<f:selectItem itemValue="1" itemLabel="un"/>
<f:selectItem itemValue="2" itemLabel="deux"/>
<f:selectItem itemValue="3" itemLabel="trois"/>
<f:selectItem itemValue="4" itemLabel="quatre"/>
<f:selectItem itemValue="5" itemLabel="cinq"/>
</h:selectManyMenu>
<p><input type="button" value="#{msg['form.buttonRazText']}" onclick="this.form['formulaire:selectManyMenu'].selectedIndex=-1;" /></p>
</h:panelGroup>
<h:outputText value="#{form.selectManyMenuValue}" styleClass="prompt"/>
Il modello del tag <h:selectManyMenu> in [Form.java] è il seguente:
private String[] selectManyMenu=new String[]{"1","2"};
In risposta alla richiesta iniziale della pagina [index.xhtml], il codice precedente genera la pagina:
![]() |
L'elenco [1] contiene i testi "uno", ..., "cinque" con gli elementi "uno" e "due" selezionati. Il codice HTML generato è il seguente:
<tr>
<td class="col1"><span class="info">selectManyMenu</span></td>
<td class="col2"><span class="prompt">choix multiple : </span><select id="formulaire:selectManyMenu" name="formulaire:selectManyMenu" multiple="multiple" size="1">
<option value="1" selected="selected">un</option>
<option value="2" selected="selected">deux</option>
<option value="3">trois</option>
<option value="4">quatre</option>
<option value="5">cinq</option>
</select>
<p><input type="button" value="Raz" onclick="this.form['formulaire:selectManyMenu'].selectedIndex=-1;" /></p>
</td>
<td class="col3"><span class="prompt">[ 1 2]</span></td>
</tr>
Come si vede sopra, alle righe 4 e 5, gli elementi "uno" e "due" sono selezionati (presenza dell'attributo selected).
È difficile fornire uno screenshot di un esempio di esecuzione poiché non è possibile mostrare gli elementi selezionati nel menu. Il lettore è invitato a provare personalmente (per selezionare più elementi nell’elenco, tenere premuto il tasto Ctrl mentre si clicca).
2.5.18. Tag <h:inputHidden>
Il tag <h:inputHidden> non ha una rappresentazione visiva. Serve solo a inserire un tag <input type="hidden" value="..."/> nel flusso della pagina. Inclusi all’interno di un tag <h:form>, i loro valori fanno parte dei dati inviati al server quando il modulo viene inviato. Poiché si tratta di campi del modulo e l’utente non li vede, vengono chiamati campi nascosti. L’utilità di questi campi è quella di conservare la memoria tra i diversi cicli di richiesta/risposta dello stesso client:
- il client richiede un modulo F. Il server glielo invia e inserisce un’informazione I in un campo nascosto C, nella forma <h:inputHidden id="C" value="I"/>,
- quando il client ha compilato il modulo F e lo invia al server, il valore I del campo C viene rinviato al server. Quest’ultimo può quindi recuperare l’informazione I che aveva memorizzato nella pagina. Si è così creata una memoria tra i due cicli richiesta/risposta,
- JSF utilizza a sua volta questa tecnica. L’informazione I che memorizza nel modulo F è il valore di tutti i suoi componenti. A tal fine utilizza il seguente campo nascosto:
<input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="H4sIAAAAAAAAANV...8PswawAA" />
Il campo nascosto si chiama javax.faces.ViewState e il suo valore è una stringa che rappresenta in forma codificata il valore di tutti i componenti della pagina inviata al client. Quando quest’ultimo rinvia la pagina dopo aver inserito dei dati nel modulo, il campo nascosto javax.faces.ViewState viene rinviato insieme ai valori inseriti. È questo che permette al controller JSF di ricostruire la pagina così come era stata inviata inizialmente. Questo meccanismo è stato spiegato a pagina 72.
Il codice JSF dell’esempio è il seguente:
<!-- riga 9 -->
<h:outputText value="inputHidden" styleClass="info"/>
<h:inputHidden id="inputHidden" value="#{form.inputHidden}"/>
<h:outputText value="#{form.inputHidden}"/>
Il modello del tag <h:inputHidden> in [Form.java] è il seguente:
private String inputHidden="initial";
Ciò determina la seguente visualizzazione al momento della richiesta iniziale della pagina [index.xhtml]:
- la riga 2 genera [1], la riga 4 [2]. La riga 3 non genera alcun elemento visivo.
Il codice HTML generato è il seguente:
<tr>
<td class="col1"><span class="info">inputHidden</span></td>
<td class="col2"><input id="formulaire:inputHidden" type="hidden" name="formulaire:inputHidden" value="initial" /></td>
<td class="col3">initial</td>
</tr>
Al momento dell’invio del modulo con il codice POST, il valore “iniziale” del campo denominato modulo:inputHidden della riga 3 verrà inviato insieme agli altri valori del modulo. Il campo
private String inputHidden;
verrà aggiornato con questo valore, che è quello che aveva già inizialmente. Questo valore verrà integrato nella nuova pagina restituita al cliente. Si ottiene quindi sempre la schermata sopra riportata.
Il valore inviato per il campo nascosto è il seguente:
2.5.19. Tag <h:selectBooleanCheckBox>
Il tag <h:selectBooleanCheckBox> genera un tag HTML <input type="checkbox" ...>.
Consideriamo il seguente codice JSF:
<!-- riga 10 -->
<h:outputText value="selectBooleanCheckbox" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectBooleanCheckboxPrompt']}" styleClass="prompt" />
<h:selectBooleanCheckbox id="selectBooleanCheckbox" value="#{form.selectBooleanCheckbox}"/>
</h:panelGroup>
<h:outputText value="#{form.selectBooleanCheckbox}"/>
Il modello del tag <h:selectBooleanCheckbox> della riga 5 sopra riportata in [Form.java] è il seguente:
private boolean selectBooleanCheckbox=true;
Quando la pagina [index.xhtml] viene richiesta per la prima volta, la pagina ottenuta è la seguente:
![]() |
- la riga 2 del codice XHTML genera [1],
- il testo [2] viene generato dalla riga 4. La casella di controllo [3] viene generata dalla riga [5]. In questo caso, è stato utilizzato il metodo getSelectBooleanCheckbox di [Form.java] per selezionare o deselezionare la casella. Poiché il metodo restituisce il valore booleano true (cfr. codice Java), la casella è stata selezionata;
- la riga 7 del codice XHTML genera [4]. È nuovamente il metodo getSelectBooleanCheckbox di [Form.java] che viene utilizzato per generare il testo [4].
Il flusso HTML generato dal codice JSF precedente è il seguente:
<tr>
<td class="col1"><span class="info">selectBooleanCheckbox</span></td>
<td class="col2"><span class="prompt">marié(e) : </span>
<input id="formulaire:selectBooleanCheckbox" type="checkbox" name="formulaire:selectBooleanCheckbox" checked="checked" /></td>
<td class="col3">true</td>
</tr>
In [4], si vede il tag HTML <input type="checkbox"> che è stato generato. Il valore true del modello associato ha fatto sì che all’elemento venisse aggiunto l’attributo checked="checked". Di conseguenza, la casella è selezionata.
Ora, qui sotto, deselezioniamo la casella [1], inviamo il modulo [2] e osserviamo il risultato ottenuto [3, 4]:
![]() |
Poiché la casella non è spuntata, non è stato inserito alcun valore per il campo [1].
La convalida del modulo tramite [2] ha provocato l'aggiornamento del modello [Form.java] tramite l'inserimento [1]. Il campo selectBooleanCheckbox di [Form.java] ha quindi ricevuto il valore false. La visualizzazione di [index.xhtml] mostra che il campo selectBooleanCheckbox del modello è stato effettivamente aggiornato a [3] e [4]. È interessante notare che è proprio grazie al campo nascosto javax.faces.ViewState che JSF è stato in grado di rilevare che la casella di controllo inizialmente selezionata era stata deselezionata dall’utente. Infatti, il valore di una casella deselezionata non fa parte dei valori inviati dal browser. Grazie all’albero dei componenti memorizzato nel campo nascosto javax.faces.ViewState, JSF rileva che nel modulo era presente una casella di controllo denominata “selectBooleanCheckbox” e che il suo valore non fa parte dei valori inviati dal browser client. Può quindi dedurre che fosse deselezionata nel modulo inviato, il che gli consente di assegnare il valore booleano false al modello Java associato:
private boolean selectBooleanCheckbox;
2.5.20. Tag <h:selectManyCheckBox>
Il tag <h:selectManyCheckBox> genera un gruppo di caselle di controllo e quindi diversi tag HTML <input type="checkbox" ...>. Questo tag è l'equivalente del tag <h:selectManyListBox>, con la differenza che gli elementi da selezionare sono presentati sotto forma di caselle di controllo contigue anziché sotto forma di elenco. Quanto detto per il tag <h:selectManyListBox> rimane valido anche in questo caso.
Consideriamo il seguente codice JSF:
<!-- riga 11 -->
<h:outputText value="selectManyCheckbox" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectManyCheckboxPrompt']}" styleClass="prompt" />
<h:selectManyCheckbox id="selectManyCheckbox" value="#{form.selectManyCheckbox}">
<f:selectItem itemValue="1" itemLabel="rouge"/>
<f:selectItem itemValue="2" itemLabel="bleu"/>
<f:selectItem itemValue="3" itemLabel="blanc"/>
<f:selectItem itemValue="4" itemLabel="noir"/>
</h:selectManyCheckbox>
</h:panelGroup>
<h:outputText value="#{form.selectManyCheckboxValue}"/>
Il modello del tag <h:selectManyCheckbox> della riga 5 sopra riportata in [Form.java] è il seguente:
private String[] selectManyCheckbox=new String[]{"1","3"};
Quando la pagina [index.xhtml] viene richiesta per la prima volta, la pagina ottenuta è la seguente:
![]() |
- la riga 2 del codice XHTML genera [1],
- il testo [2] viene generato dalla riga 4. Le caselle di controllo [3] vengono generate dalle righe 5-10. Per ciascuna di esse:
- l'attributo itemLabel definisce il testo visualizzato accanto alla casella di selezione;
- l'attributo itemvalue definisce il valore che verrà inviato al server se la casella è selezionata;
Il modello delle quattro caselle è il seguente campo Java:
private String[] selectManyCheckbox=new String[]{"1","3"};
Questa tabella definisce:
- quando la pagina viene visualizzata, le caselle che devono essere selezionate. Ciò avviene tramite il loro valore, c.a.d, e il loro campo itemValue. Nell’esempio sopra riportato, le caselle i cui valori sono presenti nella tabella {"1","3"} saranno selezionate. È ciò che si vede nella schermata sopra riportata;
- quando la pagina viene inviata, il modello selectManyCheckbox riceve l’array dei valori delle caselle che l’utente ha selezionato. È ciò che vedremo tra poco,
- la riga 12 del codice XHTML genera [4]. È il metodo getSelectManyCheckboxValue che segue ad aver generato [4]:
public String getSelectManyCheckboxValue(){
return getValue(getSelectManyCheckbox());
}
private String getValue(String[] chaines){
String value="[";
for(String chaine : chaines){
value+=" "+chaine;
}
return value+"]";
}
Il flusso HTML generato dal codice precedente JSF è il seguente:
<tr>
<td>
<input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:0" value="1" type="checkbox" checked="checked" /><label for="formulaire:selectManyCheckbox:0"> rouge</label></td>
<td>
<input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:1" value="2" type="checkbox" /><label for="formulaire:selectManyCheckbox:1"> bleu</label></td>
<td>
<input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:2" value="3" type="checkbox" checked="checked" /><label for="formulaire:selectManyCheckbox:2"> blanc</label></td>
<td>
<input name="formulaire:selectManyCheckbox" id="formulaire:selectManyCheckbox:3" value="4" type="checkbox" /><label for="formulaire:selectManyCheckbox:3"> noir</label></td>
</tr>
</table></td>
<td class="col3">[ 1 3]</td>
</tr>
Sono stati generati quattro tag HTML <input type="checkbox" ...>. I tag delle righe 3 e 7 hanno l’attributo checked="checked", che fa sì che appaiano spuntati. Si noti che hanno tutte lo stesso attributo name="form:selectManyCheckbox", ovvero i quattro campi HTML hanno lo stesso nome. Se le caselle delle righe 5 e 9 vengono selezionate dall’utente, il browser invierà i valori delle quattro caselle di selezione nella forma:
e il modello delle quattro caselle
private String[] selectManyCheckbox=new String[]{"1","3"};
riceverà l’array {"2","4"}.
Verifichiamolo qui di seguito. In [1] si effettua la modifica, in [2] si invia il modulo. In [3] il risultato ottenuto:
![]() |
I valori inviati per i campi [1] sono i seguenti:
2.5.21. Tag <h:selectOneRadio>
Il tag <h:selectOneRadio> genera un gruppo di pulsanti di opzione mutuamente esclusivi.
Consideriamo il seguente codice JSF:
<!-- riga 12 -->
<h:outputText value="selectOneRadio" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectOneRadioPrompt']}" />
<h:selectOneRadio id="selectOneRadio" value="#{form.selectOneRadio}">
<f:selectItem itemValue="1" itemLabel="voiture"/>
<f:selectItem itemValue="2" itemLabel="vélo"/>
<f:selectItem itemValue="3" itemLabel="scooter"/>
<f:selectItem itemValue="4" itemLabel="marche"/>
</h:selectOneRadio>
</h:panelGroup>
<h:outputText value="#{form.selectOneRadio}"/>
Il modello del tag <h:selectOneRadio> della riga 5 sopra riportata è il seguente in [Form.java]:
private String selectOneRadio="2";
Quando la pagina [index.xhtml] viene richiesta per la prima volta, la vista ottenuta è la seguente:
![]() |
- la riga 2 del codice XHTML genera [1],
- Il testo [2] viene generato dalla riga 4. I pulsanti di opzione [3] vengono generati dalle righe 5-10. Per ciascuno di essi:
- l'attributo itemLabel definisce il testo visualizzato accanto al pulsante di opzione;
- l'attributo itemvalue definisce il valore che verrà inviato al server se il pulsante è selezionato;
Il modello dei quattro pulsanti di opzione è il seguente campo Java:
private String selectOneRadio="2";
Questo modello definisce:
- quando la pagina viene visualizzata, l'unico pulsante di opzione che deve essere selezionato. Ciò avviene tramite il loro valore, c.a.d, e il loro campo itemValue. Nell’esempio sopra riportato, il pulsante di opzione con valore “2” sarà selezionato. È ciò che si vede nella schermata sopra riportata;
- quando la pagina viene inviata, il modello selectOneRadio riceve il valore del pulsante di opzione che è stato selezionato. È ciò che vedremo tra poco,
- la riga 12 del codice XHTML genera [4].
Il flusso HTML generato dal codice JSF precedente è il seguente:
<tr>
<td class="col1"><span class="info">selectOneRadio</span></td>
<td class="col2">moyen de transport préféré : <table id="formulaire:selectOneRadio">
<tr>
<td>
<input type="radio" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:0" value="1" /><label for="formulaire:selectOneRadio:0"> voiture</label></td>
<td>
<input type="radio" checked="checked" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:1" value="2" /><label for="formulaire:selectOneRadio:1"> vélo</label></td>
<td>
<input type="radio" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:2" value="3" /><label for="formulaire:selectOneRadio:2"> scooter</label></td>
<td>
<input type="radio" name="formulaire:selectOneRadio" id="formulaire:selectOneRadio:3" value="4" /><label for="formulaire:selectOneRadio:3"> marche</label></td>
</tr>
Sono stati generati quattro tag HTML <input type="radio" ...>. Il tag della riga 8 presenta l’attributo checked="checked", che fa sì che il pulsante di opzione corrispondente appaia selezionato. Si noti che tutti i tag hanno lo stesso attributo name="form:selectOneRadio", ovvero i quattro campi HTML hanno lo stesso nome. Questa è la condizione necessaria per avere un gruppo di pulsanti di opzione esclusivi: quando uno è selezionato, gli altri non lo sono.
Di seguito, in [1], si seleziona uno dei pulsanti di opzione, in [2] si invia il modulo, in [3] il risultato ottenuto:
![]() |
Il valore inviato per il campo [1] è il seguente:
2.6. Esempio mv-jsf2-04: elenchi dinamici
2.6.1. L'applicazione
L'applicazione è la stessa di prima:
![]() |
Le uniche modifiche riguardano il modo in cui vengono generati gli elementi delle liste delle aree [1] e [2]. Qui vengono generati dinamicamente tramite codice Java, mentre nella versione precedente erano scritti "in modo statico" nel codice della pagina JSF.
2.6.2. Il progetto NetBeans
Il progetto NetBeans dell’applicazione è il seguente:
![]() |
Il progetto [mv-jsf2-04] è identico al progetto [mv-jsf2-03], con le seguenti differenze:
- in [1], nella pagina JSF, gli elementi delle liste non saranno più scritti «in modo fisso» nel codice,
- nel progetto [2], il modello della pagina JSF [1] verrà modificato,
- nella pagina [3], uno dei messaggi verrà modificato.
2.6.3. La pagina [index.xhtml] e il suo modello [Form.java]
La pagina JSF [index.xhtml] diventa la seguente:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h:form id="formulaire">
<!-- lingue -->
<h:panelGrid columns="2">
<h:commandLink value="#{msg['form.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
<h:commandLink value="#{msg['form.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
</h:panelGrid>
<h1><h:outputText value="#{msg['form.titre']}"/></h1>
<h:panelGrid columnClasses="col1,col2,col3" columns="3" border="1">
...
<!-- riga 4 -->
<h:outputText value="selectOneListBox (size=1)" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectOneListBox1Prompt']}"/>
<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
<f:selectItems value="#{form.selectOneListbox1Items}"/>
</h:selectOneListbox>
</h:panelGroup>
<h:outputText value="#{form.selectOneListBox1}"/>
<!-- riga 5 -->
<h:outputText value="selectOneListBox (size=3)" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectOneListBox2Prompt']}"/>
<h:selectOneListbox id="selectOneListBox2" value="#{form.selectOneListBox2}" size="3">
<f:selectItems value="#{form.selectOneListbox2Items}"/>
</h:selectOneListbox>
</h:panelGroup>
<h:outputText value="#{form.selectOneListBox2}"/>
<!-- riga 6 -->
<h:outputText value="selectManyListBox (size=3)" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectManyListBoxPrompt']}"/>
<h:selectManyListbox id="selectManyListBox" value="#{form.selectManyListBox}" size="3">
<f:selectItems value="#{form.selectManyListBoxItems}"/>
</h:selectManyListbox>
<p><input type="button" value="#{msg['form.buttonRazText']}" onclick="this.form['formulaire:selectManyListBox'].selectedIndex=-1;" /></p>
</h:panelGroup>
<h:outputText value="#{form.selectManyListBoxValue}"/>
<!-- riga 7 -->
<h:outputText value="selectOneMenu" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectOneMenuPrompt']}"/>
<h:selectOneMenu id="selectOneMenu" value="#{form.selectOneMenu}">
<f:selectItems value="#{form.selectOneMenuItems}"/>
</h:selectOneMenu>
</h:panelGroup>
<h:outputText value="#{form.selectOneMenu}"/>
<!-- riga 8 -->
<h:outputText value="selectManyMenu" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectManyMenuPrompt']}" styleClass="prompt" />
<h:selectManyMenu id="selectManyMenu" value="#{form.selectManyMenu}" >
<f:selectItems value="#{form.selectManyMenuItems}"/>
</h:selectManyMenu>
<p><input type="button" value="#{msg['form.buttonRazText']}" onclick="this.form['formulaire:selectManyMenu'].selectedIndex=-1;" /></p>
</h:panelGroup>
<h:outputText value="#{form.selectManyMenuValue}" styleClass="prompt"/>
...
<!-- riga 11 -->
<h:outputText value="selectManyCheckbox" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectManyCheckboxPrompt']}" styleClass="prompt" />
<h:selectManyCheckbox id="selectManyCheckbox" value="#{form.selectManyCheckbox}">
<f:selectItems value="#{form.selectManyCheckboxItems}"/>
</h:selectManyCheckbox>
</h:panelGroup>
<h:outputText value="#{form.selectManyCheckboxValue}"/>
<!-- riga 12 -->
<h:outputText value="selectOneRadio" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.selectOneRadioPrompt']}" />
<h:selectOneRadio id="selectOneRadio" value="#{form.selectOneRadio}">
<f:selectItems value="#{form.selectOneRadioItems}"/>
</h:selectOneRadio>
</h:panelGroup>
<h:outputText value="#{form.selectOneRadio}"/>
</h:panelGrid>
<p>
<h:commandButton type="submit" id="submit" value="#{msg['form.submitText']}"/>
</p>
</h:form>
</h:body>
</f:view>
</html>
Le modifiche apportate sono illustrate dalle righe 26-28. Dove prima c'era il codice:
<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
<f:selectItem itemValue="1" itemLabel="un"/>
<f:selectItem itemValue="2" itemLabel="deux"/>
<f:selectItem itemValue="3" itemLabel="trois"/>
</h:selectOneListbox>
ora si trova questo:
<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
<f:selectItems value="#{form.selectOneListbox1Items}"/>
</h:selectOneListbox>
I tre tag <f:selectItem> delle righe 2-4 sono stati sostituiti dall'unico tag <f:selectItems> della riga b. Questo tag ha un attributo value il cui valore è una collezione di elementi di tipo </span>*javax.faces.model.SelectItem. Quanto sopra, il valore dell'attributo value verrà ottenuto richiamando il seguente metodo [form].</span>**getSelectOneListbox1Items*:
public SelectItem[] getSelectOneListbox1Items() {
return getItems("A",3);
}
private SelectItem[] getItems(String label, int qte) {
SelectItem[] items=new SelectItem[qte];
for(int i=0;i<qte;i++){
items[i]=new SelectItem(i,label+i);
}
return items;
}
- alla riga 1, il metodo getSelectOneListbox1Items restituisce un array di elementi di tipo javax.faces.model.SelectItem costruito dal metodo privato getItems della riga 5. Si noti che il metodo getSelectOneListbox1Items non è il getter di un campo privato selectOneListBox1Items,
- la classe javax.faces.model.SelectItem ha diversi costruttori.

Utilizziamo la riga 8 del metodo getItems, il costruttore SelectItem(Object value, String label) che corrisponde al tag JSF
<f:selectItem itemValue="value" labelValue="label"/>
- righe 5-10: il metodo getItems(String label, int qte) crea un array di qte elementi di tipo SelectItem, dove l'elemento i è ottenuto tramite il costruttore SelectItem(i, label+i).
Il codice JSF
<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
<f:selectItems value="#{form.selectOneListbox1Items}"/>
</h:selectOneListbox>
diventa quindi funzionalmente equivalente al seguente codice JSF:
<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
<f:selectItem itemValue="0" itemLabel="A0"/>
<f:selectItem itemValue="1" itemLabel="A1"/>
<f:selectItem itemValue="2" itemLabel="A2"/>
</h:selectOneListbox>
Lo stesso vale per tutti gli altri elenchi della pagina JSF. Nel modello [Form.java] si trovano quindi i seguenti nuovi metodi:
public SelectItem[] getSelectOneListbox1Items() {
return getItems("A",3);
}
public SelectItem[] getSelectOneListbox2Items() {
return getItems("B",4);
}
public SelectItem[] getSelectManyListBoxItems() {
return getItems("C",5);
}
public SelectItem[] getSelectOneMenuItems() {
return getItems("D",3);
}
public SelectItem[] getSelectManyMenuItems() {
return getItems("E",4);
}
public SelectItem[] getSelectManyCheckboxItems() {
return getItems("F",3);
}
public SelectItem[] getSelectOneRadioItems() {
return getItems("G",4);
}
private SelectItem[] getItems(String label, int qte) {
SelectItem[] items=new SelectItem[qte];
for(int i=0;i<qte;i++){
items[i]=new SelectItem(i,label+i);
}
return items;
}
2.6.4. Il file dei messaggi
Viene modificato un solo messaggio:
[messages_fr.properties]
form.titre=Java Server Faces - remplissage dynamique des listes
[messages_en.properties]
form.titre=Java Server Faces - dynamic filling of lists of elements
2.6.5. Test
Il lettore è invitato a testare questa nuova versione.
Nella maggior parte dei casi, gli elementi dinamici di un modulo sono il risultato di un'elaborazione aziendale o provengono da un database:
![]() |
Esaminiamo la richiesta iniziale della pagina JSF [index.xhtml] da parte del browser tramite GET:
- viene richiesta la pagina JSF [1],
- il controller [Faces Servlet] ne richiede la visualizzazione in [3]. Il motore JSF che elabora la pagina richiama il modello [Form.java] della stessa, ad esempio il metodo getSelectOneListBox1Items. Questo metodo potrebbe benissimo restituire un array di elementi di tipo SelectItem, a partire da informazioni memorizzate in un database. A tal fine, farebbe ricorso al livello [métier] [2b].
2.7. Esempio mv-jsf2-05: navigazione – sessione – gestione delle eccezioni
2.7.1. L'applicazione
L’applicazione è la stessa di prima, tranne per il fatto che il modulo si presenta ora sotto forma di una procedura guidata a più pagine:
![]() |
- in [1], la pagina 1 del modulo – è accessibile anche tramite il link 1 di [2]
- in [2], un gruppo di 5 link.
- in [3], la pagina 2 del modulo ottenuta tramite il link 2 di [2]
![]() |
![]() |
- in [4], la pagina 3 del modulo ottenuta tramite il link 3 di [2]
- in [5], la pagina ottenuta tramite il link «Genera un'eccezione» di [2]
![]() |
- in [6], la pagina ottenuta tramite il link 4 di [2]. Essa riassume i dati inseriti nelle pagine da 1 a 3.
2.7.2. Il progetto NetBeans
Il progetto NetBeans dell’applicazione è il seguente:
![]() |
Il progetto [mv-jsf2-05] introduce due novità:
- in [1], la pagina JSF [index.xhtml] è stata suddivisa in tre pagine [form1.xhtml, form2.xhtml, form3.xhtml] su cui sono stati distribuiti i dati inseriti. La pagina [form4.xhtml] è una copia della pagina [index.xhtml] del progetto precedente. In [2], la classe [Form.java] rimane invariata. Servirà da modello per le quattro pagine JSF precedenti,
- in [3] viene aggiunta una pagina [exception.xhtml]: verrà utilizzata quando si verificherà un'eccezione nell'applicazione.
2.7.3. Le pagine [form.xhtml] e il loro modello [Form.java]
2.7.3.1. Il codice delle pagine XHTML
La pagina JSF [form1.xhtml] è la seguente:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h:form id="formulaire">
<!-- collegamenti -->
<h:panelGrid columns="2">
<h:commandLink value="#{msg['form.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
<h:commandLink value="#{msg['form.langue2']}" action="#{changeLocale.setEnglishLocale}"/>
</h:panelGrid>
<h1><h:outputText value="#{msg['form1.titre']}"/></h1>
<h:panelGrid columnClasses="col1,col2" columns="2" border="1">
<h:outputText value="#{msg['form.headerCol1']}" styleClass="entete"/>
<h:outputText value="#{msg['form.headerCol2']}" styleClass="entete"/>
<!-- riga 1 -->
<h:outputText value="inputText" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.loginPrompt']}"/>
<h:inputText id="inputText" value="#{form.inputText}"/>
</h:panelGroup>
<!-- riga 2 -->
<h:outputText value="inputSecret" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.passwdPrompt']}"/>
<h:inputSecret id="inputSecret" value="#{form.inputSecret}"/>
</h:panelGroup>
<!-- riga 3 -->
<h:outputText value="inputTextArea" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.descPrompt']}"/>
<h:inputTextarea id="inputTextArea" value="#{form.inputTextArea}" rows="4"/>
</h:panelGroup>
</h:panelGrid>
<!-- collegamenti -->
<h:panelGrid columns="6">
<h:commandLink value="1" action="form1"/>
<h:commandLink value="2" action="#{form.doAction2}"/>
<h:commandLink value="3" action="form3"/>
<h:commandLink value="4" action="#{form.doAction4}"/>
<h:commandLink value="#{msg['form.pagealeatoireLink']}" action="#{form.doAlea}"/>
<h:commandLink value="#{msg['form.exceptionLink']}" action="#{form.throwException}"/>
</h:panelGrid>
</h:form>
</h:body>
</f:view>
</html>
e corrisponde alla seguente visualizzazione:
![]() |
Si notino i seguenti punti:
- riga 16: la tabella, che in precedenza aveva tre colonne, ora ne ha solo due. La colonna 3, che visualizzava i valori del modello, è stata eliminata. Sarà [form4.xhtml] a visualizzarli,
- righe 40-46: una tabella di sei link. I link delle righe 44 e 46 hanno una navigazione statica: il loro attributo `action` è codificato in modo fisso. Gli altri link hanno una navigazione dinamica: il loro attributo `action` punta a un metodo del bean del modulo incaricato di restituire la chiave di navigazione. I metodi a cui si fa riferimento in [Form.java] sono i seguenti:
// eventi
public String doAction2(){
return "form2";
}
public String doAction4(){
return "form4";
}
public String doAlea(){
// un numero casuale compreso tra 1 e 3
int i=1+(int)(3*Math.random());
// si restituisce la chiave di navigazione
return "form"+i;
}
public String throwException() throws java.lang.Exception{
throw new Exception("Exception test");
}
Per il momento tralasceremo il metodo throwException della riga 17. Ci torneremo in seguito. I metodi doAction2 e doAction4 si limitano a restituire la chiave di navigazione senza eseguire alcuna elaborazione. Avremmo quindi potuto scrivere anche:
<h:commandLink value="1" action="form1"/>
<h:commandLink value="2" action="form2"/>
<h:commandLink value="3" action="form3"/>
<h:commandLink value="4" action="form4"/>
<h:commandLink value="#{msg['form.pagealeatoireLink']}" action="#{form.doAlea}"/>
<h:commandLink value="#{msg['form.exceptionLink']}" action="#{form.throwException}"/>
Il metodo doAlea, invece, genera una chiave di navigazione casuale il cui valore è tratto dall’insieme {"form1", "form2", "form3"}.
Il codice delle pagine [form2.xhtml, form3.xhtml, form3.xhtml] è analogo a quello della pagina [form1.xhtml].
2.7.3.2. Durata del modello [Form.java] delle pagine [form*.xhtml]
Consideriamo la seguente sequenza di azioni:
![]() |
- in [1], si compila la pagina 1 e si passa alla pagina 3,
- in [2], si compila la pagina 3 e si torna alla pagina 1,
![]() |
- in [3], si ritrova la pagina 1 così come è stata inserita. Si torna quindi alla pagina 3,
- in [4], la pagina 3 viene ritrovata così come è stata inserita.
Il meccanismo del campo nascosto [javax.faces.ViewState] non è sufficiente a spiegare questo fenomeno.
Durante il passaggio da [1] a [2], si verificano diverse fasi:
- il modello [Form.java] viene aggiornato con il POST proveniente da [form1.jsp]. In particolare, il campo inputText riceve il valore «un altro testo»,
- la chiave di navigazione "form3" determina la visualizzazione di [form3.xhtml]. Il ViewState incorporato in [form3.xhtml] rappresenta lo stato dei soli componenti di [form3.xhtml], non quelli di [form1.xhtml].
Durante il passaggio da [2] a [3]:
- il modello [Form.java] viene aggiornato con il POST di [form3.xhtml]. Se viene richiesta la durata di vita del modello [Form.java], viene creato un oggetto [Form.java] completamente nuovo, che viene poi aggiornato con il valore POST derivante da [form3.xhtml]. In questo caso, il campo inputText del modello torna al suo valore predefinito:
private String inputText="texte";
e lo mantiene: infatti, nel POST derivato da [form3.xhtml], non vi è nulla che aggiorni il campo inputText, che fa parte del modello di [form1.xhtml] e non di quello di [form3.xhtml],
- la chiave di navigazione "form1" fa sì che venga visualizzato [form1.xhtml]. La pagina visualizza il proprio modello. Nel nostro caso, il campo di immissione login collegato al modello inputText visualizzerà texte e non il valore “un altro testo” immesso in [1]. Affinché il campo inputText mantenga il valore inserito in [1], la durata del modello [Form.java] deve essere impostata su “sessione” e non su “richiesta”. In questo caso,
- al termine dell'elaborazione di POST da [form1.xhtml], il modello verrà inserito nella sessione del cliente. Il campo inputText avrà il valore "un altro testo",
- al momento dell’esecuzione di POST da [form3.xhtml], il modello verrà ricercato in questa sessione e aggiornato da POST da [form3.xhtml]. Il campo inputText non verrà aggiornato da questo POST, ma manterrà il valore "un altro testo" acquisito al termine del POST da [form1.xhtml] e [1].
La dichiarazione del bean [Form.java] è quindi la seguente:
package forms;
import javax.enterprise.context.SessionScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.model.SelectItem;
@ManagedBean
@SessionScoped
public class Form {
La riga 8 assegna al bean un ambito di sessione.
2.7.4. Gestione delle eccezioni
Torniamo all’architettura generale di un’applicazione JSF:
![]() |
Cosa succede quando un gestore di eventi o un modello rileva un'eccezione proveniente dal livello business, ad esempio una disconnessione imprevista dal database?
- I gestori di eventi [2a] possono intercettare qualsiasi eccezione proveniente dal livello [métier] e restituire al controller [Faces Servlet] una chiave di navigazione verso una pagina di errore specifica per l’eccezione,
- Per i modelli, questa soluzione non è applicabile poiché, quando vengono richiamati [3,4], ci si trova nella fase di rendering di una pagina specifica XHTML e non più nella fase di selezione della stessa. Come si fa a cambiare pagina mentre ci si trova nella fase di rendering di una di esse? Una soluzione semplice, ma non sempre adeguata, consiste nel non gestire l’eccezione, che verrà quindi inoltrata al contenitore di servlet che esegue l’applicazione. Quest’ultimo può essere configurato per visualizzare una pagina specifica quando un’eccezione viene inoltrata al contenitore di servlet. Questa soluzione è sempre utilizzabile e la esamineremo ora.
2.7.4.1. Configurazione dell’applicazione web per la gestione delle eccezioni
La configurazione di un’applicazione web per la gestione delle eccezioni avviene nel file [web.xml]:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<context-param>
<param-name>javax.faces.FACELETS_SKIP_COMMENTS</param-name>
<param-value>true</param-value>
</context-param>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<url-pattern>/faces/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
<welcome-file-list>
<welcome-file>faces/form1.xhtml</welcome-file>
</welcome-file-list>
<error-page>
<error-code>500</error-code>
<location>/faces/exception.xhtml</location>
</error-page>
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/faces/exception.xhtml</location>
</error-page>
</web-app>
Alle righe 32-39 si trova la definizione di due pagine di errore. È possibile inserire tutti i tag <error-page> necessari. Il tag <location> indica la pagina da visualizzare in caso di errore. Il tipo di errore associato alla pagina può essere definito in due modi:
- tramite il tag <exception-type>, che definisce il tipo Java dell’eccezione gestita. Pertanto, il tag <error-page> delle righe 36-39 indica che se il contenitore di servlet rileva un'eccezione di tipo [java.lang.Exception] o derivata (riga 37) durante l'esecuzione dell'applicazione, deve visualizzare la pagina [/faces/exception.xhtml] (riga 38). Utilizzando qui il tipo di eccezione più generico [java.lang.Exception], ci si assicura di gestire tutte le eccezioni,
- tramite il tag <error-code> (riga 33) che definisce un codice di errore HTTP. Ad esempio, se un browser richiede l'URL [http://machine:port/contexte/P] e la pagina P non esiste nel contesto dell'applicazione, quest'ultima non interviene nella risposta. È il contenitore dei servlet che genera questa risposta inviando una pagina di errore predefinita. La prima riga del flusso HTTP della sua risposta contiene un codice di errore 404 che indica che la pagina P richiesta non esiste. Si potrebbe voler generare una risposta che, ad esempio, rispetti la grafica dell’applicazione o che fornisca dei link per risolvere il problema. In questo caso, si utilizzerà un tag <error-page> con un tag <error-code>404</error-code>.
Sopra, il codice di errore 500 HTTP è il codice restituito in caso di "arresto anomalo" dell'applicazione. È il codice che verrebbe restituito se un'eccezione venisse segnalata al contenitore dei servlet. I due tag <error-page> alle righe 28-35 sono quindi probabilmente ridondanti. Sono stati inseriti entrambi per illustrare i due modi di gestire un errore.
2.7.4.2. Simulazione dell’eccezione
Un'eccezione viene generata artificialmente dal collegamento [Lancer une exception]:
![]() |
![]() |
Cliccando sul collegamento [Lancer une exception] [1] viene visualizzata la pagina [2].
Nel codice delle pagine [formx.xhtml], il link [Lancer une exception] viene generato nel modo seguente:
<!-- link -->
<h:panelGrid columns="6">
<h:commandLink value="1" action="form1"/>
...
<h:commandLink value="#{msg['form.exceptionLink']}" action="#{form.throwException}"/>
</h:panelGrid>
Alla riga 5 si vede che, cliccando sul collegamento, verrà eseguito il metodo [form].throwException. Il codice è il seguente:
public String throwException() throws java.lang.Exception{
throw new Exception("Exception test");
}
Qui viene generata un'eccezione di tipo [java.lang.Exception]. L'eccezione verrà propagata fino al contenitore dei servlet, che visualizzerà quindi la pagina [/faces/exception.xhtml].
2.7.4.3. Le informazioni relative a un'eccezione
Quando un'eccezione viene segnalata al contenitore dei servlet, quest'ultimo visualizzerà la pagina di errore corrispondente, trasmettendo ad essa le informazioni relative all'eccezione. Tali informazioni vengono inserite come nuovi attributi della richiesta in fase di elaborazione. La richiesta di un browser e la risposta che riceverà sono incapsulate in oggetti Java di tipo [HttpServletRequest request] e [HttpServletResponse response]. Questi oggetti sono disponibili in tutte le fasi di elaborazione della richiesta del browser.
![]() |
Alla ricezione della richiesta HTTP proveniente dal browser, il contenitore di servlet la incapsula nell’oggetto Java [HttpServletRequest request] e crea l’oggetto [HttpServletResponse response] che consentirà di generare la risposta. In questo oggetto si trova in particolare il canale TCP-IP da utilizzare per il flusso HTTP della risposta. Tutti i livelli t1, t2, ..., tn che interverranno nell’elaborazione dell’oggetto request hanno accesso a questi due oggetti. Ciascuno di essi può accedere agli elementi della richiesta iniziale request e preparare la risposta arricchendo l’oggetto response. Un livello di localisation potrà, ad esempio, impostare il localisation della risposta tramite il metodo response.setLocale(Locale l).
I diversi livelli di elaborazione possono scambiarsi informazioni tramite l’oggetto request. Quest’ultimo dispone di un dizionario di attributi, inizialmente vuoto, che può essere arricchito dai livelli di elaborazione successivi. Questi ultimi possono inserire negli attributi dell’oggetto request le informazioni necessarie al livello di elaborazione successivo. Esistono due metodi per gestire gli attributi dell’oggetto request:
- void setAttribute(String s, Object o), che consente di aggiungere agli attributi un oggetto o identificato dalla stringa s,
- Object getAttribute(String s), che consente di ottenere l'attributo o identificato dalla stringa s.
Quando un'eccezione viene segnalata al contenitore dei servlet, quest'ultimo inserisce i seguenti attributi nella richiesta in fase di elaborazione:
chiave | valore |
il codice di errore HTTP che verrà restituito al cliente | |
il tipo Java dell'eccezione accompagnato dal messaggio di errore. | |
l'URL richiesto al momento in cui si è verificata l'eccezione | |
il servlet che stava elaborando la richiesta quando si è verificata l'eccezione |
Utilizzeremo questi attributi della richiesta nella pagina [exception.xhtml] per visualizzarli.
2.7.4.4. La pagina di errore [exception.xhtml]
Il suo contenuto è il seguente:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<f:view locale="#{changeLocale.locale}">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h:form id="formulaire">
<h3><h:outputText value="#{msg['exception.header']}"/></h3>
<h:panelGrid columnClasses="col1,col2" columns="2" border="1">
<h:outputText value="#{msg['exception.httpCode']}"/>
<h:outputText value="#{requestScope['javax.servlet.error.status_code']}"/>
<h:outputText value="#{msg['exception.message']}"/>
<h:outputText value="#{requestScope['javax.servlet.error.exception']}"/>
<h:outputText value="#{msg['exception.requestUri']}"/>
<h:outputText value="#{requestScope['javax.servlet.error.request_uri']}"/>
<h:outputText value="#{msg['exception.servletName']}"/>
<h:outputText value="#{requestScope['javax.servlet.error.servlet_name']}"/>
</h:panelGrid>
<!-- link -->
<h:panelGrid columns="6">
<h:commandLink value="1" action="form1"/>
<h:commandLink value="2" action="#{form.doAction2}"/>
<h:commandLink value="3" action="form3"/>
<h:commandLink value="4" action="#{form.doAction4}"/>
<h:commandLink value="#{msg['form.pagealeatoireLink']}" action="#{form.doAlea}"/>
</h:panelGrid>
</h:form>
</h:body>
</f:view>
</html>
2.7.4.4.1. Le espressioni della pagina di eccezione
Nella catena di elaborazione della richiesta del cliente, la pagina XHTML è normalmente l'ultimo anello della catena:
![]() |
Tutti gli elementi della catena sono classi Java, compresa la pagina XHTML. Quest’ultima viene infatti trasformata in un servlet dal contenitore di servlet, c.a.d, in una normale classe Java. Più precisamente, la pagina XHTML viene trasformata in codice Java che viene eseguito all’interno del seguente metodo:
public void _jspService(HttpServletRequest request, HttpServletResponse response)
throws java.io.IOException, ServletException {
JspFactory _jspxFactory = null;
PageContext pageContext = null;
HTTPSession session = null;
ServletContext application = null;
ServletConfig config = null;
JspWriter out = null;
Object page = this;
JspWriter _jspx_out = null;
PageContext _jspx_page_context = null;
...
...code de la page XHTML
A partire dalla riga 14, si trova il codice Java corrispondente alla pagina XHTML. Questo codice disporrà di un certo numero di oggetti inizializzati dal metodo _jspService, riga 1 sopra:
- riga 1: HttpServletRequest request: la richiesta in fase di elaborazione,
- riga 1: HttpServletResponse response: la risposta che verrà inviata al client,
- riga 7: ServletContext application: un oggetto che rappresenta l'applicazione web stessa. Come l’oggetto request, anche l’oggetto application può avere degli attributi. Questi sono condivisi da tutte le richieste di tutti i clienti. Si tratta in genere di attributi di sola lettura,
- riga 6: HTTPSession sessione: rappresenta la sessione del cliente. Come gli oggetti request e application, l’oggetto session può avere degli attributi. Questi sono condivisi da tutte le richieste dello stesso cliente,
- riga 9: JspWriter out: un flusso di scrittura verso il browser del cliente. Questo oggetto è utile per il debug di una pagina XHTML. Tutto ciò che viene scritto tramite out.println(testo) verrà visualizzato nel browser del cliente.
Quando nella pagina JSF si scrive #{espressione}, l'espressione può essere la chiave di un attributo degli oggetti request, session o application sopra indicati. L'attributo corrispondente viene cercato in successione in questi tre oggetti. Pertanto, #{chiave} viene valutata nel modo seguente:
- request.getAttribute(chiave)
- session.getAttribute(chiave)
- application.getAttribute(chiave)
Non appena si ottiene un valore diverso da null, la valutazione di #{chiave} viene interrotta. È possibile specificare con maggiore precisione il contesto in cui deve essere cercato l’attributo:
- #{requestScope['clé']} per cercare l'attributo nell'oggetto request,
- #{sessionScope['clé']} per cercare l'attributo nell'oggetto session,
- #{applicationScope['clé']} per cercare l'attributo nell'oggetto application.
Questo è quanto è stato fatto nella pagina [exception.xhtml] a pagina 116. Gli attributi utilizzati sono i seguenti:
chiave | dominio | valore |
richiesta | cfr. paragrafo 2.7.4.3. | |
idem | idem | |
idem | idem | |
idem | idem |
I vari messaggi necessari per la pagina JSF [exception.xhtml] sono stati aggiunti ai file di messaggi già esistenti:
[messages_fr.properties]
exception.header=L'exception suivante s'est produite
exception.httpCode=Code HTTP de l'erreur
exception.message=Message de l'exception
exception.requestUri=URL demandée lors de l'erreur
exception.servletName=Nom de la servlet demandée lorsque l'erreur s'est produite
[messages_en.properties]
exception.header=The following error occurred
exception.httpCode=HTTP error code
exception.message=Exception message
exception.requestUri=URL requested when error occurred
exception.servletName=Servlet requested when error occurred
2.8. Esempio mv-jsf2-06: convalida e conversione dei dati inseriti
2.8.1. L'applicazione
L'applicazione presenta un modulo di inserimento dati. Una volta convalidato, lo stesso modulo viene restituito come risposta, con eventuali messaggi di errore qualora i dati inseriti risultino errati.
![]() |
![]() |
2.8.2. Il progetto NetBeans
Il progetto NetBeans dell'applicazione è il seguente:
![]() |
Il progetto [mv-jsf2-06] si basa nuovamente su un'unica pagina [index.html] [1] e sul relativo modello [Form.java] [2]. Continua a utilizzare messaggi tratti da [messages.properties], ma esclusivamente in francese ([3]). Non è disponibile l'opzione per cambiare lingua.
2.8.3. L'ambiente dell'applicazione
Di seguito riportiamo il contenuto dei file che configurano l’applicazione senza fornire spiegazioni particolari. Questi file consentono di comprendere meglio quanto segue.
[faces-config.xml]
<?xml version='1.0' encoding='UTF-8'?>
<!-- =========== FULL CONFIGURATION FILE ================================== -->
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<application>
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
<message-bundle>messages</message-bundle>
</application>
</faces-config>
La riga 17 è nuova. Verrà spiegata in seguito.
Il file dei messaggi [messages_fr.properties]
form.titre=Jsf - validations et conversions
saisie1.prompt=1-Nombre entier de type int
saisie2.prompt=2-Nombre entier de type int
saisie3.prompt=3-Nombre entier de type int
data.required=Vous devez entrer une donn\u00e9e
integer.required=Vous devez entrer un nombre entier
saisie4.prompt=4-Nombre entier de type int dans l'intervalle [1,10]
saisie4.error=4-Vous devez entrer un nombre entier dans l'intervalle [1,10]
saisie5.prompt=5-Nombre r\u00e9el de type double
double.required=Vous devez entrer un nombre
saisie6.prompt=6-Nombre r\u00e9el>=0 de type double
saisie6.error=6-Vous devez entrer un nombre >=0
saisie7.prompt=7-Bool\u00e9en
saisie7.error=7-Vous devez entrer un bool\u00e9en
saisie8.prompt=8-Date au format jj/mm/aaaa
saisie8.error=8-Vous devez entrer une date valide au format jj/mm/aaaa
date.required=Vous devez entrer une date
saisie9.prompt=9-Cha\u00eene de 4 caract\u00e8res
saisie9.error=9-Vous devez entrer une cha\u00eene de 4 caract\u00e8res exactement
saisie9B.prompt=9B-Heure au format hh:mm
saisie9B.error=La cha\u00eene saisie ne respecte pas le format hh:mm
submit=Valider
cancel=Annuler
saisie.type=Type de la saisie
saisie.champ=Champ de saisie
saisie.erreur=Erreur de saisie
bean.valeur=Valeurs du mod\u00e8le du formulaire
saisie10.prompt=10-Nombre entier de type int <1 ou >7
saisie10.incorrecte=10-Saisie n\u00b0 10 incorrecte
saisie10.incorrecte_detail=10-Vous devez entrer un nombre entier <1 ou >7
saisies11et12.incorrectes=La propri\u00e9t\u00e9 saisie11+saisie12=10 n'est pas v\u00e9rifi\u00e9e
saisies11et12.incorrectes_detail=La propri\u00e9t\u00e9 saisie11+saisie12=10 n'est pas v\u00e9rifi\u00e9e
saisie11.prompt=11-Nombre entier de type int
saisie12.prompt=12-Nombre entier de type int
error.sign="!"
error.sign_detail="!"
Il foglio di stile [styles.css] è il seguente:
.info{
font-family: Arial,Helvetica,sans-serif;
font-size: 14px;
font-weight: bold
}
.col1{
background-color: #ccccff
}
.col2{
background-color: #ffcccc
}
.col3{
background-color: #ffcc66
}
.col4{
background-color: #ccffcc
}
.error{
color: #ff0000
}
.saisie{
background-color: #ffcccc;
border-color: #000000;
border-width: 5px;
color: #cc0033;
font-family: cursive;
font-size: 16px
}
.entete{
font-family: 'Times New Roman',Times,serif;
font-size: 14px;
font-weight: bold
}
2.8.4. La pagina [index.xhtml] e il relativo modello [Form.java]
La pagina [index.xhtml] è la seguente:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h2><h:outputText value="#{msg['form.titre']}"/></h2>
<h:form id="formulaire">
<h:messages globalOnly="true" />
<h:panelGrid columns="4" columnClasses="col1,col2,col3,col4" border="1">
<!-- riga 1 -->
<h:outputText value="#{msg['saisie.type']}" styleClass="entete"/>
<h:outputText value="#{msg['saisie.champ']}" styleClass="entete"/>
<h:outputText value="#{msg['saisie.erreur']}" styleClass="entete"/>
<h:outputText value="#{msg['bean.valeur']}" styleClass="entete"/>
<!-- riga 2 -->
<h:outputText value="#{msg['saisie1.prompt']}"/>
<h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
<h:message for="saisie1" styleClass="error"/>
<h:outputText value="#{form.saisie1}"/>
<!-- riga 3 -->
<h:outputText value="#{msg['saisie2.prompt']}" />
<h:inputText id="saisie2" value="#{form.saisie2}" styleClass="saisie"/>
<h:message for="saisie2" showSummary="true" showDetail="false" styleClass="error"/>
<h:outputText value="#{form.saisie2}"/>
<!-- riga 4 -->
<h:outputText value="#{msg['saisie3.prompt']}" />
<h:inputText id="saisie3" value="#{form.saisie3}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
<h:message for="saisie3" styleClass="error"/>
<h:outputText value="#{form.saisie3}"/>
<!-- riga 5 -->
<h:outputText value="#{msg['saisie4.prompt']}" />
<h:inputText id="saisie4" value="#{form.saisie4}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}" validatorMessage="#{msg['saisie4.error']}">
<f:validateLongRange minimum="1" maximum="10" />
</h:inputText>
<h:message for="saisie4" styleClass="error"/>
<h:outputText value="#{form.saisie4}"/>
<!-- riga 6 -->
...
<!-- riga 7 -->
...
<!-- riga 8 -->
...
<!-- riga 9 -->
...
<!-- riga 10 -->
...
<!-- riga 11 -->
...
<!-- riga 12 -->
...
<!-- riga 13 -->
...
</h:panelGrid>
<!-- pulsanti di comando -->
<h:panelGrid columns="2">
<h:commandButton value="#{msg['submit']}" action="#{form.submit}"/>
<h:commandButton value="#{msg['cancel']}" immediate="true" action="#{form.cancel}"/>
</h:panelGrid>
</h:form>
</h:body>
</html>
La novità principale è rappresentata dalla presenza dei tag:
- per visualizzare i messaggi di errore <h:messages> (riga 14), <h:message> (righe 24, 29, 34),
- che impongono vincoli di validità sugli inserimenti <f:validateLongRange> (riga 39), <f:validateDoubleRange>, <f:validateLength>, <f:validateRegex>,
- che definiscono un convertitore tra l'inserimento e il relativo modello come <f:convertDateTime>.
Il modello di questa pagina è la seguente classe [Form.java]:
package forms;
import com.corejsf.util.Messages;
import java.util.Date;
import javax.enterprise.context.RequestScoped;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.ValidatorException;
@ManagedBean
@RequestScoped
public class Form {
public Form() {
}
// immissioni
private Integer saisie1 = 0;
private Integer saisie2 = 0;
private Integer saisie3 = 0;
private Integer saisie4 = 0;
private Double saisie5 = 0.0;
private Double saisie6 = 0.0;
private Boolean saisie7 = true;
private Date saisie8 = new Date();
private String saisie9 = "";
private Integer saisie10 = 0;
private Integer saisie11 = 0;
private Integer saisie12 = 0;
private String errorSaisie11 = "";
private String errorSaisie12 = "";
// azioni
public String submit() {
...
}
public String cancel() {
...
}
// convalidatori
public void validateSaisie10(FacesContext context, UIComponent component, Object value) {
...
}
// getter e setter
...
}
La novità in questo caso è che i campi del modello non sono più solo di tipo String, ma di vari tipi.
2.8.5. I diversi campi del modulo
Esaminiamo ora in successione i diversi campi del modulo.
2.8.5.1. Campi da 1 a 4: inserimento di un numero intero
La pagina [index.xhtml] presenta il campo 1 nella forma seguente:
<!-- riga 2 -->
<h:outputText value="#{msg['saisie1.prompt']}"/>
<h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
<h:message for="saisie1" styleClass="error"/>
<h:outputText value="#{form.saisie1}"/>
Il modello form.saisie1 è definito come segue in [Form.java]:
private Integer saisie1 = 0;
Su un GET del browser, la pagina [index.xhtml] associata al suo modello [Form.java] produce visivamente quanto segue:
- la riga 2 produce [1],
- la riga 3 produce [2],
- la riga 4 produce [3],
- la riga 5 produce [4].
Supponiamo che venga inserito e poi confermato il seguente dato:
![]() |
Si ottiene quindi il seguente risultato nel modulo restituito dall'applicazione:
![]() |
- in [1], l'immissione errata,
- in [2], il messaggio di errore che lo segnala,
- in [3], si vede che il valore del campo Integer «saisie1» del modello non è cambiato.
Spieghiamo cosa è successo. A tal fine torniamo al ciclo di elaborazione di una pagina JSF:
![]() |
Esaminiamo questo ciclo per il componente:
<h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
e il relativo modello:
private Integer saisie1 = 0;
- in [A], la pagina [index.xhtml] inviata durante il GET del browser viene ripristinata. In [A], la pagina è tale e quale a come l’utente l’ha ricevuta. Il componente id="saisie1" riprende il suo valore iniziale “0”,
- in [B], i componenti della pagina ricevono come valori quelli inviati dal browser. In [B], la pagina è così come l’utente l’ha compilata e convalidata. Il componente id="saisie1" riceve come valore il valore inviato «x»,
- in [C], se la pagina contiene validatori e convertitori espliciti, questi vengono eseguiti. Vengono eseguiti anche i convertitori impliciti se il tipo del campo associato al componente non è di tipo String. È questo il caso in questione, in cui il campo form.saisie1 è di tipo Integer. JSF tenterà di convertire il valore "x" del componente id="saisie1" in un tipo Integer. Ciò provocherà un errore che interromperà il ciclo di elaborazione [A-F]. Questo errore sarà associato al componente id="saisie1". Tramite [D2], si passa quindi direttamente alla fase di rendering della risposta. Viene restituita la stessa pagina [index.xhtml],
- la fase [D] ha luogo solo se tutti i componenti di una pagina hanno superato la fase di conversione/convalida. È in questa fase che il valore del componente id="saisie1" verrà assegnato al suo modello form.saisie1.
Se la fase [C] fallisce, la pagina viene visualizzata nuovamente e il codice seguente viene eseguito di nuovo:
<!-- riga 2 -->
<h:outputText value="#{msg['saisie1.prompt']}"/>
<h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
<h:message for="saisie1" styleClass="error"/>
<h:outputText value="#{form.saisie1}"/>
![]() |
Il messaggio visualizzato in [2] proviene dalla riga 4 di [index.xhtml]. Il tag <h:message for="idComposant"/> visualizza il messaggio di errore associato al componente indicato dall'attributo for, in caso di errore. Il messaggio visualizzato in [2] è standard e si trova nel file [javax/faces/Messages.properties] dell'archivio [jsf-api.jar]:
![]() |
In [2] si nota che il file dei messaggi esiste in diverse varianti. Esaminiamo il contenuto di [Messages_fr.properties]:
Il file contiene messaggi suddivisi in categorie:
- errori su un componente, riga 3,
- errori di conversione tra un componente e il suo modello, riga 12
- errori di convalida quando nella pagina sono presenti validatori, riga 23.
L'errore verificatosi sul componente id="saisie1" è di tipo errore di conversione da un tipo String a un tipo Integer. Il messaggio di errore associato è quello della riga 18 del file dei messaggi.
javax.faces.converter.IntegerConverter.INTEGER_detail={2} : «{0}» doit être un nombre compris entre -2147483648 et 2147483647. Exemple : {1}
Il messaggio di errore visualizzato è riportato di seguito:
![]() |
Si nota che nel messaggio:
- il parametro {2} è stato sostituito dall'identificativo del componente per il quale si è verificato l'errore di conversione,
- il parametro {0} è stato sostituito dal valore inserito in [1] per il componente,
- il parametro {1} è stato sostituito dal numero 9346.
La maggior parte dei messaggi relativi ai componenti presenta due versioni: una versione sintetica (summary) e una versione dettagliata (detail). È il caso delle righe 16-18:
Il messaggio con chiave _detail (riga 2) è il cosiddetto messaggio dettagliato. L’altro è il cosiddetto messaggio sintetico. Il tag <h:message> visualizza per impostazione predefinita il messaggio dettagliato. Questo comportamento può essere modificato tramite gli attributi showSummary e showDetail. È ciò che viene fatto per il componente con id saisie2:
<!-- riga 3 -->
<h:outputText value="#{msg['saisie2.prompt']}" />
<h:inputText id="saisie2" value="#{form.saisie2}" styleClass="saisie"/>
<h:message for="saisie2" showSummary="true" showDetail="false" styleClass="error"/>
<h:outputText value="#{form.saisie2}"/>
Riga 2, il componente saisie2 è collegato al campo form.saisie2 successivo:
private Integer saisie2 = 0;
Il risultato ottenuto è il seguente:
![]() |
- in [1], il messaggio dettagliato; in [2], il messaggio di riepilogo.
Il tag <h:messages> visualizza sotto forma di elenco tutti i messaggi di errore riassuntivi di tutti i componenti, nonché i messaggi di errore non associati a un componente. Anche in questo caso, alcuni attributi possono modificare questo comportamento predefinito:
- showDetail: true / false per richiedere o meno i messaggi dettagliati,
- showSummary: true / false per richiedere o meno i messaggi sintetici,
- globalOnly: true / false per scegliere se visualizzare o meno solo i messaggi di errore non associati a componenti. Un messaggio di questo tipo potrebbe, ad esempio, essere creato dallo sviluppatore.
Il messaggio di errore associato a una conversione può essere modificato in vari modi. Innanzitutto, è possibile indicare all’applicazione di utilizzare un altro file di messaggi. Questa modifica va effettuata in [faces-config.xml]:
<faces-config ...">
<application>
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
<message-bundle>messages</message-bundle>
</application>
...
</faces-config>
Le righe 3-8 definiscono un file dei messaggi, ma non è questo il file utilizzato dai tag <h:message> e <h:messages>. Per definirlo, occorre utilizzare il tag <message-bundle> della riga 9. La riga 9 indica ai tag <h:message(s)> che il file [messages.properties] deve essere analizzato prima del file [javax.faces.Messages.properties]. Pertanto, se si aggiungono le seguenti righe al file [messages_fr.properties]:
# conversioni
javax.faces.converter.IntegerConverter.INTEGER=erreur
javax.faces.converter.IntegerConverter.INTEGER_detail=erreur d\u00e9taill\u00e9e
l’errore restituito per i componenti saisie1 e saisie2 diventa:

Un altro modo per modificare il messaggio di errore di conversione consiste nell’utilizzare l’attributo converterMessage del componente, come illustrato di seguito per il componente saisie3:
<!-- riga 4 -->
<h:outputText value="#{msg['saisie3.prompt']}" />
<h:inputText id="saisie3" value="#{form.saisie3}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
<h:message for="saisie3" styleClass="error"/>
<h:outputText value="#{form.saisie3}"/>
Il componente saisie3 è collegato al seguente campo form.saisie3:
private Integer saisie3 = 0;
- riga 3, l'attributo converterMessage specifica esplicitamente il messaggio da visualizzare in caso di errore di conversione,
- riga 3, l'attributo required="true" indica che l'inserimento è obbligatorio. Il campo non può rimanere vuoto. Un campo è considerato vuoto se non contiene alcun carattere o se contiene una sequenza di spazi. Anche in questo caso, in [javax.faces.Messages.properties] è presente un messaggio predefinito:
L'attributo requiredMessage consente di sostituire questo messaggio predefinito. Se il file [messages.properties] contiene i seguenti messaggi:
...
data.required=Vous devez entrer une donnée
integer.required=Vous devez entrer un nombre entier
si otterrà il seguente risultato:
![]() |
oppure questo:
![]() |
Verificare che un valore inserito corrisponda effettivamente a un numero intero non è sempre sufficiente. A volte è necessario verificare che il numero inserito rientri in un determinato intervallo. In tal caso si utilizza un validatore. L’inserimento n. 4 ne fornisce un esempio. Il suo codice in [index.xhtml] è il seguente:
<!-- riga 5 -->
<h:outputText value="#{msg['saisie4.prompt']}" />
<h:inputText id="saisie4" value="#{form.saisie4}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}" validatorMessage="#{msg['saisie4.error']}">
<f:validateLongRange minimum="1" maximum="10" />
</h:inputText>
<h:message for="saisie4" styleClass="error"/>
<h:outputText value="#{form.saisie4}"/>
Riga 3: il componente saisie4 è collegato al seguente modello form.saisie4:
private Integer saisie4 = 0;
Righe 3-5: il tag <h:inputText> ha un tag figlio <f:validateLongRange> che ammette due attributi facoltativi, minimum e maximum. Questo tag, chiamato anche validatore, consente di aggiungere un vincolo al valore immesso: esso deve essere non solo un numero intero, ma un numero intero compreso nell’intervallo [minimum, maximum] se sono presenti entrambi gli attributi minimum e maximum, maggiore o uguale a minimum se è presente solo l’attributo minimum, minore o uguale a maximum se è presente solo l’attributo maximum. Il validatore <f:validateLongRange> presenta messaggi di errore predefiniti in [javax.faces.Messages.properties]:
Anche in questo caso è possibile sostituire questi messaggi con altri. Esiste un attributo validatorMessage che consente di definire un messaggio specifico per il componente. Pertanto, con il seguente codice JSF:
<h:inputText id="saisie4" value="#{form.saisie4}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}" validatorMessage="#{msg['saisie4.error']}">
<f:validateLongRange minimum="1" maximum="10" />
</h:inputText>
e il seguente messaggio in [messages.properties]:
saisie4.error=4-Vous devez entrer un nombre entier dans l'intervalle [1,10]
si ottiene il seguente risultato:

2.8.5.2. Campi 5 e 6: inserimento di un numero reale
L'inserimento dei numeri reali segue regole simili a quelle per l'inserimento dei numeri interi. Il codice XHTML per i campi 5 e 6 è il seguente:
<!-- riga 6 -->
<h:outputText value="#{msg['saisie5.prompt']}" />
<h:inputText id="saisie5" value="#{form.saisie5}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['double.required']}"/>
<h:message for="saisie5" styleClass="error"/>
<h:outputText value="#{form.saisie5}"/>
<!-- riga 7 -->
<h:outputText value="#{msg['saisie6.prompt']}"/>
<h:inputText id="saisie6" value="#{form.saisie6}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['double.required']}" validatorMessage="#{msg['saisie6.error']}">
<f:validateDoubleRange minimum="0.0"/>
</h:inputText>
<h:message for="saisie6" styleClass="error"/>
<h:outputText value="#{form.saisie6}"/>
Gli elementi del modello [Form.java] collegati ai componenti saisie5 e saisie6:
private Double saisie5 = 0.0;
private Double saisie6 = 0.0;
Messaggi di errore associati ai convertitori e ai validatori dei componenti saisie5 e saisie6, in [messages.properties]:
double.required=Vous devez entrer un nombre
saisie6.error=6-Vous devez entrer un nombre >=0
Ecco un esempio di esecuzione:

2.8.5.3. Inserimento 7: inserimento di un valore booleano
L'inserimento di un valore booleano dovrebbe normalmente avvenire tramite una casella di controllo. Se viene effettuato tramite un campo di immissione, la stringa "true" viene convertita nel valore booleano true e qualsiasi altra stringa nel valore booleano false.
Il codice XHTML dell’esempio:
<!-- riga 8 -->
<h:outputText value="#{msg['saisie7.prompt']}"/>
<h:inputText id="saisie7" value="#{form.saisie7}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['double.required']}"/>
<h:message for="saisie7" styleClass="error"/>
<h:outputText value="#{form.saisie7}"/>
Il modello del componente saisie7:
private Boolean saisie7 = true;
Ecco un esempio di immissione dati e la relativa risposta:
![]() |
In [1], il valore inserito. Per conversione, questa stringa "x" diventa il valore booleano false. È ciò che mostra [2]. Il valore [3] del modello non è cambiato. Cambia solo quando tutte le conversioni e le convalide della pagina sono andate a buon fine. In questo esempio non è stato così.
2.8.5.4. Inserimento 8: inserimento di una data
L’inserimento di una data viene effettuato nell’esempio con il seguente codice XHTML:
<!-- riga 9 -->
<h:outputText value="#{msg['saisie8.prompt']}"/>
<h:inputText id="saisie8" value="#{form.saisie8}" styleClass="saisie" required="true" requiredMessage="#{msg['date.required']}" converterMessage="#{msg['saisie8.error']}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:inputText>
<h:message for="saisie8" styleClass="error"/>
<h:outputText value="#{form.saisie8}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:outputText>
Il componente saisie8 della riga 3 utilizza un convertitore java.lang.String <--> java.util.Date. Il modello form.saisie8 associato al componente saisie8 è il seguente:
private Date saisie8 = new Date();
Anche il componente definito dalle righe 7-9 utilizza un convertitore, ma solo nella direzione java.util.Date --> java.lang.String.
Il convertitore <f:convertDateTime> supporta diversi attributi, tra cui l’attributo pattern che definisce il formato della stringa da convertire in data o il formato con cui una data deve essere visualizzata.
Al momento della richiesta iniziale della pagina [index.xhtml], la riga 8 precedente viene visualizzata come segue:
I campi [1] e [2] visualizzano entrambi il valore del modello form.saisie8:
private Date saisie8 = new Date();
dove saisie8 assume come valore la data odierna. Il convertitore utilizzato in entrambi i casi per la visualizzazione della data è il seguente:
<f:convertDateTime pattern="dd/MM/yyyy"/>
dove dd (day) indica il numero del giorno, MM (Month) il numero del mese e yyyy (year) l’anno. In [1], il convertitore viene utilizzato per la conversione inversa java.lang.String --> java.util.Date. La data inserita dovrà quindi seguire il formato "dd/MM/yyyy" per essere valida.
In [javax.faces.Messages.properties] sono presenti messaggi predefiniti per le date non valide:
che è possibile sostituire con messaggi personalizzati. Ad esempio:
<h:inputText id="saisie8" value="#{form.saisie8}" styleClass="saisie" required="true" requiredMessage="#{msg['date.required']}" converterMessage="#{msg['saisie8.error']}">
<f:convertDateTime pattern="dd/MM/yyyy"/>
</h:inputText>
il messaggio visualizzato in caso di errore di conversione sarà il seguente messaggio con chiave saisie8.error:
saisie8.error=8-Vous devez entrer une date valide au format jj/mm/aaaa
Ecco un esempio:
![]()
2.8.5.5. Inserimento 9: inserimento di una stringa di lunghezza vincolata
L'esempio 9 mostra come imporre che una stringa inserita abbia un numero di caratteri compreso in un intervallo:
<!-- riga 10 -->
<h:outputText value="#{msg['saisie9.prompt']}"/>
<h:inputText id="saisie9" value="#{form.saisie9}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validatorMessage="#{msg['saisie9.error']}">
<f:validateLength minimum="4" maximum="4"/>
</h:inputText>
<h:message for="saisie9" styleClass="error"/>
<h:outputText value="#{form.saisie9}"/>
Riga 4: il validatore <f:validateLength minimum="4" maximum="4"/> impone che la stringa inserita contenga esattamente 4 caratteri. È possibile utilizzare solo uno dei due attributi: "minimum" per un numero minimo di caratteri, "maximum" per un numero massimo.
Il modello form.saisie9 del componente saisie9 della riga 3 è il seguente:
private String saisie9 = "";
Esistono messaggi di errore predefiniti per questo tipo di convalida:
che è possibile sostituire utilizzando l’attributo validatorMessage come nella riga 3 sopra riportata. Il messaggio relativo alla chiave saisie9.error è il seguente:
saisie9.error=9-Vous devez entrer une chaîne de 4 caractères exactement
Ecco un esempio di esecuzione:
![]()
2.8.5.6. Inserimento 9B: inserimento di una stringa che deve rispettare un modello
L'immissione 9B mostra come imporre che una stringa immessa abbia un numero di caratteri compreso in un intervallo:
<!-- riga 10B -->
<h:outputText value="#{msg['saisie9B.prompt']}"/>
<h:inputText id="saisie9B" value="#{form.saisie9B}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validatorMessage="#{msg['saisie9B.error']}">
<f:validateRegex pattern="^\s*\d{2}:\d{2}\s*$"/>
</h:inputText>
<h:message for="saisie9B" styleClass="error"/>
<h:outputText value="#{form.saisie9B}"/>
Riga 4, il validatore <f:validateRegex pattern="^\s*\d{2}:\d{2}\s*$"/> impone che la stringa inserita corrisponda al modello di un'espressione regolare, in questo caso: una sequenza di 0 o più spazi, 2 cifre, il segno :, 2 cifre, una sequenza di 0 o più spazi.
Il modello form.saisie9B del componente saisie9B della riga 3 è il seguente:
private String saisie9B;
Esistono messaggi di errore predefiniti per questo tipo di convalida:
che è possibile sostituire utilizzando l’attributo validatorMessage come nella riga 3 sopra riportata. Il messaggio relativo alla chiave saisie9.error è il seguente:
saisie9B.error=La cha\u00eene saisie ne respecte pas le format hh:mm
Ecco un esempio di esecuzione:
![]()
2.8.5.7. Esercizio 10: scrivere un metodo di convalida specifico
Riassumiamo: JSF consente di verificare, tra i valori inseriti, la validità dei numeri (interi, reali), delle date, la lunghezza delle stringhe e la conformità di un dato rispetto a un'espressione regolare. JSF consente di aggiungere ai validatori e ai convertitori esistenti i propri validatori e convertitori. Questo punto non viene trattato in questa sede, ma è possibile consultare [ref2] per approfondirlo.
Qui presentiamo un altro metodo: quello che consiste nel convalidare un dato inserito tramite un metodo del modello del modulo. Ecco l’esempio seguente:
<!-- riga 11 -->
<h:outputText value="#{msg['saisie10.prompt']}"/>
<h:inputText id="saisie10" value="#{form.saisie10}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validator="#{form.validateSaisie10}"/>
<h:message for="saisie10" styleClass="error"/>
<h:outputText value="#{form.saisie10}"/>
Il modello form.saisie10 associato al componente saisie10 della riga 3 è il seguente:
private Integer saisie10 = 0;
Si desidera che il numero inserito sia <1 o >7. Non è possibile verificarlo con i validatori di base di JSF. Si scrive quindi un proprio metodo di validazione del componente saisie10. Lo si indica con l’attributo validator del componente da validare:
<h:inputText id="saisie10" value="#{form.saisie10}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validator="#{form.validateSaisie10}"/>
Il componente saisie10 viene convalidato dal metodo form.validateSaisie10. Il codice del metodo è il seguente:
public void validateSaisie10(FacesContext context, UIComponent component, Object value) {
int saisie = (Integer) value;
if (!(saisie < 1 || saisie > 7)) {
FacesMessage message = Messages.getMessage(null, "saisie10.incorrecte", null);
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
}
La firma di un metodo di convalida deve necessariamente corrispondere a quella della riga 1:
- FacesContext context: contesto di esecuzione della pagina - fornisce accesso a varie informazioni, in particolare agli oggetti HttpServletRequest request e HttpServletResponse response,
- UIComponent component: il componente da convalidare. Il tag <h:inputText> è rappresentato da un componente di tipo UIInput derivato da UIComponent. In questo caso è proprio questo componente UIInput che viene ricevuto come secondo parametro,
- Valore dell'oggetto: il valore inserito da verificare, convertito nel tipo del modello. È importante comprendere che, se la conversione da String al tipo del modello non va a buon fine, il metodo di validazione non viene eseguito. Quando si arriva al metodo validateSaisie10, significa che la conversione da String a Integer è riuscita. Il terzo parametro è quindi di tipo Integer.
- riga 2: il valore inserito viene convertito nel tipo int,
- riga 3: si verifica che il valore immesso sia <1 o >7. Se è così, la convalida è terminata. In caso contrario, il validatore deve segnalare l’errore generando un’eccezione di tipo ValidatorException.
La classe ValidatorException dispone di due costruttori:
![]() |
- il costruttore [1] ha come parametro un messaggio di errore di tipo FacesMessage. Questo tipo di messaggio è quello visualizzato dai tag <h:messages> e <h:message>,
- il costruttore [2] consente inoltre di incapsulare la causa di tipo Throwable o derivata dall'errore.
Dobbiamo creare un messaggio di tipo FacesMessage. Questa classe dispone di diversi costruttori:
![]() |
Il costruttore [1] definisce le proprietà di un oggetto FacesMessage:
- FacesMessage.Severity severity: un livello di gravità tratto dalla seguente enumerazione: SEVERITY_ERROR, SEVERITY_FATAL, SEVERITY_INFO, SEVERITY_WARN,
- String summary: la versione sintetica del messaggio di errore, visualizzata dai tag <h:message showSummary="true"> e <h:messages>,
- Stringa di dettaglio: la versione dettagliata del messaggio di errore - viene visualizzata dai tag <h:message> e <h:messages showDetail="true">.
È possibile utilizzare qualsiasi costruttore; i parametri mancanti possono essere impostati in un secondo momento tramite i metodi set.
Il costruttore [1] non consente di specificare un messaggio contenuto in un file di messaggi internazionalizzato. È ovviamente un peccato. David Geary e Cay Horstmann colmano questa lacuna nel loro libro "Core JavaServer Faces" con la classe di utilità com.corejsf.util.Messages. È questa classe che viene utilizzata alla riga 4 del codice Java per creare il messaggio di errore. Contiene solo metodi statici, tra cui il metodo getMessage utilizzato alla riga 4:
public static FacesMessage getMessage(String bundleName, String resourceId, Object[] params)
Il metodo getMessage accetta tre parametri:
- String bundleName: il nome di un file di messaggi senza il suffisso .properties, ma con il nome del pacchetto. In questo caso, il nostro primo parametro potrebbe essere messages per indicare il file [messages.properties]. Prima di utilizzare il file indicato dal primo parametro, getMessage tenta di utilizzare il file dei messaggi dell’applicazione, se presente. Pertanto, se in [faces-config.xml] è stato dichiarato un file di messaggi con il tag:
<application>
...
<message-bundle>messages</message-bundle>
</application>
è possibile passare null come primo parametro al metodo getMessage. È ciò che è stato fatto in questo caso (cfr. [web.xm], pagina 120),
- Stringa resourceId: la chiave del messaggio da elaborare nel file dei messaggi. Abbiamo visto che un messaggio può avere sia una versione sintetica che una versione dettagliata. resourceId è l’identificativo della versione sintetica. La versione dettagliata verrà ricercata automaticamente con la chiave resourceId_detail. Avremo quindi due messaggi in [messages.properties] relativi all’errore nell’inserimento n. 10:
saisie10.incorrecte=10-Saisie n° 10 incorrecte
saisie10.incorrecte_detail=10-Vous devez entrer un nombre entier <1 ou >7
Il messaggio di tipo FacesMessage generato dal metodo Messages.getMessage include sia la versione sintetica che quella dettagliata, se sono state trovate. Entrambe le versioni devono essere presenti; in caso contrario, viene generata un'eccezione di tipo [NullPointerException],
- Object[] params: i parametri effettivi del messaggio se questo presenta parametri formali {0}, {1}, ... Tali parametri formali saranno sostituiti dagli elementi dell'array params.
Torniamo al codice del metodo di convalida del componente saisie10:
public void validateSaisie10(FacesContext context, UIComponent component, Object value) {
int saisie = (Integer) value;
if (!(saisie < 1 || saisie > 7)) {
FacesMessage message = Messages.getMessage(null, "saisie10.incorrecte", null);
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
}
- in [4], il messaggio di tipo FacesMessage viene creato utilizzando il metodo statico Messages.getMessage,
- in [5], si imposta il livello di gravità del messaggio,
- in [6], viene generata un'eccezione di tipo ValidatorException con il messaggio creato in precedenza. Il metodo di convalida è stato chiamato dal codice XHTML seguente:
<!-- riga 11 -->
<h:outputText value="#{msg['saisie10.prompt']}"/>
<h:inputText id="saisie10" value="#{form.saisie10}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" validator="#{form.validateSaisie10}"/>
<h:message for="saisie10" styleClass="error"/>
<h:outputText value="#{form.saisie10}"/>
Alla riga 3, il metodo di convalida viene eseguito per il componente con ID saisie10. Pertanto, il messaggio di errore generato dal metodo validateSaisie10 viene associato a questo componente e quindi visualizzato dalla riga 4 (attributo for="saisie10"). È la versione dettagliata che viene visualizzata per impostazione predefinita dal tag <h:message>.
Ecco un esempio di esecuzione:
![]()
2.8.5.8. Campi 11 e 12: convalida di un gruppo di componenti
Finora, i metodi di convalida incontrati convalidavano solo un singolo componente. Come procedere se la convalida desiderata riguarda più componenti? È ciò che vedremo ora. Nel modulo:

vogliamo che i campi 11 e 12 siano due numeri interi la cui somma sia pari a 10.
Il codice JSF sarà il seguente:
<!-- riga 12 -->
<h:outputText value="#{msg['saisie11.prompt']}"/>
<h:inputText id="saisie11" value="#{form.saisie11}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
<h:panelGroup>
<h:message for="saisie11" styleClass="error"/>
<h:outputText value="#{form.errorSaisie11}" styleClass="error"/>
</h:panelGroup>
<h:outputText value="#{form.saisie11}"/>
<!-- riga 13 -->
<h:outputText value="#{msg['saisie12.prompt']}"/>
<h:inputText id="saisie12" value="#{form.saisie12}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
<h:panelGroup>
<h:message for="saisie12" styleClass="error"/>
<h:outputText value="#{form.errorSaisie12}" styleClass="error"/>
</h:panelGroup>
<h:outputText value="#{form.saisie12}"/>
e il modello associato:
private Integer saisie11 = 0;
private Integer saisie12 = 0;
private String errorSaisie11 = "";
private String errorSaisie12 = "";
Alla riga 3 del codice JSF, si utilizzano le tecniche già illustrate per verificare che il valore inserito per il componente saisie11 sia effettivamente un numero intero. Lo stesso vale, alla riga 11, per il componente saisie12. Per verificare che saisie11 + saisie12 = 10, si potrebbe creare un validatore specifico. Questa è la soluzione preferibile. Anche in questo caso, per scoprirla si consulterà [ref2]. Qui seguiamo un altro approccio.
La pagina [index.xhtml] viene convalidata da un pulsante [Valider] il cui codice JSF è il seguente:
<!-- pulsanti di comando -->
<h:panelGrid columns="2">
<h:commandButton value="#{msg['submit']}" action="#{form.submit}"/>
...
</h:panelGrid>
dove il messaggio msg['submit'] è il seguente:
submit=Valider
Come si vede alla riga 3, il metodo form.submit verrà eseguito per gestire il clic sul pulsante [Valider]. Il codice è il seguente:
// azioni
public String submit() {
// ultime convalide
validateForm();
// si invia lo stesso modulo
return null;
}
// convalide globali
private void validateForm() {
if ((saisie11 + saisie12) != 10) {
...
}
È importante comprendere che quando viene eseguito il metodo submit:
- tutti i validatori e i convertitori del modulo sono stati eseguiti con esito positivo,
- i campi del modello [Form.java] hanno ricevuto i valori inviati dal client.
Infatti, torniamo al ciclo di elaborazione di un POST JSF:
![]() |
Il metodo submit è un gestore di eventi. Gestisce l’evento clic relativo al pulsante [Valider]. Come tutti i gestori di eventi, viene eseguito nella fase [E], una volta che tutti i validatori e i convertitori sono stati eseguiti con esito positivo ([C]) e il modello è stato aggiornato con i valori inviati ([D]). Non si tratta quindi più di generare eccezioni di tipo [ValidatorException] come abbiamo fatto in precedenza. Ci limiteremo a rinviare il modulo con i messaggi di errore:
![]() |
In [1] avviseremo l’utente, mentre in [2] e [3] inseriremo un indicatore di errore. Nel codice JSF, il messaggio [1] verrà ottenuto nel modo seguente:
<h:form id="formulaire">
<h:messages globalOnly="true" />
<h:panelGrid columns="4" columnClasses="col1,col2,col3,col4" border="1">
<!-- riga 1 -->
...
Nella riga 2, il tag <h:messages> visualizza per impostazione predefinita la versione sintetica dei messaggi di errore relativi a tutti i dati inseriti in modo errato nei componenti del modulo, nonché tutti i messaggi di errore non correlati ai componenti. L’attributo globalOnly="true" limita la visualizzazione a questi ultimi.
I messaggi [2] e [3] vengono visualizzati con semplici tag <h:outputText>:
<!-- riga 12 -->
<h:outputText value="#{msg['saisie11.prompt']}"/>
<h:inputText id="saisie11" value="#{form.saisie11}" styleClass="saisie" required="true" requiredMessage="#{msg['data.required']}" converterMessage="#{msg['integer.required']}"/>
<h:panelGroup>
<h:message for="saisie11" styleClass="error"/>
<h:outputText value="#{form.errorSaisie11}" styleClass="error"/>
</h:panelGroup>
<h:outputText value="#{form.saisie11}"/>
<!-- riga 13 -->
...
<h:outputText value="#{form.errorSaisie12}" styleClass="error"/>
...
Righe 4-7: il componente saisie11 presenta due possibili messaggi di errore:
- quello che indica una conversione errata o la mancanza di dati. Questo messaggio, generato dallo stesso JSF, sarà contenuto in un tipo FacesMessage e visualizzato dal tag <h:message> della riga 5,
- quello che genereremo se inserimento11 + inserimento12 non è uguale a 10. Verrà visualizzato dalla riga 6. Il messaggio di errore sarà contenuto nel modello form.errorSaisie11.
I due messaggi corrispondono a errori che non possono verificarsi contemporaneamente. La verifica «saisie11 + saisie12 = 10» viene effettuata nel metodo submit, che viene eseguito solo se nel modulo non sono presenti errori. Quando verrà eseguito, il componente saisie11 sarà già stato verificato e il suo modello form.saise11 avrà ricevuto il proprio valore. Il messaggio della riga 5 non potrà più essere visualizzato. Al contrario, se il messaggio della riga 5 viene visualizzato, significa che nel modulo è presente almeno un errore e il metodo submit non verrà eseguito. Il messaggio della riga 6 non verrà visualizzato. Affinché i due possibili messaggi di errore si trovino nella stessa colonna della tabella, sono stati raggruppati in un tag <h:panelGroup> (righe 4 e 7).
Il metodo submit è il seguente:
// azioni
public String submit() {
// ultime convalide
validateForm();
// viene inviato nuovamente lo stesso modulo
return null;
}
// convalide globali
private void validateForm() {
if ((saisie11 + saisie12) != 10) {
// messaggio globale
FacesMessage message = Messages.getMessage(null, "saisies11et12.incorrectes", null);
message.setSeverity(FacesMessage.SEVERITY_ERROR);
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, message);
// messaggi relativi ai campi
message = Messages.getMessage(null, "error.sign", null);
setErrorSaisie11(message.getSummary());
setErrorSaisie12(message.getSummary());
} else {
setErrorSaisie11("");
setErrorSaisie12("");
}
}
- riga 4: il metodo submit chiama il metodo validateForm per eseguire le ultime verifiche,
- riga 11: si verifica se saisie11+saisie12=10,
- se non è così, alle righe 13-14, si crea un messaggio di tipo FacesMessage con l’ID saisies11et12.incorrectes. Il messaggio è il seguente:
saisies11et12.incorrectes=La propriété saisie11+saisie12=10 n'est pas vérifiée
- il messaggio così creato viene aggiunto (righe 15-16) all’elenco dei messaggi di errore dell’applicazione. Questo messaggio non è associato a un componente specifico. Si tratta di un messaggio globale dell’applicazione. Verrà visualizzato tramite il tag <h:messages globalOnly="true"/> presentato in precedenza,
- riga 18: si crea un nuovo messaggio di tipo FacesMessage con l’ID error.sign. Il testo è il seguente:
error.sign="!"
Abbiamo detto che il metodo statico [Messages.getMessage] costruisce un messaggio di tipo FacesMessage con una versione sintetica e una versione dettagliata, se presenti. In questo caso, esiste solo la versione sintetica del messaggio error.sign. Si ottiene la versione sintetica di un messaggio m tramite m.getSummary(). Righe 19 e 20: la versione sintetica del messaggio error.sign viene inserita nei campi errorSaisie11 e errorSaisie12 del modello. Questi saranno visualizzati dai seguenti tag JSF:
<h:outputText value="#{form.saisie11}"/>
...
<h:outputText value="#{form.saisie12}"/>
- righe 22-23: se la proprietà saisie11+saisie12=10 è verificata, allora i due campi errorSaisie11 e errorSaisie12 del modello vengono svuotati in modo da cancellare un eventuale messaggio di errore precedente. È importante ricordare che il modello viene conservato tra una richiesta e l’altra, nella sessione del cliente.
Ecco un esempio di esecuzione:
![]() |
Si noti nella colonna [1] che il modello ha ricevuto i valori inviati, il che dimostra che tutte le operazioni di convalida e conversione tra i valori inviati e il modello sono andate a buon fine. Il gestore di eventi form.submit, che gestisce il clic sul pulsante [Valider], ha potuto così essere eseguito. È stato proprio questo gestore a generare i messaggi visualizzati in [2] e [3]. Si nota che il modello è stato aggiornato nonostante il modulo sia stato rifiutato e rinviato al cliente. In un caso del genere, si potrebbe preferire che il modello non venga aggiornato. Infatti, supponendo che l’utente annulli l’aggiornamento tramite il pulsante [Annuler] [4], non sarà possibile tornare al modello iniziale a meno che non sia stato salvato.
2.8.5.9. POST di un modulo senza verifica dei dati inseriti
Consideriamo il modulo sopra riportato e supponiamo che l’utente, non rendendosi conto dei propri errori, voglia interrompere la compilazione del modulo. A quel punto utilizzerà il pulsante [Annuler] generato dal seguente codice JSF:
<!-- pulsanti di comando -->
<h:panelGrid columns="2">
<h:commandButton value="#{msg['submit']}" action="#{form.submit}"/>
<h:commandButton value="#{msg['cancel']}" immediate="true" action="#{form.cancel}"/>
</h:panelGrid>
Riga 4, il messaggio msg['cancel'] è il seguente:
cancel=Annuler
Il metodo form.cancel associato al pulsante [Annuler] verrà eseguito solo se il modulo è valido. È quanto abbiamo illustrato per il metodo form.submit associato al pulsante [Valider]. Se l’utente desidera annullare l’inserimento dei dati nel modulo, è ovviamente inutile verificare la validità dei dati inseriti. Questo risultato si ottiene con l’attributo immediate="true", che indica a JSF di eseguire il metodo form.cancel senza passare attraverso la fase di convalida e conversione. Torniamo al ciclo di elaborazione di POST JSF:
![]() |
Gli eventi dei componenti di azione <h:commandButton> e <h:commandLink> con l'attributo immediate="true" vengono elaborati nella fase [C], dopodiché il ciclo JSF passa direttamente alla fase [E] di rendering della risposta.
Il metodo form.cancel è il seguente:
public String cancel() {
saisie1 = 0;
saisie2 = 0;
saisie3 = 0;
saisie4 = 0;
saisie5 = 0.0;
saisie6 = 0.0;
saisie7 = true;
saisie8 = new Date();
saisie9 = "";
saisie10 = 0;
return null;
}
Se si utilizza il pulsante [Annuler] nel modulo precedente, si ottiene in risposta la pagina seguente:
![]() |
- si ottiene nuovamente il modulo poiché il gestore di eventi form.cancel restituisce la chiave di navigazione null. Viene quindi restituita la pagina [index.xhtml],
- il modello [Form.java] è stato modificato dal metodo form.cancel. Ciò è indicato dalla colonna [2] che mostra questo modello,
- mentre la colonna [3] riflette il valore registrato per i componenti.
Torniamo al codice JSF del componente saisie1 [4];
<!-- riga 1 -->
<h:outputText value="#{msg['saisie1.prompt']}"/>
<h:inputText id="saisie1" value="#{form.saisie1}" styleClass="saisie"/>
<h:message for="saisie1" styleClass="error"/>
<h:outputText value="#{form.saisie1}"/>
Riga 4: il valore del componente saisie1 è collegato al modello form.saisie1. Ciò comporta diverse conseguenze:
- durante un GET da [index.xhtml], il componente saisie1 visualizzerà il valore del modello form.saisie1,
- in caso di un POST derivante da [index.xhtml], il valore inserito per il componente saisie1 viene assegnato al modello form.saisie1 solo se tutte le validazioni e le conversioni del modulo hanno esito positivo. Indipendentemente dal fatto che il modello sia stato aggiornato o meno dai valori inviati, se il modulo viene rinviato al termine del POST, i componenti visualizzano il valore che è stato inviato e non il valore del modello a loro associato. È quanto mostra la schermata sopra riportata, in cui le colonne [2] e [3] non presentano gli stessi valori.
2.9. Esempio mv-jsf2-07: eventi legati al cambiamento di stato dei componenti JSF
2.9.1. L'applicazione
L'applicazione mostra un esempio di POST realizzato senza l'ausilio di un pulsante o di un link. Il modulo è il seguente:
![]() |
Il contenuto dell'elenco combo2 [2] è collegato all'elemento selezionato nel combo1 [1]. Quando si modifica la selezione in [1], viene generato un POST del modulo, durante il quale il contenuto di combo2 viene modificato per riflettere l'elemento selezionato in [1], dopodiché il modulo viene restituito. Durante questo POST non viene eseguita alcuna convalida.
2.9.2. Il progetto NetBeans
Il progetto NetBeans dell’applicazione è il seguente:
![]() |
È presente un unico modulo [index.xhtml] con il relativo modello [Form.java].
2.9.3. L'ambiente dell'applicazione
Il file dei messaggi [messages_fr.properties]:
app.titre=intro-07
app.titre2=JSF - Listeners
combo1.prompt=combo1
combo2.prompt=combo2
saisie1.prompt=Nombre entier de type int
submit=Valider
raz=Raz
data.required=Donnée requise
integer.required=Entrez un nombre entier
saisie.type=Type de la saisie
saisie.champ=Champ de saisie
saisie.erreur=Erreur de saisie
bean.valeur=Valeurs du modèle du formulaire
Il foglio di stile [styles.css]:
.info{
font-family: Arial,Helvetica,sans-serif;
font-size: 14px;
font-weight: bold
}
.col1{
background-color: #ccccff
}
.col2{
background-color: #ffcccc
}
.col3{
background-color: #ffcc66
}
.col4{
background-color: #ccffcc
}
.error{
color: #ff0000
}
.saisie{
background-color: #ffcccc;
border-color: #000000;
border-width: 5px;
color: #cc0033;
font-family: cursive;
font-size: 16px
}
.combo{
color: green;
}
.entete{
font-family: 'Times New Roman',Times,serif;
font-size: 14px;
font-weight: bold
}
2.9.4. Il modulo [index.xhtml]
Il modulo [index.xhtml] è il seguente:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
...
</h:head>
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h2><h:outputText value="#{msg['app.titre2']}"/></h2>
<h:form id="formulaire">
<h:messages globalOnly="true"/>
<h:panelGrid columns="4" border="1" columnClasses="col1,col2,col3,col4">
<!-- intestazioni -->
<h:outputText value="#{msg['saisie.type']}" styleClass="entete"/>
<h:outputText value="#{msg['saisie.champ']}" styleClass="entete"/>
<h:outputText value="#{msg['saisie.erreur']}" styleClass="entete"/>
<h:outputText value="#{msg['bean.valeur']}" styleClass="entete"/>
<!-- riga 1 -->
<h:outputText value="#{msg['combo1.prompt']}"/>
<h:selectOneMenu id="combo1" value="#{form.combo1}" immediate="true" onchange="submit();" valueChangeListener="#{form.combo1ChangeListener}" styleClass="combo">
<f:selectItems value="#{form.combo1Items}"/>
</h:selectOneMenu>
<h:panelGroup></h:panelGroup>
<h:outputText value="#{form.combo1}"/>
<!-- riga 2 -->
<h:outputText value="#{msg['combo2.prompt']}"/>
<h:selectOneMenu id="combo2" value="#{form.combo2}" styleClass="combo">
<f:selectItems value="#{form.combo2Items}"/>
</h:selectOneMenu>
<h:panelGroup></h:panelGroup>
<h:outputText value="#{form.combo2}"/>
<!-- riga 3 -->
<h:outputText value="#{msg['saisie1.prompt']}"/>
<h:inputText id="saisie1" value="#{form.saisie1}" required="true" requiredMessage="#{msg['data.required']}" styleClass="saisie" converterMessage="#{msg['integer.required']}"/>
<h:message for="saisie1" styleClass="error"/>
<h:outputText value="#{form.saisie1}"/>
</h:panelGrid>
<!-- pulsanti di comando -->
<h:panelGrid columns="2" border="0">
<h:commandButton value="#{msg['submit']}"/>
...
</h:panelGrid>
</h:form>
</h:body>
</html>
La novità risiede nel codice dell'elenco combo1, righe 24-26. Compaiono nuovi attributi:
- onchange: attributo HTML - dichiara una funzione o del codice JavaScript che deve essere eseguito quando l'elemento selezionato in combo1 cambia. In questo caso, il codice JavaScript submit() invia il modulo al server,
- valueChangeListener: attributo JSF - dichiara il nome del metodo da eseguire sul lato server quando cambia l'elemento selezionato in combo1. In totale, vengono eseguiti due metodi: uno sul lato client e l'altro sul lato server,
- immediate=true: attributo JSF - stabilisce il momento in cui deve essere eseguito il gestore di eventi lato server: dopo che il modulo è stato ricostituito in base a quanto inserito dall’utente, ma prima dei controlli di validità dei dati inseriti. In questo caso, si desidera compilare l’elenco combo2 in base all’elemento selezionato nell’elenco combo1, anche se nel modulo potrebbero esserci dati inseriti in modo errato. Ecco un esempio:
![]() |
- in [1], un primo inserimento,
- in [2], si trasferisce l’elemento selezionato da combo1 da A a B.
Il risultato ottenuto è il seguente:
![]() |
Si è verificato l'evento POST. Il contenuto di combo2 e [2] è stato adattato all'elemento selezionato in combo1 [1], sebbene l'immissione in [3] fosse errata. È stato l’attributo immediate=true a far sì che il metodo form.combo1ChangeListener venisse eseguito prima dei controlli di validità. Senza questo attributo, non sarebbe stato eseguito poiché il ciclo di elaborazione si sarebbe interrotto ai controlli di validità a causa dell’errore in [3].
I messaggi associati al modulo in [messages.properties] sono i seguenti:
app.titre=intro-07
app.titre2=JSF - Listeners
combo1.prompt=combo1
combo2.prompt=combo2
saisie1.prompt=Nombre entier de type int
submit=Valider
raz=Raz
data.required=Donnée requise
integer.required=Entrez un nombre entier
saisie.type=Type de la saisie
saisie.champ=Champ de saisie
saisie.erreur=Erreur de saisie
bean.valeur=Valeurs du modèle du formulaire
La durata di [Form.java] è impostata su «request»:
package forms;
...
@ManagedBean
@RequestScoped
public class Form {
Alla riga 6, si imposta l’ambito del bean su «request».
2.9.5. Il modello [Form.java]
Il modello [Form.java] è il seguente:
package forms;
import java.util.logging.Logger;
import javax.enterprise.context.RequestScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.SelectItem;
@ManagedBean
@RequestScoped
public class Form {
public Form() {
}
// campi del modulo
private String combo1="A";
private String combo2="A1";
private Integer saisie1=0;
// campi di lavoro
final private String[] combo1Labels={"A","B","C"};
private String combo1Label="A";
private static final Logger logger=Logger.getLogger("forms.Form");
// metodi
public SelectItem[] getCombo1Items(){
// inizializza combo1
SelectItem[] combo1Items=new SelectItem[combo1Labels.length];
for(int i=0;i<combo1Labels.length;i++){
combo1Items[i]=new SelectItem(combo1Labels[i],combo1Labels[i]);
}
return combo1Items;
}
public SelectItem[] getCombo2Items(){
// inizializzazione combo2 in base a combo1
SelectItem[] combo2Items=new SelectItem[5];
for(int i=1;i<=combo2Items.length;i++){
combo2Items[i-1]=new SelectItem(combo1Label+i,combo1Label+i);
}
return combo2Items;
}
// listener
public void combo1ChangeListener(ValueChangeEvent event){
// monitoraggio
logger.info("combo1ChangeListener");
// si recupera il valore inviato da combo1
combo1Label=(String)event.getNewValue();
// si restituisce la risposta perché si desidera bypassare le convalide
FacesContext.getCurrentInstance().renderResponse();
}
public String raz(){
// seguito
logger.info("raz");
// azzeramento del modulo
combo1Label="A";
combo1="A";
combo2="A1";
saisie1=0;
return null;
}
// getter - setter
...
}
Colleghiamo il modulo [index.xhtml] al suo modello [Form.java]:
L'elenco combo1 viene generato dal seguente codice JSF:
<h:selectOneMenu id="combo1" value="#{form.combo1}" immediate="true" onchange="submit();" valueChangeListener="#{form.combo1ChangeListener}" styleClass="combo">
<f:selectItems value="#{form.combo1Items}"/>
</h:selectOneMenu>
Esso ottiene i propri elementi tramite il metodo getCombo1Items del proprio modello (riga 2). Quest’ultimo è definito alle righe 28-35 del codice Java. Genera un elenco di tre elementi {"A", "B", "C"}.
L'elenco combo2 viene generato dal seguente codice JSF:
<h:selectOneMenu id="combo2" value="#{form.combo2}" styleClass="combo">
<f:selectItems value="#{form.combo2Items}"/>
</h:selectOneMenu>
Ottiene i propri elementi tramite il metodo getCombo2Items del proprio modello (riga 2). Quest’ultimo è definito alle righe 37-44 del codice Java. Genera una lista di cinque elementi {"X1", "X2", "X3", "X4", "X5"}, dove X è l'elemento combo1Label della riga 16. Pertanto, al momento della generazione iniziale del modulo, l’elenco combo2 contiene gli elementi {"A1","A2","A3", "A4", "A5"}.
Quando l'utente modificherà l'elemento selezionato nell'elenco combo1,
- l’evento onchange="submit();" verrà elaborato dal browser client. Il modulo verrà quindi inviato al server,
- sul lato server, JSF rileverà che il componente combo1 ha cambiato valore. Verrà eseguito il metodo combo1ChangeListener delle righe 47-54. Un metodo di tipo ValueChangeListener riceve come parametro un oggetto di tipo javax.faces.event.ValueChangeEvent. Questo oggetto consente di ottenere il valore precedente e quello nuovo del componente il cui valore è cambiato tramite i seguenti metodi:

In questo caso il componente è la lista combo1 di tipo UISelectOne. Il suo valore è di tipo String.
- riga 51 del modello Java: il nuovo valore di combo1 viene memorizzato in combo1Label, che serve a generare gli elementi della lista combo2,
- riga 53: si restituisce la risposta. È importante ricordare che il gestore combo1ChangeListener viene eseguito con l'attributo immediate="true". Viene quindi eseguito dopo la fase in cui l'albero dei componenti della pagina è stato aggiornato con i valori inviati e prima del processo di convalida di tali valori. Tuttavia, si vuole evitare questo processo di convalida perché l’elenco combo2 deve essere aggiornato anche se nel modulo rimangono dei dati inseriti in modo errato. Si richiede quindi che la risposta venga inviata immediatamente senza passare attraverso la fase di convalida dei dati inseriti.
- Il modulo verrà rinviato così come è stato compilato. Tuttavia, gli elementi delle liste combo1 e combo2 non sono valori inseriti. Verranno generati nuovamente richiamando i metodi getCombo1Items e getCombo2Items. Quest’ultimo metodo utilizzerà quindi il nuovo valore di combo1Label impostato da combo1ChangeListener e gli elementi della lista combo2 cambieranno.
2.9.6. Il pulsante [Raz]
Con il pulsante [Raz] vogliamo riportare il modulo allo stato iniziale, come mostrato di seguito:
![]() |
![]() |
![]() |
In [1], il modulo prima del POST del pulsante [Raz]; in [2], il risultato del POST.
Sebbene semplice dal punto di vista funzionale, la gestione di questo caso d’uso risulta piuttosto complessa. Si possono provare diverse soluzioni, in particolare quella utilizzata per il pulsante [Annuler] dell’esempio precedente:
<h:commandButton value="#{msg['raz']}" immediate="true" action="#{form.raz}"/>
dove il metodo form.raz è il seguente:
public String raz(){
// azzeramento del modulo
combo1Label="A";
combo1="A";
combo2="A1";
saisie1=0;
return null;
}
Il risultato ottenuto dal pulsante [Raz] nell’esempio precedente è quindi il seguente:
![]() |
La colonna [1] mostra che il metodo form.raz è stato eseguito. Tuttavia, la colonna [1] continua a visualizzare i valori inviati:
- per combo1, il valore registrato era “B”. Questo elemento viene quindi selezionato nell’elenco,
- per combo2, il valore registrato era "B5". A seguito dell'esecuzione di form.raz, gli elementi {"B1", ..., "B5"} di combo2 sono stati modificati in {"A1", ..., "A5"}. L'elemento "B5" non esiste più e non può quindi essere selezionato. Viene quindi visualizzato il primo elemento dell'elenco,
- per saisie1, il valore inviato era 10.
Questo è il comportamento normale con l’attributo immediate="true". Per ottenere un risultato diverso, è necessario inviare i valori che si desidera visualizzare nel nuovo modulo, anche se l’utente ha inserito valori diversi. Ciò si ottiene con un po’ di codice JavaScript lato client. Il modulo diventa il seguente:
<script language="javascript">
function raz(){
document.forms['formulaire'].elements['formulaire:combo1'].value="A";
document.forms['formulaire'].elements['formulaire:combo2'].value="A1";
document.forms['formulaire'].elements['formulaire:saisie1'].value=0;
//document.forms['formulaire'].submit();
}
</script>
...
<h:commandButton value="#{msg['raz']}" onclick='raz()' immediate="true" action="#{form.raz}"/>
- riga 10, l’attributo onclick='raz()' indica di eseguire la funzione JavaScript raz quando l’utente clicca sul pulsante [Raz],
- riga 3: si assegna il valore "A" all'elemento HTML con nome 'form:combo1'. I diversi elementi della riga 3 sono i seguenti:
- documento: pagina visualizzata dal browser,
- document.forms: insieme dei moduli del documento,
- document.forms['formulaire']: il modulo con l'attributo name="formulaire",
- documents.forms['formulaire'].elements: insieme degli elementi del modulo che hanno l'attributo name="formulaire",
- document.forms['formulaire'].elements['formulaire:combo1']: elemento del modulo con l'attributo name="formulaire:combo1"
- document.forms['formulaire'].elements['formulaire:combo1'].value: valore che verrà inviato dall’elemento del modulo con l’attributo name="formulaire:combo1".
Per conoscere gli attributi name dei diversi elementi della pagina visualizzata dal browser, è possibile consultare il codice sorgente (di seguito con IE7):
![]() |
<form id="formulaire" name="formulaire" ...>
...
<select id="formulaire:combo1" name="formulaire:combo1" ...>
Detto questo, si può comprendere che nel codice JavaScript della funzione raz:
- la riga 3 fa sì che il valore inviato per il componente combo1 sia la stringa A,
- la riga 4 fa sì che il valore inviato al componente combo2 sia la stringa A1,
- la riga 5 fa sì che il valore inviato al componente saisie1 sia la stringa 0.
A questo punto, il POST del modulo, associato a qualsiasi pulsante di tipo <h:commandButton> (riga 10), verrà attivato. Verrà eseguito il metodo form.raz e il modulo verrà restituito così come è stato inviato. Si ottiene quindi il seguente risultato:
![]() |
Questo risultato nasconde molte cose. I valori "A", "A1", "0" dei componenti combo1, combo2, saisie1 vengono inviati al server. Supponiamo che il valore precedente di combo1 fosse “B”. In tal caso, si verifica una modifica del valore del componente combo1 e dovrebbe essere eseguito anche il metodo form.combo1ChangeListener. Abbiamo due gestori di eventi con l'attributo immediate="true". Verranno eseguiti entrambi? Se sì, in quale ordine? Solo uno? Se sì, quale?
Per saperne di più, creiamo dei log nell’applicazione:
package forms;
import java.util.logging.Logger;
...
public class Form {
...
// campi del modulo
private String combo1="A";
private String combo2="A1";
private Integer saisie1=0;
// campi di lavoro
final private String[] combo1Labels={"A","B","C"};
private String combo1Label="A";
private static final Logger logger=Logger.getLogger("forms.Form");
// listener
public void combo1ChangeListener(ValueChangeEvent event){
// monitoraggio
logger.info("combo1ChangeListener");
// si recupera il valore inviato tramite POST da combo1
combo1Label=(String)event.getNewValue();
// si restituisce la risposta perché si desidera bypassare le validazioni
FacesContext.getCurrentInstance().renderResponse();
}
public String raz(){
// seguito
logger.info("raz");
// azzeramento del modulo
combo1Label="A";
combo1="A";
combo2="A1";
saisie1=0;
return null;
}
...
}
- riga 16: viene creato un generatore di log. Il parametro di getLogger consente di distinguere le origini dei log. In questo caso, il generatore di log si chiama forms.Form,
- riga 21: viene registrato il passaggio nel metodo combo1ChangeListener,
- riga 30: viene registrato l'accesso al metodo raz.
Quali log vengono generati dal pulsante [Raz] o dalla modifica del valore di combo1? Consideriamo diversi casi:
- si utilizza il pulsante [Raz] mentre l’elemento selezionato in combo1 è “A”. “A” è quindi l’ultimo valore del componente combo1. Abbiamo visto che il pulsante [Raz] eseguiva una funzione JavaScript che inviava il valore "A" per il componente combo1. Il componente combo1 non cambia quindi valore. I log mostrano quindi che viene eseguito solo il metodo form.raz:
- si utilizza il pulsante [Raz] mentre l’elemento selezionato in combo1 non è “A”. Il componente combo1 cambia quindi valore: il suo ultimo valore non era “A” e il pulsante [Raz] gli invierà il valore “A”. I log mostrano quindi che vengono eseguiti due metodi. Nell’ordine: combo1ChangeListener, raz:
![]() |
- si modifica il valore di combo1 senza utilizzare il pulsante [Raz]. I log mostrano che viene eseguito solo il metodo combo1ChangeListener:
![]() |
2.10. Esempio mv-jsf2-08: il tag <h:dataTable>
2.10.1. L'applicazione
L'applicazione mostra un elenco di persone con la possibilità di eliminarle:
![]() |
- in [1], un elenco di persone,
- in [2], i link che consentono di eliminarle.
2.10.2. Il progetto NetBeans
Il progetto NetBeans dell'applicazione è il seguente:
![]() |
È presente un unico modulo [index.xhtml] con il relativo modello [Form.java].
2.10.3. L'ambiente dell'applicazione
Il file di configurazione [faces-config.xml]:
<?xml version='1.0' encoding='UTF-8'?>
<!-- =========== FULL CONFIGURATION FILE ================================== -->
<faces-config version="2.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd">
<application>
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
<message-bundle>messages</message-bundle>
</application>
</faces-config>
Il file dei messaggi [messages_fr.properties]:
app.titre=intro-08
app.titre2=JSF - DataTable
submit=Valider
personnes.headers.id=Id
personnes.headers.nom=Nom
personnes.headers.prenom=Pr\u00e9nom
Il foglio di stile [styles.css]:
.headers {
text-align: center;
font-style: italic;
color: Snow;
background: Teal;
}
.id {
height: 25px;
text-align: center;
background: MediumTurquoise;
}
.nom {
text-align: left;
background: PowderBlue;
}
.prenom {
width: 6em;
text-align: left;
color: Black;
background: MediumTurquoise;
}
2.10.4. Il modulo [index.xhtml] e il relativo modello [Form.java]
Ricordiamo la vista associata alla pagina [index.xhtml]:
![]() |
Il modulo [index.xhtml] è il seguente:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h2><h:outputText value="#{msg['app.titre2']}"/></h2>
<h:form id="formulaire">
<h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
........................
</h:dataTable>
</h:form>
</h:body>
</html>
Alla riga 14, il tag <h:dataTable> utilizza il campo #{form.personnes} come fonte di dati. È il seguente:
private List<Persona> persone;
La classe [Personne] è la seguente:
package forms;
public class Personne {
// dati
private int id;
private String nom;
private String prénom;
// costruttori
public Personne(){
}
public Personne(int id, String nom, String prénom){
this.id=id;
this.nom=nom;
this.prénom=prénom;
}
// toString
public String toString(){
return String.format("Personne[%d,%s,%s]", id,nom,prénom);
}
// getter e setter
...
}
Torniamo al contenuto del tag <h:dataTable>:
<h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
...
</h:dataTable>
- l'attributo var="personne" definisce il nome della variabile che rappresenta la persona corrente all'interno del tag <h:datatable>,
- l'attributo headerClass="headers" definisce lo stile delle intestazioni delle colonne della tabella,
- l'attributo columnClasses="...." definisce lo stile di ciascuna colonna della tabella.
Esaminiamo una delle colonne della tabella e vediamo come è strutturata:
![]() |
Il codice XHTML della colonna Id è il seguente:
<h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['personnes.headers.id']}"/>
</f:facet>
<h:outputText value="#{personne.id}"/>
</h:column>
...
</h:dataTable>
lignes 3-5 : la balise <f:facet name="header"> définit le titre de la colonne,
ligne 4 : le titre de la colonne est pris dans le fichier des messages,
ligne 6 : personne fait référence à l'attribut var de la balise <h:dataTable ...> (ligne 1). On écrit donc l'id de la personne courante.
<h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['personnes.headers.id']}"/>
</f:facet>
<h:outputText value="#{personne.id}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['personnes.headers.nom']}"/>
</f:facet>
<h:outputText value="#{personne.nom}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['personnes.headers.prenom']}"/>
</f:facet>
<h:outputText value="#{personne.prénom}"/>
</h:column>
...
</h:dataTable>
- righe 3-7: la colonna "id" della tabella,
- righe 8-13: la colonna «cognome» della tabella,
- righe 14-19: la colonna «nome» della tabella.
Ora esaminiamo la colonna dei link [Retirer]:
![]() |
Questa colonna è generata dal seguente codice:
<h:dataTable value="#{form.personnes}" var="personne" headerClass="headers" columnClasses="id,nom,prenom">
...
<h:column>
<h:commandLink value="Retirer" action="#{form.retirerPersonne}">
<f:setPropertyActionListener target="#{form.personneId}" value="#{personne.id}"/>
</h:commandLink>
</h:column>
</h:dataTable>
Il link [Retirer] è generato dalle righe 4-6. Quando si clicca sul link, verrà eseguito il metodo [Form].retirerPersonne. È ora di esaminare la classe [Form.java]:
package forms;
import java.util.ArrayList;
import java.util.List;
import javax.enterprise.context.RequestScoped;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
@ManagedBean
@SessionScoped
public class Form {
// modello
private List<Personne> personnes;
private int personneId;
// costruttore
public Form() {
// inizializzazione dell'elenco delle persone
personnes = new ArrayList<Personne>();
personnes.add(new Personne(1, "dupont", "jacques"));
personnes.add(new Personne(2, "durand", "élise"));
personnes.add(new Personne(3, "martin", "jacqueline"));
}
public String retirerPersonne() {
// si cerca la persona selezionata
int i = 0;
for (Personne personne : personnes) {
// persona corrente = persona selezionata?
if (personne.getId() == personneId) {
// si elimina la persona corrente dall'elenco
personnes.remove(i);
// operazione completata
break;
} else {
// persona successiva
i++;
}
}
// si esegue il test sulla stessa pagina
return null;
}
// getter e setter
...
}
- righe 18-24: il costruttore inizializza l’elenco delle persone della riga 14,
- riga 10: poiché questo elenco deve persistere nel corso delle richieste, l’ambito del bean è la sessione.
Quando viene eseguito il metodo [retirerPersonne] alla riga 26, il campo alla riga 15 è stato inizializzato con l’ID della persona il cui link [Retirer] è stato cliccato:
<h:commandLink value="Retirer" action="#{form.retirerPersonne}">
<f:setPropertyActionListener target="#{form.personneId}" value="#{personne.id}"/>
</h:commandLink>
Il tag <f:setPropertyActionListener> consente di trasferire informazioni al modello. In questo caso, il valore dell’attributo value viene copiato nel campo del modello identificato dall’attributo target. Pertanto, l’ID della persona corrente, quella che deve essere rimossa dall’elenco delle persone, viene copiato nel campo [Form].personneId tramite il getter di tale campo. Ciò avviene prima dell’esecuzione del metodo a cui fa riferimento l’attributo action della riga 1.
Righe 26-43: il metodo [supprimerPersonne] elimina la persona il cui id è uguale a personneId.
2.11. Esempio e mv-jsf2-09: impaginazione di un'applicazione JSF
2.11.1. L'applicazione
L'applicazione mostra come impaginare un'applicazione JSF con due viste:
![]() |
L'applicazione presenta due viste:
- in [1], la pagina 1,
- in [2], la pagina 2.
È possibile navigare tra le due pagine. Ciò che vogliamo mostrare qui è che le pagine 1 e 2 condividono una formattazione comune, come si vede nelle schermate sopra riportate.
2.11.2. Il progetto NetBeans
Il progetto NetBeans dell'applicazione è il seguente:
![]() |
L'applicazione contiene solo pagine XHTML. Non è presente alcun modello Java associato.
2.11.3. La pagina [layout.xhtml]
La pagina [layout.xhtml] definisce il layout delle pagine dell'applicazione:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<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:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h:form id="formulaire">
<table style="width: 400px">
<tr>
<td colspan="2" bgcolor="#ccccff">
<ui:include src="entete.xhtml"/>
</td>
</tr>
<tr style="height: 200px">
<td bgcolor="#ffcccc">
<ui:include src="menu.xhtml"/>
</td>
<td>
<ui:insert name="contenu" >
<h2>Contenu</h2>
</ui:insert>
</td>
</tr>
<tr bgcolor="#ffcc66">
<td colspan="2">
<ui:include src="basdepage.xhtml"/>
</td>
</tr>
</table>
</h:form>
</h:body>
</html>
Alla riga 7 compare un nuovo spazio dei nomi ui. Questo spazio dei nomi contiene i tag che consentono di formattare le pagine di un'applicazione. I tag di questo spazio vengono utilizzati alle righe 17, 22, 25 e 32.
La pagina [layout.xhtml] visualizza le informazioni in una tabella HTML (riga 14). È possibile richiamare questa pagina con un browser:
![]() |
- in [1], la pagina URL richiesta.
Il campo [2] è stato generato dal seguente codice XHTML:
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h:form id="formulaire">
<table style="width: 400px">
<tr>
<td colspan="2" bgcolor="#ccccff">
<ui:include src="entete.xhtml"/>
</td>
</tr>
...
</table>
</h:form>
</h:body>
Il tag <ui:include> alla riga 6 consente di includere nella pagina un codice XHTML esterno. Il file [entete.xhtml] è il seguente:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<body>
<h2>entête</h2>
</body>
</html>
Tutto il codice delle righe da 3 a 8 verrà inserito in [layout.xhtml]. Pertanto, i tag <html> e <body> verranno inseriti all’interno di un tag <td>. Ciò non provoca errori. Pertanto, le pagine inserite tramite <ui:include> sono pagine XHTML complete. Da un punto di vista visivo, solo la riga 6 avrà un effetto. I tag <html> e <body> sono presenti per motivi sintattici.
L'area [3] è stata generata dal seguente codice XHTML:
<h:form id="formulaire">
<table style="width: 400px">
<tr style="height: 200px">
<td bgcolor="#ffcccc">
<ui:include src="menu.xhtml"/>
</td>
...
</tr>
...
</table>
</h:form>
Il tag <ui:include> della riga 5 include il seguente file [menu.xhtml]:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<body>
<h2>menu</h2>
</body>
</html>
L'area [4] è stata generata dal seguente codice XHTML:
<h:form id="formulaire">
<table style="width: 400px">
...
<tr bgcolor="#ffcc66">
<td colspan="2">
<ui:include src="basdepage.xhtml"/>
</td>
</tr>
</table>
</h:form>
Il tag <ui:include> alla riga 6 include il seguente file [basdepage.xhtml]:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://java.sun.com/jsf/html">
<body>
<h2>bas de page</h2>
</body>
</html>
L'area [5] è stata generata dal seguente codice XHTML:
<h:form id="formulaire">
...
<td>
<ui:insert name="contenu" >
<h2>Contenu</h2>
</ui:insert>
</td>
...
</table>
</h:form>
Il tag <ui:insert> alla riga 5 definisce un'area denominata «contenuto». Si tratta di un'area che può contenere contenuti variabili. Vedremo come. Quando abbiamo richiesto la pagina [layout.xhtml], non era stato definito alcun contenuto per l’area denominata «contenuto». In questo caso, viene utilizzato il contenuto del tag <ui:insert> delle righe 4-6. Viene quindi visualizzata la riga 5.
2.11.4. La pagina [page1.xhtml]
La pagina [layout.xhtml] non è destinata alla visualizzazione. Funge da modello per le pagine [page1.xhtml] e [page2.xhtml]. Si parla di modello di pagina. La pagina [page1.xhtml] è la seguente:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<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:ui="http://java.sun.com/jsf/facelets">
<ui:composition template="layout.xhtml">
<ui:define name="contenu">
<h2>page 1</h2>
<h:commandLink value="page 2" action="page2"/>
</ui:define>
</ui:composition>
</html>
- alla riga 6 si utilizza lo spazio dei nomi ui,
- alla riga 7 si indica che la pagina è associata al modello [layout.xhtml] tramite un tag <ui:composition>,
- alla riga 8, questa associazione fa sì che ogni tag <ui:define> venga associato a un tag <ui:insert> del template utilizzato, in questo caso [layout.xhtml]. Il collegamento avviene tramite l’attributo name di entrambi i tag, che devono essere identici.
La pagina visualizzata è [layout.xhtml], dove il contenuto di ogni tag <ui:insert> viene sostituito dal contenuto del tag <ui:define> della pagina richiesta. In questo caso, è come se la pagina visualizzata fosse:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<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:ui="http://java.sun.com/jsf/facelets">
<h:head>
<title>JSF</title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h:form id="formulaire">
<table style="width: 400px">
<tr>
<td colspan="2" bgcolor="#ccccff">
<ui:include src="entete.xhtml"/>
</td>
</tr>
<tr style="height: 200px">
<td bgcolor="#ffcccc">
<ui:include src="menu.xhtml"/>
</td>
<td>
<h2>page 1</h2>
<h:commandLink value="page 2" action="page2"/>
</td>
</tr>
<tr bgcolor="#ffcc66">
<td colspan="2">
<ui:include src="basdepage.xhtml"/>
</td>
</tr>
</table>
</h:form>
</h:body>
</html>
Le righe 25-26 di [page1.xhtml] sono state inserite al posto del tag <ui:insert> di [layout.xml].
La pagina [page2.xhtml] è analoga a [page1.xhtml]:
<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<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:ui="http://java.sun.com/jsf/facelets">
<ui:composition template="layout.xhtml">
<ui:define name="contenu">
<h2>page 2</h2>
<h:commandLink value="page 1" action="page1"/>
</ui:define>
</ui:composition>
</html>
2.12. Conclusion
L'analisi appena effettuata su JSF 2 è ben lungi dall'essere esaustiva. Tuttavia, è sufficiente per comprendere gli esempi che seguiranno. Per approfondire, si rimanda a [ref2].
2.13. I test con Eclipse
Vediamo come eseguire i test dei progetti Maven con la SpringSource Tool Suite:
![]() |
- in [1], si importa un progetto Maven [2] che si seleziona tramite il pulsante [3]. In questo caso si utilizza il progetto Maven [mv-jsf2-09] per Eclipse
- in [4], il progetto importato è stato correttamente riconosciuto come un progetto Maven [5],
![]() |
- in [6]; il progetto è stato importato nell’esploratore dei progetti,
- in [7], viene eseguito su un server [8] Tomcat [9],
![]() |
- in [10], è stato avviato Tomcat 7,
- in [11], la pagina iniziale del progetto [mv-jsf2-09] [11] viene visualizzata in un browser interno a Eclipse.

























































































































































