12. Versione 7 - Applicazione web PAM multi-vista/multi-pagina
Qui torniamo all'architettura iniziale in cui il livello [business] era simulato. Ora sappiamo che questo può essere facilmente sostituito dal livello [business] effettivo. Il livello [business] simulato facilita i test.
![]() |
Un'applicazione JSF è di tipo MVC (Model-View-Controller):
- il [Faces Servlet] è il controller generico fornito da JSF. Questo controller è esteso da gestori di eventi specifici dell'applicazione. I gestori di eventi incontrati finora erano metodi di classi che fungevano da modelli per le pagine JSF
- Le pagine JSF inviano le risposte al browser del client. Queste sono le viste dell'applicazione.
- Le pagine JSF contengono elementi dinamici noti come modello di pagina. Si noti che per alcuni autori, il modello comprende le entità manipolate dall'applicazione, come le classi PayrollSheet o Employee. Per distinguere tra questi due modelli, possiamo fare riferimento al modello dell'applicazione e al modello di pagina JSF.
Nell'architettura JSF, il passaggio da una pagina JSFi a una pagina JSFj può essere problematico.
- La pagina JSFi è stata visualizzata. Da questa pagina, l'utente attiva un POST tramite un evento [1]
- In JSF, questa richiesta POST viene generalmente gestita [2a,2b] da un metodo C del modello Mi della pagina JSFi. Possiamo dire che il metodo C è un controller secondario.
- Se, al termine di questo metodo, è necessario visualizzare la pagina JSFj, il controller C deve:
- aggiornare [2c] il modello Mj della pagina JSFj
- restituire [2a] al controller principale la chiave di navigazione che consentirà la visualizzazione della pagina JSFj
Il passaggio 1 richiede che il modello Mi della pagina JSFi abbia un riferimento al modello Mj della pagina JSFj. Ciò complica un po' le cose, rendendo i modelli Mi dipendenti l'uno dall'altro. Infatti, il gestore C del modello Mi che aggiorna il modello Mj deve essere a conoscenza di quest'ultimo. Se il modello Mj deve essere modificato, anche il gestore C del modello Mi dovrà essere modificato.
Esiste un caso in cui è possibile evitare le dipendenze tra i modelli: quando c'è un unico modello M utilizzato da tutte le pagine JSF. Questa architettura è adatta solo per applicazioni con poche viste, ma è molto facile da usare. È quella che stiamo utilizzando attualmente.
In questo contesto, l'architettura dell'applicazione è la seguente:
![]() |
12.1. Le viste dell'applicazione
Le diverse schermate presentate all'utente saranno le seguenti:
-
la vista [VueSaisies], che visualizza il modulo di simulazione
-
la vista [VueSimulation], utilizzata per visualizzare i risultati dettagliati della simulazione:


- la vista [SimulationsView], che elenca le simulazioni eseguite dal cliente

- la [EmptySimulationsView], che indica che il cliente non ha simulazioni o non ha più simulazioni:

- la vista [ErrorView], che indica uno o più errori:

12.2. Il progetto NetBeans per il livello [web]
Il progetto NetBeans per questa versione sarà il seguente progetto Maven:
![]() |
- in [1], i file di configurazione,
- in [2], le pagine JSF,
- in [3], il foglio di stile e l'immagine di sfondo per le viste,
- in [4], le classi del livello [web],
- in [5], i livelli inferiori dell'applicazione,
- in [6], il file dei messaggi per l'internazionalizzazione dell'applicazione,
- in [7], le dipendenze del progetto.
Esaminiamo alcuni di questi elementi.
12.2.1. File di configurazione
I file [web.xml] e [faces-config.xml] sono identici a quelli del progetto precedente, con l'eccezione di un dettaglio in [web.xml]:
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mv-pam-ejb-metier-dao-eclipselink</artifactId>
<version>${project.version}</version>
<type>ejb</type>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mv-pam-jsf2-ejb</artifactId>
<version>${project.version}</version>
<type>war</type>
</dependency>
</dependencies>
- righe 4-6: la home page è la pagina [saisie.xhtml]
12.2.2. Il foglio di stile
Il file [styles.css] è il seguente:
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
...
<welcome-file-list>
<welcome-file>faces/saisie.xhtml</welcome-file>
</welcome-file-list>
...
</web-app>
Ecco alcuni esempi di codice JSF che utilizza questi stili:
Vista Simulazioni
.simulationsHeader {
text-align: center;
font-style: italic;
color: Snow;
background: Teal;
}
.simuNum {
height: 25px;
text-align: center;
background: MediumTurquoise;
}
.simuNom {
text-align: left;
background: PowderBlue;
}
.simuPrenom {
width: 6em;
text-align: left;
color: Black;
background: MediumTurquoise;
}
.simuHT {
width: 3em;
text-align: center;
color: Black;
background: PowderBlue;
}
.simuJT {
width: 3em;
text-align: center;
color: Black;
background: MediumTurquoise;
}
.simuSalaireBase {
width: 9em;
text-align: center;
color: Black;
background: PowderBlue;
}
.simuIndemnites {
width: 3em;
text-align: center;
color: Black;
background: MediumTurquoise;
}
.simuCotisationsSociales {
width: 6em;
text-align: center;
background: PowderBlue;
}
.simuSalaireNet {
width: 10em;
text-align: center;
background: MediumTurquoise;
}
.erreursHeaders {
background: Teal;
background-color: #ff6633;
color: Snow;
font-style: italic;
text-align: center
}
.erreurClasse {
background: MediumTurquoise;
background-color: #ffcc66;
height: 25px;
text-align: center
}
.erreurMessage {
background: PowderBlue;
background-color: #ffcc99;
text-align: left
}
<h:dataTable value="#{form.simulations}" var="simulation"
Il risultato è il seguente:

