Skip to content

19. Caso di studio – versione 1

19.1. Il livello [metier] simulato

Torniamo all’architettura dell’applicazione che stiamo realizzando:

Disponiamo dell’archivio dei livelli [metier, dao, jpa] e abbiamo illustrato gli elementi di tali livelli che il livello [web] doveva conoscere. Siamo pronti a scriverlo utilizzando il framework Struts.

Per semplificare i test della nostra applicazione in fase di sviluppo, creeremo un livello business simulato che rispetterà l’interfaccia del livello [metier]. L’architettura sarà la seguente:

Svilupperemo il livello [web] con il livello [métier] simulato. I test saranno più semplici da eseguire poiché nell’architettura non è più presente alcun database. Grazie a Spring e all’utilizzo delle interfacce, la successiva sostituzione del livello simulato [metier] con la vera architettura [metier, dao, jpa] non avrà alcun impatto sul codice del livello [web / struts2]. Il livello [web / struts2] che svilupperemo ora potrà essere utilizzato così com’è.

Il livello simulato [metier] che utilizzeremo è il seguente:


package metier;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jpa.Cotisation;
import jpa.Employe;
import jpa.Indemnite;

public class MetierSimule implements IMetier {

  // elenco dei dipendenti
  private Map<String, Employe> hashEmployes = new HashMap<String, Employe>();
  private List<Employe> listEmployes;

  // ottenere la busta paga
  public FeuilleSalaire calculerFeuilleSalaire(String SS,
          double nbHeuresTravaillées, int nbJoursTravaillés) {
    // 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
      for (Employe e : listEmployes) {
        hashEmployes.put(e.getSS(), e);
      }
    }
    // viene visualizzato l'elenco dei dipendenti
    return listEmployes;
  }
}
  • riga 11: la classe [MetierSimule] implementa l’interfaccia [IMetier] che il vero livello [metier] implementa.
  • riga 14: un dizionario dei dipendenti indicizzati in base al loro numero INSEE
  • riga 15: l'elenco dei dipendenti
  • righe 27-39: implementazione del metodo findAllEmployes dell'interfaccia [IMetier].
  • righe 30-33: creazione di un elenco di due dipendenti
  • righe 34-36: creazione del dizionario dei dipendenti indicizzato in base al n. INSEE
  • righe 18-24: implementazione del metodo calculerSalaire dell'interfaccia [IMetier]. Qui viene restituita una busta paga fittizia.

19.2. Il progetto NetBeans

Il progetto NetBeans è il seguente:

  • in [1]:
    • [applicationContext.xml] è il file di configurazione di Spring
    • [tiles.xml] è il file di configurazione di un framework chiamato Tiles.
    • [web.xml] è il file di configurazione dell'applicazione web
  • in [2]: le diverse viste dell'applicazione
  • in [3]:
    • [messages.properties]: il file dei messaggi
    • [struts.xml]: il file di configurazione di Struts
  • in [4]: i codici sorgente dell'applicazione. Le azioni Struts si trovano nel pacchetto [web.actions].
  • in [5]: il livello [metier] simulato
  • in [6]: gli archivi utilizzati. Sono presenti gli archivi dei vari strumenti utilizzati: Spring, Tiles, Struts 2, il plugin di integrazione Struts 2 / Spring, il plugin di integrazione Struts 2 / Tiles.
  • in [7]: l'archivio del vero livello [metier, dao, jpa]. Ci permette di accedere alle entità JPA, all’interfaccia [IMetier], alle classi [FeuilleSalaire] e [ElementsSalaire]. Tutti questi elementi sono infatti utilizzati dalla nostra classe [MetierSimule].

19.3. Configurazione del progetto

Il progetto è configurato tramite diversi file:

  • [web.xml], che configura l’applicazione web
  • [struts.xml] che configura il framework Struts
  • [applicationContext.xml], che configura il framework Spring
  • [tiles.xml], che configura il framework Tiles

19.3.1. Configurazione dell'applicazione web

Il file [web.xml] è il seguente:


