19. Casestudy – versie 1
19.1. De gesimuleerde [metier]-laag
Laten we nog eens terugkomen op de architectuur van de applicatie die we aan het bouwen zijn:
![]() |
We beschikken over het archief van de lagen [metier, dao, jpa] en we hebben de elementen van deze lagen gepresenteerd die de laag [web] moest kennen. We zijn klaar om deze te schrijven met behulp van het Struts-framework.
Om het testen van onze applicatie tijdens de ontwikkeling te vereenvoudigen, gaan we een gesimuleerde bedrijfslaag maken die voldoet aan de interface van de laag [metier]. De architectuur ziet er dan als volgt uit:
![]() |
We gaan de laag [web] ontwikkelen met de gesimuleerde laag [métier]. Het testen wordt eenvoudiger omdat er geen database meer in de architectuur zit. Dankzij Spring en het gebruik van interfaces zal het later vervangen van de gesimuleerde laag [metier] door de echte architectuur [metier, dao, jpa] geen invloed hebben op de code van de laag [web / struts2]. De laag [web / struts2] die we nu gaan ontwikkelen, kan ongewijzigd worden gebruikt.
De gesimuleerde laag [metier] die we gaan gebruiken, is de volgende:
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 {
// lijst met werknemers
private Map<String, Employe> hashEmployes = new HashMap<String, Employe>();
private List<Employe> listEmployes;
// loonstrook ophalen
public FeuilleSalaire calculerFeuilleSalaire(String SS,
double nbHeuresTravaillées, int nbJoursTravaillés) {
// de medewerker met nr. ophalen SS
Employe e = hashEmployes.get(SS);
// een fictieve loonstrook genereren
return new FeuilleSalaire(e, new Cotisation(3.49, 6.15, 9.39, 7.88), new ElementsSalaire(100, 100, 100, 100, 100));
}
// lijst met werknemers
public List<Employe> findAllEmployes() {
if (listEmployes == null) {
// een lijst met twee werknemers aanmaken
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)));
// werknemerslijst
for (Employe e : listEmployes) {
hashEmployes.put(e.getSS(), e);
}
}
// de lijst met werknemers weergeven
return listEmployes;
}
}
- regel 11: de klasse [MetierSimule] implementeert de interface [IMetier] die de echte laag [metier] implementeert.
- regel 14: een woordenboek van werknemers, geïndexeerd op hun nummer INSEE
- regel 15: de lijst met werknemers
- regels 27-39: implementatie van de methode findAllEmployes van de interface [IMetier].
- regels 30-33: aanmaken van een lijst met twee medewerkers
- regels 34-36: aanmaken van het werknemerswoordenboek, geïndexeerd op nummer INSEE
- regels 18-24: implementatie van de methode calculerSalaire van de interface [IMetier]. Hier wordt een fictieve loonstrook weergegeven.
19.2. Het NetBeans-project
Het NetBeans-project ziet er als volgt uit:
![]() |
- in [1]:
- [applicationContext.xml] is het configuratiebestand van Spring
- [tiles.xml] is het configuratiebestand van een framework genaamd Tiles.
- [web.xml] is het configuratiebestand van de webapplicatie
- in [2]: de verschillende weergaven van de applicatie
- in [3]:
- [messages.properties]: het berichtenbestand
- [struts.xml]: het configuratiebestand van Struts
![]() |
- in [4]: de broncodes van de applicatie. De Struts-acties bevinden zich in het pakket [web.actions].
- in [5]: de gesimuleerde laag [metier]
- in [6]: de gebruikte archieven. Hierin bevinden zich de archieven van de verschillende gebruikte tools: Spring, Tiles, Struts 2, de Struts 2/Spring-integratieplugin en de Struts 2/Tiles-integratieplugin.
- in [7]: het archief van de daadwerkelijke laag [metier, dao, jpa]. Hiermee krijgen we toegang tot de JPA-entiteiten, de interface [IMetier] en de klassen [FeuilleSalaire] en [ElementsSalaire]. Al deze elementen worden namelijk gebruikt door onze klasse [MetierSimule].
19.3. Projectconfiguratie
Het project wordt geconfigureerd via verschillende bestanden:
- [web.xml], dat de webapplicatie configureert
- [struts.xml], dat het Struts-framework configureert
- [applicationContext.xml], dat het Spring-framework configureert
- [tiles.xml], dat het Tiles-framework configureert
19.3.1. Configuratie van de webapplicatie
Het bestand [web.xml] ziet er als volgt uit:
<?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>
- regels 13-20: configureren het Struts 2-filter – al eerder gezien
- regels 22-24: configureren de Spring-listener – al eerder gezien
- regels 9-11: configureren de Tiles-listener. De klasse [org.apache.struts2.tiles.StrutsTilesListener] wordt geïnstantieerd bij het opstarten van de webapplicatie. Deze zal vervolgens het configuratiebestand gebruiken. Dit wordt gedefinieerd door de regels 5-8. Het configuratiebestand van Tiles is dus het bestand [WEB-INF/tiles.xml].
Uiteindelijk worden bij het opstarten van de Struts-applicatie drie klassen geïnstantieerd:
- één voor het Struts 2-filter. Deze zorgt voor de C in MVC.
- een andere voor de Spring-listener. Spring gebruikt het bestand [applicationContext.xml] om de lagen [métier, dao, jpa] van de applicatie te instantiëren. Spring zal ook, net als in een eerder voorbeeld, een klasse [Config] instantiëren die de gegevens van het Application-bereik bevat. Ten slotte zal Spring in elke Struts-actie die dit nodig heeft, een verwijzing naar deze unieke instantie [Config] injecteren.
- En nog een voor de Tiles-listener. Dit framework zorgt voor het beheer van de weergaven. We komen hier straks op terug.
19.3.2. Configuratie van het Struts-framework
Het Struts-framework wordt geconfigureerd via het volgende bestand [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>
<!-- internationalisering -->
<constant name="struts.custom.i18n.resources" value="messages" />
<!-- Spring-integratie -->
<constant name="struts.objectFactory.spring.autoWire" value="name" />
<!-- Struts/Tiles-acties -->
<package name="default" namespace="/" extends="tiles-default">
<!-- standaardactie -->
<default-action-ref name="index" />
<action name="index">
<result type="redirectAction">
<param name="actionName">Formulaire</param>
<param name="namespace">/</param>
</result>
</action>
<!-- formulieractie -->
<action name="Formulaire" class="web.actions.Formulaire" method="input">
<result name="success" type="tiles">saisie</result>
<result name="exception" type="tiles">exception</result>
</action>
<!-- actie 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>
<!-- actie 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>
<!-- actie RetourFormulaire -->
<action name="RetourFormulaire" >
<result type="redirectAction">
<param name="actionName">Formulaire</param>
<param name="namespace">/</param>
</result>
</action>
<!-- actie VoirSimulations -->
<action name="VoirSimulations" class="web.actions.Voir">
<result name="success" type="tiles">simulations</result>
</action>
<!-- actie 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>
<!-- actie 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>
We zullen de verschillende Struts-acties toelichten naarmate we ze bestuderen. Voorlopig kunnen we de volgende punten opmerken:
- regel 8: definieert het berichtenbestand
- regel 10: definieert de wijze waarop Spring-beans in de Struts-acties worden geïnjecteerd. De injectie gebeurt op basis van de naam van de bean. Het veld van de Struts-actie dat door Spring moet worden geïnitialiseerd, moet dezelfde naam hebben als de te injecteren bean.
- regel 25: definieert de weergave die moet worden getoond voor de navigatiesleutel success van de actie [Formulaire]. We zien dat het resultaat <result> een attribuut type='tiles' heeft dat we niet kennen. We kenden het type redirect, waarmee de client naar een weergave kan worden omgeleid. Hier wordt de weergave van het type tiles beheerd door het framework Tiles. Het type tiles is gedefinieerd in het bestand [struts-plugin.xml] uit het archief [struts2-tiles-plugin-2.2.3.1.jar]:
- regels 3-5: de definitie van het resultaattype tiles.
- regel 2: dit type is gedefinieerd in het pakket [tiles-default], dat het pakket [struts-default] uitbreidt.
- regel 14: definieert het pakket [default] waarin alle acties van de applicatie zullen worden ondergebracht. Om gebruik te kunnen maken van de definitie van het weergavetype tiles, breidt het pakket [tiles-default] uit.
19.3.3. Configuratie van het Spring-framework
Het Spring-framework wordt geconfigureerd via het volgende bestand [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">
<!-- toepassingslagen -->
<!-- web -->
<bean id="config" class="web.Config" init-method="init">
<property name="metier" ref="metier"/>
</bean>
<!-- bedrijfslogica -->
<bean id="metier" class="metier.MetierSimule"/>
</beans>
- regel 13: de gesimuleerde laag [metier], geïnstantieerd door de klasse [metier.MetierSimule]
- regels 9-11: hiermee wordt een bean met de naam config geconfigureerd. Net als in een eerder besproken voorbeeld zal deze bean dienen om de informatie van het bereik Application in te kapselen. De klasse die aan deze bean is gekoppeld, is de volgende klasse [Config]:
package web;
import java.util.List;
import jpa.Employe;
import metier.IMetier;
public class Config {
// door Spring geïnitialiseerde bedrijfslaag
private IMetier metier;
// lijst met medewerkers
private List<Employe> employes;
// fouten
private Exception initException;
// constructor
public Config() {
}
// Spring-methode voor het initialiseren van het object
public void init() {
// de lijst met medewerkers wordt opgevraagd
try {
employes = metier.findAllEmployes();
} catch (Exception ex) {
initException = ex;
}
}
// getters en setters
...
}
Laten we teruggaan naar de configuratie van de bean config:
<bean id="config" class="web.Config" init-method="init">
<property name="metier" ref="metier"/>
</bean>
<!-- bedrijfslogica -->
<bean id="metier" class="metier.Metier">
...
</bean>
Op regel 2 is te zien dat de bean metier uit regel 5 wordt geïnjecteerd (ref) in het veld met de naam metier (name) van het object [Config]. De bean metier is een verwijzing naar de laag [metier]:
![]() |
Om te communiceren met de laag [metier], hebben alle Struts-acties van de laag [web] een verwijzing naar deze laag nodig. Men kan stellen dat de verwijzing naar de laag [metier] een gegeven is met het bereik Application. Alle verzoeken van alle gebruikers hebben deze nodig. Daarom plaatsen we deze verwijzing in het object [Config]. Bovendien heeft de configuratie van de bean config in regel 1 een attribuut init-method. Dit attribuut geeft de methode van de bean aan die moet worden uitgevoerd na het instantiëren van de bean. Hier wordt aangegeven dat na het instantiëren van de klasse [web.Config] de methode init moet worden uitgevoerd. Deze luidt als volgt:
// door Spring geïnitialiseerde bedrijfslaag
private IMetier metier;
// lijst met medewerkers
private List<Employe> employes;
// fouten
private Exception initException;
// constructor
public Config() {
}
// Spring-methode voor het initialiseren van het object
public void init() {
// de lijst met werknemers opvragen
try {
employes = metier.findAllEmployes();
} catch (Exception ex) {
initException = ex;
}
}
Wanneer de methode init wordt uitgevoerd, is het veld metier van de klasse door Spring geïnstantieerd. De methode init heeft dus toegang tot de businesslaag van de interface [IMetier] (regel 2):
package metier;
import java.util.List;
import jpa.Employe;
public interface IMetier {
// loonstrook opvragen
FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés );
// lijst met werknemers
List<Employe> findAllEmployes();
}
- regel 8: met deze methode kan het salaris van een werknemer worden berekend
- regel 10: met deze methode kan de lijst met werknemers worden opgehaald
We zien dat de methode init de lijst met werknemers opvraagt bij de laag [métier]. Deze lijst wordt opgeslagen in het veld van regel 4. Als er een uitzondering optreedt, wordt deze opgeslagen in het veld van regel 6.
Kortom, het unieke object [Config] bevat:
- een verwijzing naar de laag [métier]
- de lijst met medewerkers
19.4. Genereren van Tiles-weergaven
Zoals we in het Struts-configuratiebestand hebben gezien, worden de weergaven gegenereerd door het framework Tiles. We zullen alleen het strikt noodzakelijke uitleggen dat nuttig is voor het schrijven van onze applicatie.
Met Tiles kunnen we weergaven genereren op basis van een masterpagina. Deze masterpagina, hier [MasterPage.JSP] genoemd, zal bestaan uit de volgende fragmenten van JSP:
Deze fragmenten JSP zijn gedefinieerd in het NetBeans-project:
![]() |
Met het framework Tiles kunnen we bepalen welke fragmenten in de masterpagina worden ingevoegd.
De masterpagina [MasterPage.JSP] ziet er als volgt uit:
<%@ 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>
De masterpagina is een container voor fragmenten JSP. Hier bestaat deze uit zes fragmenten, namelijk die van de regels 17 tot en met 23. Bij het genereren kunnen er 0 tot 6 fragmenten in de masterpagina worden samengevoegd. Dit generatieproces wordt geregeld door het bestand [WEB-INF/tiles.xml], dat de weergaven Tiles definieert:
<?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>
<!-- de hoofdpagina -->
<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>
<!-- het invoerscherm -->
<definition name="saisie" extends="masterPage">
<put-attribute name="saisie" value="/Saisie.JSP"/>
</definition>
<!-- de simulatieweergave -->
<definition name="simulation" extends="saisie">
<put-attribute name="simulation" value="/Simulation.JSP"/>
</definition>
<!-- de simulatieweergave -->
<definition name="simulations" extends="masterPage">
<put-attribute name="simulations" value="/Simulations.JSP"/>
</definition>
<!-- de uitzonderingsweergave -->
<definition name="exception" extends="masterPage">
<put-attribute name="exception" value="/Exception.JSP"/>
</definition>
<!-- de foutweergave -->
<definition name="erreur" extends="masterPage">
<put-attribute name="erreur" value="/Erreur.JSP"/>
</definition>
</tiles-definitions>
- het bovenstaande bestand definieert zes Tiles-weergaven met de namen: masterPage (regel 9), invoer (regel 20), simulatie (regel 25), simulaties (regel 30), uitzondering (regel 35), fout (regel 40).
- regels 9-17: definiëren een weergave met de naam masterPage (name) die is gekoppeld aan de masterpagina [MasterPage.JSP] (template). We hebben gezien dat deze pagina JSP zes subweergaven definieerde. Een weergave Tiles die aan de hoofdpagina is gekoppeld, moet het fragment JSP aangeven dat aan elk van de zes subweergaven is gekoppeld. We zien dat sommige subweergaven de lege tekenreeks (value) als waarde krijgen. Deze subweergaven worden niet opgenomen in de hoofdpagina [MasterPage.JSP]. De weergave Tiles, genaamd masterPage, bestaat dus uitsluitend uit het subfragment [Entete.JSP].
- regels 20-22: definieert een weergave met de naam saisie (name) die (extends) de eerder genoemde weergave masterPage uitbreidt. Dit betekent dat deze alle definities van de weergave masterPage overneemt. De definitie ervan komt overeen met het volgende:
<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>
We zien dat deze is gekoppeld aan de pagina JSP [MasterPage.JSP] en dat hij daarom de zes subweergaven van deze pagina moet definiëren. We zien dat de definitie op regel 9 die op regel 4 tenietdoet. De weergave Tiles, genaamd saisie, bestaat dus uit de fragmenten JSP en [Entete.JSP, Saisie.JSP]
Als we deze redenering voortzetten, krijgen we de volgende tabel:
weergave Tiles | pagina’s JSP |
19.5. De berichtbestanden
De applicatie is geïnternationaliseerd. De berichten zijn te vinden in de bestanden [messages.properties] en [Formulaire.properties].
Het bestand [messages.properties] ziet er als volgt uit:
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
# formaten
Format.heure = {0,time}
Format.nombre = {0,number,#0.0##}
Format.pourcent = {0,number,##0.00' %'}
Format.monnaie={0,number,##0.00' \u20ac'}
# lijst met simulaties
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
# foutmelding
Erreur.sessionexpiree=La session a expir\u00e9
Erreur.numSimulation=N\u00b0 de simulation incorrect
# conversiefout
xwork.default.invalid.fieldvalue=Valeur invalide pour le champ "{0}".
Het bestand [Formulaire.properties] is als volgt:
# zodat dubbele waarden in het lokale formaat worden weergegeven
double.format={0,number,#0.00##}
# foutmelding
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. Het stylesheet
De Tiles-weergaven maken gebruik van het volgende stylesheet [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. De startweergave
Om de applicatie te bestuderen, zullen we deze presenteren aan de hand van de verschillende acties van de gebruiker. We zullen telkens de actie Struts bestuderen die deze actie uitvoert en de weergave Tiles die als reactie wordt teruggestuurd.
In [struts.xml] hebben we de volgende acties:
<!-- standaardactie -->
<default-action-ref name="index" />
<action name="index">
<result type="redirectAction">
<param name="actionName">Formulaire!input</param>
<param name="namespace">/</param>
</result>
</action>
<!-- formulieractie -->
<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>
- regels 2-8: de standaardactie van de applicatie is [Formulaire!input].
De klasse [Formulaire] is als volgt:
package web.actions;
...
public class Formulaire extends ActionSupport implements Preparable, SessionAware {
// configuratie geïnitialiseerd door Spring
private Config config;
// lijst met medewerkers
private List<Employe> employes;
// lijst met fouten
private List<Erreur> erreurs;
// loonstrook
private FeuilleSalaire feuilleSalaire;
// invoer
private String comboEmployesValue;
private Double heuresTravaillees;
private Integer joursTravailles;
// sessie
private Map<String, Object> session;
// menu
private Menu menu;
@Override
public void prepare() throws Exception {
...
}
@Override
public String input() {
....
}
// loonberekening
public String calculSalaire() {
...
}
}
@Override
public void validate() {
...
}
@Override
public void setSession(Map<String, Object> map) {
session = map;
}
// getters en setters
...
}
- regel 4: de actie [Formulaire] implementeert de interface Preparable. Deze heeft slechts één methode, namelijk de methode prepare op regel 24. Deze methode wordt één keer uitgevoerd vóór elke methode van de actie. Deze wordt doorgaans gebruikt om het model van de actie te initialiseren.
Het actiemodel bestaat uit de regels 6-21:
- regel 7: het veld config wordt door Spring geïnitialiseerd zoals eerder uitgelegd. Het geeft toegang tot de gegevens op applicatieniveau:
- een verwijzing naar de laag [métier]
- een verwijzing naar de lijst met medewerkers.
- een verwijzing naar de uitzondering die mogelijk is opgetreden tijdens het instantiëren van het object [Config]
- regel 9: een lijst met medewerkers. Deze lijst wordt gebruikt om de keuzelijst met medewerkers in het fragment [Saisie.JSP] te vullen.
- regel 11: een lijst met fouten. Deze lijst wordt gebruikt voor het fragment [Erreur.JSP].
- regel 21: de lijst met menuopties van het fragment [Entete.JSP]
![]() |
In [1] worden de links van het weergegeven menu aangestuurd door het veld menu van de actie [Formulaire].
De methode prepare wordt uitgevoerd vóór de methode input. Dit is de volgende:
@Override
public void prepare() throws Exception {
// configuratiefout?
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();
}
}
- regel 4: de uitzondering wordt opgehaald in het object [Config] dat door Spring is geïnstantieerd
- regel 5: als er een uitzondering is opgetreden bij het instantiëren van het object [Config], dan wordt de foutenlijst van regel 11 geïnitialiseerd. De klasse [Erreur] is als volgt:
package web.entities;
import java.io.Serializable;
public class Erreur implements Serializable{
public Erreur() {
}
// velden
private String classe;
private String message;
// constructor
public Erreur(String classe, String message){
this.setClasse(classe);
this.message=message;
}
// getters en setters
...
}
De klasse dient om de uitzonderingsstapel op te slaan:
- regel 11: de uitzonderingsklasse
- regel 12: het bericht van de uitzondering
Laten we teruggaan naar de methode prepare:
- regel 13: de lijst met medewerkers van het object [Config] wordt opgeslagen in het veld employes van de actie.
Zodra de methode prepare is uitgevoerd, wordt de methode input op haar beurt uitgevoerd. Dit is de volgende:
@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";
}
}
De methode input zorgt alleen voor het instellen van de lijst met weer te geven menuopties. De klasse [Menu] is als volgt:
package web.entities;
import java.io.Serializable;
public class Menu implements Serializable {
// menu-items
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;
}
// getters en setters
...
}
- regels 8-13: er zijn 6 mogelijke links in het menu
- regels 18-25: met de constructor van de klasse kun je instellen welke links wel en welke niet moeten worden weergegeven.
De menu-links worden weergegeven in [Entete.JSP], fragment JSP dat in alle weergaven Tiles aanwezig is. Elke actie heeft een veld menu om de weergave van het menu van [Entete.JSP] te regelen.
Laten we teruggaan naar de methode 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";
}
}
- regels 3-6: als de foutenlijst leeg is, wordt het menu [Faire la simulation, Voir les simulations, Terminer la session] weergegeven en wordt de sleutel input geretourneerd.
- regels 9-10: als de foutenlijst niet leeg is, is het menu leeg en wordt de sleutel exception geretourneerd.
Laten we teruggaan naar de configuratie van de actie [Formulaire] in [struts.xml]:
<!-- formulieractie -->
<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>
- regel 5: de sleutel input geeft de Tiles-weergave met de naam saisie weer
- regel 4: de sleutel exception toont de Tiles-weergave met de naam exception
Laten we beginnen met de Tiles-weergave met de naam saisie. Deze bestaat uit de fragmenten JSP, [Entete.JSP] en [Saisie.JSP].
Het fragment [Entete.JSP] ziet er als volgt uit:
![]() |
De code ervan is als volgt:
<%@ 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>
- regels 8-25: weergave van de zes menu-links [Simulatie uitvoeren (regels 8-10), Simulatie wissen (regels 11-13), Simulaties bekijken (regels 14-16), Terug naar het formulier (regels 17-19), Simulatie opslaan (regels 20-22), Sessie beëindigen (regels 23-25).
- regels 8, 11, 14, 17, 20, 23: de weergave van de links wordt bepaald door het veld menu van de huidige actie.
Merk op dat het fragment [Entete.JSP] een HTML-tabel weergeeft (regels 4-28), maar geen volledige HTML-pagina is. Vergeet hierbij niet dat alle weergaven van de applicatie worden ingevoegd in de volgende hoofdpagina [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>
Het fragment [Entete.JSP] wordt ingevoegd op regel 17, binnen een gewone HTML-pagina.
Het fragment [Saisie.JSP] wordt op regel 19 ingevoegd. Dit is de volgende weergave:
![]() |
De code van het fragment [Saisie.JSP] is als volgt:
<%@ 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(){
// het formulier verzenden
document.forms['Saisie'].elements['action'].name='action:Formulaire!calculSalaire'
document.forms['Saisie'].submit();
}
</script>
<!-- gegevens invoeren -->
<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>
- regel 17: het formulier heeft geen attribuut action. Standaard is dit action='Formulaire'.
- regel 18: weergave van de keuzelijst met medewerkers. De inhoud van de keuzelijst (attribuut list) wordt geleverd door het veld employes van de huidige actie. Het attribuut value van de opties is het nummer SS van de medewerkers (attribuut listKey). De weergave voor elke optie is de voor- en achternaam van de werknemer (attribuut listValue). Het nummer SS van de in de keuzelijst geselecteerde medewerker wordt in het veld [Formulaire].comboEmployesvalue geplaatst (attribuut name).
- regel 19: invoerveld voor gewerkte uren. De weergegeven waarde (attribuut value) is die van het veld heuresTravaillees van de actie [Formulaire] in de volgende indeling (Formulaire.properties):
double.format={0,number,#0.00##}
De waarde wordt bijgewerkt in het veld [Formulaire].heuresTravaillees (attribuut name).
- regel 20: invoerveld voor gewerkte dagen. De weergegeven waarde (attribuut value) is die van het veld joursTravailles van de actie [Formulaire].
De waarde wordt bijgewerkt in het veld [Formulaire].joursTravailles (attribuut name).
Uiteindelijk ziet de weergave Tiles saisie er bij het opstarten, wanneer er geen fouten zijn, als volgt uit:
![]() |
Laten we teruggaan naar de configuratie van de actie [Formulaire]:
<!-- formulieractie -->
<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>
We hebben gezien dat de actie [Formulaire].input ook de sleutel exception uit regel 4 kan opleveren. In dit geval wordt de Tiles-weergave met de naam exception weergegeven. Deze bestaat uit de fragmenten [Entete.JSP] en [Exception.JSP]. Het fragment [Entete.JSP] hebben we al besproken. Het fragment [Exception.JSP] is als volgt:
![]() |
Dit is de startpagina van versie 2 van de applicatie wanneer het Sgbd niet is gestart. De code JSP van het fragment [Erreur.JSP] is als volgt:
<%@ 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>
- regels 10-14: een iterator over de verzameling List<Fout> fouten van de actie [Formulaire]. We herinneren ons dat er in geval van een fout een stapel uitzonderingen in was opgeslagen.
19.8. Een simulatie uitvoeren
Zodra het startscherm is verkregen, kan er een salarisberekening worden uitgevoerd via de link [Faire une simulation].
19.8.1. Validatie van de invoer
Laten we de volgende reeks eens bekijken:
![]() |
- naar [1], een foutieve invoer
- naar [2], het verzonden antwoord.
Laten we eens kijken naar de configuratie van de actie [Formulaire] in [struts.xml]:
<!-- formulieractie -->
<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>
We weten dat bij een validatiefout de validatie-interceptor de sleutel input terugstuurt. Het is dus de Tiles-weergave saisie die wordt teruggestuurd. Het validatieproces zorgt ervoor dat de foutieve velden worden voorzien van foutmeldingen.
De validatie van de actie [Formulaire] wordt verzorgd door het volgende bestand [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>
- de regels 6-20 controleren of het veld heuresTravaillees een reëel getal is binnen het interval [0,300].
- De regels 22-36 controleren of het veld joursTravailles een geheel getal is binnen het interval [0,31].
Laten we teruggaan naar de configuratie van de actie [Formulaire] in [struts.xml]:
<!-- formulieractie -->
<action name="Formulaire" class="web.actions.Formulaire">
...
<result name="input" type="tiles">saisie</result>
</action>
We weten dat bij een validatiefout de validatie-interceptor de sleutel input retourneert. Het is dus de Tiles-weergave saisie die wordt geretourneerd.
Ter herinnering: deze bestaat uit de fragmenten [Entete.JSP] en [Saisie.JSP], waarbij [Entete.JSP] een titel en een keuzelijst bevat en [Saisie.JSP] het invoerformulier. Bij invoerfouten zorgt het validatieproces ervoor dat de foutieve velden worden voorzien van foutmeldingen en bovendien hun foutieve waarde weergeven. Het fragment [Entete.JSP] speelt geen enkele rol in het validatieproces. Laten we de code ervan eens bekijken:
<%@ 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>
De zes links worden geconfigureerd via het veld menu van het sjabloon (regels 8, 11, 14, 17, 20, 23). Wanneer er een fout optreedt, wordt dit sjabloon niet bijgewerkt door de actie, waardoor er een pagina zonder menu wordt weergegeven. Om dit probleem op te lossen, bevat de klasse [Formulaire] de volgende methode 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() {
...
}
// loonberekening
public String calculSalaire() {
...
}
@Override
public void validate() {
// fouten?
if (!getFieldErrors().isEmpty()) {
// menu
menu = new Menu(true, false, false, true, false, true);
}
}
// getters en setters
...
}
- regel 27: we weten dat wanneer deze aanwezig is, de methode validate wordt uitgevoerd door het validatieproces. We maken hiervan gebruik om het menu op regel 4 bij te werken, dat deel uitmaakt van het fragmentmodel [Entete.JSP].
- regels 29-32: als er validatiefouten zijn opgetreden, wordt het menu zo ingesteld dat de Tiles-weergave saisie opnieuw wordt weergegeven. Als er geen validatiefouten zijn opgetreden, wordt er niets gedaan. De methode calculSalaire is vervolgens verantwoordelijk voor het aanmaken van het sjabloon van de weer te geven weergave.
19.8.2. De berekening van het salaris
Laten we teruggaan naar de code JSP van de koptekst:
<%@ 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>
- regel 9: wanneer de gebruiker op de link [Faire la simulation] klikt, wordt de JavaScript-functie doSimulation uitgevoerd. Deze is gedefinieerd in het fragment [Saisie.JSP]:
<script language="javascript" type="text/javascript">
function doSimulation(){
// het formulier verzenden
document.forms['Saisie'].elements['action'].name='action:Formulaire!calculSalaire'
document.forms['Saisie'].submit();
}
</script>
<!-- gegevens invoeren -->
<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>
- regel 14: een verborgen veld met de naam action wordt verzonden naar de actie [Formulaire]. Met dit veld kunnen we de actie en de methode specificeren die moeten worden uitgevoerd bij POST van het formulier. Misschien herinneren we ons nog uit de eerste voorbeelden dat deze kunnen worden gespecificeerd in een parameter met de naam action:Action!methode. De waarde van deze parameter doet er niet toe. Het volstaat dat hij aanwezig is.
- regels 2-6: de JavaScript-functie die wordt uitgevoerd wanneer de gebruiker op de link [Faire la simulation] van het fragment [Entete.JSP] klikt.
- regel 4: het attribuut name van het verborgen veld action wordt gewijzigd. We zorgen ervoor dat het de door Struts verwachte vorm action:Action!methode heeft.
- regel 5: het formulier met de naam Saisie uit regel 5 wordt verzonden. Hierdoor wordt de volgende reeks parameters verzonden:
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.
De methode [Formulaire].calculSalaire is als volgt:
// salarisberekening
public String calculSalaire() {
try {
// salarisberekening
feuilleSalaire = config.getMetier().calculerFeuilleSalaire(comboEmployesValue, heuresTravaillees, joursTravailles);
// de simulatie in de sessie plaatsen
session.put("simulation", new Simulation(0, "" + heuresTravaillees, "" + joursTravailles, feuilleSalaire));
// menu
menu = new Menu(true, true, true, true, false, true);
// klaar
return "simulation";
} catch (Throwable th) {
...
}
}
- regel 5: de berekening van de loonstrook wordt aangevraagd bij de laag [métier]
- regel 7: er wordt een object van het type Simulation in de sessie van de gebruiker geplaatst. Dit kan namelijk nodig zijn bij een latere aanvraag. De klasse [Simulation] is als volgt:
package web.entities;
import java.io.Serializable;
import metier.FeuilleSalaire;
public class Simulation implements Serializable{
public Simulation() {
}
// velden van een simulatie
private Integer num;
private FeuilleSalaire feuilleSalaire;
private String heuresTravaillées;
private String joursTravaillés;
// constructor
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 en setters
...
}
- regel 12: het simulatienummer. Dit wordt bij elke nieuwe geregistreerde simulatie verhoogd.
- regel 13: de loonstrook van de werknemer
- regel 14: het aantal gewerkte uren
- regel 15: het aantal gewerkte dagen
- regel 25: de methode getIndemnites geeft het totaal van de vergoedingen van de werknemer weer
We zullen zien dat de klasse [Simulation] het sjabloon is voor het fragment [Simulations.JSP], waarin alle uitgevoerde simulaties worden weergegeven.
Terug naar de methode [Formulaire].calculSalaire:
// loonberekening
public String calculSalaire() {
try {
// salarisberekening
feuilleSalaire = config.getMetier().calculerFeuilleSalaire(comboEmployesValue, heuresTravaillees, joursTravailles);
// de simulatie in de sessie plaatsen
session.put("simulation", new Simulation(0, "" + heuresTravaillees, "" + joursTravailles, feuilleSalaire));
// menu
menu = new Menu(true, true, true, true, false, true);
// klaar
return "simulation";
} catch (Throwable th) {
...
}
- regel 9: het menu bijwerken
- regel 11: terugsturen van de navigatiesleutel simulation.
Terug naar de configuratie van de actie [Formulaire]:
<!-- formulieractie -->
<action name="Formulaire" class="web.actions.Formulaire">
...
<result name="simulation" type="tiles">simulation</result>
</action>
Regel 4 laat zien dat de navigatiesleutel simulation de Tiles-weergave met de naam simulation weergeeft. Deze bestaat uit de volgende fragmenten JSP: [Entete, Saisie, Simulation].
De weergegeven weergave is als volgt:
![]() |
- in [1], het fragment [Entete.JSP]
- in [2], het fragment [Saisie.JSP]
- in [3], het fragment [Simulation.JSP]. Ter herinnering: de getoonde loonstrook is de fictieve loonstrook die wordt weergegeven door de laag [metier].
De eerste twee fragmenten zijn al getoond. Het fragment [Simulation.JSP] is als volgt:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<hr/>
<!-- medewerkergegevens -->
<span class="titreInfos">
<s:text name="Simulation.Infos.employe"/>
</span>
<br/><br/>
<table>
<!-- regel 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>
<!-- regel 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>
<!-- regel 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>
<!-- regel 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>
<!-- informatie over premies -->
<br/>
<span class="titreInfos">
<s:text name="Simulation.Infos.cotisations"/>
</span>
<br/><br/>
<table>
<!-- regel 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>
<!-- regel 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>
<!-- informatie over vergoedingen -->
<br/>
<span class="titreInfos">
<s:text name="Form.Infos.indemnites"/>
</span>
<br/><br/>
<table>
<!-- regel 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>
<!-- regel 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>
<!-- informatie over het salaris -->
<br/>
<span class="titreInfos">
<s:text name="Simulation.Infos.Salaire"/>
</span>
<br/><br/>
<table>
<!-- regel 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>
<!-- regel 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>
<!-- Nettoloon-->
<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>
Het is lang ... maar functioneel gezien eenvoudig. Dit fragment toont de verschillende eigenschappen van het veld [Formulaire].feuilleSalaire, dat de loonstrook van de werknemer vertegenwoordigt.
Terug naar de methode [Formulaire].calculSalaire:
// salarisberekening
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";
}
}
De loonberekening kan misgaan. Dit zou met name het geval zijn als de verbinding met het Sgbd zou worden verbroken. In dat geval wordt de uitzondering die optreedt afgehandeld. We zijn dit geval al tegengekomen bij de bespreking van de methode [Formulaire].input.
- regels 7-11: er wordt een lijst met Erreur-objecten aangemaakt op basis van de uitzonderingsstack
- regel 13: we stellen het menu in
- regel 14: de sleutel exception wordt teruggegeven.
De sleutel exception zorgt ervoor dat de weergave Tiles exception wordt weergegeven:
<!-- actie Formulier -->
<action name="Formulaire" class="web.actions.Formulaire">
<result name="exception" type="tiles">exception</result>
...
</action>
Deze Tiles-weergave is al eerder getoond. Ze ziet er als volgt uit:
![]() |
19.9. Een simulatie opslaan
Nadat de gebruiker een simulatie heeft uitgevoerd, wil hij deze wellicht opslaan in de sessie.
![]() |
![]() |
- in [1] wordt de simulatie opgeslagen
- in [2] wordt het antwoord opgeslagen, dat de lijst met reeds uitgevoerde simulaties bevat, waaraan de nieuwe simulatie is toegevoegd
De link [Enregistrer la simulation] bevindt zich in het fragment [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>
We zien dat een klik op de link ervoor zorgt dat de actie [EnregistrerSimulation] wordt uitgevoerd. Deze is in het bestand [struts.xml] als volgt geconfigureerd:
<!-- actie 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>
- regel 1: de actie [EnregistrerSimulation] is gekoppeld aan de klasse [Enregistrer] en aan de bijbehorende methode execute.
De klasse [Enregistrer] ziet er als volgt uit:
package web.actions;
...
public class Enregistrer extends ActionSupport implements SessionAware {
// sessie
private Map<String, Object> session;
// menu
private Menu menu;
@Override
public void setSession(Map<String, Object> session) {
this.session = session;
}
// actie uitvoeren
public String execute() {
// de laatste simulatie in de sessie wordt opgehaald
Simulation simulation = (Simulation) session.get("simulation");
if (simulation == null) {
return ERROR;
}
...
}
// getters en setters
...
}
- regel 4: omdat de actie toegang moet hebben tot de sessie, implementeert deze de interface SessionAware.
- regel 7: de sessie
- regel 9: het menu
Wanneer de actie [Enregistrer] is geïnstantieerd, wordt de methode execute ervan uitgevoerd. Ter herinnering: deze methode heeft als taak de laatste simulatie in de sessie op te nemen. Deze wordt toegevoegd aan de lijst met reeds uitgevoerde simulaties, die eveneens in de sessie wordt bewaard.
- regel 19: de laatste simulatie die in de sessie is geplaatst, wordt opgehaald.
- regels 20-22: als deze niet wordt gevonden, is de sessie waarschijnlijk verlopen. De sessie duurt namelijk slechts een bepaalde tijd, die kan worden ingesteld in het bestand [web.xml] waarmee de toepassing wordt geconfigureerd.
- regel 21: de sleutel error wordt teruggegeven.
Terug naar de configuratie van de actie [EnregistrerSimulation]:
<!-- actie 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>
We zien dat de sleutel error (regel 3) ervoor zorgt dat de weergave ‘ -tegels’ met de naam erreur wordt getoond. Deze bestaat uit de fragmenten [Entete.JSP] en [Erreur.JSP] en ziet er als volgt uit:
![]() |
Het fragment [Erreur.JSP] is als volgt:
<%@ 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>
Terug naar de methode [Enregistrer].execute:
// de actie wordt uitgevoerd
public String execute() {
// de laatste simulatie in de sessie wordt opgehaald
Simulation simulation = (Simulation) session.get("simulation");
if (simulation == null) {
return ERROR;
}
// het nummer van de laatste simulatie wordt opgehaald
Integer numDerniereSimulation = (Integer) session.get("numDerniereSimulation");
if (numDerniereSimulation == null) {
numDerniereSimulation = 0;
}
// het nummer wordt verhoogd
numDerniereSimulation++;
// het nieuwe nummer wordt teruggezet in de sessie
session.put("numDerniereSimulation", numDerniereSimulation);
// de lijst met simulaties wordt opgehaald
List<Simulation> simulations = (List<Simulation>) session.get("simulations");
if (simulations == null) {
simulations = new ArrayList<Simulation>();
session.put("simulations", simulations);
}
// de huidige simulatie wordt aan de lijst toegevoegd
simulation.setNum(numDerniereSimulation);
simulations.add(simulation);
// de lijst met simulaties wordt weergegeven
menu = new Menu(false, false, false, false, true, true);
return "simulations";
}
- regels 9-16: de verschillende simulaties worden genummerd vanaf 1. Het laatst toegekende nummer wordt in de sessie opgeslagen onder de sleutel numDerniereSimulation. De code in de regels 9-16 bestaat erin deze sleutel op te halen en de bijbehorende waarde te verhogen.
- regels 18-22: de lijst met simulaties wordt bewaard in de sessie die gekoppeld is aan de sleutel simulations. De regels 18-22 dienen om deze lijst op te halen als deze bestaat, of om deze aan te maken als deze niet bestaat.
- regels 24-25: zodra de lijst met simulaties is verkregen, wordt de huidige simulatie hieraan toegevoegd (regel 25). Eerder heeft de huidige simulatie een nummer gekregen (regel 24).
- regel 27: het weer te geven menu wordt vastgelegd
- regel 28: de navigatiesleutel wordt ingesteld op simulations.
Terug naar de configuratie van de actie [EnregistrerSimulation] in [struts.xml]:
<!-- actie 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>
Regel 4: de sleutel simulations zorgt ervoor dat de weergave Tiles met de naam simulations wordt weergegeven. Deze weergave bestaat uit de fragmenten [Entete.JSP] en [Simulations.JSP]. De weergegeven weergave ziet er als volgt uit:
![]() |
- in [1], het fragment [Entete.JSP] dat we inmiddels goed kennen.
- in [2], het fragment [Simulations.JSP]
Het fragment [Simulations.JSP] is als volgt:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!-- lege lijst met simulaties -->
<s:if test="#session['simulations']==null || #session['simulations'].size()==0">
<h2><s:text name="Pam.SimulationsVides.titre"/></h2>
</s:if>
<!-- niet-lege lijst met simulaties -->
<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"/>">Verwijderen</a></td>
</tr>
</s:iterator>
</table>
</s:if>
- regels 5-7: als er geen simulaties in de sessie zijn, wordt de volgende weergave getoond:
![]() |
- regels 13-21: weergave van de kolomkoppen van de tabel
- regels 23-55: doorloop van de lijst met simulaties die in de sessie zijn gevonden
- regels 24-26: aanmaken van een URL met de naam URL (attribuut id). De HTML-link die door deze URL wordt gegenereerd, is de volgende:
<a href="<a href="view-source:http://localhost:8084/pam/SupprimerSimulation.action?id=1">/pam/SupprimerSimulation.action?id=1</a>">Retirer</a>
We zien dat de link verwijst naar de actie [SupprimerSimulation] met de parameter id, die het nummer aangeeft van de simulatie die uit de lijst moet worden verwijderd.
- regels 28-54: bij elke doorloop van de lijst met simulaties worden de eigenschappen van de huidige simulatie weergegeven.
![]() |
19.10. Een simulatie verwijderen
De gebruiker kan een simulatie uit de lijst met simulaties willen verwijderen:
![]() |
![]() |
- in [1] wordt simulatie nr. 1 verwijderd
- in [2] is simulatie nr. 1 verwijderd
De link [Retirer] bevindt zich in het fragment [Simulations.JSP] dat we al hebben bekeken:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!-- lege simulatielijst -->
<s:if test="#session['simulations']==null || #session['simulations'].size()==0">
<h2><s:text name="Pam.SimulationsVides.titre"/></h2>
</s:if>
<!-- niet-lege simulatielijst -->
<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"/>">Verwijderen</a></td>
</tr>
</s:iterator>
</table>
</s:if>
- regels 16-18: genereren de HTML-link
<a href="<a href="view-source:http://localhost:8084/pam-01/SupprimerSimulation.action?id=2">/pam-01/SupprimerSimulation.action?id=</a>num">Retirer</a>
waarbij num het nummer is van de simulatie die moet worden verwijderd.
De actie [SupprimerSimulation] is als volgt gedefinieerd in het bestand [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>
<!-- internationalisering -->
<constant name="struts.custom.i18n.resources" value="messages" />
<!-- Spring-integratie -->
<constant name="struts.objectFactory.spring.autoWire" value="name" />
<!-- Struts/Tiles-acties -->
<package name="default" namespace="/" extends="tiles-default">
...
<!-- actie RetirerSimulation -->
<action name="SupprimerSimulation" class="web.actions.Supprimer">
<result name="erreur" type="tiles">erreur</result>
<result name="simulations" type="tiles">simulations</result>
</action>
...
</package>
<!-- Voeg hier pakketten toe -->
</struts>
- regel 16: de actie [SupprimerSimulation] is gekoppeld aan de klasse [Supprimer]. Omdat er geen methode is opgegeven, wordt de methode execute uitgevoerd. De klasse [Supprimer] ziet er als volgt uit:
package web.actions;
...
public class Supprimer extends ActionSupport implements SessionAware {
// sessie
private Map<String, Object> session;
// ID van de te verwijderen simulatie
private String id;
// menu
private Menu menu;
@Override
public void setSession(Map<String, Object> session) {
this.session = session;
}
// uitvoering van de actie
public String execute() {
// de simulaties uit de sessie ophalen
List<Simulation> simulations = (List<Simulation>) session.get("simulations");
if (simulations == null) {
// afwijkende situatie – de sessie is waarschijnlijk verlopen
menu = new Menu(false, false, false, false, true, true);
return "erreur";
}
// ID-test
int num = 0;
boolean erreur = false;
try {
num = Integer.parseInt(id);
erreur = num <= 0;
} catch (NumberFormatException ex) {
// afwijkend
erreur = true;
}
// fout?
if (erreur) {
menu = new Menu(false, false, false, false, true, true);
return "erreur";
}
// de te verwijderen simulatie wordt opgezocht
for (int i = 0; i < simulations.size(); i++) {
if (num == simulations.get(i).getNum()) {
simulations.remove(i);
break;
}
}
// de lijst met simulaties wordt weergegeven
menu = new Menu(false, false, false, false, true, true);
return "simulations";
}
// getters en setters
...
}
- regel 4: de actie [Supprimer] implementeert de interface [SessionAware] om toegang te krijgen tot de sessie.
- regel 7: de sessie
- regel 9: het nummer van de te verwijderen simulatie. We herinneren ons immers dat we bij de instantiëring van de klasse [Supprimer] komen via de 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>
waarbij num het nummer is van de te verwijderen simulatie. Dit nummer wordt opgeslagen in het veld id van regel 9.
- regel 11: het menu voor de weergave die als antwoord op het verzoek wordt getoond
- regel 19: de methode execute die het antwoord op de aanvraag zal genereren.
- regel 21: de lijst met simulaties die al in de sessie zijn uitgevoerd, wordt opgehaald
- regels 22-26: als deze lijst niet in de sessie wordt opgehaald, betekent dit waarschijnlijk dat de sessie is verlopen. Dit is al eerder voorgekomen. We retourneren de sleutel erreur, die de weergave Tiles erreur weergeeft:
<!-- actie RetirerSimulation -->
<action name="SupprimerSimulation" class="web.actions.Supprimer">
<result name="erreur" type="tiles">erreur</result>
...
</action>
De Tiles-weergave erreur is besproken in paragraaf 19.9.
- regels 28-36: we controleren of de tekenreeks id op regel 9 inderdaad een geheel getal >0 is.
- regels 38-40: als dat niet het geval is, wordt opnieuw de sleutel erreur geretourneerd, waardoor de Tiles-weergave erreur wordt weergegeven
- regels 43-48: de te verwijderen simulatie wordt opgezocht in de lijst met simulaties. Als deze wordt gevonden, wordt deze verwijderd.
- regel 50: het menu wordt bijgewerkt voor de weergave Tiles simulations.
- regel 51: de sleutel simulations wordt teruggegeven. Deze zorgt ervoor dat de weergave Tiles simulations wordt weergegeven:
<!-- actie RetirerSimulation -->
<action name="SupprimerSimulation" class="web.actions.Supprimer">
...
<result name="simulations" type="tiles">simulations</result>
</action>
De Tiles-weergave simulations is besproken in paragraaf 19.9.
19.11. Terug naar het formulier
Vanuit de Tiles-weergave simulations kan de gebruiker terugkeren naar het formulier:
![]() |
![]() |
- in [1] klikt men op de link om terug te keren naar het formulier
- in [2] verschijnt een leeg formulier
De link [Retour au formulaire de simulation] is gedefinieerd in het fragment [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>
- regel 10: de link verwijst naar de actie [RetourFormulaire]. Deze is als volgt gedefinieerd in het bestand [struts.xml]:
<!-- actie RetourFormulaire -->
<action name="RetourFormulaire" >
<result type="redirectAction">
<param name="actionName">Formulaire!input</param>
<param name="namespace">/</param>
</result>
</action>
We zien dat deze actie aan geen enkele klasse is gekoppeld. Ze leidt de clientbrowser gewoon door naar de actie [/Formulaire!input]. We bevinden ons nu in hetzelfde geval als bij het weergeven van de startweergave die in paragraaf 19.7 is uitgelegd. We zien dus opnieuw deze startweergave [2].
19.12. De lijst met simulaties bekijken
Vanuit de Tiles-weergaven simulation of saisie kan de gebruiker de simulaties opvragen:
![]() |
![]() |
- in [1] klikt men op de link [Voir les simulations]
- in [2] vindt men de lijst met simulaties
De link [Voir les simulations] is gedefinieerd in het fragment [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>
- regel 10: de koppeling [Voir les simulations] roept de actie [VoirSimulations] op. Deze is als volgt gedefinieerd in het bestand [struts.xml]:
<!-- actie VoirSimulations -->
<action name="VoirSimulations" class="web.actions.Voir">
<result name="success" type="tiles">simulations</result>
</action>
De actie [VoirSimulations] is gekoppeld aan de klasse [Voir] zonder dat er een methode is gespecificeerd. Daarom wordt de methode [Voir].execute uitgevoerd. De klasse [Voir] is als volgt:
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);
// getters en setters
public Menu getMenu() {
return menu;
}
public void setMenu(Menu menu) {
this.menu = menu;
}
}
De actie [Voir] doet maar één ding: het menu instellen voor de weergave 'Tiles simulations' (regel 8). Er is geen methode execute. Daarom wordt de methode van de bovenliggende klasse [ActionSupport] uitgevoerd. We weten dat deze methode niets anders doet dan de sleutel success teruggeven.
Terug naar de actie in [struts.xml]:
<!-- actie VoirSimulations -->
<action name="VoirSimulations" class="web.actions.Voir">
<result name="success" type="tiles">simulations</result>
</action>
Op regel 3 zien we dat de sleutel success leidt tot de weergave van de Tiles-weergave simulations. Deze is beschreven op pagina 161.
19.13. De huidige simulatie wissen
Vanuit de Tiles-weergave simulation kan de gebruiker vragen om de huidige simulatie te wissen:
![]() |
![]() |
- in [1] wordt de huidige simulatie gewist
- in [2] verschijnt het lege invoerformulier
De link [Effacer la simulation] is gedefinieerd in het fragment [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>
Op regel 10 zien we dat de koppeling [Effacer la simulation] de actie [Formulaire!input] activeert. We weten dat deze actie leidt naar de startweergave [2].
19.14. De huidige sessie beëindigen
Vanuit alle Tiles-weergaven kan de gebruiker vragen om de sessie te beëindigen:
![]() |
![]() |
![]() |
- in [1]: vanuit de simulatieweergave wordt de sessie beëindigd
- in [2] verschijnt het lege invoerformulier. We vragen om de simulaties te bekijken.
- in [3] is de lijst met simulaties nu leeg.
De link [Terminer la session] is gedefinieerd in het fragment [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>
Op regel 10 zien we dat de koppeling [Terminer la session] de actie [TerminerSession] activeert. Deze is als volgt gedefinieerd in het bestand [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>
- regel 1: we zien dat de klasse [Terminer] wordt geïnstantieerd en dat de methode execute wordt uitgevoerd.
- regels 2-5: na uitvoering van de methode [Terminer].execute vindt er een omleiding plaats naar het oorspronkelijke invoerscherm. Dit verklaart scherm nr. 2.
De klasse [Terminer] is als volgt:
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 {
// sessie
private Map<String, Object> session;
@Override
public String execute() {
// de huidige sessie afsluiten
session.clear();
return SUCCESS;
}
@Override
public void setSession(Map<String, Object> session) {
this.session = session;
}
}
De rol van de actie [Terminer] is het wissen van de attributen van de huidige sessie.
- regel 7: de actie [Terminer] implementeert de interface [SessionAware] om toegang te krijgen tot de sessie.
- regel 10: het sessie-woordenboek
- regel 13: de methode execute die wordt uitgevoerd
- regel 15: deze methode leegt het sessiedictionary. Daardoor verdwijnt de lijst met simulaties die in de sessie aanwezig is. Dit verklaart scherm nr. 3.
- regel 16: deze methode retourneert de sleutel success, die, zoals we hebben gezien, de weergave Tiles saisie [2] zal weergeven.
19.15. Conclusion
We hebben versie 1 van onze casestudy, die werkt met een gesimuleerde laag [metier], volledig van commentaar voorzien:
![]() |
Nu hoeven we alleen nog maar de echte bedrijfslaag aan de [web]-laag te koppelen.






