Visualizzazione errore
headerClass="simulationsHeaders" columnClasses="simuNum,simuNom,simuPrenom,simuHT,simuJT,simuSalaireBase,simuIndemnites,simuCotisationsSociales,simuSalaireNet">
<h:dataTable value="#{form.erreurs}" var="erreur"

12.2.3. Il file dei messaggi
Il file dei messaggi [messages_fr.properties] è il seguente:
headerClass="erreursHeaders" columnClasses="erreurClasse,erreurMessage">
12.2.4. Il livello [business]
Il livello [aziendale] simulato viene modificato come segue:
form.titre=Simulateur de calcul de paie
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
form.menu.faireSimulation=| Faire la simulation
form.menu.effacerSimulation=| Effacer la simulation
form.menu.enregistrerSimulation=| Enregistrer la simulation
form.menu.retourSimulateur=| Retour au simulateur
form.menu.voirSimulations=| Voir les simulations
form.menu.terminerSession=| Terminer la session
simulations.headers.nom=Nom
simulations.headers.nom=Nom
simulations.headers.prenom=Pr\u00e9nom
simulations.headers.heuresTravaillees=Heures travaill\u00e9es
simulations.headers.joursTravailles=Jours Travaill\u00e9s
simulations.headers.salaireBase=Salaire de base
simulations.headers.indemnites=Indemnit\u00e9s
simulations.headers.cotisationsSociales=Cotisations sociales
simulations.headers.salaireNet=SalaireNet
simulations.headers.numero=N\u00b0
erreur.titre=Une erreur s'est produite.
erreur.exceptions=Cha\u00eene des exceptions
exception.type=Type de l'exception
exception.message=Message associ\u00e9
- riga 8: l'elenco dei dipendenti
- riga 7: lo stesso elenco come dizionario indicizzato dai numeri di previdenza sociale dei dipendenti
- righe 24–39: il metodo findAllEmployees, che restituisce l'elenco dei dipendenti. Questo metodo crea un elenco hardcoded e lo fa riferimento tramite il campo employees della riga 8.
- righe 27–33: vengono creati un elenco e un dizionario di due dipendenti
- riga 35: un dipendente viene aggiunto all'elenco dei dipendenti (riga 8) ma non al dizionario hashEmployees (riga 7). In questo modo il dipendente appare nel menu a tendina dei dipendenti ma non viene riconosciuto dal metodo calculatePayroll (riga 14), causando il lancio di un'eccezione (riga 17).
- Righe 11–21: il metodo calculatePayroll
- Riga 14: il dipendente viene cercato nel dizionario `hashEmployees` utilizzando il suo numero di previdenza sociale (SSN). Se non viene trovato, viene generata un'eccezione (righe 16–18). Pertanto, otterremo un'eccezione per il dipendente con SSN X aggiunto alla riga 35 all'elenco `employees` ma non al dizionario `hashEmployees`.
- Riga 20: viene creato e restituito un cedolino fittizio.
12.3. I bean dell'applicazione
Ci saranno tre bean con tre diversi ambiti:
![]() |
12.3.1. Il bean ApplicationData
Il bean ApplicationData avrà ambito applicativo:
package metier;
...
public class Metier implements ImetierLocal, Serializable {
// list of employees
private Map<String,Employe> hashEmployes=new HashMap<String,Employe>();
private List<Employe> listEmployes;
// get your payslip
public FeuilleSalaire calculerFeuilleSalaire(String SS,
double nbHeuresTravaillées, int nbJoursTravaillés) {
// we get the employee
Employe e=hashEmployes.get(SS);
// an exception is thrown if the employee does not exist
if(e==null){
throw new PamException(String.format("L'employé de n° SS [%s] n'existe pas",SS),1);
}
// a fictitious payslip is returned
return new FeuilleSalaire(e,new Cotisation(3.49,6.15,9.39,7.88),e.getIndemnite(),new ElementsSalaire(100,100,100,100,100));
}
// list of employees
public List<Employe> findAllEmployes() {
if(listEmployes==null){
// create a list of three employees
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)));
// employee dictionary
for(Employe e:listEmployes){
hashEmployes.put(e.getSS(),e);
}
// we add an employee who doesn't exist in the dictionary
listEmployes.add(new Employe("X","Y","Z","La brûlerie","St Marcel","49014",new Indemnite(1,1.93,2,3,12)));
}
// we return the list of employees
return listEmployes;
}
}
- riga 11: l'annotazione @Named rende la classe un managed bean. Si noti che, a differenza del progetto precedente, non abbiamo utilizzato l'annotazione @ManagedBean. Il motivo è che un riferimento a questa classe deve essere iniettato in un'altra classe utilizzando l'annotazione @Inject, e @Inject inietta solo classi con l'annotazione @Named.
- Riga 12: L'annotazione @ApplicationScoped rende la classe un oggetto con ambito applicativo. Si noti che la classe dell'annotazione è [javax.enterprise.context.ApplicationScoped] (riga 6) e non [javax.faces.bean.ApplicationScoped] come nei bean del progetto precedente.
Il bean ApplicationData ha due scopi:
- riga 16: mantenere un riferimento al livello [business],
- righe 18–19: definire un logger che può essere utilizzato da altri bean per registrare i log nella console di GlassFish.
12.3.2. Il bean SessionData
Il bean SessionData avrà ambito di sessione:
package web.beans.application;
import java.io.Serializable;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
import metier.IMetierLocal;
import metier.Metier;
@Named
@ApplicationScoped
public class ApplicationData implements Serializable {
// business layer
private IMetierLocal metier = new Metier();
// logger
private boolean logsEnabled = true;
private final Logger logger = Logger.getLogger("pam");
public ApplicationData() {
}
@PostConstruct
public void init() {
// log
if (isLogsEnabled()) {
logger.info("ApplicationData");
}
}
// getters and setters
...
}
- Riga 13: la classe SessionData è un managed bean (@Named) che può essere iniettato in altri managed bean,
- riga 14: ha ambito di sessione (@SessionScoped),
- righe 18–19: un riferimento al bean ApplicationData viene iniettato al suo interno (@Inject),
- righe 21–32: i dati dell'applicazione che devono essere mantenuti tra le sessioni.
- riga 21: l'elenco delle simulazioni eseguite dall'utente,
- riga 22: il numero dell'ultima simulazione salvata,
- riga 23: l'ultima simulazione eseguita,
- righe 25–30: le opzioni del menu,
- riga 32: le impostazioni locali dell'applicazione.
Righe 39–44: il metodo init viene eseguito dopo l'istanziazione della classe (@PostConstruct). Qui viene utilizzato solo per registrare la sua esecuzione. Dobbiamo essere in grado di verificare che venga eseguito una sola volta per utente, poiché la classe ha ambito di sessione. Riga 42: il metodo utilizza il logger definito nella classe ApplicationData. Questo è il motivo per cui abbiamo dovuto iniettare un riferimento a questo bean (righe 18–19).
12.3.3. Il bean del modulo
Il bean Form ha ambito a livello di richiesta:
package web.beans.session;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import javax.inject.Named;
import web.beans.application.ApplicationData;
import web.entities.Simulation;
@Named
@SessionScoped
public class SessionData implements Serializable {
// application
@Inject
private ApplicationData applicationData;
// simulations
private List<Simulation> simulations = new ArrayList<Simulation>();
private int numDerniereSimulation = 0;
private Simulation simulation;
// menus
private boolean menuFaireSimulationIsRendered = true;
private boolean menuEffacerSimulationIsRendered = true;
private boolean menuEnregistrerSimulationIsRendered;
private boolean menuVoirSimulationsIsRendered;
private boolean menuRetourSimulateurIsRendered;
private boolean menuTerminerSessionIsRendered = true;
// local
private String locale="fr_FR";
// manufacturer
public SessionData() {
}
@PostConstruct
public void init() {
// log
if (applicationData.isLogsEnabled()) {
applicationData.getLogger().info("SessionData");
}
}
// menu management
public void setMenu(boolean menuFaireSimulationIsRendered, boolean menuEnregistrerSimulationIsRendered, boolean menuEffacerSimulationIsRendered, boolean menuVoirSimulationsIsRendered, boolean menuRetourSimulateurIsRendered, boolean menuTerminerSessionIsRendered) {
this.setMenuFaireSimulationIsRendered(menuFaireSimulationIsRendered);
this.setMenuEnregistrerSimulationIsRendered(menuEnregistrerSimulationIsRendered);
this.setMenuVoirSimulationsIsRendered(menuVoirSimulationsIsRendered);
this.setMenuEffacerSimulationIsRendered(menuEffacerSimulationIsRendered);
this.setMenuRetourSimulateurIsRendered(menuRetourSimulateurIsRendered);
this.setMenuTerminerSessionIsRendered(menuTerminerSessionIsRendered);
}
// getters and setters
...
}
- riga 17, la classe è un managed bean (@Named),
- riga 18, con ambito di richiesta (@RequestScoped),
- righe 24–25: iniezione di un riferimento al bean con ambito dell'applicazione ApplicationData,
- righe 26–27: iniezione di un riferimento al bean SessionData con ambito di sessione.
12.4. Le pagine dell'applicazione
![]() |
12.4.1. [layout.xhtml]
La pagina [layout.xhtml] gestisce il layout di tutte le viste:
package web.beans.request;
import java.util.ArrayList;
import java.util.List;
import javax.enterprise.context.RequestScoped;
import javax.faces.context.FacesContext;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpServletRequest;
import jpa.Employe;
import metier.FeuilleSalaire;
import web.beans.application.ApplicationData;
import web.beans.session.*;
import web.entities.Erreur;
import web.entities.Simulation;
@Named
@RequestScoped
public class Form {
public Form() {
}
// other beans
@Inject
private ApplicationData applicationData;
@Inject
private SessionData sessionData;
// the view model
private String comboEmployesValue = "";
private String heuresTravaillées = "";
private String joursTravaillés = "";
private Integer numSimulationToDelete;
private List<Erreur> erreurs = new ArrayList<Erreur>();
private FeuilleSalaire feuilleSalaire;
// list of employees
public List<Employe> getEmployes(){
return applicationData.getMetier().findAllEmployes();
}
// menu action
public String faireSimulation() {
...
}
public String enregistrerSimulation() {
...
}
public String effacerSimulation() {
...
}
public String voirSimulations() {
...
}
public String retourSimulateur() {
...
}
public String terminerSession() {
...
}
public String retirerSimulation() {
...
}
private void razFormulaire() {
...
}
// getters and setters
...
}
Ogni vista è composta dai seguenti elementi:
- un'intestazione visualizzata dal frammento [entete.xhtml] (riga 24),
- un corpo costituito da due frammenti denominati part1 (righe 26–28) e part2 (riga 29),
- riga 8: l'attributo locale garantisce l'internazionalizzazione delle pagine. Qui ce ne sarà solo uno: fr_FR,
- righe 14–19: codice JavaScript.
La visualizzazione della pagina [layout.xhtml] è la seguente:
![]() |
- in [1], l'intestazione [entete.xhtml],
- in [2], il frammento part1.
12.4.2. L'intestazione [entete.xhtml]
Il codice della pagina [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"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:ui="http://java.sun.com/jsf/facelets">
<f:view locale="#{sessionData.locale}">
<h:head>
<title><h:outputText value="#{msg['form.titre']}"/></title>
<h:outputStylesheet library="css" name="styles.css"/>
</h:head>
<script type="text/javascript">
function raz(){
// on change les valeurs postées
document.forms['formulaire'].elements['formulaire:comboEmployes'].value="0";
document.forms['formulaire'].elements['formulaire:heuresTravaillees'].value="0";
document.forms['formulaire'].elements['formulaire:joursTravailles'].value="0";
}
</script>
<h:body style="background-image: url('${request.contextPath}/resources/images/standard.jpg');">
<h:form id="formulaire">
<!-- entete -->
<ui:include src="entete.xhtml" />
<!-- content -->
<ui:insert name="part1" >
Gestion des assistantes maternelles
</ui:insert>
<ui:insert name="part2"/>
</h:form>
</h:body>
</f:view>
</html>
- riga 12: il titolo dell'applicazione,
- righe 16–21: i sei link corrispondenti alle sei azioni che l'utente può eseguire. Questi link sono controllati (attributo rendered) da valori booleani nel bean SessionData,
- riga 17: cliccando sul link [Clear Simulation] si attiva l'esecuzione della funzione JavaScript raz. Questa funzione è stata definita nel template [layout.xhtml],
- riga 18: l'attributo immediate=true garantisce che la validità dei dati non venga verificata prima dell'esecuzione del metodo [Form].enregistrerSimulation. Ciò è intenzionale. Potresti voler salvare l'ultima simulazione eseguita un minuto fa anche se i dati inseriti da allora nel modulo (ma non ancora convalidati) non sono validi. Lo stesso vale per le azioni [Visualizza simulazioni], [Cancella simulazione], [Torna al simulatore] e [Termina sessione].
12.5. Casi d'uso dell'applicazione
12.5.1. Visualizzazione della pagina iniziale
La pagina iniziale è la seguente pagina [saisie.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>
<!-- entete -->
<h:panelGrid columns="2">
<h:panelGroup>
<h2><h:outputText value="#{msg['form.titre']}"/></h2>
</h:panelGroup>
<h:panelGroup>
<h:panelGrid columns="1">
<h:commandLink id="cmdFaireSimulation" value="#{msg['form.menu.faireSimulation']}" action="#{form.faireSimulation}" rendered="#{sessionData.menuFaireSimulationIsRendered}"/>
<h:commandLink id="cmdEffacerSimulation" onclick="raz()" value="#{msg['form.menu.effacerSimulation']}" action="#{form.effacerSimulation}" rendered="#{sessionData.menuEffacerSimulationIsRendered}"/>
<h:commandLink id="cmdEnregistrerSimulation" immediate="true" value="#{msg['form.menu.enregistrerSimulation']}" action="#{form.enregistrerSimulation}" rendered="#{sessionData.menuEnregistrerSimulationIsRendered}"/>
<h:commandLink id="cmdVoirSimulations" immediate="true" value="#{msg['form.menu.voirSimulations']}" action="#{form.voirSimulations}" rendered="#{sessionData.menuVoirSimulationsIsRendered}"/>
<h:commandLink id="cmdRetourSimulateur" immediate="true" value="#{msg['form.menu.retourSimulateur']}" action="#{form.retourSimulateur}" rendered="#{sessionData.menuRetourSimulateurIsRendered}"/>
<h:commandLink id="cmdTerminerSession" immediate="true" value="#{msg['form.menu.terminerSession']}" action="#{form.terminerSession}" rendered="#{sessionData.menuTerminerSessionIsRendered}"/>
</h:panelGrid>
</h:panelGroup>
</h:panelGrid>
<hr/>
</ui:composition>
</html>
- riga 8: viene visualizzata all'interno della pagina [layout.xhtml],
- riga 9: al posto del frammento denominato part1. In questo frammento viene visualizzata la pagina [saisie2.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="part1">
<ui:include src="saisie2.xhtml"/>
</ui:define>
</ui:composition>
</html>
Questo codice visualizza la seguente vista:
![]() |
Domanda: Completa la riga 13 del codice XHTML. L'elenco delle voci nella casella combinata dei dipendenti è fornito da un metodo del bean [Form]. Scrivi questo metodo. Le voci visualizzate nella casella combinata avranno la proprietà itemValue impostata sul numero di previdenza sociale del dipendente, mentre la proprietà itemLabel sarà una stringa composta dal nome e dal cognome del dipendente.
Domanda: Come dovrebbe essere inizializzato il bean [SessionData] in modo che, durante la richiesta GET iniziale effettuata al form, il menu di intestazione sia quello mostrato sopra?
12.5.2. L'azione [ faireSimulation]
Il codice JSF per l'azione [runSimulation] è 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"
xmlns:ui="http://java.sun.com/jsf/facelets">
<h:panelGrid columns="3">
<h:outputText value="#{msg['form.comboEmployes.libellé']}"/>
<h:outputText value="#{msg['form.heuresTravaillées.libellé']}"/>
<h:outputText value="#{msg['form.joursTravaillés.libellé']}"/>
<h:selectOneMenu id="comboEmployes" value="#{form.comboEmployesValue}">
<f:selectItems .../>
</h:selectOneMenu>
<h:inputText id="heuresTravaillees" value="#{form.heuresTravaillées}" required="true" requiredMessage="#{msg['form.heuresTravaillées.required']}" validatorMessage="#{msg['form.heuresTravaillées.validation']}">
<f:validateDoubleRange minimum="0" maximum="300"/>
</h:inputText>
<h:inputText id="joursTravailles" value="#{form.joursTravaillés}" required="true" requiredMessage="#{msg['form.joursTravaillés.required']}" validatorMessage="#{msg['form.joursTravaillés.validation']}">
<f:validateLongRange minimum="0" maximum="31"/>
</h:inputText>
<h:panelGroup></h:panelGroup>
<h:message for="heuresTravaillees" styleClass="error"/>
<h:message for="joursTravailles" styleClass="error"/>
</h:panelGrid>
<hr/>
</html>
L'azione [faireSimulation] calcola una busta paga:

Dalla pagina precedente otteniamo il seguente risultato:
![]() |
![]() |
La simulazione viene visualizzata nella seguente pagina [simulation.xhtml]:
<h:commandLink id="cmdFaireSimulation" value="#{msg['form.menu.faireSimulation']}" action="#{form.faireSimulation}" rendered="#{sessionData.menuFaireSimulationIsRendered}"/>
- Riga 8: la pagina [simulation.xhtml] viene inserita nella pagina [layout.xhtml],
- righe 9–11: l'area di input viene visualizzata nel frammento part1 della pagina di layout,
- righe 12–91: la simulazione viene visualizzata nel frammento part2 della pagina di layout.
Domanda: Scrivi il metodo [doSimulation] per la classe [Form]. La simulazione verrà salvata nel bean SessionData.
12.5.3. Gestione degli errori
Vogliamo essere in grado di gestire correttamente le eccezioni che potrebbero verificarsi durante il calcolo di una simulazione. A tal fine, il codice del metodo [runSimulation] utilizzerà un blocco try/catch:
<?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="part1">
<ui:include src="saisie2.xhtml"/>
</ui:define>
<ui:define name="part2">
<h:outputText value="#{msg['form.infos.employé']}" styleClass="titreInfos"/>
<br/><br/>
<h:panelGrid columns="3" rowClasses="libelle,info">
<h:outputText value="#{msg['form.employe.nom']}"/>
<h:outputText value="#{msg['form.employe.prénom']}"/>
<h:outputText value="#{msg['form.employe.adresse']}"/>
<h:outputText value="#{form.feuilleSalaire.employe.nom}"/>
<h:outputText value="#{form.feuilleSalaire.employe.prenom}"/>
<h:outputText value="#{form.feuilleSalaire.employe.adresse}"/>
</h:panelGrid>
<h:panelGrid columns="3" rowClasses="libelle,info">
<h:outputText value="#{msg['form.employe.ville']}"/>
<h:outputText value="#{msg['form.employe.codePostal']}"/>
<h:outputText value="#{msg['form.employe.indice']}"/>
<h:outputText value="#{form.feuilleSalaire.employe.ville}"/>
<h:outputText value="#{form.feuilleSalaire.employe.codePostal}"/>
<h:outputText value="#{form.feuilleSalaire.employe.indemnite.indice}"/>
</h:panelGrid>
<br/>
<h:outputText value="#{msg['form.infos.cotisations']}" styleClass="titreInfos"/>
<br/><br/>
<h:panelGrid columns="4" rowClasses="libelle,info">
<h:outputText value="#{msg['form.cotisations.csgrds']}"/>
<h:outputText value="#{msg['form.cotisations.csgd']}"/>
<h:outputText value="#{msg['form.cotisations.retraite']}"/>
<h:outputText value="#{msg['form.cotisations.secu']}"/>
<h:outputText value="#{form.feuilleSalaire.cotisation.csgrds} %"/>
<h:outputText value="#{form.feuilleSalaire.cotisation.csgd} %"/>
<h:outputText value="#{form.feuilleSalaire.cotisation.retraite} %"/>
<h:outputText value="#{form.feuilleSalaire.cotisation.secu} %"/>
</h:panelGrid>
<br/>
<h:outputText value="#{msg['form.infos.indemnites']}" styleClass="titreInfos"/>
<br/><br/>
<h:panelGrid columns="4" rowClasses="libelle,info">
<h:outputText value="#{msg['form.indemnites.salaireHoraire']}"/>
<h:outputText value="#{msg['form.indemnites.entretienJour']}"/>
<h:outputText value="#{msg['form.indemnites.repasJour']}"/>
<h:outputText value="#{msg['form.indemnites.congésPayés']}"/>
<h:outputFormat value="{0,number,currency}">
<f:param value="#{form.feuilleSalaire.employe.indemnite.baseHeure}"/>
</h:outputFormat>
<h:outputFormat value="{0,number,currency}">
<f:param value="#{form.feuilleSalaire.employe.indemnite.entretienJour}"/>
</h:outputFormat>
<h:outputFormat value="{0,number,currency}">
<f:param value="#{form.feuilleSalaire.employe.indemnite.repasJour}"/>
</h:outputFormat>
<h:outputText value="#{form.feuilleSalaire.employe.indemnite.indemnitesCP} %"/>
</h:panelGrid>
<br/>
<h:outputText value="#{msg['form.infos.salaire']}" styleClass="titreInfos"/>
<br/><br/>
<h:panelGrid columns="4" rowClasses="libelle,info">
<h:outputText value="#{msg['form.salaire.base']}"/>
<h:outputText value="#{msg['form.salaire.cotisationsSociales']}"/>
<h:outputText value="#{msg['form.salaire.entretien']}"/>
<h:outputText value="#{msg['form.salaire.repas']}"/>
<h:outputFormat value="{0,number,currency}">
<f:param value="#{form.feuilleSalaire.elementsSalaire.salaireBase}"/>
</h:outputFormat>
<h:outputFormat value="{0,number,currency}">
<f:param value="#{form.feuilleSalaire.elementsSalaire.cotisationsSociales}"/>
</h:outputFormat>
<h:outputFormat value="{0,number,currency}">
<f:param value="#{form.feuilleSalaire.elementsSalaire.indemnitesEntretien}"/>
</h:outputFormat>
<h:outputFormat value="{0,number,currency}">
<f:param value="#{form.feuilleSalaire.elementsSalaire.indemnitesRepas}"/>
</h:outputFormat>
</h:panelGrid>
<br/>
<h:panelGrid columns="3" columnClasses="libelle,col2,info">
<h:outputText value="#{msg['form.salaire.net']}"/>
<h:panelGroup></h:panelGroup>
<h:outputFormat value="{0,number,currency}">
<f:param value="#{form.feuilleSalaire.elementsSalaire.salaireNet}"/>
</h:outputFormat>
</h:panelGrid>
</ui:define>
</ui:composition>
</html>
L'elenco degli errori generati alla riga 15 è il seguente:
// action du menu
public String faireSimulation(){
try{
// on calcule la feuille de salaire
feuilleSalaire= ...
// on affiche la simulation
...
// on met à jour le menu
...
// on rend la vue simulation
return "simulation";
}catch(Throwable th){
// on vide la liste des erreurs précédentes
...
// on crée la nouvelle liste des erreurs
...
// on affiche la vue vueErreur
...
// on met à jour le menu
...
// on affiche la vue erreur
return "erreurs";
}
}
La classe Error è definita come segue:
// le modèle des vues
...
private List<Erreur> erreurs=new ArrayList<Erreur>();
...
Gli errori saranno eccezioni il cui nome della classe è memorizzato nel campo classe e il cui messaggio è memorizzato nel campo messaggio.
L'elenco degli errori costruito nel metodo [faireSimulation] è costituito da:
- l'eccezione iniziale th di tipo Throwable che si è verificata,
- la sua causa th.getCause() se ne ha una,
- la causa della causa h.getCause().getCause() se esiste.
- ...
Ecco un esempio di elenco di errori:

Nell'esempio sopra, il dipendente [Z Y] non esiste nel dizionario dei dipendenti utilizzato dal livello [business] simulato. In questo caso, il livello [business] simulato genera un'eccezione (riga 6 qui sotto):
package web.entities;
public class Erreur {
public Erreur() {
}
// field
private String classe;
private String message;
// manufacturer
public Erreur(String classe, String message){
this.setClasse(classe);
this.message=message;
}
// getters and setters
...
}
Il risultato è il seguente:

Questa vista viene visualizzata dalla seguente pagina [errors.xhtml]:
public FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés) {
// we get the employee
Employe e=hashEmployes.get(SS);
// an exception is thrown if the employee does not exist
if(e==null){
throw new PamException(String.format("L'employé de n° SS [%s] n'existe pas",SS),1);
}
...
}
Domanda: Completa il metodo [faireSimulation] in modo che, in caso di eccezione, visualizzi la vista [vueErreur].
12.5.4. L'azione [ effacerSimulation]
L'azione [clearSimulation] permette all'utente di tornare a un modulo vuoto:
![]() |
Il codice JSF per il link [clearSimulation] è 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"
xmlns:ui="http://java.sun.com/jsf/facelets">
<ui:composition template="layout.xhtml">
<ui:define name="part1">
<h3><h:outputText value="#{msg['erreur.titre']}"/></h3>
<h:dataTable value="#{form.erreurs}" var="erreur"
headerClass="erreursHeaders" columnClasses="erreurClasse,erreurMessage">
<f:facet name="header">
<h:outputText value="#{msg['erreur.exceptions']}"/>
</f:facet>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['exception.type']}"/>
</f:facet>
<h:outputText value="#{erreur.classe}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['exception.message']}"/>
</f:facet>
<h:outputText value="#{erreur.message}"/>
</h:column>
</h:dataTable>
</ui:define>
</ui:composition>
</html>
Cliccando sul link [ClearSimulation] si attiva innanzitutto una chiamata alla funzione JavaScript raz(). Questo metodo è definito nella pagina [layout.xhtml]:
<h:commandLink id="cmdEffacerSimulation" onclick="raz()" value="#{msg['form.menu.effacerSimulation']}" action="#{form.effacerSimulation}" rendered="#{sessionData.menuEffacerSimulationIsRendered}"/>
Le righe 4–6 modificano i valori inviati. Si noti che
- i valori inviati sono validi, ovvero supereranno i controlli di validità per i campi di input hoursWorked e daysWorked.
- La funzione `raz` non invia il modulo. L'invio avverrà invece tramite il link `cmdEffacerSimulation`. L'invio avrà luogo dopo l'esecuzione della funzione JavaScript `raz`.
Durante l'invio, i valori inviati seguiranno il normale processo: convalida, quindi assegnazione ai campi del modello. Questi sono i seguenti nella classe [Form]:
<script type="text/javascript">
function raz(){
// change posted values
document.forms['formulaire'].elements['formulaire:comboEmployes'].value="0";
document.forms['formulaire'].elements['formulaire:heuresTravaillees'].value="0";
document.forms['formulaire'].elements['formulaire:joursTravailles'].value="0";
}
</script>
// the view model
private String comboEmployesValue;
private String heuresTravaillées;
private String joursTravaillés;
Questi tre campi riceveranno i tre valori inviati {"0", "0", "0"}. Una volta completata questa assegnazione, verrà eseguito il metodo clearSimulation.
Domanda: Scrivi il metodo [effacerSimulation] per la classe [Form]. Assicurati che:
-
venga visualizzato solo il campo di immissione,
-
la casella combinata sia impostata sul primo elemento,
-
i campi di immissione hoursWorked e daysWorked visualizzino stringhe vuote.
12.5.5. L'azione [ enregistrerSimulation]
Il codice JSF per il link [saveSimulation] è il seguente:
...
L'azione [saveSimulation] associata al link salva la simulazione corrente in un elenco di simulazioni gestito nella classe [SessionData]:
<h:commandLink id="cmdEnregistrerSimulation" immediate="true" value="#{msg['form.menu.enregistrerSimulation']}" action="#{form.enregistrerSimulation}" rendered="#{sessionData.menuEnregistrerSimulationIsRendered}"/>
La classe Simulation nell' e web.entities è la seguente:
private List<Simulation> simulations=new ArrayList<Simulation>();
Questa classe consente di salvare una simulazione creata dall'utente:
- riga 11: il numero della simulazione,
- riga 12: la busta paga che è stata calcolata,
- riga 13: il numero di ore lavorate,
- riga 14: il numero di giorni lavorati.
Ecco un esempio di record:

Dalla pagina precedente otteniamo il seguente risultato:

Il numero di simulazione viene incrementato ad ogni nuovo record. Appartiene al bean SessionData:
package web.entities;
import metier.FeuilleSalaire;
public class Simulation {
public Simulation() {
}
// simulation fields
private Integer num;
private FeuilleSalaire feuilleSalaire;
private String heuresTravaillées;
private String joursTravaillés;
// manufacturer
public Simulation(Integer num,String heuresTravaillées, String joursTravaillés, FeuilleSalaire feuilleSalaire){
this.setNum(num);
this.setFeuilleSalaire(feuilleSalaire);
this.setHeuresTravaillées(heuresTravaillées);
this.setJoursTravaillés(joursTravaillés);
}
public double getIndemnites(){
return feuilleSalaire.getElementsSalaire().getIndemnitesEntretien()+ feuilleSalaire.getElementsSalaire().getIndemnitesRepas();
}
// getters and setters
...
}
- Riga 2: il numero dell'ultima simulazione eseguita.
Il metodo [saveSimulation] può procedere come segue:
- recuperare il numero dell'ultima simulazione dal bean [SessionData] e incrementarlo,
- aggiungere la nuova simulazione all'elenco delle simulazioni gestito dalla classe [SessionData],
- visualizzare la tabella delle simulazioni:
![]() |
La tabella di simulazione viene visualizzata dalla pagina [simulations.xhtml]:
// simulations
private List<Simulation> simulations = new ArrayList<Simulation>();
private int numDerniereSimulation = 0;
private Simulation simulation;
- alla riga 8, la pagina [simulations.xhtml] viene inserita all'interno della pagina [layout.xhtml],
- riga 9, al posto del frammento denominato part1,
- riga 11, il tag <h:dataTable> utilizza il campo #{sessionData.simulations} come fonte dati, ovvero il seguente campo:
<?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="part1">
<!-- simulation table -->
<h:dataTable value="#{sessionData.simulations}" var="simulation"
headerClass="simulationsHeaders" columnClasses="simuNum,simuNom,simuPrenom,simuHT,simuJT,simuSalaireBase,simuIndemnites,simuCotisationsSociales,simuSalaireNet">
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.numero']}"/>
</f:facet>
<h:outputText value="#{simulation.num}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.nom']}"/>
</f:facet>
<h:outputText value="#{simulation.feuilleSalaire.employe.nom}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.prenom']}"/>
</f:facet>
<h:outputText value="#{simulation.feuilleSalaire.employe.prenom}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.heuresTravaillees']}"/>
</f:facet>
<h:outputText value="#{simulation.heuresTravaillées}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.joursTravailles']}"/>
</f:facet>
<h:outputText value="#{simulation.joursTravaillés}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.salaireBase']}"/>
</f:facet>
<h:outputText value="#{simulation.feuilleSalaire.elementsSalaire.salaireBase}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.indemnites']}"/>
</f:facet>
<h:outputText value="#{simulation.indemnites}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.cotisationsSociales']}"/>
</f:facet>
<h:outputText value="#{simulation.feuilleSalaire.elementsSalaire.cotisationsSociales}"/>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.salaireNet']}"/>
</f:facet>
<h:outputText value="#{simulation.feuilleSalaire.elementsSalaire.salaireNet}"/>
</h:column>
<h:column>
<h:commandLink value="Retirer" action="#{form.retirerSimulation}">
<f:setPropertyActionListener target="#{form.numSimulationToDelete}" value="#{simulation.num}"/>
</h:commandLink>
</h:column>
</h:dataTable>
</ui:define>
</ui:composition>
</html>
-
L'attributo var="simulation" imposta il nome della variabile che rappresenta la simulazione corrente all'interno del tag <h:datatable>
-
L'attributo headerClass="simulationsHeaders" 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 JSF per la colonna Nome è il seguente:
// simulations
private List<Simulation> simulations;
- Righe 2–4: il tag <f:facet name="header"> definisce l'intestazione della colonna
- riga 5: viene scritto il nome del dipendente:
- simulation fa riferimento all'attributo var del tag <h:dataTable ...>:
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.nom']}"/>
</f:facet>
<h:outputText value="#{simulation.feuilleSalaire.employe.nom}"/>
</h:column>
simulation si riferisce alla simulazione corrente nell'elenco delle simulazioni: prima la 1°, poi la 2°, …
- (continua)
- simulation.payroll si riferisce al campo payroll della simulazione corrente
- simulation.payroll.employee si riferisce al campo dipendente all'interno del campo busta paga
- simulation.payroll.employee.name si riferisce al campo nome del campo dipendente
La stessa tecnica viene ripetuta per tutte le colonne della tabella. C'è un problema con la colonna Indennità, che viene generata utilizzando il seguente codice:
<h:dataTable value="#{sessionData.simulations}" var="simulation" ...>
Alla riga 5 viene visualizzato il valore di simulation.indemnites. Tuttavia, la classe Simulation non dispone di un campo indemnites. È importante ricordare che il campo indemnites non viene utilizzato direttamente, ma tramite il metodo simulation.getIndemnites(). Pertanto, è sufficiente che questo metodo esista. Il campo indemnites in sé potrebbe non esistere. Il metodo getIndemnites deve restituire il totale delle indennità del dipendente. Ciò richiede un calcolo intermedio poiché questo totale non è direttamente disponibile nella busta paga. Il metodo getIndemnites è fornito nella Sezione 12.5.5.
Domanda: Scrivere il metodo [enregistrerSimulation] per la classe [Form].
12.5.6. L'azione [ retourSimulateur]
Il codice JSF per il link [retourSimulateur] è il seguente:
<h:column>
<f:facet name="header">
<h:outputText value="#{msg['simulations.headers.indemnites']}"/>
</f:facet>
<h:outputText value="#{simulation.indemnites}"/>
</h:column>
L'azione [returnSimulator] associata al link consente all'utente di tornare dalla [simulationsView] alla [inputsView]:

Il risultato ottenuto:

Domanda: Scrivere il metodo [retourSimulateur] per la classe [Form]. Il modulo di input mostrato deve essere vuoto, come sopra.
12.5.7. L'azione [viewSimulations]
Il codice JSF per il link [viewSimulations] è il seguente:
<h:commandLink id="cmdRetourSimulateur" immediate="true" value="#{msg['form.menu.retourSimulateur']}" action="#{form.retourSimulateur}" rendered="#{sessionData.menuRetourSimulateurIsRendered}"/>
L'azione [viewSimulations] associata al link permette all'utente di visualizzare la tabella delle simulazioni, indipendentemente dallo stato dei propri inserimenti:

Il risultato ottenuto:

Domanda: Scrivi il metodo [viewSimulations] per la classe [Form].
Ci assicureremo che, se l'elenco delle simulazioni è vuoto, la vista visualizzata sia [vueSimulationsVides]:

Per visualizzare la vista sopra indicata, useremo la seguente pagina [simulationsVides.xhtml]:
<h:commandLink id="cmdVoirSimulations" immediate="true" value="#{msg['form.menu.voirSimulations']}" action="#{form.voirSimulations}" rendered="#{sessionData.menuVoirSimulationsIsRendered}"/>
12.5.8. L'azione [ removeSimulation]
L'utente può rimuovere le simulazioni dal proprio elenco:

Il risultato è il seguente:

Se rimuoviamo l'ultima simulazione sopra riportata, otterremo il seguente risultato:

Il codice JSF per la colonna [Rimuovi] nella tabella di simulazione è 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"
xmlns:ui="http://java.sun.com/jsf/facelets">
<ui:composition template="layout.xhtml">
<ui:define name="part1">
<h2>Votre liste de simulations est vide.</h2>
</ui:define>
</ui:composition>
</html>
- Riga 5: Il link [Rimuovi] è associato al metodo [removeSimulation] della classe [Form]. Questo metodo deve conoscere il numero della simulazione da rimuovere. Tale numero è fornito dal tag <f:setPropertyActionListener> alla riga 8. Questo tag ha due attributi, target e value: l'attributo target specifica un campo nel modello a cui verrà assegnato il valore dell'attributo value. In questo caso, il numero della simulazione da rimuovere, #{simulation.num}, verrà assegnato al campo numSimulationToDelete della classe [Form]:
<h:dataTable value="#{form.simulations}" var="simulation"
headerClass="simulationsHeaders" columnClasses="simuNum,simuNom,simuPrenom,simuHT,simuJT,simuSalaireBase,simuIndemnites,simuCotisationsSociales,simuSalaireNet">
...
<h:column>
<h:commandLink value="Retirer" action="#{form.retirerSimulation}">
<f:setPropertyActionListener target="#{form.numSimulationToDelete}" value="#{simulation.num}"/>
</h:commandLink>
</h:column>
</h:dataTable>
Quando viene eseguito il metodo [removeSimulation] della classe [Form], sarà possibile utilizzare il valore precedentemente memorizzato nel campo numSimulationToDelete.
Domanda: Scrivere il metodo [retirerSimulation] della classe [Form].
12.5.9. L'azione [ terminateSession]
Il codice JSF per il link [Fine sessione] è il seguente:
// the view model
...
private Integer numSimulationToDelete;
L'azione [EndSession] associata al link consente all'utente di terminare la propria sessione e tornare al modulo di immissione dati vuoto:


Se l'utente disponeva di un elenco di simulazioni, questo viene cancellato. Inoltre, la numerazione delle simulazioni viene reimpostata a 1.
Domanda: Scrivere il metodo [endSession] per la classe [Form].
12.6. Integrazione del livello web in un'architettura a tre livelli JSF/EJB
L'architettura della precedente applicazione web era la seguente:
![]() |
Sostituiamo il livello [business] simulato con i livelli [business, DAO, JPA] implementati dagli EJB nella Sezione 7.1:
![]() |
Esercizio pratico: integrare i livelli JSF ed EJB seguendo la metodologia descritta nella Sezione 11.
![]() |














