10. Versione 5 - Applicazione Web PAM / JSF
10.1. Architettura dell’applicazione
L'architettura dell'applicazione web PAM sarà la seguente:
![]() |
In questa versione, il server Glassfish ospiterà tutti i livelli dell’applicazione:
- il livello [web] è ospitato dal contenitore di servlet del server (1 di seguito)
- gli altri livelli [metier, DAO, jpa] sono ospitati dal contenitore EJB3 del server (2 qui sotto)
![]() |
Gli elementi [metier, DAO] dell’applicazione in esecuzione nel contenitore EJB3 sono già stati descritti nell’applicazione client/server analizzata nel paragrafo 7.1, la cui architettura era la seguente:
![]() |
I livelli [metier, DAO] venivano eseguiti nel contenitore EJB3 del server Glassfish e il livello [ui] in un'applicazione console o Swing su un'altra macchina:
![]() |
Nell’architettura della nuova applicazione:
![]() |
è necessario scrivere solo il livello [web / jsf]. Gli altri livelli [metier, DAO, jpa] sono già disponibili.
Nel documento [ref3], viene illustrato che un’applicazione web in cui il livello web è implementato con Java Server Faces presenta un’architettura simile alla seguente:
![]() |
Questa architettura implementa il Design Pattern MVC (Modello, Vista, Controller). L'elaborazione di una richiesta da parte di un client avviene nel modo seguente:
Se la richiesta viene effettuata tramite un GET, vengono eseguiti i due passaggi seguenti:
- richiesta - il browser del cliente invia una richiesta al controller [Faces Servlet]. Quest’ultimo gestisce tutte le richieste dei clienti. È il punto di ingresso dell’applicazione. È la C di MVC.
- risposta - il controller C richiede alla pagina JSF selezionata di essere visualizzata. Si tratta della vista, la V di MVC. La pagina JSF utilizza un modello M per inizializzare le parti dinamiche della risposta che deve inviare al cliente. Questo modello è una classe Java che può ricorrere al livello [métier] [4a] per fornire alla vista V i dati di cui ha bisogno.
Se la richiesta viene effettuata con un POST, tra la richiesta e la risposta si inseriscono due fasi aggiuntive:
- richiesta - il browser del cliente invia una richiesta al controller [Faces Servlet].
- elaborazione - il controller C elabora questa richiesta. Infatti, una richiesta POST è accompagnata da dati che devono essere elaborati. A tal fine, il controller si avvale di gestori di eventi specifici dell’applicazione scritta [2a]. Tali gestori potrebbero aver bisogno del livello di business [2b]. Il gestore dell’evento potrebbe dover aggiornare alcuni modelli M [2c]. Una volta elaborata la richiesta del cliente, questa può generare diverse risposte. Un esempio classico è:
- una pagina di errore se la richiesta non è stata elaborata correttamente
- una pagina di conferma in caso contrario
Il gestore di eventi restituisce al controller [Faces Servlet] un risultato di tipo stringa di caratteri denominata chiave di navigazione.
- navigazione - il controller sceglie la pagina JSF (= vista) da inviare al cliente. Questa scelta viene effettuata in base alla chiave di navigazione restituita dal gestore di eventi.
- risposta - la pagina JSF selezionata invierà la risposta al cliente. Essa utilizza il proprio modello M per inizializzare le parti dinamiche. Anche questo modello può ricorrere al livello [métier] [4a] per fornire alla pagina JSF i dati di cui ha bisogno.
In un progetto JSF:
- il controller C è il servlet [javax.faces.webapp.FacesServlet]. Questo si trova nella libreria [jsf-api.jar].
- le viste V sono implementate dalle pagine JSF.
- i modelli M e i gestori di eventi sono implementati da classi Java spesso denominate «backing beans».
- Nelle versioni JSF e 1.x, la definizione dei bean e le regole di navigazione da una pagina all'altra sono definite nel file [faces-config.xml]. In esso sono riportati l'elenco delle viste e le regole di transizione da una all'altra. A partire dalla versione JSF 2, le definizioni dei bean possono essere effettuate tramite annotazioni e le transizioni tra le pagine possono essere implementate "in modo rigido" nel codice dei bean.
10.2. Funzionamento dell’applicazione
Quando l’applicazione viene richiamata per la prima volta, viene visualizzata la seguente pagina:
![]() |
Si compila quindi il modulo e si richiede lo stipendio:
![]() |
Si ottiene il seguente risultato:
![]() |
Questa versione calcola uno stipendio fittizio. Non bisogna prestare attenzione al contenuto della pagina, ma alla sua formattazione. Quando si utilizza il pulsante [Raz], si torna alla pagina [A].
Gli inserimenti errati vengono segnalati, come mostra il seguente esempio:
![]() |
10.3. Il progetto NetBeans
Realizzeremo una prima versione dell’applicazione in cui verrà simulato il livello [métier]. Avremo la seguente architettura:
![]() |
Quando i gestori di eventi o i modelli richiederanno dati al livello [métier] [2b, 4a], quest’ultimo fornirà loro dati fittizi. L’obiettivo è ottenere un livello web che risponda correttamente alle richieste dell’utente. Una volta raggiunto questo obiettivo, non resterà che installare il livello server sviluppato nel paragrafo 7.1:
![]() |
Questa sarà la versione 2 della versione web della nostra applicazione PAM.
Il progetto NetBeans della versione 1 è il seguente progetto Maven:
![]() |
- in [1], i file di configurazione
- in [2], le pagine in XHTML e il foglio di stile
- in [3], le classi del livello in [web]
- in [4], gli oggetti scambiati tra il livello [web] e il livello [métier] e lo stesso livello [métier]
- in [5], il file dei messaggi per l’internazionalizzazione dell’applicazione
- in [6], le dipendenze dell'applicazione
Esaminiamo alcuni di questi elementi.
10.3.1. I file di configurazione
Il file [web.xml] è quello generato di default da NetBeans, con in più la configurazione di una pagina di eccezione:
- riga 30: [index.html] è la pagina iniziale dell’applicazione
- righe 32-39: configurazione della pagina di eccezione
La pagina [exception.html] deriva da [ref3]. Il suo codice è il seguente:
Qualsiasi eccezione non gestita esplicitamente dal codice dell'applicazione web provocherà la visualizzazione di una pagina simile a quella riportata di seguito:
![]() |
Il file [faces-config.xml] sarà il seguente:
Si notino i seguenti punti:
- righe 9-14: il file [messages.properties] verrà utilizzato per l'internazionalizzazione delle pagine. Sarà accessibile nelle pagine XHTML tramite la chiave msg.
- riga 15: definisce il file [messages.properties] come file da consultare in via prioritaria per i messaggi di errore visualizzati dai tag <h:messages> e <h:message>. Ciò consente di ridefinire alcuni messaggi di errore predefiniti di JSF. Questa possibilità non viene utilizzata in questo caso.
10.3.2. Il foglio di stile
Il file [styles.css] è il seguente:
.libelle{
background-color: #ccffff;
font-family: 'Times New Roman',Times,serif;
font-size: 14px;
font-weight: bold
}
body{
background-color: #ffccff
}
.error{
color: #ff3333
}
.info{
background-color: #99cc00
}
.titreInfos{
background-color: #ffcc00
}
Ecco alcuni esempi di codice JSF che utilizzano questi stili:
| |
| |
| ![]() |
10.3.3. Il file dei messaggi
Il file dei messaggi [messages_fr.properties] è il seguente:
form.titre=Feuille de salaire
form.comboEmployes.libell\u00e9=Employ\u00e9
form.heuresTravaill\u00e9es.libell\u00e9=Heures travaill\u00e9es
form.joursTravaill\u00e9s.libell\u00e9=Jours travaill\u00e9s
form.heuresTravaill\u00e9es.required=Indiquez le nombre d'heures travaill\u00e9es
form.heuresTravaill\u00e9es.validation=Donn\u00e9e incorrecte
form.joursTravaill\u00e9s.required=Indiquez le nombre de jours travaill\u00e9s
form.joursTravaill\u00e9s.validation=Donn\u00e9e incorrecte
form.btnSalaire.libell\u00e9=Salaire
form.btnRaz.libell\u00e9=Raz
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\u00e9e lors de l'erreur
exception.servletName=Nom de la servlet demand\u00e9e lorsque l'erreur s'est produite
form.infos.employ\u00e9=Informations Employ\u00e9
form.employe.nom=Nom
form.employe.pr\u00e9nom=Pr\u00e9nom
form.employe.adresse=Adresse
form.employe.ville=Ville
form.employe.codePostal=Code postal
form.employe.indice=Indice
form.infos.cotisations=Informations Cotisations sociales
form.cotisations.csgrds=CSGRDS
form.cotisations.csgd=CSGD
form.cotisations.retraite=Retraite
form.cotisations.secu=S\u00e9curit\u00e9 sociale
form.infos.indemnites=Informations Indemnit\u00e9s
form.indemnites.salaireHoraire=Salaire horaire
form.indemnites.entretienJour=Entretien / Jour
form.indemnites.repasJour=Repas / Jour
form.indemnites.cong\u00e9sPay\u00e9s=Cong\u00e9s pay\u00e9s
form.infos.salaire=Informations Salaire
form.salaire.base=Salaire de base
form.salaire.cotisationsSociales=Cotisations sociales
form.salaire.entretien=Indemnit\u00e9s d'entretien
form.salaire.repas=Indemnit\u00e9s de repas
form.salaire.net=Salaire net
Questi messaggi sono tutti utilizzati nella pagina [index.xhtml], ad eccezione di quelli delle righe 11-15, utilizzati nella pagina [exception.xhtml].
10.3.4. L'ambito dei bean
Il bean [web.forms.Form] avrà un ambito «request»:
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
@ManagedBean
@RequestScoped
public class Form implements Serializable {
Il bean [web.utils.ChangeLocale] avrà un ambito «application»:
package web.utils;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{
// la pagina 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;
}
public void setLocale(String locale) {
this.locale = locale;
}
}
10.3.5. Il livello [métier]
Il livello [métier] implementa la seguente interfaccia IMetierLocal:
package metier;
import java.util.List;
import javax.ejb.Local;
import jpa.Employe;
@Local
public interface IMetierLocal {
// ottenere la busta paga
FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés );
// elenco dei dipendenti
List<Employe> findAllEmployes();
}
Questa interfaccia è quella utilizzata nella parte server dell'applicazione client/server descritta al paragrafo 7.1.
La classe Metier che utilizzeremo per testare il livello [web] implementa questa interfaccia nel modo seguente:
package metier;
...
public class Metier implements IMetierLocal {
// dizionario dei dipendenti indicizzato per numero SS
private Map<String,Employe> hashEmployes=new HashMap<String,Employe>();
// elenco dei dipendenti
private List<Employe> listEmployes;
// ottenere la busta paga
public FeuilleSalaire calculerFeuilleSalaire(String SS,
double nbHeuresTravaillées, int nbJoursTravaillés) {
// si recupera il dipendente con n. SS
Employe e=hashEmployes.get(SS);
// si genera una busta paga fittizia
return new FeuilleSalaire(e,new Cotisation(3.49,6.15,9.39,7.88),new ElementsSalaire(100,100,100,100,100));
}
// elenco dei dipendenti
public List<Employe> findAllEmployes() {
if(listEmployes==null){
// creazione di un elenco di due dipendenti
listEmployes=new ArrayList<Employe>();
listEmployes.add(new Employe("254104940426058","Jouveinal","Marie","5 rue des oiseaux","St Corentin","49203",new Indemnite(2,2.1,2.1,3.1,15)));
listEmployes.add(new Employe("260124402111742","Laverti","Justine","La brûlerie","St Marcel","49014",new Indemnite(1,1.93,2,3,12)));
// dizionario dei dipendenti indicizzato in base al n. SS
for(Employe e:listEmployes){
hashEmployes.put(e.getSS(),e);
}
}
// viene restituito l'elenco dei dipendenti
return listEmployes;
}
}
Lasciamo al lettore il compito di decifrare questo codice. Si noti il metodo utilizzato: per evitare di dover implementare la parte EJB dell’applicazione, simuliamo il livello [métier]. Quando il livello [web] sarà dichiarato corretto, potremo sostituirlo con il vero livello [métier].
10.4. Il modulo [index.xhtml] e il relativo modello [Form.java]
Ora creiamo la pagina XHTML del modulo e il relativo modello.
Letture consigliate in [ref3]:
- esempio n. 3 (mv-jsf2-03) per l’elenco dei tag utilizzabili in un modulo
- esempio n. 4 (mv-jsf2-04) per gli elenchi a discesa popolati dal modello
- esempio n. 6 (mv-jsf2-06) per la convalida dei dati inseriti
- esempio n. 7 (mv-jsf2-07) per la gestione del pulsante [Raz]
10.4.1. Fase 1
Domanda: Creare il modulo [index.xhtml] e il relativo modello [Form.java] necessari per ottenere la pagina seguente:
![]() |
I componenti di immissione dati sono i seguenti:
id | tipo JSF | modello | ruolo | |
comboEmployes | <h:selectOneMenu> | Stringa comboEmployesValue List<Dipendente> getEmployes() | contiene l'elenco dei dipendenti nel formato "nome cognome". | |
heuresTravaillees | <h:inputText> | Stringa heuresTravaillées | numero di ore lavorate - numero effettivo | |
joursTravailles | <h:inputText> | Stringa joursTravaillés | numero di giorni lavorati - numero intero | |
btnSalaire | <h:commandButton> | avvia il calcolo dello stipendio | ||
btnRaz | <h:commandButton> | ripristina il modulo allo stato iniziale |
- il metodo getEmployes restituirà un elenco di dipendenti che otterrà dal livello [métier]. Gli oggetti visualizzati dal menu a tendina avranno come attributo itemValue il numero SS del dipendente e, come attributo itemLabel, una stringa composta dal nome e dal cognome del dipendente.
- I pulsanti [Salaire] e [Raz] non saranno per il momento collegati a gestori di eventi.
- Verrà verificata la validità dei dati inseriti.