<?xml version="1.0" encoding="UTF-8"?>
<web-app id="pam_struts_01" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
  <display-name>Pam</display-name>
    <!-- Tiles -->
  <context-param>
    <param-name> org.apache.tiles.impl.BasicTilesContainer.DEFINITIONS_CONFIG </param-name>
    <param-value>/WEB-INF/tiles.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.apache.struts2.tiles.StrutsTilesListener</listener-class>
  </listener>
    <!-- Struts 2 -->
  <filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>struts2</filter-name>
    <URL-pattern>/*</URL-pattern>
  </filter-mapping>
     <!-- Spring -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
</web-app>
  • righe 13-20: configurano il filtro Struts 2 – già visto
  • righe 22-24: configurano il listener di Spring – già visto
  • righe 9-11: configurano il listener Tiles. La classe [org.apache.struts2.tiles.StrutsTilesListener] verrà istanziata all'avvio dell'applicazione web. A quel punto utilizzerà il proprio file di configurazione, definito dalle righe 5-8. Il file di configurazione di Tiles è quindi il file [WEB-INF/tiles.xml].

In definitiva, all’avvio dell’applicazione Struts, vengono istanziate tre classi:

  • una per il filtro Struts 2. È questa che garantisce il C di MVC.
  • un’altra per il listener Spring. Spring utilizzerà il file [applicationContext.xml] per istanziare i livelli [métier, dao, jpa] dell’applicazione. Spring istanzierà inoltre, come in un esempio precedente, una classe [Config] che conterrà i dati di ambito Application. Infine, Spring inietterà in ogni azione Struts che ne ha bisogno un riferimento a questa singola istanza [Config].
  • Un’altra per il listener Tiles. Questo framework si occuperà della gestione delle viste. Ne riparleremo presto.

19.3.2. Configurazione del framework Struts

Il framework Struts viene configurato tramite il seguente file [struts.xml]:


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
  <!-- internazionalizzazione -->
  <constant name="struts.custom.i18n.resources" value="messages" />
  <!-- integrazione Spring -->
  <constant name="struts.objectFactory.spring.autoWire" value="name" />

  
  <!-- azioni Struts / Tiles -->
  <package name="default" namespace="/" extends="tiles-default">
    <!-- azione predefinita -->
    <default-action-ref name="index" />
    <action name="index">
      <result type="redirectAction">
        <param name="actionName">Formulaire</param>
        <param name="namespace">/</param>
      </result>
    </action>
    <!-- azione Modulo -->
    <action name="Formulaire" class="web.actions.Formulaire" method="input">
      <result name="success" type="tiles">saisie</result>
      <result name="exception" type="tiles">exception</result>
    </action>
    <!-- azione FaireSimulation -->
    <action name="FaireSimulation" class="web.actions.Formulaire" method="calculSalaire">
      <result name="success" type="tiles">simulation</result>
      <result name="exception" type="tiles">exception</result>
      <result name="input" type="tiles">saisie</result>
    </action>
    <!-- azione EnregistrerSimulation -->
    <action name="EnregistrerSimulation" class="web.actions.Enregistrer" method="execute">
      <result name="error" type="tiles">erreur</result>
      <result name="simulations" type="tiles">simulations</result>
    </action>
   <!-- azione RetourFormulaire -->
    <action name="RetourFormulaire" >
      <result type="redirectAction">
        <param name="actionName">Formulaire</param>
        <param name="namespace">/</param>
      </result>
    </action>
   <!-- azione VoirSimulations -->
    <action name="VoirSimulations" class="web.actions.Voir">
      <result name="success" type="tiles">simulations</result>
    </action>
   <!-- azione RetirerSimulation -->
    <action name="SupprimerSimulation" class="web.actions.Supprimer" method="execute">
      <result name="erreur" type="tiles">erreur</result>
      <result name="simulations" type="tiles">simulations</result>
    </action>
   <!-- azione TerminerSession -->
    <action name="TerminerSession" class="web.actions.Terminer" method="execute">
      <result name="success" type="redirectAction">
        <param name="actionName">Formulaire</param>
        <param name="namespace">/</param>
      </result>
    </action>
  </package>

</struts>

Commenteremo le diverse azioni Struts man mano che le analizzeremo. Per il momento, si possono notare i seguenti punti:

  • riga 8: definisce il file dei messaggi
  • riga 10: definisce la modalità di iniezione dei bean Spring nelle azioni Struts. L’iniezione avviene in base al nome del bean. Il campo dell’azione Struts che deve essere inizializzato da Spring deve avere lo stesso nome del bean da iniettare.
  • riga 25: definisce la vista da visualizzare per la chiave di navigazione success dell’azione [Formulaire]. Si nota che il risultato <result> presenta un attributo type='tiles' che non conosciamo. Conoscevamo già il tipo redirect, che consente di reindirizzare il client verso una vista. In questo caso, la vista di tipo tiles è gestita dal framework Tiles. Il tipo tiles è definito nel file [struts-plugin.xml] dell’archivio [struts2-tiles-plugin-2.2.3.1.jar]:
1
2
3
4
5
6
7
<struts>
    <package name="tiles-default" extends="struts-default">
        <result-types>
            <result-type name="tiles" class="org.apache.struts2.views.tiles.TilesResult"/>
        </result-types>
    </package>
</struts>
  • righe 3-5: la definizione del tipo di risultato tiles.
  • riga 2: questo tipo è definito nel pacchetto [tiles-default] che estende il pacchetto [struts-default].
  • riga 14: definisce il pacchetto [default] in cui saranno contenute tutte le azioni dell'applicazione. Per poter utilizzare la definizione del tipo di vista tiles, il pacchetto estende [tiles-default].

19.3.3. Configurazione del framework Spring

Il framework Spring è configurato dal seguente file [WEB-INF/applicationContext.xml]:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
  
  <!-- livelli applicativi -->
 
<!-- web -->
  <bean id="config" class="web.Config" init-method="init">
    <property name="metier" ref="metier"/>
  </bean>
  <!-- business -->
  <bean id="metier" class="metier.MetierSimule"/>

</beans>
  • riga 13: il livello [metier] simulato istanziato dalla classe [metier.MetierSimule]
  • righe 9-11: configurano un bean denominato config. Come in un esempio visto in precedenza, questo bean servirà a incapsulare le informazioni dell'ambito Application. La classe associata a questo bean è la seguente classe [Config]:

package web;

import java.util.List;
import jpa.Employe;
import metier.IMetier;

public class Config {

  // livello di business inizializzato da Spring
  private IMetier metier;
  // elenco dei dipendenti
  private List<Employe> employes;
  // errori
  private Exception initException;

  // costruttore
  public Config() {
  }

  // metodo Spring per l'inizializzazione dell'oggetto
  public void init() {
    // richiesta dell'elenco dei dipendenti
    try {
      employes = metier.findAllEmployes();
    } catch (Exception ex) {
      initException = ex;
    }
  }

  // getter e setter
  ... 
}

Torniamo alla configurazione del bean config:


  <bean id="config" class="web.Config" init-method="init">
    <property name="metier" ref="metier"/>
  </bean>
  <!-- logica di business -->
  <bean id="metier" class="metier.Metier">
    ...  
</bean>

Alla riga 2, si può notare che il bean metier della riga 5 viene iniettato (ref) nel campo denominato metier (name) dell'oggetto [Config]. Il bean metier è un riferimento sul livello [metier]:

Per interagire con il livello [metier], tutte le azioni Struts del livello [web] dovranno fare riferimento a quest’ultimo. Si può affermare che il riferimento al livello [metier] è un dato di ambito Application. Tutte le richieste di tutti gli utenti ne avranno bisogno. Per questo motivo, inseriamo tale riferimento nell’oggetto [Config]. Inoltre, alla riga 1, la configurazione del bean config presenta un attributo init-method. Questo attributo indica il metodo del bean da eseguire dopo l’istanziazione del bean. In questo caso, si specifica che, dopo l’istanziazione della classe [web.Config], deve essere eseguito il suo metodo init. Il codice è il seguente:


// strato di business inizializzato da Spring
  private IMetier metier;
  // elenco dei dipendenti
  private List<Employe> employes;
  // errori
  private Exception initException;

  // costruttore
  public Config() {
  }

  // metodo Spring per l'inizializzazione dell'oggetto
  public void init() {
    // richiesta dell'elenco dei dipendenti
    try {
      employes = metier.findAllEmployes();
    } catch (Exception ex) {
      initException = ex;
    }
  }

Quando viene eseguito il metodo init, il campo metier della classe è stato istanziato da Spring. Il metodo init ha quindi accesso al livello di business dell’interfaccia [IMetier] (riga 2):


package metier;

import java.util.List;
import jpa.Employe;

public interface IMetier {
  // ottenere la busta paga
  FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés );
  // elenco dei dipendenti
  List<Employe> findAllEmployes();
}
  • riga 8: il metodo consente di calcolare lo stipendio di un dipendente
  • riga 10: il metodo consente di ottenere l'elenco dei dipendenti

Si nota che il metodo init richiede l'elenco dei dipendenti al livello [métier]. Tale elenco viene memorizzato nel campo della riga 4. Se si verifica un'eccezione, questa viene memorizzata nel campo della riga 6.

In conclusione, l’oggetto unico [Config] contiene:

  • un riferimento al livello [métier]
  • l'elenco dei dipendenti

19.4. Generazione delle viste Tiles

Come abbiamo visto nel file di configurazione di Struts, le viste saranno generate dal framework Tiles. Spiegheremo solo lo stretto necessario per la scrittura della nostra applicazione.

Tiles consente di generare viste a partire da una pagina master. Quest’ultima, qui denominata [MasterPage.JSP], sarà il risultato dell’assemblaggio dei seguenti frammenti JSP:

Entete.JSP
Saisie.JSP
Simulation.JSP
Simulations.JSP
Exception.JSP
Erreur.JSP

Questi frammenti JSP sono definiti nel progetto NetBeans:

  

Il framework Tiles ci permette di definire quali frammenti saranno inseriti nella pagina master.

La pagina master [MasterPage.JSP] è la seguente:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<!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">
    <link href="styles.css" rel="stylesheet" type="text/css"/>
    <title>
      <tiles:insertAttribute name="titre" ignore="true" />
    </title>
    <s:head/>
  </head>
  <body background="<s:url value="/ressources/standard.jpg"/>">
    <tiles:insertAttribute name="entete" />
    <hr/>
    <tiles:insertAttribute name="saisie" />
    <tiles:insertAttribute name="simulation" />
    <tiles:insertAttribute name="exception" />
    <tiles:insertAttribute name="erreur" />
    <tiles:insertAttribute name="simulations" />
  </body>
</html>

La pagina master è un contenitore di frammenti JSP. In questo caso, è costituita dall’assemblaggio di sei frammenti, quelli delle righe da 17 a 23. Al momento della generazione, nella pagina master possono essere assemblati da 0 a 6 frammenti. Questa generazione è gestita dal file [WEB-INF/tiles.xml] che definisce le viste Tiles:


<?xml version="1.0" encoding="UTF-8" ?>
 
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"
 "http://tiles.apache.org/dtds/tiles-config_2_0.dtd">
 
<tiles-definitions>
 
<!-- pagina principale -->
  <definition name="masterPage" template="/MasterPage.JSP">
    <put-attribute name="entete" value="/Entete.JSP"/>
    <put-attribute name="titre"  value="Pam"/>
    <put-attribute name="saisie" value=""/>     
    <put-attribute name="simulation" value=""/>
    <put-attribute name="simulations" value=""/>
    <put-attribute name="exception" value=""/>     
    <put-attribute name="erreur" value=""/>     
  </definition>
 
<!-- la vista di inserimento -->
  <definition name="saisie" extends="masterPage">
    <put-attribute name="saisie"   value="/Saisie.JSP"/>     
  </definition>

  <!-- la vista simulazione -->
 <definition name="simulation" extends="saisie">
    <put-attribute name="simulation"   value="/Simulation.JSP"/>     
  </definition>

<!-- la vista simulazioni -->
  <definition name="simulations" extends="masterPage">
    <put-attribute name="simulations"   value="/Simulations.JSP"/>     
  </definition>
  
<!-- vista "Eccezione" -->
  <definition name="exception" extends="masterPage">
    <put-attribute name="exception"   value="/Exception.JSP"/>     
  </definition>
 
<!-- la vista "errore" -->
 <definition name="erreur" extends="masterPage">
    <put-attribute name="erreur"   value="/Erreur.JSP"/>     
  </definition>
</tiles-definitions>
  • il file sopra riportato definisce sei viste Tiles denominate: masterPage (riga 9), immissione (riga 20), simulazione (riga 25), simulazioni (riga 30), eccezione (riga 35), errore (riga 40).
  • righe 9-17: definiscono una vista denominata masterPage (name) e associata alla pagina master [MasterPage.JSP] (template). Abbiamo visto che questa pagina JSP definisce sei sottoviste. Una vista Tiles associata alla pagina master deve indicare il frammento JSP associato a ciascuna delle sei sottoviste. Si nota che alcune sottoviste ricevono come valore (value) la stringa vuota. Queste sottoviste non saranno incluse nella pagina principale [MasterPage.JSP]. La vista Tiles denominata masterPage è quindi costituita dal solo sottoframmento [Entete.JSP].
  • righe 20-22: definiscono una vista denominata saisie (name) che estende (extends) la vista denominata masterPage vista in precedenza. Ciò significa che riprende tutte le definizioni della vista masterPage. La sua definizione è equivalente alla seguente:

<definition name="saisie" template="/MasterPage.JSP">
    <put-attribute name="entete" value="/Entete.JSP"/>
    <put-attribute name="titre"  value="Pam"/>
    <put-attribute name="saisie" value=""/>     
    <put-attribute name="simulation" value=""/>
    <put-attribute name="simulations" value=""/>
    <put-attribute name="exception" value=""/>     
    <put-attribute name="erreur" value=""/>
    <put-attribute name="saisie" value="/Saisie.JSP"/>     
  </definition>

Si nota che è associata alla pagina JSP [MasterPage.JSP] e che, in quanto tale, deve definire le sei sottoviste di questa pagina. Si nota che la definizione della riga 9 annulla quella della riga 4. La vista Tiles denominata saisie è quindi costituita dai frammenti JSP e [Entete.JSP, Saisie.JSP]

Se proseguiamo questo ragionamento, otteniamo la tabella seguente:

vista Tiles
pagine JSP
masterPage
Entete.JSP
saisie
Entete.JSP, Saisie.JSP
simulation
Entete.JSP, Saisie.JSP, Simulation.JSP
simulations
Entete.JSP, Simulations.JSP
exception
Entete.JSP, Exception.JSP
erreur
Entete.JSP, Erreur.JSP

19.5. I file dei messaggi

L'applicazione è stata internazionalizzata. I messaggi si trovano nei file [messages.properties] e [Formulaire.properties].

Il file [messages.properties] è il seguente:


Pam.titre=Calcul du salaire des assistantes maternelles
Pam.Erreurs.titre=Les erreurs suivantes se sont produites :
Pam.Erreurs.classe=Exception
Pam.Erreurs.message=Message
Pam.Erreur.libelle=L''erreur suivante s''est produite
Pam.Saisie.Heures.libell\u00e9=Heures travaill\u00e9es
Pam.Saisie.Jours.libell\u00e9=Jours travaill\u00e9s
Pam.Saisie.employ\u00e9=Employ\u00e9
Pam.BtnSalaire.libell\u00e9=Salaire
Pam.BtnEffacer.libell\u00e9=Effacer
Simulation.Infos.employe=Informations Employ\u00e9
Simulation.Employe.nom=Nom
Simulation.Employe.prenom=Pr\u00e9nom
Simulation.Employe.adresse=Adresse
Simulation.Employe.indice=Indice
Simulation.Employe.ville=Ville
Simulation.Employe.codePostal=Code Postal
Simulation.Infos.cotisations=Cotisations Sociales
Simulation.Cotisations.csgrds=CsgRds
Simulation.Cotisations.csgrds=Csgd
Simulation.Cotisations.retraite=Retraite
Simulation.Cotisations.secu=S\u00e9cu
Form.Infos.indemnites=Indemnit\u00e9s
Simulation.Indemnites.salaireHoraire=Salaire horaire
Simulation.Indemnites.entretienJour=Entretien/Jour
Simulation.Indemnites.repasJour=Repas/Jour
Simulation.Indemnites.cong\u00e9sPay\u00e9s=Cong\u00e9s pay\u00e9s
Simulation.Infos.Salaire=Salaire
Simulation.Salaire.salaireBase=Salaire de base
Simulation.Salaire.cotisationsSociales=Cotisations sociales
Simulation.Salaire.entretien=Indemnit\u00e9s d''entretien
Simulation.Salaire.repas=Indemnit\u00e9s de repas
Simulation.salaireNet=Salaire net
# formati
Format.heure = {0,time}
Format.nombre = {0,number,#0.0##}
Format.pourcent = {0,number,##0.00' %'}
Format.monnaie={0,number,##0.00' \u20ac'}
# elenco delle simulazioni
Pam.Simulations.titre=Liste des simulations
Pam.Simulations.num=Num\u00e9ro
Pam.Simulations.nom=Nom
Pam.Simulations.prenom=Pr\u00e9nom
Pam.Simulations.heures=Heures
Pam.Simulations.jours=Jours
Pam.Simulations.salairebase=Salaire de base
Pam.Simulations.indemnites=Indemnites
Pam.Simulations.cotisationsociales=Cotisations
Pam.Simulations.salairenet=Salaire
Pam.SimulationsVides.titre=La liste des simulations est vide
# menu
Menu.FaireSimulation=Faire la simulation
Menu.EffacerSimulation=Effacer la simulation
Menu.VoirSimulations=Voir les simulations
Menu.RetourFormulaire=Retour au formulaire de navigation
Menu.EnregistrerSimulation=Enregistrer la simulation
Menu.TerminerSession=Terminer la session
# messaggio di errore
Erreur.sessionexpiree=La session a expir\u00e9
Erreur.numSimulation=N\u00b0 de simulation incorrect
# errore di conversione
xwork.default.invalid.fieldvalue=Valeur invalide pour le champ "{0}".

Il file [Formulaire.properties] è il seguente:


# per far sì che i valori duplicati siano nel formato locale
double.format={0,number,#0.00##}
# messaggio di errore
joursTravaill\u00e9s.error=Tapez un nombre entier compris entre 1 et 31
heuresTravaill\u00e9es.error=Tapez un nombre r\u00e9el entre 0 et 300

19.6. Il foglio di stile

Le viste Tiles utilizzano il seguente foglio di stile [styles.css]:


.libelle{
  background-color: #ccffff;
  font-family: 'Times New Roman',Times,serif;
  font-size: 14px;
  font-weight: bold;;
  padding-right: 5px;
  padding-left: 5px;
  padding-bottom: 5px;
  padding-top: 5px;
}


.info{
  background-color: #99cc00;;
  padding-right: 5px;
  padding-left: 5px;
  padding-bottom: 5px;
  padding-top: 5px;
}

.titreInfos{
  background-color: #ffcc00
}

19.7. La vista iniziale

Per analizzare l'applicazione, la presenteremo partendo dalle diverse azioni dell'utente. Esamineremo ogni volta l'azione Struts che esegue tale azione e la vista Tiles che viene inviata in risposta.

In [struts.xml] sono presenti le seguenti azioni:


  <!-- azione predefinita -->
    <default-action-ref name="index" />
    <action name="index">
      <result type="redirectAction">
        <param name="actionName">Formulaire!input</param>
        <param name="namespace">/</param>
      </result>
    </action>
    <!-- azione Modulo -->
    <action name="Formulaire" class="web.actions.Formulaire">
      <result name="success" type="tiles">saisie</result>
      <result name="exception" type="tiles">exception</result>
      <result name="input" type="tiles">saisie</result>
      <result name="simulation" type="tiles">simulation</result>
</action>

  • righe 2-8: l’azione predefinita dell’applicazione è [Formulaire!input].

La classe [Formulaire] è la seguente:


package web.actions;

...
public class Formulaire extends ActionSupport implements Preparable, SessionAware {

  // configurazione inizializzata da Spring
  private Config config;
  // elenco dei dipendenti
  private List<Employe> employes;
  // elenco degli errori
  private List<Erreur> erreurs;
  // busta paga
  private FeuilleSalaire feuilleSalaire;
  // dati inseriti
  private String comboEmployesValue;
  private Double heuresTravaillees;
  private Integer joursTravailles;
  // sessione
  private Map<String, Object> session;
  // menu
  private Menu menu;

  @Override
  public void prepare() throws Exception {
    ...
  }

  @Override
  public String input() {
  ....
  }

  // calcolo dello stipendio
  public String calculSalaire() {
 ...
    }
  }

  @Override
  public void validate() {
  ...
  }

  @Override
  public void setSession(Map<String, Object> map) {
    session = map;
  }

  // getter e setter
  ...
}
  • riga 4: l'azione [Formulaire] implementa l'interfaccia Preparable. Quest'ultima ha un solo metodo, il metodo prepare alla riga 24. Questo metodo viene eseguito una volta prima di qualsiasi altro metodo dell'azione. Viene generalmente utilizzato per inizializzare il modello dell'azione.

Il modello dell’azione è costituito dalle righe 6-21:

  • riga 7: il campo config viene inizializzato da Spring come già spiegato. Esso consente di accedere ai dati nell’ambito dell’applicazione:
    • un riferimento al livello [métier]
    • un riferimento all’elenco dei dipendenti
    • un riferimento all’eccezione che potrebbe essersi verificata durante l’istanziazione dell’oggetto [Config]
  • riga 9: un elenco dei dipendenti. Questo alimenterà il menu a tendina dei dipendenti nel frammento [Saisie.JSP].
  • riga 11: un elenco di errori. Questo alimenterà il frammento [Erreur.JSP].
  • riga 21: l'elenco delle opzioni del menu del frammento [Entete.JSP]

In [1], i collegamenti del menu visualizzato sono controllati dal campo menu dell'azione [Formulaire].

Il metodo prepare viene eseguito prima del metodo input. È il seguente:


  @Override
  public void prepare() throws Exception {
    // errore di configurazione?
    Exception initException = config.getInitException();
    if (initException != null) {
      erreurs = new ArrayList<Erreur>();
      Throwable th = initException;
      while (th != null) {
        erreurs.add(new Erreur(th.getClass().getName(), th.getMessage()));
        th = th.getCause();
      }
    } else {
      employes = config.getEmployes();
    }
}
  • riga 4: si recupera l'eccezione nell'oggetto [Config] istanziato da Spring
  • riga 5: se si è verificata un'eccezione durante l'istanziazione dell'oggetto [Config], si inizializza l'elenco degli errori della riga 11. La classe [Erreur] è la seguente:

package web.entities;

import java.io.Serializable;

public class Erreur implements Serializable{
  
  public Erreur() {
  }
  
  // campi
  private String classe;
  private String message;

  // costruttore
  public Erreur(String classe, String message){
    this.setClasse(classe);
    this.message=message;
  }
  
  // getter e setter
... 
}

La classe serve a memorizzare lo stack delle eccezioni:

  • riga 11: la classe dell’eccezione
  • riga 12: il messaggio dell'eccezione

Torniamo al metodo prepare:

  • riga 13: l'elenco dei dipendenti dell'oggetto [Config] viene memorizzato nel campo employes dell'azione.

Una volta eseguito il metodo prepare, verrà eseguito a sua volta il metodo input. È il seguente:


  @Override
  public String input() {
    if (erreurs == null) {
      // menu
      menu = new Menu(true, false, false, true, false, true);
      return SUCCESS;
    } else {
      // menu
      menu = new Menu(false, false, false, false, false, false);
      return "exception";
    }
}

Il metodo input si limita a impostare l’elenco delle opzioni di menu da visualizzare. La classe [Menu] è la seguente:


package web.entities;

import java.io.Serializable;

public class Menu implements Serializable {
  // elementi del menu

  private boolean faireSimulation;
  private boolean effacerSimulation;
  private boolean enregistrerSimulation;
  private boolean voirSimulations;
  private boolean retourFormulaire;
  private boolean terminerSession;

  public Menu() {
  }

  public Menu(boolean faireSimulation, boolean effacerSimulation, boolean enregistrerSimulation, boolean voirSimulations, boolean retourFormulaire, boolean terminerSession) {
    this.faireSimulation = faireSimulation;
    this.effacerSimulation = effacerSimulation;
    this.enregistrerSimulation = enregistrerSimulation;
    this.voirSimulations = voirSimulations;
    this.retourFormulaire = retourFormulaire;
    this.terminerSession = terminerSession;
  }
  
  // getter e setter
...  
}
  • righe 8-13: nel menu sono presenti 6 possibili collegamenti
  • righe 18-25: il costruttore della classe consente di specificare i collegamenti che devono essere visualizzati e quelli che non devono esserlo.

I collegamenti del menu vengono visualizzati in [Entete.JSP], frammento JSP presente in tutte le viste Tiles. Ogni azione avrà un campo menu per controllare la visualizzazione del menu di [Entete.JSP].

Torniamo al metodo input:


  @Override
  public String input() {
    if (erreurs == null) {
      // menu
      menu = new Menu(true, false, false, true, false, true);
      return SUCCESS;
    } else {
      // menu
      menu = new Menu(false, false, false, false, false, false);
      return "exception";
    }
}
  • righe 3-6: se l’elenco degli errori è vuoto, verrà visualizzato il menu [Faire la simulation, Voir les simulations, Terminer la session] e verrà restituita la chiave input.
  • righe 9-10: se l’elenco degli errori non è vuoto, il menu sarà vuoto e verrà restituita la chiave exception.

Torniamo alla configurazione dell’azione [Formulaire] in [struts.xml]:


    <!-- azione Modulo -->
    <action name="Formulaire" class="web.actions.Formulaire">
      <result name="success" type="tiles">saisie</result>
      <result name="exception" type="tiles">exception</result>
      <result name="input" type="tiles">saisie</result>
      <result name="simulation" type="tiles">simulation</result>
</action>
  • riga 5: la chiave input visualizza la vista Tiles denominata saisie
  • riga 4: la chiave exception visualizza la vista Tiles denominata exception

Cominciamo con la vista Tiles denominata saisie. È composta dai frammenti JSP, [Entete.JSP] e [Saisie.JSP].

Il frammento [Entete.JSP] è il seguente:

 

Il suo codice è il seguente:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>

<table>
  <tr>
    <td><h1><s:text name="Pam.titre"/></h1></td>
    <td>
      <s:if test="menu.faireSimulation">
        |<a href="javascript:doSimulation()"><s:text name="Menu.FaireSimulation"/></a><br/>
      </s:if>
      <s:if test="menu.effacerSimulation">
        |<a href="<s:url action="Formulaire!input"/>"><s:text name="Menu.EffacerSimulation"/></a><br/>
      </s:if>
      <s:if test="menu.voirSimulations">
        |<a href="<s:url action="VoirSimulations"/>"><s:text name="Menu.VoirSimulations"/></a><br/>
      </s:if>
      <s:if test="menu.retourFormulaire">
        |<a href="<s:url action="RetourFormulaire"/>"><s:text name="Menu.RetourFormulaire"/></a><br/>
      </s:if>
      <s:if test="menu.enregistrerSimulation">
        |<a href="<s:url action="EnregistrerSimulation"/>"><s:text name="Menu.EnregistrerSimulation"/></a><br/>
      </s:if>
      <s:if test="menu.terminerSession">
        |<a href="<s:url action="TerminerSession"/>"><s:text name="Menu.TerminerSession"/></a><br/>
      </s:if>
    </td>
  </tr>
</table>
  • righe 8-25: visualizzazione dei sei link del menu [Esegui simulazione (righe 8-10), Cancella simulazione (righe 11-13), Visualizza le simulazioni (righe 14-16), Torna al modulo (righe 17-19), Salva la simulazione (righe 20-22), Termina la sessione (righe 23-25).
  • righe 8, 11, 14, 17, 20, 23: la visualizzazione dei collegamenti è controllata dal campo menu dell’azione corrente.

Si noti che il frammento [Entete.JSP] visualizza una tabella HTML (righe 4-28) ma non è una pagina HTML completa. È importante ricordare che tutte le viste dell’applicazione vengono inserite nella seguente pagina master [MasterPage.JSP]:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<!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">
    <link href="styles.css" rel="stylesheet" type="text/css"/>
    <title>
      <tiles:insertAttribute name="titre" ignore="true" />
    </title>
    <s:head/>
  </head>
  <body background="<s:url value="/ressources/standard.jpg"/>">
    <tiles:insertAttribute name="entete" />
    <hr/>
    <tiles:insertAttribute name="saisie" />
    <tiles:insertAttribute name="simulation" />
    <tiles:insertAttribute name="exception" />
    <tiles:insertAttribute name="erreur" />
    <tiles:insertAttribute name="simulations" />
  </body>
</html>

Il frammento [Entete.JSP] viene inserito alla riga 17, all’interno di una pagina HTML standard.

Il frammento [Saisie.JSP] viene inserito alla riga 19. Si tratta della seguente vista:

 

Il codice del frammento [Saisie.JSP] è il seguente:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  "http://www.w3.org/TR/html4/loose.dtd">


<script language="javascript" type="text/javascript">
  function doSimulation(){
    // invio del modulo
    document.forms['Saisie'].elements['action'].name='action:Formulaire!calculSalaire'
    document.forms['Saisie'].submit();
  }  
</script>

<!-- inserimento delle informazioni -->
<s:form name="Saisie" id="Saisie">
  <s:select name="comboEmployesValue" list="employes" listKey="SS" listValue="prenom+' ' +nom" key="Pam.Saisie.employé"/>
  <s:textfield name="heuresTravaillees" key="Pam.Saisie.Heures.libellé" value="%{#parameters['heuresTravaillees']!=null ? #parameters['heuresTravaillees'] : heuresTravaillees==null ? '' : getText('double.format',{heuresTravaillees})}"/>
  <s:textfield name="joursTravailles" key="Pam.Saisie.Jours.libellé" value="%{#parameters['joursTravailles']!=null ? #parameters['joursTravailles'] : joursTravailles==null ? '' : joursTravailles}"/>
  <input type="hidden" name="action"/>
</s:form>
  • riga 17: il modulo non ha l’attributo action. Per impostazione predefinita, è presente action='Formulaire'.
  • riga 18: visualizzazione del menu a tendina dei dipendenti. Il contenuto del menu a tendina (attributo list) è fornito dal campo employes dell'azione corrente. L'attributo value delle opzioni corrisponderà al n. SS dei dipendenti (attributo listKey). La dicitura visualizzata per ciascuna opzione sarà il nome e il cognome del dipendente (attributo listValue). Il n. SS del dipendente selezionato nel menu a tendina verrà inserito nel campo [Formulaire].comboEmployesvalue (attributo name).
  • riga 19: campo di inserimento delle ore lavorate. Il valore visualizzato (attributo value) corrisponde a quello del campo heuresTravaillees dell'azione [Formulaire] nel seguente formato (Formulaire.properties):

double.format={0,number,#0.00##}

Il valore verrà inserito nel campo [Formulaire].heuresTravaillees (attributo name).

  • riga 20: campo di immissione dei giorni lavorati. Il valore visualizzato (attributo value) è quello del campo joursTravailles dell’azione [Formulaire].

Il valore verrà inserito nel campo [Formulaire].joursTravailles (attributo name).

Alla fine, la vista Tiles saisie visualizzata all’avvio in assenza di errori è la seguente:

 

Torniamo alla configurazione dell’azione [Formulaire]:


    <!-- azione Modulo -->
    <action name="Formulaire" class="web.actions.Formulaire">
      <result name="success" type="tiles">saisie</result>
      <result name="exception" type="tiles">exception</result>
      <result name="input" type="tiles">saisie</result>
      <result name="simulation" type="tiles">simulation</result>
</action>

Abbiamo visto che l’azione [Formulaire].input può anche restituire la chiave exception della riga 4. In questo caso, viene visualizzata la vista Tiles denominata exception. Questa è composta dai frammenti [Entete.JSP] e [Exception.JSP]. Abbiamo già presentato il frammento [Entete.JSP]. Il frammento [Exception.JSP] è il seguente:

 

Questa è la pagina iniziale della versione 2 dell’applicazione quando l’Sgbd non è stato avviato. Il codice JSP del frammento [Erreur.JSP] è il seguente:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>

<h2><s:text name="Pam.Erreurs.titre"/></h2>
<table>
  <tr class="titreInfos">
    <th><s:text name="Pam.Erreurs.classe"/></th>
    <th><s:text name="Pam.Erreurs.message"/></th>
  </tr>
  <s:iterator value="erreurs">
    <tr>
      <td class="libelle"><s:property value="classe"/></td>
      <td class="info"><s:property value="message"/></td>
    </tr>
  </s:iterator>
</table>
  • righe 10-14: un iteratore sulla collezione List<Erreur> errori dell'azione [Formulaire]. Ricordiamo che in caso di errore vi era stato memorizzato uno stack di eccezioni.

19.8. Eseguire una simulazione

Una volta ottenuta la schermata iniziale, è possibile effettuare un calcolo dello stipendio tramite il link [Faire une simulation].

19.8.1. Convalida dei dati inseriti

Consideriamo la seguente sequenza:

  • in [1], un inserimento errato
  • in [2], la risposta inviata.

Consideriamo la configurazione dell'azione [Formulaire] in [struts.xml]:


    <!-- azione Modulo -->
    <action name="Formulaire" class="web.actions.Formulaire">
      <result name="success" type="tiles">saisie</result>
      <result name="exception" type="tiles">exception</result>
      <result name="input" type="tiles">saisie</result>
      <result name="simulation" type="tiles">simulation</result>
</action>

È noto che, in caso di errore di convalida, l’intercettatore di convalida restituisce la chiave input. Viene quindi restituita la vista Tiles saisie. Il processo di convalida fa sì che i campi errati siano accompagnati da messaggi di errore.

La convalida dell’azione [Formulaire] è garantita dal seguente file [Formulaire-validation.xml]:


<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">


<validators>
  
  <field name="heuresTravaillees" >
    <field-validator type="required" short-circuit="true">
      <message key="heuresTravaillées.error"/>
    </field-validator>
    
    <field-validator type="conversion" short-circuit="true">
      <message key="heuresTravaillées.error"/>
    </field-validator>
    
    <field-validator type="double" short-circuit="true">
      <param name="minInclusive">0</param>
      <param name="maxInclusive">300</param>
      <message key="heuresTravaillées.error"/>
    </field-validator>
  </field>
  
  <field name="joursTravailles" >
    <field-validator type="required" short-circuit="true">
      <message key="joursTravaillés.error"/>
    </field-validator>
    
    <field-validator type="conversion" short-circuit="true">
      <message key="joursTravaillés.error"/>
    </field-validator>
    
    <field-validator type="int" short-circuit="true">
      <param name="min">0</param>
      <param name="max">31</param>
      <message key="joursTravaillés.error"/>
    </field-validator>
  </field>
  
</validators>
  • le righe 6-20 verificano che il campo heuresTravaillees sia un numero reale compreso nell’intervallo [0,300].
  • le righe 22-36 verificano che il campo joursTravailles sia un numero intero compreso nell'intervallo [0,31].

Torniamo alla configurazione dell’azione [Formulaire] in [struts.xml]:


    <!-- azione Modulo -->
    <action name="Formulaire" class="web.actions.Formulaire">
      ...
      <result name="input" type="tiles">saisie</result>
</action>

Sappiamo che, in caso di errore di convalida, l’intercettatore di convalida restituisce la chiave input. Viene quindi restituita la vista Tiles saisie.

Ricordiamo che questa è composta dai frammenti [Entete.JSP] e [Saisie.JSP], dove [Entete.JSP] contiene un titolo e una selezione di opzioni, mentre [Saisie.JSP] contiene il modulo di inserimento dati. In caso di errori di inserimento, il processo di convalida fa sì che i campi errati siano accompagnati da messaggi di errore e mostrino inoltre il loro valore errato. Il frammento [Entete.JSP] non interviene in alcun modo nel processo di convalida. Diamo un’occhiata al suo codice:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>

<table>
  <tr>
    <td><h1><s:text name="Pam.titre"/></h1></td>
    <td>
      <s:if test="menu.faireSimulation">
        |<a href="javascript:doSimulation()"><s:text name="Menu.FaireSimulation"/></a><br/>
      </s:if>
      <s:if test="menu.effacerSimulation">
        |<a href="<s:url action="Formulaire!input"/>"><s:text name="Menu.EffacerSimulation"/></a><br/>
      </s:if>
      <s:if test="menu.voirSimulations">
        |<a href="<s:url action="VoirSimulations"/>"><s:text name="Menu.VoirSimulations"/></a><br/>
      </s:if>
      <s:if test="menu.retourFormulaire">
        |<a href="<s:url action="RetourFormulaire"/>"><s:text name="Menu.RetourFormulaire"/></a><br/>
      </s:if>
      <s:if test="menu.enregistrerSimulation">
        |<a href="<s:url action="EnregistrerSimulation"/>"><s:text name="Menu.EnregistrerSimulation"/></a><br/>
      </s:if>
      <s:if test="menu.terminerSession">
        |<a href="<s:url action="TerminerSession"/>"><s:text name="Menu.TerminerSession"/></a><br/>
      </s:if>
    </td>
  </tr>
</table>

I sei link sono configurati dal campo menu del modello (righe 8, 11, 14, 17, 20, 23). Quando si verifica un errore, questo modello non viene aggiornato dall’azione e si ottiene quindi una pagina senza menu. Per risolvere questo problema, la classe [Formulaire] dispone del seguente metodo validate:


package web.actions;

import com.opensymphony.xwork2.ActionSupport;
...
public class Formulaire extends ActionSupport implements Preparable, SessionAware {

  ...
  // menu
  private Menu menu;

  @Override
  public void prepare() throws Exception {
  ...
  }

  @Override
  public String input() {
  ...
  }

  // calcolo dello stipendio
  public String calculSalaire() {
  ...
  }

  @Override
  public void validate() {
    // Errori?
    if (!getFieldErrors().isEmpty()) {
      // menu
      menu = new Menu(true, false, false, true, false, true);
    }
  }

  // getter e setter
  ...
}
  • riga 27: sappiamo che, quando è presente, il metodo validate viene eseguito dal processo di convalida. Ne approfittiamo per aggiornare il menu della riga 4, che fa parte del modello del frammento [Entete.JSP].
  • righe 29-32: se si sono verificati errori di convalida, si imposta il menu per visualizzare nuovamente la vista Tiles saisie. Se non si sono verificati errori di convalida, non si esegue alcuna operazione. È il metodo calculSalaire che si occupa quindi di creare il modello della vista da visualizzare.

19.8.2. Il calcolo dello stipendio

Torniamo al codice JSP dell’intestazione:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>

<table>
  <tr>
    <td><h1><s:text name="Pam.titre"/></h1></td>
    <td>
      <s:if test="menu.faireSimulation">
        |<a href="javascript:doSimulation()"><s:text name="Menu.FaireSimulation"/></a><br/>
      </s:if>
      ...
    </td>
  </tr>
</table>
  • riga 9: quando l'utente clicca sul link [Faire la simulation], viene eseguita la funzione JavaScript doSimulation. Questa è definita nel frammento [Saisie.JSP]:

<script language="javascript" type="text/javascript">
  function doSimulation(){
    // invio del modulo
    document.forms['Saisie'].elements['action'].name='action:Formulaire!calculSalaire'
    document.forms['Saisie'].submit();
  } 
</script>

<!-- inserimento delle informazioni -->
<s:form name="Saisie" id="Saisie">
  <s:select name="comboEmployesValue" list="employes" listKey="SS" listValue="prenom+' ' +nom" key="Pam.Saisie.employé"/>
  <s:textfield name="heuresTravaillees" key="Pam.Saisie.Heures.libellé" value="%{#parameters['heuresTravaillees']!=null ? #parameters['heuresTravaillees'] : heuresTravaillees==null ? '' : getText('double.format',{heuresTravaillees})}"/>
  <s:textfield name="joursTravailles" key="Pam.Saisie.Jours.libellé" value="%{#parameters['joursTravailles']!=null ? #parameters['joursTravailles'] : joursTravailles==null ? '' : joursTravailles}"/>
  <input type="hidden" name="action"/>
</s:form>
  • riga 14: un campo nascosto denominato action verrà inviato all'azione [Formulaire]. Questo campo ci consentirà di specificare l'azione e il metodo che devono essere eseguiti al POST del modulo. Ricorderemo forse dai primi esempi che questi possono essere specificati in un parametro denominato action:Action!metodo. Il valore di questo parametro non ha importanza; è sufficiente che sia presente.
  • righe 2-6: la funzione JavaScript che viene eseguita quando l’utente clicca sul link [Faire la simulation] del frammento [Entete.JSP].
  • riga 4: si modifica l’attributo name del campo nascosto action. Si fa in modo che abbia la forma action:Action!metodo prevista da Struts.
  • riga 5: viene inviato il modulo denominato Saisie della riga 5. Di conseguenza, viene inviata la seguente stringa di parametri:
comboEmployesValue=SS1&heuresTravaillees=xx&joursTravailles=yy&action:Formulaire!calculSalaire
SS1 : n° INSEE de l'employé sélectionné dans le combo
heuresTravaillees : nombre d'heures travaillées
joursTravailles : nombre de jours travaillés
action : Formulaire!calculSalaire : les éléments ci-dessus seront postés à l'action [Formulaire] puis la méthode calculSalaire de cette action sera exécutée.

Il metodo [Formulaire].calculSalaire è il seguente:


// calcolo dello stipendio
  public String calculSalaire() {
    try {
      // calcolo dello stipendio
      feuilleSalaire = config.getMetier().calculerFeuilleSalaire(comboEmployesValue, heuresTravaillees, joursTravailles);
      // inserimento della simulazione nella sessione
      session.put("simulation", new Simulation(0, "" + heuresTravaillees, "" + joursTravailles, feuilleSalaire));
      // menu
      menu = new Menu(true, true, true, true, false, true);
      // fine
      return "simulation";
    } catch (Throwable th) {
      ...
    }
  }
  • riga 5: il calcolo della busta paga viene richiesto al livello [métier]
  • riga 7: si inserisce un oggetto di tipo Simulation nella sessione dell’utente. Infatti, potrebbe essere necessario in caso di una richiesta successiva. La classe [Simulation] è la seguente:

package web.entities;

import java.io.Serializable;
import metier.FeuilleSalaire;

public class Simulation implements Serializable{
  
  public Simulation() {
  }

  // campi di una simulazione
  private Integer num;
  private FeuilleSalaire feuilleSalaire;
  private String heuresTravaillées;
  private String joursTravaillés;
  
  // generatore
  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();
  }

  // getter e setter
...
}
  • riga 12: il numero della simulazione. Viene incrementato ad ogni nuova simulazione registrata.
  • riga 13: la busta paga del dipendente
  • riga 14: il numero di ore lavorate
  • riga 15: il numero di giorni lavorati
  • riga 25: il metodo getIndemnites restituisce il totale delle indennità del dipendente

Vedremo che la classe [Simulation] è il modello del frammento [Simulations.JSP] che presenta tutte le simulazioni effettuate.

Torniamo al metodo [Formulaire].calculSalaire:


// calcolo dello stipendio
  public String calculSalaire() {
    try {
      // calcolo dello stipendio
      feuilleSalaire = config.getMetier().calculerFeuilleSalaire(comboEmployesValue, heuresTravaillees, joursTravailles);
      // si inserisce la simulazione nella sessione
      session.put("simulation", new Simulation(0, "" + heuresTravaillees, "" + joursTravailles, feuilleSalaire));
      // menu
      menu = new Menu(true, true, true, true, false, true);
      // fine
      return "simulation";
    } catch (Throwable th) {
 ...
  }
  • riga 9: aggiornamento del menu
  • riga 11: restituzione della chiave di navigazione simulation.

Torna alla configurazione dell'azione [Formulaire]:


    <!-- azione Modulo -->
    <action name="Formulaire" class="web.actions.Formulaire">
         ...
      <result name="simulation" type="tiles">simulation</result>
</action>

La riga 4 mostra che la chiave di navigazione simulation fa visualizzare la vista Tiles denominata simulation. Quest'ultima è composta dai seguenti frammenti JSP: [Entete, Saisie, Simulation].

La vista visualizzata è la seguente:

  • in [1], il frammento [Entete.JSP]
  • in [2], il frammento [Saisie.JSP]
  • in [3], il frammento [Simulation.JSP]. Si ricorda che la busta paga presentata è quella fittizia generata dal livello [metier].

I primi due frammenti sono già stati presentati. Il frammento [Simulation.JSP] è il seguente:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>

<hr/>
<!-- informazioni sul dipendente -->
<span class="titreInfos">
  <s:text  name="Simulation.Infos.employe"/>
</span>
<br/><br/>
<table>
  <!-- riga 1 -->
  <tr>
    <th class="libelle">
      <s:text name="Simulation.Employe.nom"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Employe.prenom"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Employe.adresse"/>
    </th>
  </tr>
  <!-- riga 2 -->
  <tr>
    <td class="info">   
      <s:property value="feuilleSalaire.employe.nom"/>      
    </td>
    <td class="info">
      <s:property value="feuilleSalaire.employe.prenom"/>
    </td>
    <td class="info">
      <s:property value="feuilleSalaire.employe.adresse"/>
    </td>
</table>
<table>
  <!-- riga 1 -->
  <tr>
    <th class="libelle"><s:text name="Simulation.Employe.ville"/></th>
    <th class="libelle">
      <s:text name="Simulation.Employe.codePostal"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Employe.indice"/>
    </th>
  </tr>
  <!-- riga 2 -->
  <tr>
    <td class="info">
      <s:property value="feuilleSalaire.employe.ville"/>
    </td>
    <td class="info">
      <s:property value="feuilleSalaire.employe.codePostal"/>
    </td>
    <td class="info">
      <s:property value="feuilleSalaire.employe.indemnite.indice"/>
    </td>
</table>
<!-- Informazioni sui contributi -->
<br/>
<span class="titreInfos">
  <s:text name="Simulation.Infos.cotisations"/>
</span>

<br/><br/>
<table>
  <!-- riga 1 -->
  <tr>
    <th class="libelle">
      <s:text name="Simulation.Cotisations.csgrds"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Cotisations.csgrds"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Cotisations.retraite"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Cotisations.secu"/>
    </th>
  </tr>
  <!-- riga 2 -->
  <tr>
    <td class="info">
      <s:text name="Format.pourcent">
        <s:param value="feuilleSalaire.cotisation.csgrds"/>
      </s:text>
    </td>
    <td  class="info">
      <s:text name="Format.pourcent">
        <s:param value="feuilleSalaire.cotisation.csgd"/>
      </s:text>
    </td>
    <td class="info">
      <s:text name="Format.pourcent">
        <s:param value="feuilleSalaire.cotisation.retraite"/>
      </s:text>
    </td>
    <td class="info">
      <s:text name="Format.pourcent">
        <s:param value="feuilleSalaire.cotisation.secu"/>
      </s:text>
    </td>
</table>
<!-- informazioni Indennità -->
<br/>
<span class="titreInfos">
  <s:text name="Form.Infos.indemnites"/>
</span>
<br/><br/>
<table>
  <!-- riga 1 -->
  <tr>
    <th class="libelle">
      <s:text name="Simulation.Indemnites.salaireHoraire"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Indemnites.entretienJour"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Indemnites.repasJour"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Indemnites.congésPayés"/>
    </th>
  </tr>
  <!-- riga 2 -->
  <tr>
    <td class="info">
      <s:text name="Format.monnaie">
        <s:param value="feuilleSalaire.employe.indemnite.baseHeure"/>
      </s:text>
    </td>
    </td>
    <td class="info">
      <s:text name="Format.monnaie">
        <s:param value="feuilleSalaire.employe.indemnite.entretienJour"/>
      </s:text>
    </td>
    <td class="info">
      <s:text name="Format.monnaie">
        <s:param value="feuilleSalaire.employe.indemnite.repasJour"/>
      </s:text>
    </td>
    <td class="info">
      <s:text name="Format.monnaie">
        <s:param value="feuilleSalaire.employe.indemnite.indemnitesCP"/>
      </s:text>
    </td>
  </tr>
</table>
<!-- informazioni sullo stipendio -->
<br/>
<span class="titreInfos">
  <s:text name="Simulation.Infos.Salaire"/>
</span>
<br/><br/>
<table>
  <!-- riga 1 -->
  <tr>
    <th class="libelle">
      <s:text name="Simulation.Salaire.salaireBase"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Salaire.cotisationsSociales"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Salaire.entretien"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Salaire.repas"/>
    </th>
  </tr>

  <!-- riga 2 -->
  <tr>
    <td class="info">
      <s:text name="Format.monnaie">
        <s:param value="feuilleSalaire.elementsSalaire.salaireBase"/>
      </s:text>
    </td>
    <td class="info">
      <s:text name="Format.monnaie">
        <s:param value="feuilleSalaire.elementsSalaire.cotisationsSociales"/>
      </s:text>
    </td>
    <td class="info">
      <s:text name="Format.monnaie">
        <s:param value="feuilleSalaire.elementsSalaire.indemnitesEntretien"/>
      </s:text>
    </td>
    <td class="info">
      <s:text name="Format.monnaie">
        <s:param value="feuilleSalaire.elementsSalaire.indemnitesRepas"/>
      </s:text>
    </td>
  </tr>
</table>
<!-- Stipendio netto-->
<br/>
<table>
  <tr>
    <td class="libelle">
      <s:text name="Simulation.salaireNet"/>
    <td></td>
    <td class="info">
      <s:text name="Format.monnaie">
        <s:param value="feuilleSalaire.elementsSalaire.salaireNet"/>
      </s:text>
    </td>
  </tr>
</table>

È lungo... ma dal punto di vista funzionale è semplice. Questo frammento mostra le diverse proprietà del campo [Formulaire].feuilleSalaire che rappresenta la busta paga del dipendente.

Torniamo al metodo [Formulaire].calculSalaire:


// calcolo dello stipendio
  public String calculSalaire() {
    try {
      ...
      return "simulation";
    } catch (Throwable th) {
      erreurs = new ArrayList<Erreur>();
      while (th != null) {
        erreurs.add(new Erreur(th.getClass().getName(), th.getMessage()));
        th = th.getCause();
      }
      // menu
      menu = new Menu(false, false, false, false, true, true);
      return "exception";
    }
  }

Il calcolo dello stipendio potrebbe non andare a buon fine. Ciò accadrebbe in particolare se il collegamento con l’Sgbd venisse interrotto. In tal caso, si gestisce l’eccezione che si verifica. Abbiamo già riscontrato questo caso durante l’analisi del metodo [Formulaire].input.

  • righe 7-11: si crea un elenco di oggetti Erreur a partire dallo stack delle eccezioni
  • riga 13: si imposta il menu
  • riga 14: si restituisce la chiave exception.

La chiave exception farà visualizzare la vista Tiles exception:


    <!-- Azione Modulo -->
    <action name="Formulaire" class="web.actions.Formulaire">
      <result name="exception" type="tiles">exception</result>
      ...
</action>

Questa vista Tiles è già stata presentata. Ha il seguente aspetto:

 

19.9. Salvare una simulazione

Dopo aver eseguito una simulazione, l'utente potrebbe volerla salvare nella sessione.

  • in [1], si salva la simulazione
  • in [2], la risposta che presenta l'elenco delle simulazioni già effettuate, a cui viene aggiunta la nuova simulazione

Il collegamento [Enregistrer la simulation] si trova nel frammento [Entete.JSP]:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>

<table>
  <tr>
    <td><h1><s:text name="Pam.titre"/></h1></td>
    <td>
      ...
      <s:if test="menu.enregistrerSimulation">
        |<a href="<s:url action="EnregistrerSimulation"/>"><s:text name="Menu.EnregistrerSimulation"/></a><br/>
      </s:if>
      ...
    </td>
  </tr>
</table>

Si nota che cliccando sul link si avvia l’esecuzione dell’azione [EnregistrerSimulation]. Quest’ultima è configurata nel file [struts.xml] nel modo seguente:


   <!-- azione EnregistrerSimulation -->
    <action name="EnregistrerSimulation" class="web.actions.Enregistrer" method="execute">
      <result name="error" type="tiles">erreur</result>
      <result name="simulations" type="tiles">simulations</result>
</action>
  • riga 1: l'azione [EnregistrerSimulation] è associata alla classe [Enregistrer] e al suo metodo execute.

La classe [Enregistrer] è la seguente:


package web.actions;

...
public class Enregistrer extends ActionSupport implements SessionAware {

  // sessione
  private Map<String, Object> session;
  // menu
  private Menu menu;

  @Override
  public void setSession(Map<String, Object> session) {
    this.session = session;
  }

  // esecuzione dell'azione
  public String execute() {
    // si recupera l'ultima simulazione nella sessione
    Simulation simulation = (Simulation) session.get("simulation");
    if (simulation == null) {
      return ERROR;
    }
    ...
  }

  // getter e setter
  ...
}
  • riga 4: poiché l'azione deve avere accesso alla sessione, implementa l'interfaccia SessionAware.
  • riga 7: la sessione
  • riga 9: il menu

Quando l'azione [Enregistrer] è stata istanziata, viene eseguito il suo metodo execute. Ricordiamo che il suo compito è quello di inserire nella sessione l'ultima simulazione. Questa verrà aggiunta all'elenco delle simulazioni già effettuate, anch'esso conservato nella sessione.

  • riga 19: si recupera dalla sessione l’ultima simulazione che vi è stata inserita.
  • righe 20-22: se non la si trova, significa probabilmente che la sessione è scaduta. Infatti, la sessione ha una durata limitata, che può essere impostata nel file [web.xml] che configura l’applicazione.
  • riga 21: si restituisce la chiave error.

Torniamo alla configurazione dell’azione [EnregistrerSimulation]:


   <!-- azione EnregistrerSimulation -->
    <action name="EnregistrerSimulation" class="web.actions.Enregistrer" method="execute">
      <result name="error" type="tiles">erreur</result>
      <result name="simulations" type="tiles">simulations</result>
</action>

Si nota che la chiave error (riga 3) determina la visualizzazione della vista Tiles denominata erreur. Quest’ultima è composta dai frammenti [Entete.JSP] e [Erreur.JSP] e presenta il seguente aspetto:

 

Il frammento [Erreur.JSP] è il seguente:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>

<h2><s:text name="Pam.Erreur.libelle"/></h2>
<h4><s:text name="Erreur.sessionexpiree"/></h4>

Ritorno al metodo [Enregistrer].execute:


// esecuzione dell'azione
  public String execute() {
    // si recupera l'ultima simulazione nella sessione
    Simulation simulation = (Simulation) session.get("simulation");
    if (simulation == null) {
      return ERROR;
    }
    // si recupera il numero dell'ultima simulazione
    Integer numDerniereSimulation = (Integer) session.get("numDerniereSimulation");
    if (numDerniereSimulation == null) {
      numDerniereSimulation = 0;
    }
    // lo si incrementa
    numDerniereSimulation++;
    // si reinserisce il nuovo numero nella sessione
    session.put("numDerniereSimulation", numDerniereSimulation);
    // si recupera l'elenco delle simulazioni
    List<Simulation> simulations = (List<Simulation>) session.get("simulations");
    if (simulations == null) {
      simulations = new ArrayList<Simulation>();
      session.put("simulations", simulations);
    }
    // si aggiunge la simulazione corrente
    simulation.setNum(numDerniereSimulation);
    simulations.add(simulation);
    // si visualizza l'elenco delle simulazioni
    menu = new Menu(false, false, false, false, true, true);
    return "simulations";
  }
  • righe 9-16: le diverse simulazioni sono numerate a partire da 1. L’ultimo numero assegnato viene inserito nella sessione sotto la chiave numDerniereSimulation. Il codice delle righe 9-16 consiste nel recuperare questa chiave e nell’incrementare il valore ad essa associato.
  • righe 18-22: l'elenco delle simulazioni è conservato nella sessione associata alla chiave simulations. Le righe 18-22 consistono nel recuperare tale elenco, se esiste, oppure nel crearlo, se non esiste.
  • righe 24-25: una volta ottenuto l’elenco delle simulazioni, vi si aggiunge la simulazione corrente (riga 25). In precedenza, alla simulazione corrente è stato assegnato un numero (riga 24).
  • riga 27: si imposta il menu da visualizzare
  • riga 28: si restituisce la chiave di navigazione simulations.

Ritorno alla configurazione dell’azione [EnregistrerSimulation] in [struts.xml]:


   <!-- azione EnregistrerSimulation -->
    <action name="EnregistrerSimulation" class="web.actions.Enregistrer" method="execute">
      <result name="error" type="tiles">erreur</result>
      <result name="simulations" type="tiles">simulations</result>
</action>

Riga 4: la chiave simulations provoca la visualizzazione della vista Tiles denominata simulations. Questa vista è composta dai frammenti [Entete.JSP] e [Simulations.JSP]. La vista visualizzata è la seguente:

  • in [1], il frammento [Entete.JSP] che ormai conosciamo bene.
  • in [2], il frammento [Simulations.JSP]

Il frammento [Simulations.JSP] è il seguente:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>

<!-- elenco delle simulazioni vuoto -->
<s:if test="#session['simulations']==null || #session['simulations'].size()==0">
  <h2><s:text name="Pam.SimulationsVides.titre"/></h2>
</s:if>
<!-- elenco delle simulazioni non vuoto -->
<s:if test="#session['simulations'].size()!=0">
  <h2><s:text name="Pam.Simulations.titre"/></h2>
  <table>
    <tr class="titreInfos">
      <th><s:text name="Pam.Simulations.num"/></th>
      <th><s:text name="Pam.Simulations.nom"/></th>
      <th><s:text name="Pam.Simulations.prenom"/></th>
      <th><s:text name="Pam.Simulations.heures"/></th>
      <th><s:text name="Pam.Simulations.jours"/></th>
      <th><s:text name="Pam.Simulations.salairebase"/></th>
      <th><s:text name="Pam.Simulations.indemnites"/></th>
      <th><s:text name="Pam.Simulations.cotisationsociales"/></th>
      <th><s:text name="Pam.Simulations.salairenet"/></th>
    </tr>
    <s:iterator value="#session['simulations']">
      <s:url action="SupprimerSimulation" var="URL">
        <s:param name="id" value="num"/>
      </s:url>
      <tr>
        <td class="libelle"><s:property value="num"/></td>
        <td class="info"><s:property value="feuilleSalaire.employe.nom"/></td>
        <td class="info"><s:property value="feuilleSalaire.employe.prenom"/></td>
        <td class="info"><s:property value="heuresTravaillées"/></td>
        <td class="info"><s:property value="joursTravaillés"/></td>
        <td class="info">
          <s:text name="Format.monnaie">
            <s:param value="feuilleSalaire.elementsSalaire.salaireBase"/>
          </s:text>
        </td>
        <td class="info">
          <s:text name="Format.monnaie">
            <s:param value="indemnites"/>
          </s:text>
        </td>
        <td class="info">
          <s:text name="Format.monnaie">
            <s:param value="feuilleSalaire.elementsSalaire.cotisationsSociales"/>
          </s:text>
        </td>
        <td class="info">
          <s:text name="Format.monnaie">
            <s:param value="feuilleSalaire.elementsSalaire.salaireNet"/>
          </s:text>
        </td>
        <td class="info"><a href="<s:property value="#URL"/>">Rimuovi</a></td>
      </tr>
    </s:iterator>
  </table>
</s:if>
  • righe 5-7: se nella sessione non ci sono simulazioni, viene visualizzata la seguente schermata:
 
  • righe 13-21: visualizzazione delle intestazioni delle colonne della tabella
 
  • righe 23-55: iterazione sull'elenco delle simulazioni presenti nella sessione
  • righe 24-26: creazione di un URL denominato URL (attributo id). Il link HTML generato da questo URL è il seguente:
<a href="<a href="view-source:http://localhost:8084/pam/SupprimerSimulation.action?id=1">/pam/SupprimerSimulation.action?id=1</a>">Retirer</a>

Si nota che il link punta all’azione [SupprimerSimulation] con il parametro id, che rappresenta il numero della simulazione da rimuovere dall’elenco.

  • righe 28-54: ad ogni iterazione sull’elenco delle simulazioni, vengono visualizzate le proprietà della simulazione corrente.
 

19.10. Rimuovere una simulazione

L'utente potrebbe voler rimuovere una simulazione dall'elenco delle simulazioni:

  • in [1], si elimina la simulazione n. 1
  • in [2], la simulazione n. 1 è stata rimossa

Il collegamento [Retirer] si trova nel frammento [Simulations.JSP] che abbiamo già esaminato:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>

<!-- elenco di simulazioni vuoto -->
<s:if test="#session['simulations']==null || #session['simulations'].size()==0">
  <h2><s:text name="Pam.SimulationsVides.titre"/></h2>
</s:if>
<!-- elenco di simulazioni non vuoto -->
<s:if test="#session['simulations'].size()!=0">
  <h2><s:text name="Pam.Simulations.titre"/></h2>
  <table>
    <tr class="titreInfos">
      ...
    </tr>
    <s:iterator value="#session['simulations']">
      <s:url action="SupprimerSimulation" var="URL">
        <s:param name="id" value="num"/>
      </s:url>
      <tr>
        ...
        <td class="info">
          <s:text name="Format.monnaie">
            <s:param value="feuilleSalaire.elementsSalaire.salaireNet"/>
          </s:text>
        </td>
        <td class="info"><a href="<s:property value="#URL"/>">Rimuovi</a></td>
      </tr>
    </s:iterator>
  </table>
</s:if>
  • righe 16-18: generano il link HTML
<a href="<a href="view-source:http://localhost:8084/pam-01/SupprimerSimulation.action?id=2">/pam-01/SupprimerSimulation.action?id=</a>num">Retirer</a>

dove num è il numero della simulazione da rimuovere.

L'azione [SupprimerSimulation] è definita come segue nel file [struts.xml]:


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
  <!-- internazionalizzazione -->
  <constant name="struts.custom.i18n.resources" value="messages" />
  <!-- integrazione Spring -->
  <constant name="struts.objectFactory.spring.autoWire" value="name" />

  
  <!-- azioni Struts /Tiles -->
  <package name="default" namespace="/" extends="tiles-default">
    ...
   <!-- azione RetirerSimulation -->
    <action name="SupprimerSimulation" class="web.actions.Supprimer">
      <result name="erreur" type="tiles">erreur</result>
      <result name="simulations" type="tiles">simulations</result>
    </action>
   ...
  </package>

    <!-- Aggiungi pacchetti qui -->

</struts>
  • riga 16: l'azione [SupprimerSimulation] è associata alla classe [Supprimer]. Poiché non è specificato alcun metodo, verrà eseguito il suo metodo execute. La classe [Supprimer] è la seguente:

package web.actions;

...
public class Supprimer extends ActionSupport implements SessionAware {

  // sessione
  private Map<String, Object> session;
  // ID della simulazione da eliminare
  private String id;
  // menu
  private Menu menu;

  @Override
  public void setSession(Map<String, Object> session) {
    this.session = session;
  }

  // esecuzione dell'azione
  public String execute() {
    // si recuperano le simulazioni nella sessione
    List<Simulation> simulations = (List<Simulation>) session.get("simulations");
    if (simulations == null) {
      // caso anomalo - la sessione deve essere scaduta
      menu = new Menu(false, false, false, false, true, true);
      return "erreur";
    }
    // verifica dell'ID
    int num = 0;
    boolean erreur = false;
    try {
      num = Integer.parseInt(id);
      erreur = num <= 0;
    } catch (NumberFormatException ex) {
      // anomalia
      erreur = true;
    }
    // errore?
    if (erreur) {
      menu = new Menu(false, false, false, false, true, true);
      return "erreur";
    }
    // si sta cercando la simulazione da eliminare
    for (int i = 0; i < simulations.size(); i++) {
      if (num == simulations.get(i).getNum()) {
        simulations.remove(i);
        break;
      }
    }
    // si sta visualizzando l'elenco delle simulazioni
    menu = new Menu(false, false, false, false, true, true);
    return "simulations";
  }

  // getter e setter
...
}
  • riga 4: l’azione [Supprimer] implementa l’interfaccia [SessionAware] per accedere alla sessione.
  • riga 7: la sessione
  • riga 9: il numero della simulazione da eliminare. Ricordiamo infatti che si arriva all’istanziazione della classe [Supprimer] tramite URL Html:
<a href="<a href="view-source:http://localhost:8084/pam-01/SupprimerSimulation.action?id=2">/pam-01/SupprimerSimulation.action?id=</a>num">Retirer</a>

dove num è il numero della simulazione da rimuovere. Questo numero verrà memorizzato nel campo id della riga 9.

  • riga 11: il menu per la vista che verrà visualizzata in risposta alla richiesta
  • riga 19: il metodo execute che genererà la risposta alla richiesta.
  • riga 21: si recupera l’elenco delle simulazioni già effettuate nella sessione
  • righe 22-26: il mancato recupero di questo elenco nella sessione indica probabilmente che la sessione è scaduta. Abbiamo già riscontrato questo caso. Restituisce la chiave erreur che fa visualizzare la vista Tiles erreur:

<!-- azione RetirerSimulation -->
    <action name="SupprimerSimulation" class="web.actions.Supprimer">
      <result name="erreur" type="tiles">erreur</result>
      ...
</action>

La vista Tiles erreur è stata presentata nel paragrafo 19.9.

  • righe 28-36: si verifica che la stringa id della riga 9 rappresenti effettivamente un numero intero >0.
  • righe 38-40: se così non fosse, viene restituita nuovamente la chiave erreur che farà visualizzare la vista Tiles erreur
  • righe 43-48: la simulazione da rimuovere viene cercata nell’elenco delle simulazioni. Se viene trovata, viene eliminata.
  • riga 50: si aggiorna il menu per la vista Tiles simulations.
  • riga 51: viene restituita la chiave simulations. Questa farà visualizzare la vista Tiles simulations:

<!-- azione RetirerSimulation -->
    <action name="SupprimerSimulation" class="web.actions.Supprimer">
      ...
      <result name="simulations" type="tiles">simulations</result>
</action>

La vista Tiles simulations è stata illustrata nel paragrafo 19.9.

19.11. Torna al modulo

Dalla vista Tiles simulations, l’utente può tornare al modulo:

  • in [1], cliccare sul link di ritorno al modulo
  • in [2], si trova un modulo vuoto

Il link [Retour au formulaire de simulation] è definito nel frammento [Entete.JSP]:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>

<table>
  <tr>
    <td><h1><s:text name="Pam.titre"/></h1></td>
    <td>
      ...
      <s:if test="menu.retourFormulaire">
        |<a href="<s:url action="RetourFormulaire"/>"><s:text name="Menu.RetourFormulaire"/></a><br/>
      </s:if>
      ...
    </td>
  </tr>
</table>
  • riga 10: il link rimanda all’azione [RetourFormulaire]. Quest’ultima è definita come segue nel file [struts.xml]:

   <!-- azione RetourFormulaire -->
    <action name="RetourFormulaire" >
      <result type="redirectAction">
        <param name="actionName">Formulaire!input</param>
        <param name="namespace">/</param>
      </result>
</action>

Si nota che questa azione non è associata ad alcuna classe. Si limita a reindirizzare il browser client verso l'azione [/Formulaire!input]. Ci troviamo quindi nella stessa situazione descritta nel paragrafo 19.7, relativo alla visualizzazione della vista iniziale. Ritroviamo quindi questa vista iniziale [2].

19.12. Visualizza l’elenco delle simulazioni

Dalle viste Tiles simulation o saisie, l’utente può richiedere di visualizzare le simulazioni:

  • in [1], cliccare sul link [Voir les simulations]
  • in [2], si trova l'elenco delle simulazioni

Il link [Voir les simulations] è definito nel frammento [Entete.JSP]:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>

<table>
  <tr>
    <td><h1><s:text name="Pam.titre"/></h1></td>
    <td>
      ...
      <s:if test="menu.voirSimulations">
        |<a href="<s:url action="VoirSimulations"/>"><s:text name="Menu.VoirSimulations"/></a><br/>
      </s:if>
      ...
    </td>
  </tr>
</table>
  • riga 10: il collegamento [Voir les simulations] richiama l’azione [VoirSimulations]. Quest’ultima è definita come segue nel file [struts.xml]:

   <!-- azione VoirSimulations -->
    <action name="VoirSimulations" class="web.actions.Voir">
      <result name="success" type="tiles">simulations</result>
</action>

L'azione [VoirSimulations] è associata alla classe [Voir] senza specificare il metodo. Verrà quindi eseguito il metodo [Voir].execute. La classe [Voir] è la seguente:


package web.actions;

import com.opensymphony.xwork2.ActionSupport;
import web.entities.Menu;

public class Voir extends ActionSupport{
  // menu
  private Menu menu=new Menu(false,false,false,false,true,true);
  // getter e setter

  public Menu getMenu() {
    return menu;
  }

  public void setMenu(Menu menu) {
    this.menu = menu;
  }
  
}

L'azione [Voir] fa una sola cosa: posiziona il menu per la vista Tiles simulazioni (riga 8). Non esiste un metodo execute. Verrà quindi eseguito quello della classe padre [ActionSupport]. Sappiamo che non fa altro che restituire la chiave success.

Torniamo al codice in [struts.xml]:


   <!-- azione VoirSimulations -->
    <action name="VoirSimulations" class="web.actions.Voir">
      <result name="success" type="tiles">simulations</result>
</action>

Alla riga 3, si vede che la chiave success porta alla visualizzazione della vista Tiles simulations. Questa è stata descritta a pagina 161.

19.13. Cancellare la simulazione corrente

Dalla vista Tiles simulation, l’utente può richiedere di cancellare la simulazione corrente:

  • in [1], si cancella la simulazione corrente
  • in [2], si visualizza il modulo di inserimento vuoto

Il collegamento [Effacer la simulation] è definito nel frammento [Entete.JSP]:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>

<table>
  <tr>
    <td><h1><s:text name="Pam.titre"/></h1></td>
    <td>
      ...
      <s:if test="menu.effacerSimulation">
        |<a href="<s:url action="Formulaire!input"/>"><s:text name="Menu.EffacerSimulation"/></a><br/>
      </s:if>
      ...
    </td>
  </tr>
</table>

Riga 10: si nota che il collegamento [Effacer la simulation] attiva l'azione [Formulaire!input]. Si sa che questa azione porta alla vista iniziale [2].

19.14. Terminare la sessione corrente

Da tutte le viste Tiles, l’utente può richiedere di terminare la sessione:

  • in [1], si parte dalla vista delle simulazioni e si chiude la sessione
  • in [2], si ritrova il modulo di inserimento vuoto. Si richiede di visualizzare le simulazioni.
  • in [3], l'elenco delle simulazioni è ora vuoto.

Il collegamento [Terminer la session] è definito nel frammento [Entete.JSP]:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>

<table>
  <tr>
    <td><h1><s:text name="Pam.titre"/></h1></td>
    <td>
      ...
      <s:if test="menu.terminerSession">
        |<a href="<s:url action="TerminerSession"/>"><s:text name="Menu.TerminerSession"/></a><br/>
      </s:if>
    </td>
  </tr>
</table>

Alla riga 10, si vede che il collegamento [Terminer la session] attiva l’azione [TerminerSession]. Quest’ultima è definita come segue nel file [struts.xml]:


    <action name="TerminerSession" class="web.actions.Terminer">
      <result name="success" type="redirectAction">
        <param name="actionName">Formulaire!input</param>
        <param name="namespace">/</param>
      </result>
</action>
  • riga 1: si vede che verrà istanziata la classe [Terminer] e verrà eseguito il suo metodo execute.
  • righe 2-5: dopo l’esecuzione del metodo [Terminer].execute, avverrà il reindirizzamento alla vista iniziale di immissione dati. Questo spiega la schermata n. 2.

La classe [Terminer] è la seguente:


package web.actions;

import com.opensymphony.xwork2.ActionSupport;
import java.util.Map;
import org.apache.struts2.interceptor.SessionAware;

public class Terminer extends ActionSupport implements SessionAware {

  // sessione
  private Map<String, Object> session;
  
  @Override
  public String execute() {
    // abbandono della sessione corrente
    session.clear();
    return SUCCESS;
  }

  @Override
  public void setSession(Map<String, Object> session) {
    this.session = session;
  }
}

Il ruolo dell’azione [Terminer] è quello di svuotare la sessione corrente dai suoi attributi.

  • riga 7: l’azione [Terminer] implementa l’interfaccia [SessionAware] per accedere alla sessione.
  • riga 10: il dizionario della sessione
  • riga 13: il metodo execute che viene eseguito
  • riga 15: svuota il dizionario della sessione. Di conseguenza, l’elenco delle simulazioni presenti nella sessione scomparirà. Questo spiega la schermata n. 3.
  • riga 16: restituisce la chiave success, che, come abbiamo visto, visualizzerà la vista Tiles saisie [2].

19.15. Conclusion

Abbiamo commentato integralmente la versione 1 del nostro caso di studio che opera con un livello simulato [metier]:

Non ci resta che "collegare" il vero livello applicativo al livello [web].