2. Java Server Faces
Introdurremo ora il framework Java Server Faces. Verrà utilizzata la versione 2, ma gli esempi illustrano principalmente le funzionalità della versione 1. Tratteremo solo le funzionalità della versione 2 necessarie per l'applicazione di esempio che segue.
2.1. Il ruolo di JSF in un'applicazione web
Per prima cosa, collochiamo JSF nel contesto dello sviluppo di un'applicazione web. Molto spesso, essa sarà costruita su un'architettura a più livelli come la seguente:
![]() |
- Il livello [web] è il livello a contatto con l'utente dell'applicazione web. L'utente interagisce con l'applicazione web attraverso le pagine web visualizzate da un browser. È in questo livello che si trova JSF, e solo in questo livello;
- Il livello [business] implementa le regole di business 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 DBMS 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). Agisce come ponte tra gli oggetti gestiti dal livello [DAO] e le righe e le colonne di dati in un database relazionale;
- L'integrazione di questi livelli può essere ottenuta utilizzando un contenitore Spring o EJB3 (Enterprise JavaBeans).
Gli esempi forniti di seguito per illustrare JSF utilizzeranno un solo livello, 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 architettonico MVC (Model–View–Controller) come segue:
![]() |
Questa architettura implementa il modello di progettazione MVC (Model, View, Controller). L'elaborazione di una richiesta del client segue questi quattro passaggi:
- richiesta – il browser del client invia una richiesta al controller [Faces Servlet]. Il controller gestisce tutte le richieste dei client. È il punto di ingresso dell’applicazione. Questo è il C in MVC,
- elaborazione – il controller C elabora questa richiesta. Per farlo, è assistito da gestori di eventi specifici dell’applicazione [2a]. Questi gestori possono richiedere l’assistenza del livello business [2b]. Una volta elaborata la richiesta del client, questa può innescare varie risposte. Un esempio classico è:
- una pagina di errore se la richiesta non è stata elaborata correttamente;
- una pagina di conferma in caso contrario,
- Navigazione - Il controller seleziona la risposta (= vista) da inviare al cliente. La selezione della risposta da inviare al cliente comporta diversi passaggi:
- selezionare il Facelet che genererà la risposta. Questa è chiamata vista V, la V in MVC. Questa scelta dipende generalmente dal risultato dell'esecuzione dell'azione richiesta dall'utente;
- fornire a questo Facelet i dati necessari per generare questa risposta. Infatti, questa risposta contiene molto spesso informazioni calcolate dal controller. Queste informazioni costituiscono ciò che viene chiamato il modello M della vista, la M in MVC,
Il passaggio 3 consiste quindi nel selezionare una vista V e costruire il modello M necessario per essa.
- Risposta - il controller C istruisce il Facelet selezionato a renderizzarsi. Il Facelet utilizza il modello M preparato dal controller C per inizializzare le parti dinamiche della risposta che deve inviare al client. La forma esatta di questa risposta può variare: può essere un flusso HTML, un PDF, un Excel, ecc.
In un progetto JSF:
- il controller C è il servlet [javax.faces.webapp.FacesServlet]. 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 chiamate "backing beans" o semplicemente beans.
Ora, chiariamo la relazione tra l'architettura web MVC e l'architettura a livelli. Si tratta di due concetti diversi che a volte vengono confusi. Consideriamo un'applicazione web JSF a singolo livello:
![]() |
Se implementiamo il livello [web] con JSF, avremo effettivamente un'architettura web MVC ma non un'architettura a più livelli. In questo caso, il livello [web] gestirà tutto: presentazione, logica di business e 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 un framework e senza seguire il modello MVC. Abbiamo quindi un'architettura a più livelli, ma il livello web non implementa il modello MVC.
In MVC, abbiamo detto che il modello M è quello della vista V, ovvero l'insieme dei dati visualizzati dalla vista V. Spesso viene fornita un'altra definizione del modello M in MVC:
![]() |
Molti autori ritengono che ciò che si trova a destra del livello [web] costituisca il modello M dell'MVC. Per evitare ambiguità, faremo riferimento a:
- il modello di dominio quando ci riferiamo a tutto ciò che si trova a destra del livello [web],
- il modello di vista quando ci riferiamo ai dati visualizzati da una vista V.
D'ora in poi, il termine "modello M" si riferirà esclusivamente al modello di una vista V.
2.3. Esempio mv-jsf2-01: i componenti di un progetto JSF
I primi esempi si limiteranno al livello web implementato con JSF 2:
![]() |
Una volta trattate le nozioni di base, studieremo esempi più complessi con architetture multilivello.
2.3.1. Generazione del progetto
Generiamo il nostro primo progetto JSF2 utilizzando NetBeans 7.
![]() |
- In [1], creare un nuovo progetto,
- in [2], selezionare la categoria [Maven] e il tipo di progetto [Web Application],
![]() |
- in [3], specificare la cartella principale per il nuovo progetto,
- in [4], assegnare un nome al progetto,
- in [5], scegli un server. Con NetBeans 7, puoi scegliere tra i server Apache Tomcat e GlassFish. La differenza tra i due è che GlassFish supporta gli EJB (Enterprise Java Beans) mentre Tomcat no. I nostri esempi JSF non useranno gli EJB. Quindi qui puoi scegliere uno dei due server,
- in [6], selezionare la versione Web Java EE 6,
- in [7], il progetto generato.
Esaminiamo gli elementi del progetto e spieghiamo il ruolo di ciascuno.
![]() |
- In [1]: i diversi rami del progetto:
- [Pagine Web]: conterrà le pagine web (.xhtml, .jsp, .html), le risorse (immagini, vari documenti), la configurazione del livello web e la configurazione del framework JSF;
- [Pacchetti sorgente]: le classi Java del progetto;
- [Dipendenze]: gli archivi .jar richiesti dal progetto e gestiti dal framework Maven;
- [Dipendenze Java]: gli archivi .jar richiesti dal progetto e non gestiti dal framework Maven;
- [File di progetto]: file di configurazione di Maven e NetBeans,
![]() |
- in [2]: il ramo [Pagine Web],
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>
Questa è una pagina web che visualizza la stringa "Ciao mondo" 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 del tipo 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 abbiamo classi. NetBeans ha generato un pacchetto predefinito che può essere eliminato [4].
![]() |
- in [5], il ramo [Dependencies],
Questo ramo mostra tutte le librerie richieste dal progetto e gestite da Maven. Tutte le librerie elencate qui verranno scaricate automaticamente da Maven. Questo è il motivo per cui un progetto Maven richiede l'accesso a Internet. Le librerie scaricate verranno memorizzate localmente. Se un altro progetto necessita di una libreria già presente localmente, questa non verrà scaricata. Vedremo che questo elenco di librerie, così come i repository in cui si trovano, sono definiti nel file di configurazione del progetto Maven.
![]() |
- in [6], le librerie richieste dal 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 esso.
- [pom.xml]: il file di configurazione di Maven. POM sta per Project Object Model. A volte potrebbe essere necessario modificare direttamente 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'artefatto Java che verrà creato dal progetto Maven. Queste informazioni provengono dalla procedura guidata utilizzata durante la creazione del progetto:
![]() |
Un artefatto Maven è definito da quattro proprietà:
- [groupId]: un'informazione che assomiglia al nome di un pacchetto. Ad esempio, le librerie del framework Spring hanno groupId=org.springframework, mentre quelle del framework JSF hanno groupId=javax.faces,
- [artifactId]: il nome dell'artefatto Maven. Nel gruppo [org.springframework] troviamo i seguenti artifactID: spring-context, spring-core, spring-beans, ... Nel gruppo [javax.faces] troviamo l'artifactId jsf-api,
- [version]: il numero di versione dell'artefatto Maven. Pertanto, l'artefatto org.springframework.spring-core ha le seguenti versioni: 2.5.4, 2.5.5, 2.5.6, 2.5.6.SECO1, ...
- [packaging]: il formato dell'artefatto, più comunemente 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 identificare in modo univoco un artefatto Maven.
Le righe 17–24 elencano le dipendenze del progetto Maven, ovvero l'elenco delle librerie richieste dal progetto. Ogni libreria è definita dalle quattro informazioni (groupId, artifactId, version, packaging). Quando le informazioni sul packaging mancano, come in questo caso, viene utilizzato il packaging jar. Viene aggiunta un'altra informazione: scope, che specifica in quali fasi del ciclo di vita del progetto è necessaria la libreria. Il valore predefinito è compile, che indica che la libreria è richiesta sia per la compilazione che per l'esecuzione. Il valore provided significa che la libreria è richiesta durante la compilazione ma non durante l'esecuzione. Qui, in fase di esecuzione, sarà fornita dal server Tomcat 7.
2.3.2. Esecuzione del progetto
Eseguiamo il progetto:
![]() |
In [1], viene eseguito il progetto Maven. Se non è già in esecuzione, viene avviato il server Tomcat. Viene inoltre avviato un browser e viene richiesto l'URL del contesto del progetto [2]. Poiché non viene richiesto alcun documento, se presente, viene utilizzata la pagina index.html, index.jsp o index.xhtml. In questo caso, sarà la pagina [index.jsp].
2.3.3. Il file system di un progetto Maven
![]() |
- [1]: Il file system 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 dalla compilazione del progetto,
- [5]: Qui, la compilazione del progetto ha creato un archivio [mv-jsf2-01-1.0-SNAPSHOT.war]. Questo è l'archivio che è stato eseguito dal server Tomcat.
2.3.4. Configurazione di un progetto per JSF
Il nostro progetto attuale non è un progetto JSF. Mancano le librerie del framework JSF. Per trasformare il progetto attuale in un progetto JSF, procedere come segue:
![]() |
- In [1], accedere alle proprietà del progetto,
- in [2], selezionare la categoria [Frameworks],
- in [3], aggiungi un framework,
![]() |
- in [4], selezionare Java Server Faces,
- In [5], NetBeans propone la versione 2.1 del framework. Accettarla,
- 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. Il Central Repository è utilizzato per impostazione predefinita. È possibile aggiungere ulteriori repository utilizzando il tag <repository>. Qui 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 con 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). Contiene tag HTML, ma in formato XML. Questo si chiama XHTML. La tecnologia utilizzata per creare pagine web con JSF 2 si chiama Facelets. Pertanto, la pagina XHTML viene talvolta definita 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 per i tag HTML. A questi tag verrà anteposto il prefisso h: come indicato da xmlns:h. Questi tag si trovano alle righe 5, 7, 8 e 10.
Quando incontra una dichiarazione di namespace, il server web cercherà nelle directory [META-INF] nel classpath dell'applicazione i file con il suffisso .tld (TagLib Definition). Qui li troverà nell'archivio [jsf-impl.jar] [1,2]:
![]() |
Diamo un'occhiata [3] al file [HTML_basic.tld]:
- alla riga 19, l'URI della libreria di 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 anch'esse nell'artefatto [jsf-impl.jar].
Torniamo al nostro progetto JSF. È stato ampliato con un nuovo ramo:
![]() |
Il ramo [Other Sources] [1] contiene file che devono essere presenti nel classpath del progetto e che non sono codice Java. Ciò vale per i file di messaggi JSF. Abbiamo visto che, senza aggiungere 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 [Pagine Web]:
![]() |
È 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, ovvero una classe Java in grado di elaborare le richieste dei client. Un'applicazione JSF funziona nel modo seguente:
![]() |
Questa architettura implementa il modello di progettazione MVC (Model, View, Controller). Rivediamo quanto detto in precedenza. L'elaborazione di una richiesta del client comporta i seguenti quattro passaggi:
1 - Richiesta - il browser del client invia una richiesta al controller [Faces Servlet]. Il controller gestisce tutte le richieste dei client. È il punto di ingresso dell'applicazione. Questo è la C in MVC,
2 - elaborazione - il controller C elabora questa richiesta. Per farlo, è assistito da gestori di eventi specifici dell'applicazione [2a]. Questi gestori potrebbero richiedere l'assistenza del livello business [2b]. Una volta che la richiesta del client è stata elaborata, può innescare varie 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 seleziona la risposta (= vista) da inviare al cliente. La selezione della risposta da inviare al cliente comporta diversi passaggi:
- selezionare il Facelet che genererà la risposta. Questa è chiamata vista V, la V in MVC. Questa scelta dipende generalmente dal risultato dell'esecuzione dell'azione richiesta dall'utente;
- fornire a questo Facelet i dati necessari per generare tale risposta. Infatti, questa risposta contiene molto spesso informazioni calcolate dal controller. Queste informazioni costituiscono ciò che viene chiamato il modello M della vista, la M in MVC,
Il passaggio 3 consiste quindi nel selezionare una vista V e costruire il modello M necessario per essa.
4 - Risposta - Il controller C istruisce il Facelet selezionato a renderizzarsi. Il Facelet utilizza il modello M preparato dal controller C per inizializzare le parti dinamiche della risposta che deve inviare al client. Il formato esatto di questa risposta può variare: può essere un flusso HTML, un PDF, un Excel, ecc.
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.
Diamo un'altra occhiata 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> viene utilizzato per associare un servlet a un URL richiesto dal browser del client. In questo caso, specifica che gli URL del tipo [/faces/*] devono essere gestiti dal servlet denominato [Faces Servlet]. Questo servlet è definito nelle righe 7–10. Poiché non ci sono altri tag <servlet-mapping> nel file, ciò significa che [Faces Servlet] gestirà solo gli URL del tipo [/faces/*]. Abbiamo visto che il contesto dell'applicazione è denominato [/mv-jsf2-01]. Gli URL client gestiti dal [Faces Servlet] avranno quindi il formato [http://machine:port/mv-jsf2-01/faces/*]. Le pagine .html e .jsp saranno gestite per impostazione predefinita dal contenitore dei servlet stesso, non da un servlet specifico. Questo perché il contenitore dei servlet sa come gestirle,
- righe 7–10: definiscono il [Faces Servlet]. Poiché tutti gli URL accettati vengono indirizzati ad esso, è il controller C nel modello MVC,
- riga 10: indica che il servlet deve essere caricato in memoria non appena il server web si avvia. Per impostazione predefinita, un servlet viene caricato solo al ricevimento della prima richiesta che gli viene inviata,
- righe 3–6: definiscono un parametro per il [Faces Servlet]. Il parametro javax.faces.PROJECT_STAGE definisce la fase del progetto in esecuzione. Nella fase di Sviluppo, il [Faces Servlet] visualizza messaggi di errore utili per il debug. Nella fase di Produzione, questi messaggi non vengono più visualizzati,
- righe 17–19: durata della sessione in minuti. Un client interagisce con l'applicazione attraverso una serie di cicli di richiesta/risposta. Ogni ciclo utilizza una propria connessione TCP/IP, che viene stabilita ex novo ad ogni ciclo. Pertanto, se un client C effettua due richieste, D1 e D2, il server S non ha modo di sapere che le due richieste appartengono allo stesso client C. Il server S non dispone della memoria dell' e del client. Questa è una caratteristica del protocollo HTTP (HyperText Transfer Protocol): il client comunica con il server attraverso una serie di cicli client-richiesta/server-risposta, ciascuno dei quali utilizza una nuova connessione TCP-IP. Questo è noto come protocollo stateless. In altri protocolli, come l'FTP (File Transfer Protocol), il client C utilizza la stessa connessione per tutta la durata della sua interazione con il server S. Una connessione è quindi legata a un client specifico. Il server S sa sempre con chi ha a che fare. Per riconoscere che una richiesta appartiene a un determinato client, il server web può utilizzare la tecnica della sessione:
- quando un client effettua la sua prima richiesta, il server S invia la risposta prevista insieme a un token, ovvero una stringa casuale di caratteri univoca per quel client;
- ad ogni richiesta successiva, il client C invia il token ricevuto al server S, consentendo così al server S di riconoscerlo.
L'applicazione può ora chiedere al server di memorizzare le informazioni associate a un cliente specifico. Questo viene definito "sessione del 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 terminata e le informazioni in essa contenute vanno perse. Alla sua richiesta successiva, tutto procederà come se fosse un nuovo cliente e inizierà una nuova sessione;
- Righe 21–23: l'elenco delle pagine da visualizzare quando l'utente richiede il contesto senza specificare una pagina, ad esempio qui [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 tal caso, visualizza la prima pagina trovata nell'elenco. Se quella pagina non esiste, visualizza la seconda pagina e così via fino a quando non viene trovata una pagina esistente. In questo caso, quando il client richiede l'URL [http://machine:port/mv-jsf2-01], verrà servito l'URL [http://machine:port/mv-jsf2-01/index.xhtml].
2.3.5. Esegui il progetto
Quando si esegue il nuovo progetto, il risultato visualizzato nel browser è il seguente:
![]() |
- in [1], il contesto è stato richiesto senza specificare un documento,
- in [2], come spiegato, viene visualizzata la home page (welcome-file) [index.xhtml].
Potresti essere curioso di dare un'occhiata al codice sorgente ricevuto [3]:
Abbiamo ricevuto del codice HTML. Tutti i tag <h:xx> presenti in index.xhtml sono stati convertiti nei corrispondenti tag HTML.
2.3.6. Il repository Maven locale
Abbiamo accennato al fatto che Maven scarica le dipendenze necessarie per il progetto e le memorizza localmente. È possibile esplorare questo repository locale:
![]() |
- In [1], selezionare l'opzione [Finestra / Altro / Browser repository Maven],
- in [2] si apre una scheda [Maven Repositories],
- in [3], contiene due rami, uno per il repository locale e l'altro per il repository centrale. Quest'ultimo è enorme. Per visualizzarne il contenuto, è necessario aggiornare il suo indice [4]. Questo aggiornamento richiede diverse decine di minuti.
![]() |
- in [5], le librerie nel repository locale,
- in [6], troverai un ramo [istia.st] che corrisponde al [groupId] del nostro progetto,
- in [7], puoi accedere alle proprietà del repository locale,
- in [8], vedete il percorso del repository locale. È utile saperlo perché a volte (raramente) Maven non utilizza più l’ultima versione del progetto. Apportate delle modifiche e notate che non vengono applicate. Potete quindi eliminare manualmente il ramo nel repository locale corrispondente al vostro [groupId]. Questo costringe Maven a ricreare il ramo a partire dall’ultima versione del progetto.
2.3.7. Ricerca di un artefatto con Maven
Ora impariamo come cercare un artefatto con Maven. Iniziamo con l'elenco delle dipendenze attuali nel 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, mentre le righe 45–58 specificano i repository in cui sono disponibili, oltre al repository centrale, che viene sempre utilizzato. Modificheremo le dipendenze per utilizzare le librerie nelle loro versioni più recenti.
![]() |
Per prima cosa, rimuoviamo 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ù nel file [pom.xml]. Ora cerchiamole nei repository Maven.
![]() |
- In [1], aggiungiamo una dipendenza al progetto;
- in [2], dobbiamo specificare le informazioni relative all'artefatto che stiamo cercando (groupId, artifactId, version, packaging (Type) e scope). Iniziamo specificando il [groupId] [3],
- in [4], premiamo [spazio] per visualizzare l'elenco dei possibili artefatti. Qui, [jsf-api] e [jsf-impl]. Scegliamo [jsf-api],
- in [5], seguendo la stessa procedura, selezioniamo la versione più recente. Il tipo di packaging è jar.
Procediamo in questo modo per tutti gli artefatti:
![]() | ![]() | ![]() | ![]() |
![]() |
Al punto [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 che desideriamo. Ad esempio, vogliamo utilizzare Hibernate come ORM (Object Relational Mapper) e questo è tutto ciò che sappiamo. Possiamo quindi andare sul sito [http://mvnrepository.com/]:
![]() |
In [1], è possibile inserire delle parole chiave. Digita hibernate ed esegui la ricerca.
![]() |
- In [2], selezionare [groupId] org.hibernate e [artifactId] hibernate-core,
- In [3], seleziona 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 scarica quindi le nuove dipendenze. Il progetto si evolve come segue:
![]() |
- in [5], la dipendenza [hibernate-core-4.1.2-Final]. Nel repository in cui è stata trovata, questo [artifactId] è descritto anche da un file [pom.xml]. Questo file è stato letto e Maven ha scoperto che l'[artifactId] aveva delle dipendenze. Le scarica anche quelle. Lo farà per ogni [artifactId] scaricato. Alla fine, in [6] troviamo dipendenze che non abbiamo richiesto direttamente. Sono indicate da un'icona diversa da quella dell'[artifactId] principale.
In questo documento, utilizziamo Maven principalmente per questa funzionalità. Questo 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, ci assicuriamo che ogni sviluppatore stia effettivamente utilizzando le stesse librerie.
Negli esempi che seguono, forniremo semplicemente il file [pom.xml] utilizzato. Il lettore dovrà solo utilizzarlo per replicare le condizioni descritte nel documento. Inoltre, i progetti Maven sono supportati dai principali IDE Java (Eclipse, NetBeans, IntelliJ, JDeveloper). Pertanto, il lettore può utilizzare il proprio IDE 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:
![]() |
- [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] riporta alla pagina iniziale.
![]() |
- Nella pagina iniziale [1], i link [2] consentono di cambiare la lingua,
- in [3], la pagina iniziale in inglese.
2.4.2. Il progetto NetBeans
Genereremo un nuovo progetto web come spiegato nella sezione 2.3.1. Lo chiameremo mv-jsf2-02:
![]() |
- in [1], il progetto generato,
- in [2], abbiamo rimosso il pacchetto [istia.st.mvjsf202] e il file [index.jsp],
- in [3], abbiamo aggiunto le dipendenze Maven utilizzando 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] per sostituire le vecchie dipendenze.
![]() |
- In [4, 5]: creare una cartella [src/main/resources] nella scheda [File],
- in [6], nella scheda [Progetti], questo ha creato il ramo [Altre fonti].
Ora abbiamo un progetto JSF. Al suo interno 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. Ha questo aspetto:
<?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à parlato di questo file nella sezione 2.3.4. Rivediamo le sue caratteristiche principali:
- tutti gli URL del tipo faces/* sono gestiti dal servlet [javax.faces.webapp.FacesServlet],
- la pagina [index.xhtml] è la home page 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 nella Sezione 2.3.4.
Ora creiamo una classe Java:
![]() |
- In [1], creare una classe Java nel ramo [Source Packages],
- in [2], le diamo un nome e la inseriamo in un 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], creare un file [Properties],
- in [2], inserisci 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 richiesto con JSF 1. È facoltativo con JSF 2. Tuttavia, è necessario se il sito JSF è internazionalizzato. Questo sarà il caso in seguito. Quindi ora vi mostreremo come creare questo file di configurazione.
![]() |
- In [1] creiamo il file di configurazione JSF,
- in [2] inseriamo il suo nome e in [3] la sua 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. Dovremo compilarlo in seguito.
Ora disponiamo di tutti gli elementi necessari per creare un progetto JSF. Negli esempi che seguono, presenteremo il progetto JSF completo e poi ne descriveremo gli elementi uno per uno. Presentiamo ora un progetto per spiegare i concetti:
- gestione degli eventi dei moduli,
- internazionalizzazione delle pagine su un sito JSF,
- la navigazione tra le pagine.
Il progetto [mv-jsf2-02] si presenta così. I lettori possono trovarlo sul sito web degli esempi (vedere la Sezione 1.2).
![]() |
- in [1], i file di configurazione del progetto JSF,
- in [2], le pagine JSF del progetto,
- in [3], la singola 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 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>
...
</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 quelli con prefisso f sono tag specifici di JSF,
- riga 10: il tag <f:view> viene utilizzato per delimitare il codice che il motore JSF deve elaborare, dove compaiono i tag <f:xx>. L'attributo locale consente di specificare una lingua di visualizzazione per la pagina. Qui ne useremo due: inglese e francese. Il valore dell'attributo locale è espresso come un'espressione EL (Expression Language) #{espressione}. La forma dell'espressione può variare. La esprimeremo più spesso come bean['key'] o bean.field. 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 possiamo utilizzare annotazioni che trasformano una classe Java in un bean riconosciuto da JSF 2. Il file di messaggi deve 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 come [ChangeLocale].getLocale(). In generale, l'espressione #{bean.field} viene valutata come [Bean].getField(), dove [Bean] è un'istanza della classe Java a cui è stato assegnato il nome bean e getField è il getter associato al campo 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{
// page locale
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 impostato utilizzando l'attributo name dell'annotazione: @ManagedBean(name="xx"). Se l'attributo name viene omesso, viene utilizzato il nome della classe con il primo carattere 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. Esistono diversi ambiti. Utilizzeremo comunemente i seguenti tre:
- RequestScoped: la durata del bean è quella del ciclo richiesta del browser/risposta del server. Se questo bean è nuovamente necessario per elaborare una nuova richiesta dallo stesso browser o da un altro, verrà istanziato nuovamente,
- SessionScoped: la durata del bean è quella della sessione di un cliente specifico. Il bean viene inizialmente creato per gestire una delle richieste di quel cliente. Rimane quindi in memoria all'interno della sessione di quel cliente. Un bean di questo tipo memorizza tipicamente dati specifici per un determinato cliente. Verrà distrutto quando la sessione del cliente verrà terminata,
- ApplicationScoped: La durata del bean è quella dell'applicazione stessa. Un bean con questo ambito è molto spesso condiviso da tutti i client dell'applicazione. Viene generalmente 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ò richiede la creazione del 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>
Al di fuori del tag radice <beans>, il file è vuoto. Questo è sufficiente. È richiesta solo la sua presenza.
Infine, si noti che la classe [ChangeLocale] implementa l'interfaccia [Serializable]. Ciò è richiesto per i bean con ambito di sessione, che il server web potrebbe dover serializzare in file. Torneremo al bean [ChangeLocale] più avanti.
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.title']} nella forma #{bean['field']}. bean è il nome di una classe Java o di un file di messaggi. In questo caso, è il nome di un file di messaggi. Il file di messaggi 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> viene utilizzato per configurare l'applicazione JSF,
- righe 12–17: il tag <resource-bundle> viene utilizzato per definire le risorse dell'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 sarà denominato messages[_LanguageCode][_CountryCode].properties. Il tag <base-name> definisce solo la prima parte del nome. Il resto è sottinteso. Potrebbero esserci più file di messaggi, uno per ogni lingua:
![]() |
- in [1], vediamo quattro file di messaggi corrispondenti al nome base `messages` definito in [faces-config.xml],
- messages_fr.properties: contiene i messaggi in francese (codice fr);
- messages_en.properties: contiene messaggi in inglese (codice en);
- messages_es_ES.properties: contiene messaggi in spagnolo (codice es) della Spagna (codice ES). Esistono altri tipi di spagnolo, come quello della Bolivia (es_BO);
- messages.properties: viene utilizzato dal server quando la lingua del computer su cui è in esecuzione non ha un file di messaggi associato. Verrebbe utilizzato, ad esempio, se l'applicazione fosse in esecuzione su un computer in Germania dove la lingua predefinita è il tedesco (de). Poiché non esiste un file [messages_de.properties], l'applicazione utilizzerebbe il file [messages.properties],
- in [2]: i codici lingua sono soggetti a uno standard internazionale,
- in [3]: lo stesso vale per i codici paese.
Il nome del file dei messaggi è definito alla riga 14. Verrà cercato nel Classpath del progetto. Se si trova all'interno di un pacchetto, tale pacchetto deve essere definito alla riga 14, ad esempio resources.messages, se il file [messages.properties] si trova nella cartella [resources] del Classpath. Poiché il nome alla riga 14 non include un pacchetto, il file [messages.properties] deve essere collocato nella radice della cartella [src/main/resources]:
![]() |
In [1], nella scheda [Projects] del progetto NetBeans, il file [messages.properties] viene visualizzato come un elenco delle diverse versioni di messaggi definite. Le versioni sono identificate da una sequenza di uno a tre codici [languageCode_countryCode_variantCode]. In [1] è stato utilizzato solo il [codice lingua]: en per l'inglese, fr per il francese. Ogni versione è memorizzata in un file separato nel file system.
Nel nostro esempio, il file dei messaggi in francese [messages_fr.properties] conterrà quanto segue:
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] avrà questo aspetto:
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]. In definitiva, 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 nel file dei messaggi sarà referenziata dall'identificatore msg nelle pagine JSF. Questo identificatore è utilizzato nel file [index.xhtml] descritto:
<?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> alla riga 8 visualizzerà il valore del messaggio (presenza dell'identificatore msg) associato alla chiave welcome.title. Questo messaggio viene cercato e trovato nel file [messages.properties] relativo alla lingua attualmente attiva. Ad esempio, per il francese:
welcome.titre=Tutoriel JSF (JavaServer Faces)
Un messaggio segue il formato chiave=valore. La riga 8 del file [index.xhtml] diventa la seguente dopo aver valutato l'espressione #{msg['welcome.title']}:
<title><h:outputText value="Tutoriel JSF (JavaServer Faces)" /></title>
Questo meccanismo dei file di messaggi semplifica la modifica della lingua delle pagine in un progetto JSF. Si parla in questo caso di internazionalizzazione del progetto, o più comunemente con la sua abbreviazione i18n, poiché la parola internationalization inizia con i e finisce con 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 input (testo, pulsanti di opzione, caselle di controllo, elenchi a discesa, ecc.);
- tag di convalida del modulo (pulsanti, link). È tramite un pulsante o un link che l'utente invia i propri dati al server, che li elaborerà,
Qualsiasi tag JSF può essere identificato da un attributo id. Il più delle volte non è necessario, e questo vale per la maggior parte dei tag JSF qui utilizzati. Tuttavia, questo attributo è utile in determinate situazioni. Alla riga 17, il modulo è identificato dall'id "form". 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. Genera il tag HTML `<table>`.
- Il modulo presenta tre collegamenti che ne attivano l'elaborazione, alle righe 19, 20 e 23. Il tag <h:commandLink> presenta almeno due attributi:
- value: il testo del link;
- action: una stringa C o il riferimento a un metodo che, una volta eseguito, restituisce la stringa C. Questa stringa C può essere:
- il nome di una pagina JSF nel progetto,
- oppure un nome definito nelle regole di navigazione del file [faces-config.xml] e associato a una pagina JSF nel 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 utilizzando come esempio il link alla riga 13:
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>}"/>
In primo luogo, il file dei messaggi viene utilizzato 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="#" 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 clicca sul link [French], verrà eseguito il codice JavaScript. Questo codice è incorporato nella pagina ricevuta dal browser ed è il browser stesso che lo esegue. JavaScript è ampiamente utilizzato in JSF e AJAX (Asynchronous JavaScript and XML). Il suo scopo generale è migliorare l'usabilità e la reattività delle applicazioni web. Il più delle volte viene generato automaticamente da strumenti software, quindi non è necessario comprenderlo. Tuttavia, a volte uno sviluppatore potrebbe aver bisogno di aggiungere codice JavaScript alle proprie pagine JSF. In questi casi, la conoscenza di JavaScript è necessaria.
In questa sede non è necessario comprendere il codice JavaScript generato per il tag JSF <h:commandLink>. Tuttavia, vale la pena notare due punti:
- il codice JavaScript utilizza l'ID 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. Ecco un esempio: j_idt8. Assegnare ai tag identificatori chiari rende più facile comprendere il codice JavaScript generato, se necessario. Ciò vale in particolare quando lo sviluppatore deve aggiungere autonomamente codice JavaScript per manipolare i componenti della pagina. In tal caso, deve conoscere gli ID dei propri componenti.
Cosa succede quando l'utente clicca sul link [Francese] nella pagina sopra? 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 l'URL [http://localhost:8080/mv-jsf2-02/faces/index.xhtml]. È sempre così: le immissioni effettuate in un modulo JSF inizialmente ottenuto tramite l'URL URLFormulaire vengono inviate a quello stesso URL. Il browser ha due modi per inviare i valori inseriti: GET e POST. Con il metodo GET, i valori inseriti vengono inviati dal browser nell'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
Utilizzando il metodo POST in questo caso, il browser invia i valori inseriti al server tramite la riga 6.
- riga 3: specifica il formato di codifica dei valori del modulo,
- riga 4: specifica la dimensione in byte della riga 6,
- riga 5: una 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 nel formato elemento1=valore1&elemento2=valore2& ..., il formato di codifica definito dalla riga 3. In questo formato di codifica, alcuni caratteri vengono sostituiti dai loro valori esadecimali. È il caso dell'ultimo elemento:
formulaire=formulaire&javax.faces.ViewState=...&formulaire%3Aj_idt8=formulaire%3Aj_idt8
dove %3A rappresenta il carattere due punti. Pertanto, la stringa form:j_idt8=form:j_idt8 viene inviata al server. 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 conta in questo caso è che la presenza di questo identificatore nella stringa di valori inviata dal browser del client permette a JSF di sapere che è stato cliccato il link [French]. Utilizzerà quindi l'attributo action sopra indicato per determinare come elaborare la stringa ricevuta. L'attributo action="#{changeLocale.setFrenchLocale}" indica a JSF che la richiesta del client deve essere gestita dal metodo [setFrenchLocale] di un oggetto chiamato 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. Se questo attributo manca, come nome del bean viene utilizzato il nome della classe con il primo carattere in minuscolo.
Torniamo alla richiesta del browser:
![]() |
e al tag <h:commandLink> che ha generato il link [Francese] su cui abbiamo cliccato:
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
Il controller inoltrerà 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 accedere alla richiesta del client;
- e deve restituire un risultato C di tipo String. Questa stringa C può essere:
- il nome di una pagina JSF nel progetto;
- un nome definito nelle regole di navigazione del file [faces-config.xml] e associato a una pagina JSF nel progetto;
- oppure un puntatore nullo, se il browser del client non deve cambiare pagina,
Nell'architettura JSF sopra descritta, il controller [Faces Servlet] utilizzerà la stringa C restituita dal gestore di eventi e, se necessario, il proprio file di configurazione [faces-config.xml] per determinare quale pagina JSF inviare in risposta al client [4].
Nel tag
<h:commandLink value="#{msg['welcome.langue1']}" action="#{changeLocale.setFrenchLocale}"/>
il gestore di eventi per il clic sul link [French] è il metodo [changeLocale.setFrenchLocale], dove changeLocale è un'istanza della classe [ utils.ChangeLocale] già discussa:
package utils;
import java.io.Serializable;
import javax.enterprise.context.SessionScoped;
import javax.faces.bean.ManagedBean;
@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{
// page locale
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 ha effettivamente la firma dei gestori di eventi. Ricordiamo che il gestore di eventi deve elaborare la richiesta del client. Poiché non riceve alcun parametro, come può accedere alla richiesta? Esistono diversi modi per farlo:
- Il bean B che contiene il gestore di eventi per la pagina JSF P è spesso anche quello che contiene il modello M per quella pagina. Ciò significa che il bean B contiene campi che saranno inizializzati con i valori inseriti nella pagina P. Questa operazione viene eseguita dal controller [Faces Servlet] prima che venga chiamato il gestore di eventi del bean B. Questo gestore avrà quindi accesso, tramite i campi del bean B a cui appartiene, ai valori inseriti dal client nel modulo e sarà in grado di elaborarli.
- Il metodo statico [FacesContext.getCurrentInstance()] di tipo [FacesContext] consente di accedere al contesto di esecuzione della richiesta JSF corrente, che è un oggetto di tipo [FacesContext]. Il contesto di esecuzione della richiesta così ottenuto permette di accedere ai parametri inviati al server dal browser del client tramite 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? Vogliamo che imposti la lingua utilizzata dall'applicazione. Nel gergo Java, questo processo è chiamato "localizzazione" dell'applicazione. Questa localizzazione viene utilizzata dal tag <f:view> nella pagina JSF [index.xhtml]:
<f:view locale="#{changeLocale.locale}">
...
</f:view>
Per impostare la pagina in francese, è sufficiente impostare l'attributo locale su "fr". Per impostarla in inglese, impostarlo su "en". Il valore dell'attributo locale viene ottenuto utilizzando l'espressione [ChangeLocale].getLocale(). Questa espressione restituisce il valore del campo locale nella classe [ChangeLocale]. Da ciò deriva il codice per il metodo [ChangeLocale].setFrenchLocale(), che dovrebbe passare le pagine al francese:
public String setFrenchLocale(){
locale="fr";
return null;
}
Abbiamo spiegato che un gestore di eventi deve restituire una stringa in stile C che verrà utilizzata da [Faces Servlet] per individuare la pagina JSF da inviare in risposta al browser del client. Se la pagina da restituire è la stessa di quella attualmente in elaborazione, il gestore di eventi può semplicemente restituire il valore null. Questo è ciò che viene fatto qui alla riga 3: vogliamo restituire la stessa pagina [index.xhtml] ma in una lingua diversa.
Torniamo all'architettura di elaborazione delle richieste:
![]() |
Il gestore di eventi changeLocale.setFrenchLocale è stato eseguito e ha restituito il valore null al controller [Faces Servlet]. Il controller visualizzerà quindi nuovamente la pagina [index.xhtml]. Diamo un'altra occhiata:
<?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 di messaggi [messages.properties]. Quello utilizzato è quello che corrisponde alla "localizzazione" della pagina (riga 6). Poiché il gestore di eventi changeLocale.setFrenchLocale imposta questa locale su fr, verrà utilizzato il file [messages_fr.properties]. Cliccando sul link [English] (riga 14) si cambierà la lingua in en (vedi il 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 al nostro bean l'ambito Session:
@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{
la localizzazione eseguita durante una richiesta viene mantenuta per le richieste successive.
C'è un ultimo elemento della pagina [index.xhtml] da esaminare:
<?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 impostato su una stringa. In questo caso, non viene chiamato alcun gestore di eventi per elaborare la pagina. L'utente viene immediatamente reindirizzato alla pagina [page1.xhtml]. Esaminiamo come funziona l'applicazione in questo caso d'uso:
![]() |
L'utente fa clic sul collegamento [Pagina 1]. Il modulo viene inviato al controller [Faces Servlet]. Il controller riconosce nella richiesta ricevuta che è stato fatto clic sul collegamento [Pagina 1]. Esamina il tag corrispondente:
<h:commandLink value="#{msg['welcome.page1']}" action="page1"/>
Non c'è nessun gestore di eventi associato al link. Il controller [Faces Servlet] passa immediatamente al punto [3] sopra e visualizza la pagina [page1.xhtml]:
![]() | ![]() |
2.4.7. La pagina JSF [page1.xhtml]
La pagina [page1.xhtml] invia il seguente flusso al browser del 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 dovrebbe collegare il codice JSF alla pagina inviata al browser del cliente. Il link per tornare alla home page:
<h:commandLink value="#{msg['page1.welcome']}" action="index"/>
visualizzerà 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 la cartella [target] nella scheda [Files]. All'interno di questa cartella, troverete l'archivio del progetto [mv-jsf2-02-1.0-SNAPSHOT.war]. Questo è l'archivio che viene distribuito sul server;
- in [WEB-INF/classes] [2], troverete le classi compilate dalla 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], troverete le librerie del progetto,
- nella directory principale di [WEB-INF] [4], troverete i file di configurazione del progetto,
![]() |
- nella directory principale dell'archivio [5] troverete le pagine JSF che si trovavano nel ramo [Web Pages] del progetto,
- Una volta compilato, il progetto può essere eseguito [6]. Verrà eseguito in base alla sua configurazione di runtime [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 è chiamato distribuzione del progetto sul server delle applicazioni,
- in [9], ti verrà chiesto di avviare un browser al momento dell'esecuzione. Il browser richiederà il contesto dell'applicazione [10], ovvero l'URL [http://localhost:8080/mv-jsf2-02]. In base alle regole nel file [web.xml] (vedi pagina 44), il file [faces/index.xhtml] verrà servito al browser del client. Poiché l'URL è nella forma [/faces/*], verrà gestito dal controller [Faces Servlet] (vedi [web.xml] a pagina 44). Questo controller elaborerà la pagina e invierà il seguente output HTML:
![]() |
- Il controller [Faces Servlet] gestirà quindi gli eventi che si verificano su 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>
Questa è la struttura minima dei file per un'applicazione JSF 2 internazionalizzata. Qui abbiamo utilizzato le nuove funzionalità di JSF 2 rispetto a JSF 1:
- dichiarando i bean e il loro ambito tramite le annotazioni @ManagedBean, @RequestScoped, @SessionScoped e @ApplicationScoped,
- navigando tra le pagine utilizzando i nomi delle pagine XHTML (senza il suffisso .xhtml) come chiavi di navigazione.
È possibile scegliere di non utilizzare queste funzionalità e dichiarare invece questi elementi del progetto JSF nel file [faces-config.xml] come in JSF 1. In tal caso, il file [faces-config.xml] potrebbe apparire 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 -->
<application>
<resource-bundle>
<base-name>
messages
</base-name>
<var>msg</var>
</resource-bundle>
</application>
<!-- managed beans -->
<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>
<!-- navigation -->
<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: la regola può essere descritta. Qui non l'abbiamo fatto;
- riga 29: la pagina da cui inizia la navigazione (punto di partenza);
- righe 30–33: un caso di navigazione. Ce ne possono essere diversi;
- riga 31: la chiave di navigazione;
- riga 32: la pagina verso cui si sta navigando.
Le regole di navigazione possono essere visualizzate in modo più intuitivo. Durante la modifica del 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é il bean è ora 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"/>
Assegniamo la chiave di navigazione </span>**<span style="color: #000000">welcome</span>**<span style="color: #000000">, definita in [faces-config], all'attributo action;
- i metodi setFrenchLocale e setEnglishLocale, che devono restituire una chiave di navigazione, non devono essere modificati poiché in precedenza restituivano null per indicare che l'utente rimaneva sulla stessa pagina.
2.4.10. Conclusione
Torniamo al progetto NetBeans che abbiamo scritto:
![]() |
Questo progetto segue la seguente architettura:
![]() |
In ogni progetto JSF, troveremo i seguenti elementi:
- pagine JSF [A] che vengono inviate [4] ai browser dei client dal controller [Faces Servlet] [3],
- 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 fungono da modelli per le pagine JSF [3]. Molto spesso, i livelli [business] e [DAO] vengono sviluppati e testati separatamente. Il livello [web] viene quindi testato con un livello [business] fittizio. Se i livelli [business] e [DAO] sono disponibili, di solito lavoriamo con i loro file .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], dove useremo sempre la versione semplificata.
2.5. Esempio mv-jsf2-03: modulo di inserimento dati - Componenti JSF
D'ora in poi non mostreremo più la struttura del progetto. Presenteremo progetti già pronti e ne spiegheremo il funzionamento. Il lettore può scaricare tutti gli esempi dal sito web di questo documento (vedere la sezione 1.2).
2.5.1. L'applicazione
L'applicazione presenta una singola vista:
![]() |
L'applicazione presenta i principali componenti JSF che possono essere utilizzati in un modulo di inserimento dati:
- La colonna [1] indica il nome del tag JSF/HTML utilizzato,
- la colonna [2] mostra un esempio di inserimento dati per ciascuno dei tag presenti,
- la colonna [3] visualizza i valori del bean che funge da modello della pagina,
- i dati inseriti in [2] vengono convalidati tramite il pulsante [4]. Questa convalida aggiorna semplicemente il bean modello della pagina. Viene quindi restituita la stessa pagina. Pertanto, dopo la convalida, la colonna [3] visualizza i nuovi valori del bean modello, consentendo all'utente di verificare l'impatto dei propri inserimenti sul modello della pagina.
2.5.2. Il progetto NetBeans
Il progetto NetBeans per l'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]
Mostriamo 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 richieste per un progetto JSF. Negli esempi che seguono, questo file verrà mostrato solo quando subisce delle modifiche.
2.5.4. Il file [ web.xml]
Il file [web.xml] è stato configurato in modo che la pagina [index.xhtml] sia la home page 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 home page,
- righe 11–14: un parametro per il [Faces Servlet]. Richiede che i commenti in un facelet come:
<!-- langues -->
vengano ignorati. Senza questo parametro, i commenti causano problemi difficili da comprendere,
- righe 3–6: un parametro per il [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: configurare il file dei messaggi dell'applicazione.
2.5.6. Il file dei messaggi [messages.properties]
I file dei messaggi (vedi [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 nelle seguenti posizioni 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] per la 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]:
<!-- line 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}"/>
Quando la pagina [index.xhtml] viene richiesta per la prima volta, il codice sopra riportato genera la riga 2 della tabella di input:
![]() |
La riga 2 visualizza il campo [1], le righe 3-6 visualizzano il campo [2] e la riga 7 visualizza il campo [3].
Le righe 5 e 7 utilizzano un'espressione EL che fa riferimento al form bean definito nella classe [Form.java] come segue:
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. Questo sarà quindi il nome della classe che inizia con una lettera minuscola: form,
- Il bean ha ambito di richiesta. Ciò significa che in un ciclo richiesta client/risposta server, viene istanziato quando la richiesta lo richiede e distrutto quando la risposta al client è stata restituita.
Nel codice sottostante tratto dalla pagina [index.xhtml]:
<!-- line 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 del modulo. Per comprendere i collegamenti tra una pagina P e il suo modello M, dobbiamo tornare al ciclo richiesta client/risposta server ( ) che caratterizza un'applicazione web:
![]() |
Dobbiamo distinguere tra il caso in cui la pagina P viene inviata come risposta al browser (fase 4)—ad esempio, durante la richiesta iniziale della pagina—e il caso in cui l'utente attiva un evento sulla pagina P, che viene poi gestito dal controller [Faces Servlet] (fase 1).
Possiamo 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,
- quando invia i valori inseriti nella pagina, il browser esegue un'operazione POST sull'URL della pagina.
In entrambi i casi, viene richiesto lo stesso URL. A seconda che la richiesta del browser sia di tipo GET o POST, la richiesta verrà elaborata in modo diverso.
[Caso 1 – Richiesta iniziale della pagina P]
Il browser richiede l'URL della pagina utilizzando una richiesta GET. Il controller [Faces Servlet] passerà direttamente al passaggio [4] di rendering della risposta e la pagina [index.xhtml] verrà inviata al client. Il controller JSF darà istruzioni a ciascun tag della pagina di eseguire il rendering. Prendiamo l'esempio della riga 5 del codice [index.xhtml]:
<h:inputText id="inputText" value="#{form.inputText}"/>
Il tag JSF <h:inputText value="value"/> genera il tag HTML <input type="text" value="value"/>. La classe responsabile dell'elaborazione di questo tag incontra l'espressione #{form.inputText}, che deve valutare:
- se il bean del modulo non esiste ancora, viene creato istanziando la classe forms.Form,
- l'espressione #{form.inputText} viene valutata chiamando il metodo form.getInputText(),
- Il testo <input id="form:inputText" type="text" name="form:inputText" value="text" /> viene inserito nel flusso HTML che verrà inviato al client, supponendo che il metodo form.getInputText() abbia restituito la stringa "text". JSF assegnerà inoltre un nome (name) al componente HTML inserito nel flusso. Questo nome è costruito dagli identificatori id del componente JSF analizzato e da quelli dei suoi componenti padre, in questo caso il tag <h:form id="form"/>.
Si noti che se, in una pagina P, si utilizza l'espressione #{M.field} dove M è il bean del modello della pagina P, questo bean deve avere un metodo getField() pubblico. Il tipo restituito da questo metodo deve essere convertibile in un tipo String. Un modello M comune è il seguente:
dove T è un tipo che può essere convertito in una String, eventualmente utilizzando un metodo toString.
Sempre per quanto riguarda la visualizzazione della pagina P, l'elaborazione della riga:
<h:outputText value="#{form.inputText}"/>
sarà simile e verrà generato il seguente output HTML:
Internamente sul server, la pagina P è rappresentata come un albero di componenti, che rispecchia l'albero dei tag della pagina inviata al client. Ci riferiremo a questo albero come alla vista della pagina o all' . Questo stato viene memorizzato. Può essere memorizzato in due modi a seconda della configurazione 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 utilizzando vari tag <context-param>, compreso quello alle righe 3–6, che specifica 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 un campo nascosto a ogni pagina HTML che invia, il cui valore è lo stato corrente 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 nei valori inviati dal browser quando il modulo viene inviato. Utilizzando 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 client viene salvato nella sessione del client. Quando il browser del client invia i valori inseriti nel modulo, invierà anche il proprio token di sessione. Utilizzando questo token, il controller JSF recupererà lo stato della pagina inviata al client e lo ripristinerà.
La codifica dello stato di una pagina JSF può richiedere diverse centinaia di byte. Poiché questo stato viene mantenuto per ogni utente dell'applicazione, potrebbero sorgere problemi di memoria se il numero di utenti è elevato. Per questo motivo, abbiamo scelto in questo caso di salvare lo stato della pagina sul client (vedere [web.xml], sezione 2.5.4, pagina 66).
[Caso 2 – Elaborazione della pagina P]
![]() |
Ci troviamo al passaggio [1] sopra indicato, in cui il controller [Faces Servlet] riceverà una richiesta POST dal browser del client a cui aveva precedentemente inviato la pagina [index.xhtml]. Stiamo affrontando l'elaborazione di un evento di pagina. Prima che l'evento possa essere elaborato in [2a], si verificheranno diversi passaggi. Il ciclo di elaborazione di una richiesta POST da parte del controller JSF è il seguente:

- In [A], grazie al campo nascosto `javax.faces.ViewState`, viene ricostruita la vista inizialmente inviata al browser del client. Qui, i componenti della pagina riacquistano i valori che avevano nella pagina inviata. Il nostro componente `inputText` riacquista il suo valore "text",
- in [B], i valori inviati dal browser del client vengono utilizzati per aggiornare i componenti della vista. Pertanto, se nel campo di input HTML denominato inputText l'utente ha digitato "jean", il valore "jean" sostituisce il valore "text". La vista ora riflette la pagina così come modificata dall'utente e non più come era stata inviata al browser,
- in [C], i valori inviati vengono verificati. Supponiamo che il precedente componente inputText sia un campo di immissione dell'età. Il valore inserito deve 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 potrebbe essere completamente diverso. Si verifica quindi una conversione da un tipo String a un altro tipo T. Questa conversione potrebbe fallire. In questo caso, il ciclo richiesta/risposta viene interrotto e la pagina P costruita in [B] viene rinviata al browser del cliente con messaggi di errore, se l’autore della pagina P li ha forniti. Si noti che l’utente vede la pagina esattamente come l’ha inserita, senza alcuno sforzo da parte dello sviluppatore. In un’altra tecnologia, come JSP, lo sviluppatore deve ricostruire la pagina P autonomamente utilizzando i valori inseriti dall’utente. Il valore di un componente può anche essere sottoposto a un processo di convalida. Continuando con l'esempio del componente inputText, che è il campo di immissione dell'età, il valore immesso non deve essere solo un numero intero, ma un numero intero compreso nell'intervallo [1,N]. Se il valore immesso supera la fase di conversione, potrebbe non superare la fase di convalida. In questo caso, anche il ciclo richiesta/risposta viene completato e la pagina P costruita in [B] viene rinviata al browser del cliente,
- in [D], se tutti i componenti della pagina P superano le fasi 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 questo valore verrà assegnato al modello del modulo della pagina eseguendo il 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 input su P devono avere un metodo set,
- una volta che il modello M della pagina P è stato aggiornato con i valori inviati, l'evento che ha innescato il POST della pagina P può essere elaborato. Questo è il passo [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 di quello stesso bean.
- Il passaggio [E] restituisce una chiave di navigazione al controller JSF. Nei nostri esempi, questa sarà sempre il nome della pagina XHTML da visualizzare, senza il suffisso .xhtml. Questo è il passaggio [F]. Un altro approccio consiste nel restituire una chiave di navigazione che verrà cercata nel file [faces-config.xml]. Abbiamo descritto questo caso.
Da quanto sopra, possiamo concludere che:
- una pagina P visualizza i campi C del suo modello M utilizzando i metodi [M].getC(),
- i campi C del modello M su una pagina P vengono inizializzati con i valori inseriti nella pagina P utilizzando i metodi [M].setC(input). In questo passaggio, potrebbero verificarsi processi di conversione e convalida che potrebbero fallire. In questo caso, l'evento che ha innescato il POST della pagina P non viene elaborato e la pagina viene rinviata al client esattamente come è stata inserita dal client.
Il modello [Form.java] per la pagina [index.xhtml] sarà il seguente:
package forms;
import javax.enterprise.context.RequestScoped;
import javax.faces.bean.ManagedBean;
@ManagedBean
@RequestScoped
public class Form {
/** Creates a new instance of Form */
public Form() {
}
// form fields
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";
// events
public String submit(){
return null;
}
// getters and setters
...
}
I campi nelle righe 16–27 vengono utilizzati nelle seguenti parti 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">
<!-- languages -->
<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">
<!-- headers -->
<h:outputText value="#{msg['form.headerCol1']}" styleClass="entete"/>
<h:outputText value="#{msg['form.headerCol2']}" styleClass="entete"/>
<h:outputText value="#{msg['form.headerCol3']}" styleClass="entete"/>
<!-- line 1 -->
...
<!-- line 2 -->
...
<!-- line 3 -->
...
<!-- line 4 -->
...
<!-- line 5 -->
...
<!-- line 6 -->
...
<!-- line 7 -->
...
<!-- line 8 -->
...
<!-- line 9 -->
...
<!-- line 10 -->
...
<!-- line 11 -->
...
<!-- line 12 -->
...
</h:panelGrid>
<p>
<h:commandButton type="submit" id="submit" value="#{msg['form.submitText']}"/>
</p>
</h:form>
</h:body>
</f:view>
</html>
Esamineremo uno per uno i componenti principali 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 del modulo devono trovarsi all'interno di un tag <h:form> (righe 12–16). Il tag <f:view> (righe 7–18) è necessario se l'applicazione è internazionalizzata. Inoltre, un modulo deve avere un modo per essere inviato (POST), spesso un link o un pulsante come nella riga 14. Può anche essere inviato tramite vari eventi (cambiando una selezione in un elenco, cambiando il campo attivo, digitando un carattere in un campo di immissione, ecc.
2.5.9. Lo stile del modulo
Per rendere più leggibili le colonne della tabella del modulo, il modulo include 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 head HTML utilizzando il seguente tag:
<h:outputStylesheet library="css" name="styles.css"/>
Il foglio di stile si troverà 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 come utilizzare questo foglio di stile:
<h:panelGrid columnClasses="col1,col2,col3" columns="3" border="1">
Il tag <h:panelGrid columns="3"/> definisce una griglia a tre colonne. L'attributo columnClasses viene utilizzato per definire lo stile di queste colonne. I valori col1, col2 e col3 nell'attributo columnClasses specificano i rispettivi stili per le colonne 1, 2 e 3 della griglia. 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` viene utilizzato per definire lo stile del testo nella prima riga della tabella:
<!-- entêtes -->
<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" viene utilizzato per definire lo stile del testo nella prima colonna della tabella:
<!-- ligne 1 -->
<h:outputText value="inputText" styleClass="info"/>
Non ci soffermeremo sull'uso dei fogli di stile, poiché meriterebbero un libro a sé stante e, inoltre, la loro creazione è spesso affidata a specialisti. Tuttavia, abbiamo scelto di utilizzarne uno minimalista per ricordare ai lettori che il loro uso è essenziale.
Ora vediamo 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 è impostata dall'attributo style del tag <h:body>. Questo attributo consente di impostare elementi di stile. L'immagine di sfondo si trova nella cartella [resources/images/standard.jpg]:
![]() |
È possibile accedere a questa immagine tramite l'URL [/mv-jsf2-03/resources/images/standard.jpg]. Potremmo 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 utilizzando l'espressione EL ${request.contextPath}. Pertanto, preferiamo il seguente attributo di stile:
style="background-image: url('${request.contextPath}/resources/images/standard.jpg');"
che sarà valido indipendentemente dal contesto.
2.5.10. I due cicli di richiesta client/risposta server di un modulo
Rivediamo quanto già spiegato nella Sezione 2.5.7 in un caso generale e applichiamolo al modulo in esame. Questo modulo verrà testato nell'ambiente JSF standard:
![]() |
Qui non ci saranno gestori di eventi né un livello [business]. I passaggi [2x] non esisteranno quindi. Distingueremo tra il caso in cui il modulo F viene inizialmente richiesto dal browser e il caso in cui l'utente attiva un evento nel modulo F, che viene poi elaborato dal controller [Faces Servlet]. Esistono due distinti cicli di richiesta client/risposta server.
- Il primo, corrispondente alla richiesta iniziale della pagina, viene attivato da un'operazione GET dal browser sull'URL del modulo,
- il secondo, corrispondente all'invio dei valori inseriti nella pagina, è attivato da un'operazione POST su quello stesso URL.
A seconda che la richiesta del browser sia di tipo GET o POST, il controller [Faces Servlet] elabora la richiesta in modo diverso.
[Caso 1 – Richiesta iniziale per il modulo F]
Il browser richiede l'URL della pagina con un GET. Il controller [Faces Servlet] passerà direttamente al passaggio [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 questo caso, gli scambi HTTP client/server sono i seguenti:
Richiesta HTTP del client:
La riga 1 mostra la richiesta GET del browser.
Risposta HTTP dal server:
Non mostrato qui, la riga 7 è seguita da una riga vuota e dal codice HTML del modulo. Questo è il codice che il browser interpreta e visualizza.
[Caso 2 – Elaborazione dei valori inseriti nel Modulo F]
L'utente compila il modulo e lo invia utilizzando il pulsante [Invia]. Il browser invia quindi una richiesta POST all'URL del modulo. Il controller [Faces Servlet] elabora questa richiesta, aggiorna il modello [Form.java] per il modulo [index.xhtml] e restituisce il modulo [index.xhtml] aggiornato in base a questo nuovo modello. Esaminiamo questo ciclo utilizzando un esempio:

Nell'esempio sopra riportato, l'utente ha inserito i propri dati e li ha inviati. In risposta, riceve la seguente visualizzazione:

In questo caso, gli scambi HTTP client/server sono i seguenti:
Richiesta HTTP del client:
Alla riga 1, la richiesta POST effettuata dal browser. Alla riga 14, i valori inseriti dall'utente. Ad esempio, è possibile vedere 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 la sua richiesta GET iniziale.
Risposta HTTP dal server:
Non mostrato qui, la riga 6 è seguita da una riga vuota e dal codice HTML del modulo, aggiornato dal suo nuovo modello derivato dalla richiesta POST.
Esamineremo ora i vari componenti di questo modulo.
2.5.11. Tag <h:inputText>
Il tag <h:inputText> genera un tag HTML <input type="text" ...>.
Si consideri il codice seguente:
<!-- line 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 relativo 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 risultante è la seguente:
- La riga 2 del codice XHTML genera [1],
- il tag <h:panelGroup> (righe 3–6) consente di raggruppare più elementi all’interno di una singola cella della tabella generata dal tag <h:panelGrid> alla riga 20 del codice completo della pagina (vedi sezione 2.5.8). Il testo [2] è generato dalla riga 4. Il campo di immissione [3] è generato dalla riga [5]. Qui, il metodo getInputText da [Form.java] (righe 3–5 del codice Java) è stato utilizzato per generare il testo del campo di immissione,
- la riga 7 del codice XHTML genera [4]. Ancora una volta, il metodo getInputText da [Form.java] viene utilizzato per generare il testo [4].
L'output 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 utilizzando il pulsante [Invia] [2]. Come risposta riceviamo la seguente pagina [3, 4]:
![]() |
Il valore del campo [1] viene inviato come segue:
In [2], il modulo viene inviato utilizzando il seguente pulsante:
<h:commandButton id="submit" type="submit" value="#{msg['form.submitText']}"/>
Il tag <h:commandButton> non ha alcun attributo action. In questo caso, non viene richiamato alcun gestore di eventi e non viene applicata alcuna regola di navigazione. Dopo l'elaborazione, viene restituita la stessa pagina. Esaminiamo il suo ciclo di elaborazione:

- in [A], la pagina P viene ripristinata esattamente come è stata inviata. Ciò significa che il componente con id inputText viene ripristinato con il suo valore iniziale "text",
- 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 "new text",
- in [C], avvengono le conversioni e le validazioni. Qui 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 sono state create regole di convalida. Le creeremo in seguito.
- In [D], i valori inseriti vengono assegnati al modello. Il campo inputText in [Form.java] riceve il valore "new text",
- in [E], non succede nulla perché nessun gestore di eventi è stato associato al pulsante [Validate].
- In [F], la pagina P viene rinviata al client poiché il pulsante [Validate] non ha un attributo action. Vengono quindi eseguite le seguenti righe di [index.xhtml]:
<!-- line 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 è "nuovo testo". Il risultato è la seguente visualizzazione:
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 al tag JSF <h:inputText>, con la differenza che ogni carattere digitato dall'utente viene visivamente sostituito da un asterisco (*).
Si consideri il codice seguente:
<!-- line 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 relativo modello in [Form.java]:
private String inputSecret="secret";
Quando la pagina [index.xhtml] viene richiesta per la prima volta, la pagina risultante è 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 in [Form.java]. Esiste un'eccezione quando il campo di immissione è di tipo "password". Il tag <h:inputSecret> viene utilizzato solo per leggere un'immissione, non per visualizzarla.
- La riga 7 del codice XHTML genera [4]. Qui, il metodo getInputSecret da [Form.java] è stato utilizzato per generare il testo [4] (vedi riga 1 del codice Java).
L'output 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 utilizzando il pulsante [Invia] [2]. Come risposta otteniamo la seguente pagina [3]:
![]() |
Il valore del campo [1] viene inviato come segue:
L'invio del modulo tramite [2] ha causato l'aggiornamento del modello [Form.java] con l'immissione da [1]. Il campo inputSecret in [Form.java] ha quindi ricevuto il valore "mdp". Poiché il modulo [index.xhtml] non ha definito alcuna regola di navigazione o gestore di eventi, viene visualizzato nuovamente dopo l'aggiornamento del suo modello. Si ritorna quindi alla vista visualizzata quando la pagina [index.xhtml] è stata inizialmente richiesta, dove è cambiato solo il valore del campo inputSecret del modello [3].
2.5.13. Tag <h:inputTextArea>
Il tag <h:inputTextArea> genera un tag HTML <textarea ...>text</textarea>. Si tratta di un campo di immissione simile a quello del tag JSF <h:inputText>, tranne per il fatto che qui è possibile digitare più righe di testo.
Si consideri il codice seguente:
<!-- line 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 risultante è la seguente:
![]() |
- la riga 2 del codice XHTML genera [1],
- il testo [2] è generato dalla riga 4. Il campo di immissione [3] è generato dalla riga [5]. Il suo contenuto è stato generato chiamando il metodo getInputTextArea del template, che ha restituito il valore definito nella riga 1 del codice Java sopra riportato,
- la riga 7 del codice XHTML genera [4]. Qui è stato utilizzato nuovamente il metodo getInputTextArea da [Form.java]. La stringa "line1\nline2" conteneva interruzioni di riga \n. Sono ancora presenti. Tuttavia, quando inserite in un flusso HTML, vengono visualizzate come spazi dai browser. Il tag HTML <textarea>, che visualizza [3], interpreta correttamente le interruzioni di riga.
L'output 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> generato dal tag JSF <h:inputTextArea>
Ora, qui sotto, inseriamo un valore nel campo di immissione [1] e inviamo il modulo utilizzando il pulsante [Invia] [2]. Come risposta otteniamo la seguente pagina [3]:
![]() |
Il valore del campo [1] che è stato inviato è il seguente:
L'invio del modulo tramite [2] ha causato l'aggiornamento del modello [Form.java] con l'input proveniente da [1]. Il campo textArea in [Form.java] ha quindi ricevuto il valore "JSF Tutorial\npart1". Ricaricando [index.xhtml] si vede che il campo textArea del modello è stato effettivamente aggiornato [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 una barra di scorrimento.
Si consideri il codice seguente:
<!-- line 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 risultante è la seguente:
![]() |
- La riga 2 del codice XHTML genera [1]
- il testo [2] è generato dalla riga 4. L'elenco a discesa [3] è generato dalle righe [5-9]. È il valore dell'attributo size="1" che fa 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> nelle 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 della voce. Questo è il valore che verrà inviato al controller [Faces Servlet] se la voce viene selezionata dall'elenco a discesa.
L'elemento visualizzato in [3] è stato determinato chiamando il metodo getSelectOneListBox1() (riga 5). Il risultato "2" ottenuto (riga 1 del codice Java) ha fatto sì che venisse visualizzato l'elemento alla riga 7 dell'elenco a discesa, poiché il suo attributo itemValue è "2",
- la riga 11 del codice XHTML genera [4]. Qui è stato utilizzato nuovamente il metodo getSelectOneListBox1 da [Form.java].
L'output 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 value="2" sia selezionato nell'elenco è indicato dalla presenza dell'attributo selected="selected".
Ora, qui sotto, selezioniamo [1] un nuovo valore dall'elenco e inviamo il modulo utilizzando il pulsante [Invia] [2]. Come risposta riceviamo la seguente pagina [3]:
![]() |
Il valore del campo [1] inviato è il seguente:
L'invio del modulo tramite [2] ha causato l'aggiornamento del modello [Form.java] con l'inserimento [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;
ora contiene il valore "3".
Quando la pagina [index.xhtml] viene visualizzata nuovamente dopo l'elaborazione, questo valore determina la visualizzazione [3,4] mostrata sopra:
- determina quale voce dell'elenco a discesa deve essere visualizzata [3],
- e il valore del campo selectOneListBox1 viene visualizzato in [4].
Consideriamo una variante del tag <h:selectOneListBox>:
<!-- line 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] per il tag <h:selectOneListBox> alla riga 5 è il seguente:
private String selectOneListBox2="3";
Quando la pagina [index.xhtml] viene richiesta per la prima volta, la pagina risultante è la seguente:
![]() |
- La riga 2 del codice XHTML genera [1],
- il testo [2] è generato dalla riga 4. L'elenco con la barra di scorrimento [3] è generato dalle righe [5-11]. È il valore dell'attributo size="3" che determina la creazione di un elenco con barra di scorrimento anziché di un elenco a discesa. Le voci dell'elenco sono state generate dai tag <f:selectItem> nelle righe 6–8,
L'elemento selezionato in [3] è stato individuato richiamando il metodo getSelectOneListBox2() (riga 5). Il risultato "3" ottenuto (riga 1 del codice Java) ha determinato la visualizzazione dell'elemento alla riga 8 dell'elenco, poiché il suo attributo itemValue è "3",
- la riga 13 del codice XHTML genera [4]. Qui è stato utilizzato nuovamente il metodo getSelectOneListBox2 da [Form.java].
L'output 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 value="3" sia selezionato nell'elenco comporta la presenza dell'attributo selected="selected".
Ora, qui sotto, selezioniamo [1] un nuovo valore dall'elenco e inviamo il modulo utilizzando il pulsante [Invia] [2]. Come risposta otteniamo la seguente pagina [3]:
![]() |
Il valore inserito nel campo [1] è il seguente:
L'invio del modulo tramite [2] ha causato l'aggiornamento del modello [Form.java] con i dati inseriti in [1]. L'elemento HTML
<option value="5">cinq</option>
. 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. Inoltre, dopo questo aggiornamento, il campo
private String selectOneListBox2;
contenga ora il valore "5".
Quando la pagina [index.xhtml] viene visualizzata nuovamente dopo l'elaborazione, questo valore determina la visualizzazione mostrata nei punti [3,4] sopra:
- determina quale voce dell'elenco deve essere selezionata [3],
- e il valore del campo selectOneListBox2 viene visualizzato in [4].
2.5.15. Tag <h:selectManyListBox>
Il tag <h:selectManyListBox> genera un tag HTML <select multiple="multiple">...</select> che consente all'utente di selezionare più voci da un elenco.
Si consideri il codice seguente:
<!-- line 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 risultante è la seguente:
![]() |
- La riga 2 del codice XHTML genera [1]
- il testo [2] è generato dalla riga 4. L'elenco [3] è generato dalle righe [5-11]. L'attributo size="3" fa sì che l'elenco visualizzi tre di questi elementi in qualsiasi momento. Gli elementi selezionati nell'elenco sono stati determinati chiamando il metodo getSelectManyListBox() (riga 5) del modello Java. Il risultato {"1","3"} (riga 1 del codice Java) è un array di elementi String. Ciascuno di questi elementi viene utilizzato per selezionare una delle voci dell'elenco. In questo caso, verranno selezionate le voci nelle righe 6 e 10 il cui attributo itemValue è presente nell'array {"1","3"}. Ciò è mostrato in [3].
- La riga 14 del codice XHTML genera [4]. In questo caso, la chiamata non viene effettuata al metodo getSelectManyListBox del modello di elenco Java, bensì al seguente metodo getSelectManyListBoxValue:
private String[] selectManyListBox=new String[]{"1","3"};
...
// getters et setters
public String getSelectManyListBoxValue(){
return getValue(selectManyListBox);
}
private String getValue(String[] chaines){
String value="[";
for(String chaine : chaines){
value+=" "+chaine;
}
return value+"]";
}
Se avessimo chiamato il metodo getSelectManyListBox, avremmo ottenuto un array di stringhe. Per includere questo elemento nell'output HTML, il controller avrebbe chiamato il suo metodo toString. Tuttavia, per un array, questo metodo restituisce solo l'“hashcode” dell'array e non l'elenco dei suoi elementi come vorremmo. Pertanto, utilizziamo il metodo getSelectManyListBoxValue sopra riportato per ottenere una stringa che rappresenta 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 nell'attributo onclick. Sarà incorporato nella pagina HTML che verrà generata dal codice JSF. Per comprenderlo, dobbiamo conoscere l'esatta natura di quella pagina.
L'output 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 indica che si tratta di un elenco a selezione multipla,
- il fatto che il modello dell'elenco sia l'array di String {"1","3"} significa che le voci dell'elenco nelle righe 4 (value="1") e 6 (value="3") hanno l'attributo selected="selected",
- riga 10: quando si fa clic sul pulsante [Clear], viene eseguito il codice JavaScript nell'attributo onclick. La pagina è rappresentata nel browser da un albero di oggetti spesso chiamato DOM (Document Object Model). Ogni oggetto nell'albero è accessibile al codice JavaScript tramite il suo attributo name. L'elenco alla riga 3 del codice HTML sopra riportato si chiama formulaire:selectManyListBox. Il modulo stesso può essere indicato in vari modi. Qui, viene indicato utilizzando la notazione this.form, dove this si riferisce al pulsante [Reset] e this.form si riferisce al modulo in cui si trova quel pulsante. L'elenco form:selectManyListBox si trova all'interno di questo stesso modulo. Pertanto, la notazione this.form['form:selectManyListBox'] si riferisce alla posizione dell'elenco nell'albero dei componenti del modulo. L'oggetto che rappresenta un elenco ha un attributo selectedIndex il cui valore è l'indice dell'elemento selezionato nell'elenco. Questo indice parte da 0 per indicare il primo elemento dell'elenco. Il valore -1 indica che nessun elemento è selezionato nell'elenco. Il codice JavaScript che imposta l'attributo selectedIndex su -1 deseleziona tutti gli elementi dell'elenco, se ne erano stati selezionati.
Ora, qui sotto, selezioniamo [1] nuovi valori dall'elenco (per selezionare più elementi nell'elenco, tieni premuto il tasto Ctrl mentre clicchi) e invia il modulo utilizzando il pulsante [Invia] [2]. Riceviamo la seguente pagina in risposta [3,4]:
![]() |
Il valore del campo [1] inviato è il seguente:
L'invio del modulo tramite [2] ha causato l'aggiornamento del modello [Form.java] con la voce [1]. Gli elementi HTML
<option value="3">trois</option>
<option value="4">quatre</option>
<option value="5">cinq</option>
sono state selezionate. Il browser ha inviato le tre stringhe "3", "4", "5" come valori per il 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, vediamo 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 dopo l'elaborazione, questo valore determina la visualizzazione di [3,4] sopra:
- determina quali elementi dell'elenco devono essere selezionati [3],
- e 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:
<!-- line 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 per il tag <h:selectOneMenu> in [Form.java] è il seguente:
private String selectOneMenu="1";
Quando la pagina [index.xhtml] viene richiesta per la prima volta, il codice sopra riportato genera la vista:
![]() |
Un esempio di esecuzione potrebbe essere il seguente:
![]() |
Il valore indicato 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:
<!-- line 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 per il tag <h:selectManyMenu> in [Form.java] è il seguente:
private String[] selectManyMenu=new String[]{"1","2"};
Quando la pagina [index.xhtml] viene richiesta per la prima volta, il codice sopra riportato 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 mostrato 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 questo in azione perché non possiamo mostrare le voci selezionate nel menu. Invitiamo i lettori a provarlo da soli (per selezionare più voci nell'elenco, tenere premuto il tasto Ctrl mentre si fa clic).
2.5.18. Tag <h:inputHidden>
Il tag <h:inputHidden> non ha alcuna rappresentazione visiva. Viene utilizzato esclusivamente per inserire un tag HTML <input type="hidden" value="..."/> nel flusso HTML della pagina. Quando sono inclusi all'interno di un tag <h:form>, i loro valori fanno parte dei dati inviati al server al momento dell'invio del modulo. Poiché si tratta di campi del modulo che l'utente non può vedere, vengono chiamati campi nascosti. Lo scopo di questi campi è quello di conservare i dati tra i diversi cicli di richiesta/risposta dello stesso client:
- il client richiede un modulo F. Il server lo invia e inserisce l'informazione I in un campo nascosto C, nel formato <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 rispedito al server. Il server può quindi recuperare l’informazione I che aveva memorizzato nella pagina. Questo crea una memoria tra i due cicli di richiesta/risposta,
- JSF stesso utilizza 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, i valori di tutti i componenti della pagina inviata al client. Quando il client invia la pagina dopo aver inserito i dati nel modulo, il campo nascosto javax.faces.ViewState viene rispedito insieme ai valori inseriti. Ciò consente al controller JSF di ricostruire la pagina così come era stata originariamente inviata. Questo meccanismo è stato spiegato a pagina 72.
Il codice JSF per l'esempio è il seguente:
<!-- ligne 9 -->
<h:outputText value="inputHidden" styleClass="info"/>
<h:inputHidden id="inputHidden" value="#{form.inputHidden}"/>
<h:outputText value="#{form.inputHidden}"/>
Il modello per il tag <h:inputHidden> in [Form.java] è il seguente:
private String inputHidden="initial";
Ciò determina la seguente visualizzazione quando la pagina [index.xhtml] viene richiesta per la prima volta:
- La riga 2 genera [1], la riga 4 genera [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>
Quando il modulo viene inviato, il valore "initial" del campo denominato form:inputHidden alla 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 sarà incluso nella nuova pagina inviata al client. Otteniamo quindi sempre lo screenshot sopra riportato.
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" ...>.
Si consideri il seguente codice JSF:
<!-- line 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 per il tag <h:selectBooleanCheckbox> alla riga 5 sopra in [Form.java] è il seguente:
private boolean selectBooleanCheckbox=true;
Quando la pagina [index.xhtml] viene richiesta per la prima volta, la pagina risultante è 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 un valore booleano true (vedi codice Java), la casella è stata selezionata,
- la riga 7 del codice XHTML genera [4]. Ancora una volta, il metodo getSelectBooleanCheckbox da [Form.java] viene utilizzato per generare il testo [4].
L'output 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], vediamo il tag HTML <input type="checkbox"> che è stato generato. Il valore true del modello associato ha fatto sì che l'attributo checked="checked" venisse aggiunto al tag. Questo fa sì che la casella di controllo sia selezionata.
Ora, qui sotto, deselezioniamo la casella di controllo [1], inviamo il modulo [2] e osserviamo il risultato [3, 4]:
![]() |
Poiché la casella di controllo non è selezionata, non viene inviato alcun valore per il campo [1].
L'invio del modulo tramite [2] ha causato l'aggiornamento del modello [Form.java] con l'input [1]. Il campo selectBooleanCheckbox in [Form.java] ha quindi ricevuto il valore false. Ricaricando [index.xhtml] si vede che il campo selectBooleanCheckbox nel modello è stato effettivamente aggiornato [3] e [4]. Vale la pena notare qui che è grazie al campo nascosto javax.faces.ViewState che JSF è stato in grado di determinare che la casella di controllo inizialmente selezionata era stata deselezionata dall'utente. Infatti, il valore di una casella di controllo deselezionata non è incluso nei valori inviati dal browser. Grazie all'albero dei componenti memorizzato nel campo nascosto javax.faces.ViewState, JSF determina che nel modulo era presente una casella di controllo denominata "selectBooleanCheckbox" e che il suo valore non è incluso nei valori inviati dal browser client. Può quindi concludere che era 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, di conseguenza, più tag HTML <input type="checkbox" ...>. Questo tag è la controparte del tag <h:selectManyListBox>, con la differenza che gli elementi selezionabili sono presentati come caselle di controllo adiacenti anziché come un elenco. Quanto detto riguardo al tag <h:selectManyListBox> vale anche in questo caso.
Si consideri il seguente codice JSF:
<!-- line 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 per il tag <h:selectManyCheckbox> alla riga 5 sopra 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 risultante è la seguente:
![]() |
- la riga 2 del codice XHTML genera [1],
- Il testo [2] è generato dalla riga 4. Le caselle di controllo [3] sono generate dalle righe 5–10. Per ciascuna di esse:
- l'attributo itemLabel definisce il testo visualizzato accanto alla casella di controllo;
- l'attributo itemvalue definisce il valore che verrà inviato al server se la casella di controllo è selezionata,
Il modello per le quattro caselle di controllo è il seguente campo Java:
private String[] selectManyCheckbox=new String[]{"1","3"};
Questo array definisce:
- quando la pagina viene visualizzata, quali caselle di controllo devono essere selezionate. Ciò avviene tramite il loro valore, ovvero il campo itemValue. Nell'esempio sopra riportato, saranno selezionate le caselle di controllo con valori nell'array {"1","3"}. Questo è ciò che viene mostrato nella schermata sopra;
- quando la pagina viene inviata, il modello selectManyCheckbox riceve l'array di valori relativi alle caselle di controllo che l'utente ha selezionato. Questo è ciò che vedremo tra poco;
- la riga 12 del codice XHTML genera [4]. È il seguente metodo getSelectManyCheckboxValue che ha generato [4]:
public String getSelectManyCheckboxValue(){
return getValue(getSelectManyCheckbox());
}
private String getValue(String[] chaines){
String value="[";
for(String chaine : chaines){
value+=" "+chaine;
}
return value+"]";
}
L'output HTML generato dal codice JSF precedente è 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 nelle righe 3 e 7 hanno l'attributo checked="checked", che fa sì che appaiano selezionati. Si noti che hanno tutti lo stesso attributo name="formulaire:selectManyCheckbox"; in altre parole, i quattro campi HTML hanno lo stesso nome. Se le caselle di controllo nelle righe 5 e 9 vengono selezionate dall'utente, il browser invierà i valori delle quattro caselle di controllo nel seguente formato:
e il modello per le quattro caselle di controllo
private String[] selectManyCheckbox=new String[]{"1","3"};
riceverà l'array {"2","4"}.
Verifichiamolo qui di seguito. In [1] apportiamo la modifica; in [2] inviamo 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.
Si consideri il seguente codice JSF:
<!-- line 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 per il tag <h:selectOneRadio> alla 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 risultante è la seguente:
![]() |
- la riga 2 del codice XHTML genera [1],
- il testo [2] è generato dalla riga 4. I pulsanti di opzione [3] sono 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 viene selezionato,
Il modello per i quattro pulsanti di opzione è il seguente campo Java:
private String selectOneRadio="2";
Questo modello definisce:
- quando la pagina viene visualizzata, il singolo pulsante di opzione che deve essere selezionato. Ciò avviene tramite il loro valore, ovvero il campo itemValue. Nell'esempio sopra riportato, verrà selezionato il pulsante di opzione con valore "2". Questo è ciò che viene mostrato nella schermata sopra riportata;
- quando la pagina viene inviata, il modello selectOneRadio riceve il valore del pulsante di opzione che è stato selezionato. Lo vedremo tra poco;
- la riga 12 del codice XHTML genera [4].
L'output 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 alla riga 8 ha 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", il che significa che i quattro campi HTML condividono lo stesso nome. Questo è il requisito per un gruppo di pulsanti di opzione mutuamente esclusivi: quando uno è selezionato, gli altri non lo sono.
Di seguito, in [1], selezioniamo uno dei pulsanti di opzione; in [2], inviamo 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 dell'elenco per i campi [1] e [2]. In questo caso, vengono generati dinamicamente dal codice Java, mentre nella versione precedente erano hardcoded nella pagina JSF.
2.6.2. Il progetto NetBeans
Il progetto NetBeans per l'applicazione è il seguente:
![]() |
Il progetto [mv-jsf2-04] è identico al progetto [mv-jsf2-03], con le seguenti differenze:
- in [1], nella pagina JSF, le voci dell'elenco non sono più hard-coded nel codice,
- in [2], il modello per la pagina JSF [1] verrà modificato,
- in [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">
<!-- languages -->
<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">
...
<!-- line 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}"/>
<!-- line 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}"/>
<!-- line 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}"/>
<!-- line 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}"/>
<!-- line 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"/>
...
<!-- line 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}"/>
<!-- line 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 visibili alle righe 26–28. In precedenza, il codice era il seguente:
<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 abbiamo questo:
<h:selectOneListbox id="selectOneListBox1" value="#{form.selectOneListBox1}" size="1">
<f:selectItems value="#{form.selectOneListbox1Items}"/>
</h:selectOneListbox>
I tre tag <f:selectItem> nelle righe 2–4 sono stati sostituiti dal singolo tag <f:selectItems> nella riga b. Questo tag ha un attributo value il cui valore è una collezione di elementi di tipo javax.faces.model.SelectItem. Sopra, il valore dell'attributo value viene ottenuto chiamando il seguente metodo [form].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;
}
- Nella riga 1, il metodo getSelectOneListbox1Items restituisce un array di elementi di tipo javax.faces.model.SelectItem costruito dal metodo privato getItems nella riga 5. Si noti che il metodo getSelectOneListbox1Items non è il getter di un campo privato denominato selectOneListBox1Items;
- la classe javax.faces.model.SelectItem dispone di vari costruttori.

Alla riga 8 del metodo getItems, utilizziamo 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) costruisce un array di qte elementi di tipo SelectItem, dove l'elemento i-esimo viene 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>
è 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 presenti nella pagina JSF. Il modello [Form.java] ora include 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 file di messaggi:
[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
I lettori sono invitati a testare questa nuova versione.
Molto spesso, gli elementi dinamici di un modulo sono il risultato dell'elaborazione della logica di business o provengono da un database:
![]() |
Esaminiamo la richiesta iniziale per la pagina JSF [index.xhtml] tramite una richiesta GET del browser:
- viene richiesta la pagina JSF [1],
- il controller [Faces Servlet] ne richiede la visualizzazione in [3]. Il motore JSF che elabora la pagina chiama il proprio modello [Form.java], ad esempio il metodo getSelectOneListBox1Items. Questo metodo potrebbe benissimo restituire un array di elementi di tipo SelectItem, basato sulle informazioni memorizzate in un database. Per farlo, chiamerebbe il livello [business] [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 ora assume la forma di una procedura guidata a più pagine:
![]() |
- in [1], pagina 1 del modulo — è accessibile anche tramite il link 1 in [2]
- in [2], un gruppo di 5 link.
- in [3], pagina 2 del modulo, accessibile tramite il link 2 in [2]
![]() |
![]() |
- in [4], pagina 3 del modulo, accessibile tramite il link 3 in [2]
- in [5], la pagina accessibile tramite il link "Segnala un'eccezione" in [2]
![]() |
- in [6], la pagina accessibile tramite il link 4 in [2]. Essa riassume le voci inserite nelle pagine da 1 a 3.
2.7.2. Il progetto NetBeans
Il progetto NetBeans per l'applicazione è il seguente:
![]() |
Il progetto [mv-jsf2-05] introduce due nuove funzionalità:
- In [1], la pagina JSF [index.xhtml] è suddivisa in tre pagine [form1.xhtml, form2.xhtml, form3.xhtml] tra le quali sono stati distribuiti i campi. La pagina [form4.xhtml] è una copia della pagina [index.xhtml] del progetto precedente. In [2], la classe [Form.java] rimane invariata. Fungerà da modello per le quattro pagine JSF sopra menzionate,
- in [3] viene aggiunta una pagina [exception.xhtml]: verrà utilizzata quando si verifica un'eccezione nell'applicazione.
2.7.3. Le pagine [form.xhtml] e il loro modello [Form.java]
2.7.3.1. Il codice della pagina 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">
<!-- links -->
<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"/>
<!-- line 1 -->
<h:outputText value="inputText" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.loginPrompt']}"/>
<h:inputText id="inputText" value="#{form.inputText}"/>
</h:panelGroup>
<!-- line 2 -->
<h:outputText value="inputSecret" styleClass="info"/>
<h:panelGroup>
<h:outputText value="#{msg['form.passwdPrompt']}"/>
<h:inputSecret id="inputSecret" value="#{form.inputSecret}"/>
</h:panelGroup>
<!-- line 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>
<!-- links -->
<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 mostrava i valori del modello, è stata rimossa. Questi valori saranno visualizzati da [form4.xhtml],
- righe 40–46: una tabella di sei link. I link alle righe 44 e 46 hanno una navigazione statica: il loro attributo action è hard-coded. Gli altri link hanno una navigazione dinamica: il loro attributo action punta a un metodo nel form bean responsabile di restituire la chiave di navigazione. I metodi a cui si fa riferimento in [Form.java] sono i seguenti:
// événements
public String doAction2(){
return "form2";
}
public String doAction4(){
return "form4";
}
public String doAlea(){
// un nombre aléatoire entre 1 et 3
int i=1+(int)(3*Math.random());
// on rend la clé de navigation
return "form"+i;
}
public String throwException() throws java.lang.Exception{
throw new Exception("Exception test");
}
Per ora ignoreremo il metodo throwException alla riga 17. Ci torneremo più tardi. I metodi doAction2 e doAction4 restituiscono semplicemente il tasto di navigazione senza eseguire alcuna elaborazione. Avremmo potuto scrivere altrettanto facilmente:
<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 genera una chiave di navigazione casuale il cui valore viene selezionato dall'insieme {"form1", "form2", "form3"}.
Il codice delle pagine [form2.xhtml, form3.xhtml, form3.xhtml] è simile a quello della pagina [form1.xhtml].
2.7.3.2. Durata del modello [Form.java] per le pagine [form*.xhtml]
Si consideri la seguente sequenza di azioni:
![]() |
- in [1], compiliamo la pagina 1 e passiamo alla pagina 3,
- in [2], compiliamo la pagina 3 e torniamo alla pagina 1,
![]() |
- in [3], la pagina 1 viene recuperata così come è stata inserita. Si torna quindi alla pagina 3,
- in [4], la pagina 3 viene trovata esattamente come era stata inserita.
Il meccanismo del campo nascosto [javax.faces.ViewState] non è sufficiente a spiegare questo fenomeno.
Quando si passa 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 "another text",
- il tasto di navigazione "form3" attiva la visualizzazione di [form3.xhtml]. Il ViewState incorporato in [form3.xhtml] contiene solo lo stato dei componenti presenti in [form3.xhtml], non quelli di [form1.xhtml].
Quando si passa da [2] a [3]:
- il modello [Form.java] viene aggiornato con il POST da [form3.xhtml]. Se il ciclo di vita del modello [Form.java] è impostato su "request", viene creato un oggetto [Form.java] completamente nuovo prima di essere aggiornato dal POST 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 proveniente da [form3.xhtml], nulla aggiorna il campo inputText, che fa parte del modello [form1.xhtml] e non di quello di [form3.xhtml],
- il tasto di navigazione "form1" fa sì che venga visualizzato [form1.xhtml]. La pagina visualizza il proprio template. Nel nostro caso, il campo di inserimento login collegato al template inputText visualizzerà "text" e non il valore "another text" inserito in [1]. Affinché il campo inputText mantenga il valore inserito in [1], l'ambito del modello [Form.java] deve essere session e non request. In questo caso,
- dopo la richiesta POST da [form1.xhtml], il modello verrà inserito nella sessione del client. Il campo inputText avrà il valore "some other text";
- Quando [form3.xhtml] viene inviato con POST, il modello verrà recuperato da questa sessione e aggiornato dal POST proveniente da [form3.xhtml]. Il campo inputText non verrà aggiornato da questo POST, ma manterrà il valore "some other text" acquisito dopo il POST da [form1.xhtml] [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
Rivediamo l'architettura generale di un'applicazione JSF:
![]() |
Cosa succede quando un gestore di eventi o un modello rileva un'eccezione proveniente dal livello business, come ad esempio una disconnessione imprevista dal database?
- I gestori di eventi [2a] possono intercettare qualsiasi eccezione proveniente dal livello [aziendale] e restituire al controller [Faces Servlet] una chiave di navigazione che punta a una pagina di errore specifica per l'eccezione;
- Per i modelli, questa soluzione non è praticabile perché quando vengono invocati [3,4], il sistema si trova nella fase di rendering di una specifica pagina XHTML e non più nella fase di selezione. Come è possibile cambiare pagina mentre ci si trova nella fase di rendering di una di esse? Una soluzione semplice, sebbene non sempre adeguata, consiste nel non gestire l'eccezione, che si propagherà quindi fino al contenitore di servlet che esegue l'applicazione. Il contenitore può essere configurato per visualizzare una pagina specifica quando un'eccezione lo raggiunge. Questa soluzione è sempre praticabile 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 viene effettuata 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>
Le righe 32–39 contengono la definizione di due pagine di errore. È possibile inserire tutti i tag <error-page> necessari. Il tag <location> specifica la pagina da visualizzare in caso di errore. Il tipo di errore associato alla pagina può essere definito in due modi:
- utilizzando il tag <exception-type>, che definisce il tipo Java dell'eccezione gestita. Pertanto, il tag <error-page> nelle righe 36–39 specifica che se il contenitore servlet intercetta un'eccezione di tipo [java.lang.Exception] o un tipo derivato (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 assicuriamo che tutte le eccezioni vengano gestite,
- 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, l'applicazione non interviene nella risposta. È il contenitore dei servlet a generare questa risposta inviando una pagina di errore predefinita. La prima riga della risposta HTTP contiene un codice di errore 404 che indica che la pagina P richiesta non esiste. Potresti voler generare una risposta che, ad esempio, segua la guida di stile visivo dell'applicazione o fornisca collegamenti per risolvere il problema. In questo caso, dovresti utilizzare un tag <error-page> con un tag <error-code>404</error-code>.
Sopra, il codice di errore HTTP 500 è il codice restituito in caso di "arresto anomalo" dell'applicazione. Questo è il codice che verrebbe restituito se un'eccezione fosse propagata fino al contenitore servlet. I due tag <error-page> nelle righe 28-35 sono quindi probabilmente ridondanti. Li abbiamo inclusi entrambi per illustrare i due modi di gestire un errore.
2.7.4.2. Simulazione dell'eccezione
Un'eccezione viene generata artificialmente dal link [Genera un'eccezione]:
![]() |
![]() |
Cliccando sul link [Genera un'eccezione] [1] viene visualizzata la pagina [2].
Nel codice delle pagine [formx.xhtml], il link [Genera un'eccezione] viene generato come segue:
<!-- liens -->
<h:panelGrid columns="6">
<h:commandLink value="1" action="form1"/>
...
<h:commandLink value="#{msg['form.exceptionLink']}" action="#{form.throwException}"/>
</h:panelGrid>
Alla riga 5, vediamo che quando si fa clic sul link, verrà eseguito il metodo [form].throwException. È il seguente:
public String throwException() throws java.lang.Exception{
throw new Exception("Exception test");
}
Qui viene generata un'eccezione di tipo [java.lang.Exception]. Essa si propagherà fino al contenitore servlet, che visualizzerà quindi la pagina [/faces/exception.xhtml].
2.7.4.3. Informazioni relative a un'eccezione
Quando un'eccezione viene propagata fino al contenitore servlet, il contenitore visualizza la pagina di errore corrispondente passando ad essa le informazioni relative all'eccezione. Queste informazioni vengono aggiunte come nuovi attributi alla richiesta attualmente in elaborazione. La richiesta del 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 dell'elaborazione della richiesta del browser.
![]() |
Una volta ricevuta la richiesta HTTP dal browser, il contenitore servlet la incapsula nell'oggetto Java [HttpServletRequest request] e crea l'oggetto [HttpServletResponse response], che verrà utilizzato per generare la risposta. Questo oggetto contiene, in particolare, il canale TCP/IP da utilizzare per il flusso di risposta HTTP. Tutti i livelli t1, t2, ..., tn coinvolti nell'elaborazione dell'oggetto richiesta hanno accesso a questi due oggetti. Ciascuno di essi può accedere agli elementi della richiesta iniziale e preparare la risposta arricchendo l'oggetto risposta. Ad esempio, un livello di localizzazione può impostare le impostazioni locali della risposta utilizzando il metodo response.setLocale(Locale l).
I vari livelli possono scambiarsi informazioni tramite l'oggetto richiesta. Questo oggetto dispone di un dizionario di attributi, inizialmente vuoto, che può essere popolato dai livelli di elaborazione successivi. Questi livelli possono inserire negli attributi dell'oggetto richiesta le informazioni necessarie al livello di elaborazione successivo. Esistono due metodi per gestire gli attributi dell'oggetto richiesta:
- void setAttribute(String s, Object o), che aggiunge agli attributi un oggetto o identificato dalla stringa s,
- Object getAttribute(String s), che recupera l'attributo o identificato dalla stringa s.
Quando un'eccezione viene propagata al contenitore servlet, il contenitore imposta i seguenti attributi nella richiesta in elaborazione:
key | valore |
il codice di errore HTTP che verrà restituito al client | |
il tipo Java dell'eccezione insieme al messaggio di errore. | |
l'URL richiesto quando 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>
<!-- links -->
<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. Espressioni nella pagina di eccezione
Nella catena di elaborazione delle richieste del client, la pagina XHTML è normalmente l'ultimo anello della catena:
![]() |
Tutti gli elementi della catena sono classi Java, compresa la pagina XHTML. Questa pagina viene convertita in un servlet dal contenitore di servlet, ovvero in una classe Java standard. Più specificamente, la pagina XHTML viene convertita 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, troverete il codice Java corrispondente alla pagina XHTML. Questo codice conterrà una serie di oggetti inizializzati dal metodo _jspService alla riga 1 sopra:
- riga 1: HttpServletRequest request: la richiesta attualmente in 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, l'oggetto application può avere attributi. Questi sono condivisi da tutte le richieste provenienti da tutti i client. Si tratta generalmente di attributi di sola lettura,
- riga 6: HTTPSession session: rappresenta la sessione del client. Come gli oggetti request e application, l'oggetto session può avere attributi. Questi sono condivisi da tutte le richieste provenienti dallo stesso client,
- riga 9: JspWriter out: un flusso di scrittura verso il browser del client. Questo oggetto è utile per il debug di una pagina XHTML. Qualsiasi cosa scritta tramite out.println(text) verrà visualizzata nel browser del client.
Quando si scrive #{espressione} in una pagina JSF, espressione può essere la chiave di un attributo degli oggetti request, session o application menzionati sopra. L'attributo corrispondente viene cercato in successione in questi tre oggetti. Pertanto, #{chiave} viene valutato come segue:
- request.getAttribute(key)
- session.getAttribute(key)
- application.getAttribute(key)
Non appena viene ottenuto un valore non nullo, la valutazione di #{key} si interrompe. Potresti voler essere più specifico indicando il contesto in cui l'attributo deve essere cercato:
- #{requestScope['key']} per cercare l'attributo nell'oggetto richiesta,
- #{sessionScope['key']} per cercare l'attributo nell'oggetto della sessione,
- #{applicationScope['key']} per cercare l'attributo nell'oggetto dell'applicazione.
Questo è ciò che è stato fatto nella pagina [exception.xhtml] a pagina 116. Gli attributi utilizzati sono i seguenti:
key | domain | valore |
richiesta | Vedi sezione 2.7.4.3. | |
stesso | stesso | |
uguale | stesso | |
stesso | stesso |
I vari messaggi richiesti per la pagina JSF [exception.xhtml] sono stati aggiunti ai file dei messaggi 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 dell'input dell'utente
2.8.1. L'applicazione
L'applicazione visualizza un modulo di immissione dati. Al termine della convalida, viene restituito lo stesso modulo come risposta, insieme a eventuali messaggi di errore qualora l'immissione risultasse errata.
![]() |
![]() |
2.8.2. Il progetto NetBeans
Il progetto NetBeans per l'applicazione è il seguente:
![]() |
Il progetto [mv-jsf2-06] si basa nuovamente su una singola pagina [index.html] [1] e sul relativo modello [Form.java] [2]. Continua a utilizzare i messaggi da [messages.properties], ma solo in francese [3]. L'opzione per cambiare la lingua non è disponibile.
2.8.3. L'ambiente applicativo
Qui riportiamo il contenuto dei file che configurano l'applicazione senza fornire spiegazioni specifiche. Questi file aiutano a comprendere meglio quanto 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>
<message-bundle>messages</message-bundle>
</application>
</faces-config>
La riga 17 è nuova. Verrà spiegata più avanti.
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">
<!-- line 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"/>
<!-- line 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}"/>
<!-- line 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}"/>
<!-- line 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}"/>
<!-- line 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}"/>
<!-- line 6 -->
...
<!-- line 7 -->
...
<!-- line 8 -->
...
<!-- line 9 -->
...
<!-- line 10 -->
...
<!-- line 11 -->
...
<!-- line 12 -->
...
<!-- line 13 -->
...
</h:panelGrid>
<!-- control buttons -->
<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 principale novità è l'uso dei tag:
- per visualizzare messaggi di errore <h:messages> (riga 14), <h:message> (righe 24, 29, 34),
- che impongono vincoli di validità sull'input <f:validateLongRange> (riga 39), <f:validateDoubleRange>, <f:validateLength>, <f:validateRegex>,
- che definiscono un convertitore tra l'input e il suo modello, come <f:convertDateTime>.
Il modello per 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() {
}
// foreclosures
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 = "";
// actions
public String submit() {
...
}
public String cancel() {
...
}
// validators
public void validateSaisie10(FacesContext context, UIComponent component, Object value) {
...
}
// getters and setters
...
}
La novità qui è che i campi del modello non sono più solo di tipo String, ma di vari tipi.
2.8.5. I diversi campi di input del modulo
Esamineremo ora uno per uno 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 seguente forma:
<!-- ligne 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;
Quando il browser invia una richiesta GET, la pagina [index.xhtml] associata al suo modello [Form.java] visualizza quanto segue:
- la riga 2 produce [1],
- la riga 3 produce [2],
- la riga 4 visualizza [3],
- la riga 5 visualizza [4].
Supponiamo che venga inserito e inviato il seguente input:
![]() |
Otteniamo 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 nota che il valore del campo `Integer` del modello denominato `saisie1` non è cambiato.
Spieghiamo cosa è successo. Per farlo, 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 suo modello:
private Integer saisie1 = 0;
- In [A], viene ripristinata la pagina [index.xhtml] inviata durante la richiesta GET del browser. In [A], la pagina è esattamente come l'utente l'ha ricevuta. Il componente id="saisie1" torna al suo valore iniziale "0",
- in [B], i componenti della pagina ricevono i valori inviati dal browser. In [B], la pagina è così come l'utente l'ha inserita e convalidata. Il componente id="saisie1" riceve 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 qui, dove il campo form.saisie1 è di tipo Integer. JSF tenterà di convertire il valore "x" del componente id="saisie1" in un tipo Integer. Ciò causerà un errore che interromperà il ciclo di elaborazione [A-F]. Questo errore sarà associato al componente id="saisie1". Tramite [D2], si procede quindi direttamente alla fase di rendering della risposta. Viene restituita la stessa pagina [index.xhtml],
- la fase [D] si verifica 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:
<!-- ligne 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 del file [index.xhtml]. Il tag <h:message for="idComposant"/> visualizza il messaggio di errore associato al componente specificato dall'attributo for, qualora si verifichi un errore. Il messaggio visualizzato in [2] è standard e si trova nel file [javax/faces/Messages.properties] all'interno dell'archivio [jsf-api.jar]:
![]() |
In [2], possiamo vedere 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" è un errore di conversione da un tipo String a un tipo Integer. Il messaggio di errore associato è quello alla 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:
![]() |
Nel messaggio si legge:
- il parametro {2} è stato sostituito dall'ID 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 ha due versioni: una versione sintetica e una versione dettagliata. È il caso delle righe 16–18:
Il messaggio con la chiave _detail (riga 2) è il cosiddetto messaggio dettagliato. L'altro è il cosiddetto messaggio di riepilogo. Il tag <h:message> visualizza di default il messaggio dettagliato. È possibile modificare questo comportamento utilizzando gli attributi showSummary e showDetail. Ecco come si procede per il componente con id saisie2:
<!-- ligne 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 seguente campo form.saisie2:
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 tutti i messaggi di errore sintetizzati provenienti da tutti i componenti, nonché i messaggi di errore non associati a un componente specifico, sotto forma di elenco. Anche in questo caso, gli attributi possono modificare questo comportamento predefinito:
- showDetail: true / false per abilitare o disabilitare i messaggi dettagliati,
- showSummary: true / false per visualizzare o nascondere i messaggi di riepilogo,
- globalOnly: true / false per specificare se visualizzare solo i messaggi di errore non associati ai 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 file di messaggi diverso. Questa modifica viene 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 di messaggi, ma non è quello utilizzato dai tag <h:message> e <h:messages>. Per definirlo è necessario utilizzare il tag <message-bundle> alla riga 9. La riga 9 indica ai tag <h:message(s)> che il file [messages.properties] deve essere cercato prima del file [javax.faces.Messages.properties]. Quindi, se aggiungiamo le seguenti righe al file [messages_fr.properties]:
# conversions
javax.faces.converter.IntegerConverter.INTEGER=erreur
javax.faces.converter.IntegerConverter.INTEGER_detail=erreur d\u00e9taill\u00e9e
l'errore restituito per i componenti input1 e input2 diventa:

Un altro modo per modificare il messaggio di errore di conversione è utilizzare l'attributo converterMessage del componente, come mostrato di seguito per il componente saisie3:
<!-- ligne 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 imposta esplicitamente il messaggio da visualizzare in caso di errore di conversione,
- riga 3, l'attributo required="true" indica che l'input è obbligatorio. Il campo non può rimanere vuoto. Un campo è considerato vuoto se non contiene caratteri o se contiene una sequenza di spazi. Anche in questo caso, esiste un messaggio predefinito in [javax.faces.Messages.properties]:
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 ottiene il seguente risultato:
![]() |
oppure questo:
![]() |
Verificare che un dato immesso sia effettivamente un numero intero non è sempre sufficiente. A volte è necessario verificare che il numero immesso rientri in un determinato intervallo. In questi casi, si utilizza un validatore. L'Input n. 4 ne fornisce un esempio. Il codice in [index.xhtml] è il seguente:
<!-- ligne 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 è associato al seguente modello form.saisie4:
private Integer saisie4 = 0;
Righe 3–5: Il tag <h:inputText> ha un tag figlio <f:validateLongRange> che accetta due attributi opzionali, minimum e maximum. Questo tag, noto anche come validatore, consente di aggiungere un vincolo al valore di input: non solo deve essere 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; e minore o uguale a maximum se è presente solo l'attributo maximum. Il validatore <f:validateLongRange> dispone di 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. Input 5 e 6: Inserimento di un numero reale
L'inserimento di numeri reali segue regole simili a quelle per l'inserimento di numeri interi. Il codice XHTML per le voci 5 e 6 è il seguente:
<!-- ligne 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}"/>
<!-- ligne 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] relativi ai componenti saisie5 e saisie6:
private Double saisie5 = 0.0;
private Double saisie6 = 0.0;
I messaggi di errore associati ai convertitori e ai validatori per i 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. Input 7: Inserimento di un valore booleano
L'immissione di un valore booleano dovrebbe normalmente avvenire tramite una casella di controllo. Se viene effettuata tramite un campo di immissione, la stringa "true" viene convertita nel valore booleano true, mentre qualsiasi altra stringa viene convertita nel valore booleano false.
Il codice XHTML per l'esempio:
<!-- ligne 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 per il componente saisie7:
private Boolean saisie7 = true;
Ecco un esempio di un input e della relativa risposta:
![]() |
In [1], il valore inserito. Una volta convertita, questa stringa "x" diventa il valore booleano false. Questo è mostrato in [2]. Il valore [3] del modello non è cambiato. Cambia solo una volta che tutte le conversioni e le convalide sulla pagina sono andate a buon fine. In questo esempio non è stato così.
2.8.5.4. Input 8: Inserimento di una data
In questo esempio, una data viene inserita utilizzando il seguente codice XHTML:
<!-- ligne 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 alla 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 vari attributi, tra cui l'attributo pattern, che specifica il formato della stringa da convertire in una data o il formato in cui una data deve essere visualizzata.
Quando la pagina [index.xhtml] viene richiesta per la prima volta, la precedente riga 8 viene visualizzata come segue:
I campi [1] e [2] visualizzano entrambi il valore del modello form.saisie8:
private Date saisie8 = new Date();
dove saisie8 è impostato sulla data corrente. Il convertitore utilizzato in entrambi i casi per visualizzare la data è il seguente:
<f:convertDateTime pattern="dd/MM/yyyy"/>
dove dd (giorno) indica il numero del giorno, MM (mese) il numero del mese e yyyy (anno) l'anno. In [1], il convertitore viene utilizzato per la conversione inversa java.lang.String --> java.util.Date. La data inserita deve 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 può essere sostituito con i propri messaggi. 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 chiave: saisie8.error:
saisie8.error=8-Vous devez entrer une date valide au format jj/mm/aaaa
Ecco un esempio:
![]()
2.8.5.5. Input 9: Inserimento di una stringa con lunghezza limitata
L'esempio 9 mostra come garantire che una stringa inserita abbia un numero di caratteri compreso in un intervallo specificato:
<!-- ligne 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"/> richiede che la stringa inserita contenga esattamente 4 caratteri. È possibile utilizzare solo uno degli attributi: minimum per un numero minimo di caratteri o maximum per un numero massimo.
Il modello form.saisie9 per il componente saisie9 alla riga 3 è il seguente:
private String saisie9 = "";
Esistono messaggi di errore predefiniti per questo tipo di convalida:
che può essere sostituito utilizzando l'attributo validatorMessage, come mostrato nella riga 3 sopra. Il messaggio per la 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. Input 9B: Inserimento di una stringa che deve rispettare un modello
L'esempio 9B mostra come imporre che una stringa inserita debba avere un numero di caratteri compreso in un intervallo:
<!-- ligne 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*$"/> richiede che la stringa inserita corrisponda a un modello di espressione regolare, in questo caso: una sequenza di 0 o più spazi, 2 cifre, i due punti (:), 2 cifre e una sequenza di 0 o più spazi.
Il modello form.saisie9B del componente saisie9B alla riga 3 è il seguente:
private String saisie9B;
Per questo tipo di convalida sono disponibili messaggi di errore predefiniti:
che può essere sostituito utilizzando l'attributo validatorMessage, come mostrato nella riga 3 sopra. Il messaggio per la 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. Voce 10: Scrivere un metodo di convalida specifico
Riassumendo: JSF consente di verificare, tra i valori inseriti, la validità di numeri (interi, decimali), date, lunghezza delle stringhe e se un valore inserito è conforme a un'espressione regolare. JSF consente di aggiungere validatori e convertitori personalizzati a quelli esistenti. Questo argomento non viene trattato in questa sede, ma è possibile fare riferimento a [ref2] per ulteriori dettagli.
Qui presentiamo un altro metodo: uno che prevede la convalida dei dati inseriti utilizzando un metodo del modello del modulo. Ecco un esempio:
<!-- ligne 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 alla riga 3 è il seguente:
private Integer saisie10 = 0;
Vogliamo che il numero inserito sia <1 o >7. Non possiamo verificarlo utilizzando i validatori di base di JSF. Scriviamo quindi un nostro metodo di validazione per il componente saisie10. Lo specifichiamo utilizzando 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 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 essere quella mostrata nella riga 1:
- FacesContext context: il contesto di esecuzione della pagina — fornisce l'accesso a varie informazioni, inclusi gli oggetti di richiesta HttpServletRequest e di risposta HttpServletResponse,
- UIComponent component: il componente da convalidare. Il tag <h:inputText> è rappresentato da un componente UIInput derivato da UIComponent. In questo caso, tale componente UIInput viene passato come secondo parametro,
- Object value: il valore inserito da verificare, convertito nel tipo del modello. È importante comprendere che se la conversione da String al tipo del modello fallisce, il metodo di validazione non viene eseguito. Quando viene chiamato il metodo validateSaisie10, significa che la conversione da String a Integer ha avuto esito positivo. Il terzo parametro è quindi di tipo Integer.
- Riga 2: il valore inserito viene convertito in tipo int,
- riga 3: verifichiamo che il valore inserito sia <1 o >7. In tal caso, la convalida è completa. In caso contrario, il validatore deve segnalare l'errore generando un'eccezione ValidatorException.
La classe ValidatorException ha due costruttori:
![]() |
- il costruttore [1] accetta 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 l'oggetto Throwable o la causa derivata dell'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>,
- String detail: la versione dettagliata del messaggio di errore, visualizzata dai tag <h:message> e <h:messages showDetail="true">.
È possibile utilizzare uno qualsiasi dei costruttori; i parametri mancanti possono essere impostati in un secondo momento utilizzando i metodi set.
Il costruttore [1] non consente di specificare un messaggio da un file di messaggi internazionalizzato. Questo è ovviamente un peccato. David Geary e Cay Horstmann [rif. 2] affrontano questa lacuna nel loro libro "Core JavaServer Faces" con la classe di utilità com.corejsf.util.Messages. Questa è la classe utilizzata nella riga 4 del codice Java per creare il messaggio di errore. Contiene solo metodi statici, incluso il metodo getMessage utilizzato nella 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 fare riferimento al file `[messages.properties]`. Prima di utilizzare il file specificato dal primo parametro, `getMessage` tenta di utilizzare il file dei messaggi dell'applicazione, se esiste. Pertanto, se in `[faces-config.xml]` abbiamo dichiarato un file dei messaggi con il tag:
<application>
...
<message-bundle>messages</message-bundle>
</application>
possiamo passare null come primo parametro al metodo getMessage. Questo è ciò che è stato fatto qui (vedi [web.xml], pagina 120),
- String resourceId: la chiave del messaggio da recuperare dal file dei messaggi. Abbiamo visto che un messaggio può avere sia una versione sintetica che una versione dettagliata. resourceId è l'identificatore della versione sintetica. La versione dettagliata verrà recuperata automaticamente utilizzando la chiave resourceId_detail. Pertanto, avremo due messaggi in [messages.properties] per l'errore alla voce 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 presenti. Entrambe le versioni devono essere presenti; in caso contrario, viene generata un'eccezione [NullPointerException],
- Object[] params: i parametri effettivi del messaggio se questo ha parametri formali {0}, {1}, ... Questi parametri formali saranno sostituiti dagli elementi dell'array params.
Torniamo al codice del metodo di validazione 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], FacesMessage viene creato utilizzando il metodo statico Messages.getMessage,
- in [5], viene impostato il livello di gravità del messaggio,
- in [6], viene generata un'eccezione ValidatorException con il messaggio costruito in precedenza. Il metodo di convalida è stato chiamato dal seguente codice XHTML:
<!-- ligne 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}"/>
Riga 3: Il metodo di convalida viene eseguito per il componente con l'ID "saisie10". Pertanto, il messaggio di errore generato dal metodo validateSaisie10 è associato a questo componente e viene visualizzato dalla riga 4 (attributo for="saisie10"). La versione dettagliata viene visualizzata per impostazione predefinita dal tag <h:message>.
Ecco un esempio di esecuzione:
![]()
2.8.5.8. Voci 11 e 12: Convalida di un gruppo di componenti
Finora, i metodi di convalida che abbiamo visto hanno convalidato solo un singolo componente. Cosa succede se la convalida desiderata coinvolge più componenti? È proprio quello che vedremo ora. Nel modulo:

vogliamo che le voci 11 e 12 siano due numeri interi la cui somma sia pari a 10.
Il codice JSF sarà il seguente:
<!-- line 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}"/>
<!-- line 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 = "";
Nella riga 3 del codice JSF, utilizziamo le tecniche già presentate per verificare che il valore inserito per il componente saisie11 sia effettivamente un numero intero. Lo stesso vale, nella riga 11, per il componente saisie12. Per verificare che saisie11 + saisie12 = 10, potremmo creare un validatore specifico. Questa è la soluzione preferibile. Anche in questo caso, faremo riferimento a [ref2] per saperne di più. Qui, stiamo adottando un approccio diverso.
La pagina [index.xhtml] viene convalidata da un pulsante [Validate] il cui codice JSF è il seguente:
<!-- boutons de commande -->
<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 [Validate]. È il seguente:
// actions
public String submit() {
// latest validations
validateForm();
// we return the same form
return null;
}
// global validations
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 e hanno superato i controlli,
- i campi nel modello [Form.java] hanno ricevuto i valori inviati dal client.
Infatti, rivediamo il ciclo di elaborazione di un POST JSF:
![]() |
Il metodo submit è un gestore di eventi. Gestisce l'evento click sul pulsante [Validate]. 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]. Quindi non è più necessario lanciare eccezioni di tipo [ValidatorException] come facevamo in precedenza. Ci limiteremo a inviare nuovamente il modulo con i messaggi di errore:
![]() |
In [1], avviseremo l'utente, mentre in [2] e [3] visualizzeremo un indicatore di errore. Nel codice JSF, il messaggio [1] verrà generato come segue:
<h:form id="formulaire">
<h:messages globalOnly="true" />
<h:panelGrid columns="4" columnClasses="col1,col2,col3,col4" border="1">
<!-- ligne 1 -->
...
Alla riga 2, il tag <h:messages> visualizza, per impostazione predefinita, la versione sintetica dei messaggi di errore relativi a tutti i campi del modulo non validi, nonché tutti i messaggi di errore non associati a componenti. L'attributo globalOnly="true" limita la visualizzazione a questi ultimi.
I messaggi [2] e [3] vengono visualizzati utilizzando semplici tag <h:outputText>:
<!-- line 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}"/>
<!-- line 13 -->
...
<h:outputText value="#{form.errorSaisie12}" styleClass="error"/>
...
Righe 4–7: Il componente saisie11 presenta due possibili messaggi di errore:
- uno che indica una conversione non valida o dati mancanti. Questo messaggio, generato dallo stesso JSF, sarà contenuto in un tipo FacesMessage e visualizzato dal tag <h:message> alla riga 5,
- quello che genereremo se input11 + input12 non è uguale a 10. Verrà visualizzato alla riga 6. Il messaggio di errore sarà contenuto nel template form.errorSaisie11.
I due messaggi corrispondono a errori che non possono verificarsi contemporaneamente. Il controllo input11 + input12 = 10 viene eseguito nel metodo submit, che viene eseguito solo se nel modulo non sono presenti errori residui. Quando viene eseguito, il componente input11 sarà stato convalidato e il suo modello form.input11 avrà ricevuto il suo valore. Il messaggio alla riga 5 non verrà più visualizzato. Al contrario, se il messaggio alla riga 5 viene visualizzato, significa che nel modulo è presente almeno un errore e il metodo submit non verrà eseguito. Il messaggio alla riga 6 non verrà visualizzato. Per garantire che i due possibili messaggi di errore si trovino nella stessa colonna della tabella, sono stati raggruppati all'interno di un tag <h:panelGroup> (righe 4 e 7).
Il metodo submit è il seguente:
// actions
public String submit() {
// latest validations
validateForm();
// we return the same form
return null;
}
// global validations
private void validateForm() {
if ((saisie11 + saisie12) != 10) {
// global msg
FacesMessage message = Messages.getMessage(null, "saisies11et12.incorrectes", null);
message.setSeverity(FacesMessage.SEVERITY_ERROR);
FacesContext context = FacesContext.getCurrentInstance();
context.addMessage(null, message);
// field-related msg
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 verifiche finali,
- riga 11: si verifica se input11 + input12 = 10,
- in caso contrario, righe 13–14, viene creato un 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ì costruito viene aggiunto (righe 15–16) all'elenco dei messaggi di errore dell'applicazione. Questo messaggio non è collegato a un componente specifico. Si tratta di un messaggio globale dell'applicazione. Verrà visualizzato dal tag <h:messages globalOnly="true"/> mostrato sopra,
- Riga 18: creiamo un nuovo oggetto FacesMessage con l'ID "error.sign". Ecco come appare:
error.sign="!"
Abbiamo accennato al fatto che il metodo statico [Messages.getMessage] costruisce un FacesMessage con una versione sintetica e una versione dettagliata, se esistono. Qui esiste solo la versione sintetica del messaggio error.sign. Otteniamo la versione sintetica di un messaggio m utilizzando m.getSummary(). Nelle righe 19 e 20, la versione sintetica del messaggio error.sign viene inserita nei campi errorSaisie11 ed errorSaisie12 del modello. Verranno visualizzati dai seguenti tag JSF:
<h:outputText value="#{form.saisie11}"/>
...
<h:outputText value="#{form.saisie12}"/>
- Righe 22–23: Se la condizione `saisie11 + saisie12 = 10` è vera, i due campi `errorSaisie11` e `errorSaisie12` nel modello vengono azzerati in modo da cancellare qualsiasi messaggio di errore precedente. È importante ricordare che il modello viene mantenuto tra una richiesta e l'altra nella sessione del client.
Ecco un esempio di esecuzione:
![]() |
Si noti nella colonna [1] che il modello ha ricevuto i valori inviati, indicando che tutte le operazioni di convalida e conversione tra i valori inviati e il modello hanno avuto esito positivo. Il gestore di eventi form.submit, che gestisce il clic sul pulsante [Convalida], è stato quindi in grado di eseguire l'operazione. È questo gestore che ha generato i messaggi visualizzati in [2] e [3]. Possiamo vedere che il modello è stato aggiornato anche se il modulo è stato rifiutato e rinviato al client. In un caso del genere, potresti preferire che il modello non venga aggiornato. Infatti, se l'utente annulla l'aggiornamento utilizzando il pulsante [Annulla] [4], non potrai tornare al modello iniziale a meno che tu non lo abbia salvato.
2.8.5.9. Invio di un modulo tramite POST senza convalida dell'input
Consideriamo il modulo sopra riportato e supponiamo che l'utente, non rendendosi conto dei propri errori, voglia annullare l'invio del modulo. Utilizzerà quindi il pulsante [Annulla] generato dal seguente codice JSF:
<!-- boutons de commande -->
<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 [Annulla] verrà eseguito solo se il modulo è valido. Questo è quanto abbiamo illustrato per il metodo form.submit associato al pulsante [Invia]. Se l'utente desidera annullare l'invio del modulo, ovviamente non è necessario 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 POST di JSF:
![]() |
Gli eventi per i componenti di azione <h:commandButton> e <h:commandLink> con l'attributo immediate="true" vengono elaborati nella fase [C], dopodiché il ciclo JSF procede direttamente alla fase [E] per il 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 fa clic sul pulsante [Annulla] nel modulo precedente, viene visualizzata la pagina seguente:
![]() |
- il modulo viene visualizzato nuovamente perché il gestore di eventi form.cancel imposta il tasto di navigazione su null. Viene quindi restituita la pagina [index.xhtml],
- il modello [Form.java] è stato modificato dal metodo form.cancel. Ciò si riflette nella colonna [2], che mostra questo modello,
- mentre la colonna [3] riflette il valore inviato per i componenti.
Torniamo al codice JSF per il componente saisie1 [4];
<!-- ligne 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 è associato al modello form.saisie1. Ciò comporta diverse conseguenze:
- durante una richiesta GET a [index.xhtml], il componente saisie1 visualizzerà il valore del modello form.saisie1,
- durante una richiesta POST a [index.xhtml], il valore inviato per il componente saisie1 viene assegnato al modello form.saisie1 solo se tutte le convalide e le conversioni del modulo hanno esito positivo. Indipendentemente dal fatto che il modello ` ` sia stato aggiornato dai valori inviati, se il modulo viene inviato a seguito della richiesta POST, i componenti visualizzano il valore che è stato inviato e non il valore del modello ad essi associato. Ciò è mostrato nella schermata sopra, dove le colonne [2] e [3] non hanno gli stessi valori.
2.9. Esempio mv-jsf2-07: Eventi relativi alle modifiche dello stato dei componenti JSF
2.9.1. L'applicazione
L'applicazione mostra un esempio di richiesta POST eseguita senza l'uso di un pulsante o di un link. Il modulo è il seguente:
![]() |
Il contenuto di combo2 [2] è collegato all'elemento selezionato in combo1 [1]. Quando la selezione in [1] viene modificata, viene inviata una richiesta POST, durante la quale il contenuto di combo2 viene aggiornato per riflettere l'elemento selezionato in [1], quindi il modulo viene inviato. Durante questa richiesta POST non viene eseguita alcuna convalida.
2.9.2. Il progetto NetBeans
Il progetto NetBeans per l'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">
<!-- headers -->
<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"/>
<!-- line 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}"/>
<!-- line 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}"/>
<!-- line 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>
<!-- control buttons -->
<h:panelGrid columns="2" border="0">
<h:commandButton value="#{msg['submit']}"/>
...
</h:panelGrid>
</h:form>
</h:body>
</html>
La nuova funzionalità si trova nel codice di combo1, righe 24–26. Appaiono nuovi attributi:
- onchange: attributo HTML — dichiara una funzione o un codice JavaScript da eseguire quando l'elemento selezionato in combo1 cambia. Qui, il codice JavaScript submit() invia il modulo al server,
- valueChangeListener: attributo JSF — specifica il nome del metodo da eseguire sul server quando cambia l’elemento selezionato in combo1. In totale, vengono eseguiti due metodi: uno sul lato client, l’altro sul lato server,
- immediate=true: attributo JSF – imposta il momento in cui deve essere eseguito il gestore di eventi lato server: dopo che il modulo è stato ricostruito in base a quanto inserito dall'utente, ma prima dei controlli di validazione degli input. L'obiettivo qui è popolare l'elenco di combo2 in base all'elemento selezionato nell'elenco di combo1, anche se ci sono voci non valide in altre parti del modulo. Ecco un esempio:
![]() |
- in [1], una voce iniziale,
- in [2], cambiamo l'elemento selezionato in combo1 da A a B.
Il risultato è il seguente:
![]() |
La richiesta POST è stata inviata. Il contenuto di combo2 [2] è stato aggiornato in modo da corrispondere all'elemento selezionato in combo1 [1], anche se l'input [3] era errato. L'attributo immediate=true ha fatto sì che il metodo form.combo1ChangeListener venisse eseguito prima dei controlli di validità. Senza questo attributo, non sarebbe stato eseguito perché il ciclo di elaborazione si sarebbe interrotto ai controlli di validità a causa dell'errore in [3].
I messaggi associati al modulo sono i seguenti in [messages.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
L'ambito di [Form.java] è impostato su request:
package forms;
...
@ManagedBean
@RequestScoped
public class Form {
Riga 6: Impostiamo l'ambito del bean su request.
2.9.5. La classe [Form.java]
La classe [Form.java] è la 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() {
}
// form fields
private String combo1="A";
private String combo2="A1";
private Integer saisie1=0;
// working fields
final private String[] combo1Labels={"A","B","C"};
private String combo1Label="A";
private static final Logger logger=Logger.getLogger("forms.Form");
// methods
public SelectItem[] getCombo1Items(){
// init 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(){
// init combo2 as a function of 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;
}
// listeners
public void combo1ChangeListener(ValueChangeEvent event){
// follow-up
logger.info("combo1ChangeListener");
// retrieve the posted value of combo1
combo1Label=(String)event.getNewValue();
// we return the answer because we want to short-circuit the validations
FacesContext.getCurrentInstance().renderResponse();
}
public String raz(){
// follow-up
logger.info("raz");
// raz du formulaire
combo1Label="A";
combo1="A";
combo2="A1";
saisie1=0;
return null;
}
// getters - setters
...
}
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>
Recupera i propri elementi tramite il metodo getCombo1Items del proprio modello (riga 2). Questo metodo è 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 suoi elementi tramite il metodo getCombo2Items del suo modello (riga 2). Questo metodo è definito nelle righe 37–44 del codice Java. Genera un elenco di cinque elementi {"X1", "X2", "X3", "X4", "X5"}, dove X è l'elemento combo1Label della riga 16. Pertanto, quando il modulo viene generato inizialmente, l'elenco combo2 contiene gli elementi {"A1", "A2", "A3", "A4", "A5"}.
Quando l'utente modifica l'elemento selezionato nell'elenco combo1,
- l'evento onchange="submit();" verrà gestito dal browser client. Il modulo verrà quindi inviato al server,
- sul lato server, JSF rileverà che il componente combo1 ha modificato il proprio valore. Verrà eseguito il metodo combo1ChangeListener nelle righe 47–54. Un metodo ValueChangeListener riceve come parametro un oggetto di tipo javax.faces.event.ValueChangeEvent. Questo oggetto consente di recuperare i valori precedenti e nuovi del componente che è stato modificato utilizzando i seguenti metodi:

In questo caso, il componente è l'elenco 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 viene utilizzato per generare gli elementi nell'elenco combo2,
- Riga 53: viene inviata la risposta. È importante notare qui che il gestore combo1ChangeListener viene eseguito con l'attributo immediate="true". Viene quindi eseguito dopo che l'albero dei componenti della pagina è stato aggiornato con i valori inviati e prima del processo di convalida di tali valori. Tuttavia, vogliamo bypassare questo processo di convalida perché l'elenco combo2 deve essere aggiornato anche se ci sono ancora voci non valide in altre parti del modulo. Chiediamo quindi che la risposta venga inviata immediatamente senza passare attraverso la fase di convalida degli input.
- Il modulo verrà rinviato esattamente come è stato inserito. Tuttavia, gli elementi negli elenchi combo1 e combo2 non sono valori inviati. Verranno rigenerati chiamando i metodi getCombo1Items e getCombo2Items. Quest'ultimo metodo utilizzerà quindi il nuovo valore di combo1Label impostato da combo1ChangeListener e gli elementi nell'elenco combo2 cambieranno.
2.9.6. Il pulsante [ Reset]
Vogliamo che il pulsante [Reset] ripristini il modulo al suo stato iniziale, come mostrato di seguito:
![]() |
![]() |
![]() |
In [1] viene inviato il modulo prima del pulsante [Raz]; in [2], il risultato dell'invio.
Sebbene semplice dal punto di vista funzionale, la gestione di questo caso d'uso risulta piuttosto complessa. Possiamo provare varie soluzioni, inclusa quella utilizzata per il pulsante [Annulla] nell'esempio precedente:
<h:commandButton value="#{msg['raz']}" immediate="true" action="#{form.raz}"/>
dove il metodo form.raz è il seguente:
public String raz(){
// raz du formulaire
combo1Label="A";
combo1="A";
combo2="A1";
saisie1=0;
return null;
}
Il risultato ottenuto cliccando sul pulsante [Cancella] nell'esempio precedente è 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 inviato era "B". Questo elemento è quindi selezionato nell'elenco,
- per combo2, il valore inviato 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 quindi non può essere selezionato. Viene quindi visualizzato il primo elemento dell'elenco,
- per input1, 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 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['form'].submit();
}
</script>
...
<h:commandButton value="#{msg['raz']}" onclick='raz()' immediate="true" action="#{form.raz}"/>
- Riga 10: l'attributo onclick='raz()' indica al browser di eseguire la funzione JavaScript raz quando l'utente fa clic sul pulsante [Reset].
- riga 3: il valore "A" viene assegnato all'elemento HTML denominato 'form:combo1'. I vari elementi nella riga 3 sono i seguenti:
- document: la pagina visualizzata dal browser,
- document.forms: tutti i moduli nel documento,
- document.forms['form']: il modulo con l'attributo name="form",
- document.forms['form'].elements: tutti gli elementi del modulo con l'attributo name="form",
- document.forms['form'].elements['form:combo1']: l'elemento del modulo con l'attributo name impostato su "form:combo1"
- document.forms['form'].elements['form:combo1'].value: valore che verrà inviato dall'elemento del modulo con l'attributo name="form:combo1".
Per trovare gli attributi name dei vari elementi nella pagina visualizzata dal browser, è possibile visualizzarne il codice sorgente (di seguito con IE7):
![]() |
<form id="formulaire" name="formulaire" ...>
...
<select id="formulaire:combo1" name="formulaire:combo1" ...>
Chiarito questo, possiamo vedere che nel codice JavaScript della funzione raz:
- la riga 3 garantisce che il valore inviato per il componente combo1 sarà la stringa A,
- la riga 4 garantisce che il valore inviato per il componente combo2 sarà la stringa A1,
- la riga 5 garantisce che il valore inviato per il componente saisie1 sarà la stringa 0.
Una volta fatto ciò, verrà eseguito il POST del modulo, associato a qualsiasi pulsante di tipo <h:commandButton> (riga 10). Verrà eseguito il metodo form.raz e il modulo verrà restituito così come è stato inviato. Otteniamo quindi il seguente risultato:
![]() |
Questo risultato nasconde molte cose. I valori "A", "A1" e "0" dei componenti combo1, combo2 e saisie1 vengono inviati al server. Supponiamo che il valore precedente di combo1 fosse "B". A questo punto si verifica una modifica nel 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 {
...
// form fields
private String combo1="A";
private String combo2="A1";
private Integer saisie1=0;
// working fields
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){
// follow-up
logger.info("combo1ChangeListener");
// retrieve the posted value of combo1
combo1Label=(String)event.getNewValue();
// we return the answer because we want to short-circuit the validations
FacesContext.getCurrentInstance().renderResponse();
}
public String raz(){
// follow-up
logger.info("raz");
// raz du formulaire
combo1Label="A";
combo1="A";
combo2="A1";
saisie1=0;
return null;
}
...
}
- riga 16: viene creato un logger. Il parametro getLogger ci permette di differenziare le fonti dei log. Qui, il logger si chiama forms.Form,
- riga 21: viene registrato il richiamo al metodo combo1ChangeListener,
- riga 30: viene registrato l'accesso al metodo raz.
Quali registri vengono generati dal pulsante [Cancella] o quando cambia il valore di combo1? Consideriamo diversi scenari:
- Utilizziamo il pulsante [Clear] mentre l'elemento selezionato in combo1 è "A". "A" è quindi l'ultimo valore del componente combo1. Abbiamo visto che il pulsante [Clear] esegue una funzione JavaScript che invia il valore "A" per il componente combo1. Il componente combo1 non cambia quindi il proprio valore. I log mostrano quindi che viene eseguito solo il metodo form.raz:
- Utilizziamo il pulsante [Clear] quando l'elemento selezionato in combo1 non è "A". Il componente combo1 modifica quindi il proprio valore: il suo ultimo valore non era "A" e il pulsante [Clear] lo imposterà su "A". I log mostrano quindi che vengono eseguiti due metodi, nel seguente ordine: combo1ChangeListener, raz:
![]() |
- Modifichiamo 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 visualizza 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 per l'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]
Esaminiamo 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>
Riga 14: Il tag <h:dataTable> utilizza il campo #{form.personnes} come origine dati. È il seguente:
private List<Person> people;
La classe [Person] è la seguente:
package forms;
public class Personne {
// data
private int id;
private String nom;
private String prénom;
// manufacturers
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 and setters
...
}
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="person" imposta il nome della variabile che rappresenta la persona corrente all'interno del tag <h:dataTable>,
- l'attributo headerClass="headers" imposta lo stile delle intestazioni delle colonne della tabella,
- l'attributo columnClasses="...." imposta lo stile di ciascuna colonna della tabella.
Esaminiamo una delle colonne della tabella e vediamo come è costruita:
![]() |
Il codice XHTML per la 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 del cognome della tabella,
- righe 14–19: la colonna del nome della tabella.
Ora esaminiamo la colonna del link [Rimuovi]:
![]() |
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 [Rimuovi] viene generato dalle righe 4–6. Quando si fa clic sul link, verrà eseguito il metodo [Form].removePerson. È 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 {
// model
private List<Personne> personnes;
private int personneId;
// manufacturer
public Form() {
// initialization of the list of persons
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() {
// search for the selected person
int i = 0;
for (Personne personne : personnes) {
// current person = selected person?
if (personne.getId() == personneId) {
// delete the current person from the list
personnes.remove(i);
// we're done
break;
} else {
// next person
i++;
}
}
// we test on the same page
return null;
}
// getters and setters
...
}
- righe 18–24: il costruttore inizializza l'elenco delle persone dalla riga 14,
- riga 10: poiché questa lista deve persistere tra una richiesta e l'altra, l'ambito del bean è la sessione.
Quando viene eseguito il metodo [removePerson] alla riga 26, il campo alla riga 15 è stato inizializzato con l'ID della persona il cui link [Rimuovi] è 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 da rimuovere dall'elenco delle persone — viene copiato nel campo [Form].personneId tramite il getter di quel campo. Ciò avviene prima che venga eseguito il metodo a cui fa riferimento l'attributo action nella riga 1.
Righe 26–43: Il metodo [supprimerPersonne] rimuove la persona il cui ID è uguale a *personId*.
2.11. Esempio mv-jsf2-09: struttura di un'applicazione JSF
2.11.1. L'applicazione
L'applicazione mostra come strutturare un'applicazione JSF con due viste:
![]() |
L'applicazione presenta due viste:
- in [1], pagina 1,
- in [2], pagina 2.
È possibile navigare tra le due pagine. Quello che vogliamo mostrare qui è che le pagine 1 e 2 condividono un layout comune, come mostrato negli screenshot qui sopra.
2.11.2. Il progetto NetBeans
Il progetto NetBeans per l'applicazione è il seguente:
![]() |
L'applicazione è costituita esclusivamente da 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 utilizzati per formattare le pagine di un'applicazione. I tag in questo spazio dei nomi sono 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 utilizzando un browser:
![]() |
- in [1], l'URL richiesto.
L'area [2] è stata generata 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 codice XHTML esterno nella pagina. 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>
L'intero codice dalle righe 3–8 verrà inserito in [layout.xhtml]. Pertanto, i tag <html> e <body> verranno inseriti all'interno di un tag <td>. Ciò non causa alcun errore. Di conseguenza, le pagine incluse tramite <ui:include> sono pagine XHTML complete. Visivamente, 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> alla 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 "content". Si tratta di un'area in grado di contenere contenuti variabili. Vedremo come. Quando abbiamo richiesto la pagina [layout.xhtml], non era stato definito alcun contenuto per l'area denominata "content". In questo caso, viene utilizzato il contenuto del tag <ui:insert> alle 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 in questo caso 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>
- La riga 6 utilizza lo spazio dei nomi ui;
- alla riga 7, specifichiamo che la pagina è associata al modello [layout.xhtml] utilizzando un tag <ui:composition>,
- alla riga 8, questa associazione garantisce che ogni tag <ui:define> sarà associato a un tag <ui:insert> nel modello in uso, in questo caso [layout.xhtml]. Il collegamento viene stabilito tramite l'attributo name di entrambi i tag. Devono essere identici.
La pagina visualizzata è [layout.xhtml], dove il contenuto di ciascun 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> in [layout.xml].
La pagina [page2.xhtml] è simile 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. Conclusione
Lo studio su JSF 2 qui presentato è ben lungi dall'essere esaustivo. Tuttavia, è sufficiente per comprendere gli esempi che seguono. Per ulteriori approfondimenti, consultare [rif. 2].
2.13. Test con Eclipse
Vediamo come eseguire il test dei progetti Maven con SpringSource Tool Suite:
![]() |
- In [1], importare un progetto Maven [2] facendo clic sul pulsante [3]. In questo caso, utilizziamo il progetto Maven [mv-jsf2-09] per Eclipse
- In [4], il progetto importato è stato correttamente riconosciuto come progetto Maven [5],
![]() |
- in [6], il progetto viene importato nel Project Explorer,
- In [7], lo eseguiamo su un server Tomcat [8] [9],
![]() |
- in [10], è stato avviato Tomcat 7,
- in [11], la home page del progetto [mv-jsf2-09] [11] viene visualizzata in un browser all'interno di Eclipse.

























































































































