Provate questa versione. Verificate in particolare che gli errori di inserimento vengano segnalati correttamente.
Nota: è importante che gli attributi id dei componenti della pagina non contengano caratteri accentati. Con Glassfish 3.1.2, ciò causa il crash dell'applicazione.
10.4.2. Fase 2
Domanda: compilare il modulo [index.xhtml] e il relativo modello [Form.java] per ottenere la pagina seguente una volta cliccato il pulsante [Salaire]:
![]() |
Il pulsante [Salaire] sarà collegato al gestore di eventi calculerSalaire del modello. Questo metodo utilizzerà il metodo calculerFeuilleSalaire del livello [métier]. Questa busta paga verrà generata per il dipendente selezionato in [1].
Nel modello, la busta paga sarà rappresentata dal seguente campo privato:
private FeuilleSalaire feuilleSalaire;
dotato dei metodi get e set.
Per ottenere le informazioni contenute in questo oggetto, nella pagina JSF sarà possibile inserire espressioni come la seguente:
<h:outputText value="#{form.feuilleSalaire.employe.nom}"/>
L'espressione dell'attributo "value" verrà valutata come segue:
[form].getFeuilleSalaire().getEmploye().getNom(), dove [form] rappresenta un'istanza della classe [Form.java]. Il lettore potrà verificare che i metodi get qui utilizzati esistano effettivamente, rispettivamente, nelle classi [Form], [FeuilleSalaire] e [Employe]. Se così non fosse, verrebbe generata un'eccezione durante la valutazione dell'espressione.
Provate questa nuova versione.
10.4.3. Fase 3
Domanda: compilare il modulo [index.xhtml] e il relativo modello [Form.java] per ottenere le seguenti informazioni aggiuntive:
![]() |
Si seguirà la stessa procedura di prima. C'è una difficoltà con il simbolo monetario dell'euro che si trova, ad esempio, in [1]. Nel contesto di un'applicazione internazionalizzata, sarebbe preferibile disporre del formato di visualizzazione e del simbolo monetario del locale utilizzato (en, de, fr, ...). Ciò può essere ottenuto nel modo seguente:
<h:outputFormat value="{0,number,currency}">
<f:param value="#{form.feuilleSalaire.employe.indemnite.entretienJour}"/>
</h:outputFormat>
Si sarebbe potuto scrivere:
<h:outputText value="#{form.feuilleSalaire.employe.indemnite.entretienJour} є">
ma con l'impostazione locale en_GB (inglese GB) si continuerebbe ad avere una visualizzazione in euro, mentre si dovrebbe utilizzare la sterlina £. Il tag <h:outputFormat> consente di visualizzare informazioni in base alla locale della pagina JSF visualizzata:
- riga 1: visualizza il parametro {0}, che è un numero (number) che rappresenta una somma di denaro (currency)
- riga 2: il tag <f:param> assegna un valore al parametro {0}. Un secondo tag <f:param> assegnerebbe un valore al parametro indicato con {1} e così via.
10.4.4. Fase 4
Letture consigliate: esempio n. 7 (mv-jsf2-07) in [ref3].
Domanda: completare il modulo [index.xhtml] e il relativo modello [Form.java] per gestire il pulsante [Raz].
Il pulsante [Raz] riporta il modulo allo stato in cui si trovava quando è stato richiesto per la prima volta tramite un GET. Ci sono diverse difficoltà in questo caso. Alcune sono state spiegate in [ref3].
Il modulo restituito dal pulsante [Raz] non è l'intero modulo, ma solo la parte saisie di esso:

Questo risultato può essere ottenuto utilizzando un tag <f:subview> nel modo seguente:
<f:subview id="viewInfos" rendered="#{form.viewInfosIsRendered}">
... la partie du formulaire qu'on veut pouvoir ne pas afficher
</f:subview>
Il tag <f:subview> racchiude l'intera parte del modulo che può essere visualizzata o nascosta. Qualsiasi componente può essere visualizzato o nascosto tramite l'attributo rendered. Se rendered="true", il componente viene visualizzato; se rendered="false", non viene visualizzato. Se l’attributo rendered assume un valore nel modello, la visualizzazione del componente può essere controllata a livello di programma.
Nell’esempio sopra riportato, la visualizzazione della vista viewInfos verrà controllata tramite il seguente campo:
private boolean viewInfosIsRendered;
insieme ai relativi metodi get e set. I metodi che gestiscono i clic sui pulsanti [Salaire] e [Raz] aggiorneranno questo valore booleano a seconda che la vista viewInfos debba essere visualizzata o meno.

















