19. Fallstudie – Version 1
19.1. Die simulierte [Geschäfts-]Ebene
Sehen wir uns die Architektur der Anwendung an, die wir entwickeln:
![]() |
Wir verfügen über die Archivdateien der [Business-, DAO- und JPA-]Schichten und haben die Elemente dieser Schichten vorgestellt, die die [Web-]Schicht kennen muss. Wir sind nun bereit, die [Web-]Schicht unter Verwendung des Struts-Frameworks zu schreiben.
Um das Testen unserer Anwendung während der Entwicklung zu vereinfachen, werden wir eine simulierte Business-Schicht erstellen, die die Schnittstelle der [Business]-Schicht implementiert. Die Architektur sieht dann wie folgt aus:
![]() |
Wir werden die [Web]-Schicht unter Verwendung der simulierten [Business]-Schicht entwickeln. Das Testen wird einfacher, da es in der Architektur keine Datenbank mehr gibt. Dank Spring und der Verwendung von Schnittstellen hat das spätere Ersetzen der simulierten [Business]-Schicht durch die tatsächliche [Business, DAO, JPA]-Architektur keine Auswirkungen auf den Code der [Web / Struts2]-Schicht. Die [Web / Struts2]-Schicht, die wir entwickeln werden, kann unverändert verwendet werden.
Die simulierte [Business]-Schicht, die wir verwenden werden, sieht wie folgt aus:
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 {
// list of employees
private Map<String, Employe> hashEmployes = new HashMap<String, Employe>();
private List<Employe> listEmployes;
// get your payslip
public FeuilleSalaire calculerFeuilleSalaire(String SS,
double nbHeuresTravaillées, int nbJoursTravaillés) {
// we retrieve employee n° SS
Employe e = hashEmployes.get(SS);
// a fictitious payslip is returned
return new FeuilleSalaire(e, new Cotisation(3.49, 6.15, 9.39, 7.88), new ElementsSalaire(100, 100, 100, 100, 100));
}
// list of employees
public List<Employe> findAllEmployes() {
if (listEmployes == null) {
// create a list of two employees
listEmployes = new ArrayList<Employe>();
listEmployes.add(new Employe("254104940426058", "Jouveinal", "Marie", "5 rue des oiseaux", "St Corentin", "49203", new Indemnite(2, 2.1, 2.1, 3.1, 15)));
listEmployes.add(new Employe("260124402111742", "Laverti", "Justine", "La br�lerie", "St Marcel", "49014", new Indemnite(1, 1.93, 2, 3, 12)));
// employee dictionary
for (Employe e : listEmployes) {
hashEmployes.put(e.getSS(), e);
}
}
// we return the list of employees
return listEmployes;
}
}
- Zeile 11: Die Klasse [MetierSimule] implementiert die Schnittstelle [IMetier], die die eigentliche [Business]-Schicht implementiert.
- Zeile 14: Ein Verzeichnis der Mitarbeiter, indiziert nach ihrer INSEE-Nummer
- Zeile 15: die Liste der Mitarbeiter
- Zeilen 27–39: Implementierung der Methode findAllEmployees der Schnittstelle [IMetier].
- Zeilen 30–33: Erstellung einer Liste mit zwei Mitarbeitern
- Zeilen 34–36: Erstellung des nach INSEE-Nummern indizierten Mitarbeiterverzeichnisses
- Zeilen 18–24: Implementierung der Methode `calculerSalaire` aus der Schnittstelle [IMetier]. Hier geben wir eine fiktive Gehaltsabrechnung zurück.
19.2. Das NetBeans-Projekt
Das NetBeans-Projekt sieht wie folgt aus:
![]() |
- in [1]:
- [applicationContext.xml] ist die Spring-Konfigurationsdatei
- [tiles.xml] ist die Konfigurationsdatei für ein Framework namens Tiles.
- [web.xml] ist die Konfigurationsdatei der Webanwendung
- in [2]: die verschiedenen Ansichten der Anwendung
- in [3]:
- [messages.properties]: die Meldungsdatei
- [struts.xml]: die Struts-Konfigurationsdatei
![]() |
- in [4]: der Quellcode der Anwendung. Die Struts-Aktionen befinden sich im Paket [web.actions].
- in [5]: die simulierte [Business]-Schicht
- in [6]: die verwendeten Archive. Hier sind die Archive für die verschiedenen verwendeten Tools: Spring, Tiles, Struts 2, das Struts 2/Spring-Integrations-Plugin und das Struts 2/Tiles-Integrations-Plugin.
- in [7]: das Archiv der eigentlichen [Business-, DAO-, JPA-]Schicht. Es gewährt uns Zugriff auf die JPA-Entitäten, die [IMetier]-Schnittstelle sowie die Klassen [PayrollSheet] und [PayrollItems]. All diese Elemente werden tatsächlich von unserer [SimulatedBusiness]-Klasse verwendet.
19.3. Projektkonfiguration
Das Projekt wird durch verschiedene Dateien konfiguriert:
- [web.xml], die die Webanwendung konfiguriert
- [struts.xml], die das Struts-Framework konfiguriert
- [applicationContext.xml], die das Spring-Framework konfiguriert
- [tiles.xml], die das Tiles-Framework konfiguriert
19.3.1. Konfiguration der Webanwendung
Die Datei [web.xml] sieht wie folgt aus:
<?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>
- Zeilen 13–20: Konfiguration des Struts-2-Filters – bereits gesehen
- Zeilen 22–24: Konfigurieren des Spring-Listeners – bereits gesehen
- Zeilen 9–11: Konfigurieren des Tiles-Listeners. Die Klasse [org.apache.struts2.tiles.StrutsTilesListener] wird beim Start der Webanwendung instanziiert. Sie verwendet dann ihre Konfigurationsdatei. Diese ist in den Zeilen 5–8 definiert. Die Tiles-Konfigurationsdatei ist somit die Datei [WEB-INF/tiles.xml].
Letztendlich werden beim Start der Struts-Anwendung drei Klassen instanziiert:
- eine für den Struts-2-Filter. Dies ist die Komponente, die das „C“ in MVC abdeckt.
- eine weitere für den Spring-Listener. Spring verwendet die Datei [applicationContext.xml], um die [Business-, DAO- und JPA-]Schichten der Anwendung zu instanziieren. Spring instanziiert außerdem, wie im vorherigen Beispiel, eine [Config]-Klasse, die anwendungsbezogene Daten enthält. Schließlich injiziert Spring eine Referenz auf diese einzelne [Config]-Instanz in jede Struts-Aktion, die sie benötigt.
- Ein weiterer für den Tiles-Listener. Dieses Framework übernimmt die Verwaltung der Ansichten. Wir werden gleich darauf zurückkommen.
19.3.2. Konfiguration des Struts-Frameworks
Das Struts-Framework wird über die folgende [struts.xml]-Datei konfiguriert:
<?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>
<!-- internationalization -->
<constant name="struts.custom.i18n.resources" value="messages" />
<!-- spring integration -->
<constant name="struts.objectFactory.spring.autoWire" value="name" />
<!-- struts /Tiles actions -->
<package name="default" namespace="/" extends="tiles-default">
<!-- default action -->
<default-action-ref name="index" />
<action name="index">
<result type="redirectAction">
<param name="actionName">Formulaire</param>
<param name="namespace">/</param>
</result>
</action>
<!-- action Form -->
<action name="Formulaire" class="web.actions.Formulaire" method="input">
<result name="success" type="tiles">saisie</result>
<result name="exception" type="tiles">exception</result>
</action>
<!-- action 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>
<!-- action 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>
<!-- action RetourFormulaire -->
<action name="RetourFormulaire" >
<result type="redirectAction">
<param name="actionName">Formulaire</param>
<param name="namespace">/</param>
</result>
</action>
<!-- action VoirSimulations -->
<action name="VoirSimulations" class="web.actions.Voir">
<result name="success" type="tiles">simulations</result>
</action>
<!-- action 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>
<!-- action 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>
Wir werden die verschiedenen Struts-Aktionen im Laufe der Erläuterung besprechen. Beachten Sie vorerst die folgenden Punkte:
- Zeile 8: definiert die Nachrichtendatei
- Zeile 10: definiert, wie Spring-Beans in Struts-Aktionen injiziert werden. Die Injektion basiert auf dem Namen des Beans. Das Struts-Aktionsfeld, das von Spring initialisiert werden muss, muss denselben Namen wie das zu injizierende Bean haben.
- Zeile 25: definiert die Ansicht, die für den Navigationsschlüssel „success“ der [Form]-Aktion angezeigt werden soll. Wir sehen, dass das <result>-Element ein Attribut type='tiles' hat, das uns nicht bekannt ist. Wir kannten den Typ „redirect“, der es ermöglicht, den Client zu einer Ansicht umzuleiten. Hier wird die Ansicht vom Typ „tiles“ vom Tiles-Framework verwaltet. Der Typ „tiles“ ist in der Datei [struts-plugin.xml] innerhalb des Archivs [struts2-tiles-plugin-2.2.3.1.jar] definiert:
<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>
- Zeilen 3–5: Die Definition des Tiles-Ergebnistyps.
- Zeile 2: Dieser Typ ist im Paket [tiles-default] definiert, das das Paket [struts-default] erweitert.
- Zeile 14: Definiert das [default]-Paket, das alle Aktionen der Anwendung enthalten wird. Um die Vorteile der Tiles-View-Typdefinition zu nutzen, erweitert das Paket [tiles-default].
19.3.3. Konfiguration des Spring Frameworks
Das Spring-Framework wird über die folgende Datei [WEB-INF/applicationContext.xml] konfiguriert:
<?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">
<!-- application layers -->
<!-- web -->
<bean id="config" class="web.Config" init-method="init">
<property name="metier" ref="metier"/>
</bean>
<!-- business -->
<bean id="metier" class="metier.MetierSimule"/>
</beans>
- Zeile 13: Die simulierte [business]-Schicht, instanziiert durch die Klasse [business.SimulatedBusiness]
- Zeilen 9–11: Konfigurieren Sie eine Bean namens config. Wie in einem zuvor behandelten Beispiel wird diese Bean verwendet, um Informationen im Anwendungsbereich zu kapseln. Die mit dieser Bean verbundene Klasse ist die folgende [Config]-Klasse:
package web;
import java.util.List;
import jpa.Employe;
import metier.IMetier;
public class Config {
// business layer initialized by Spring
private IMetier metier;
// list of employees
private List<Employe> employes;
// errors
private Exception initException;
// manufacturer
public Config() {
}
// spring method for object initialization
public void init() {
// we ask for the list of employees
try {
employes = metier.findAllEmployes();
} catch (Exception ex) {
initException = ex;
}
}
// getters and setters
...
}
Kehren wir zur Konfiguration des Config-Beans zurück:
<bean id="config" class="web.Config" init-method="init">
<property name="metier" ref="metier"/>
</bean>
<!-- métier -->
<bean id="metier" class="metier.Metier">
...
</bean>
In Zeile 2 sehen wir, dass die Business-Bean aus Zeile 5 in das Feld namens business (name) des [Config]-Objekts injiziert (ref) wird. Die Business-Bean ist eine Referenz auf die [business]-Schicht:
![]() |
Um mit der [business]-Schicht zu interagieren, benötigen alle Struts-Aktionen in der [web]-Schicht eine Referenz darauf. Man kann sagen, dass die Referenz auf die [business]-Schicht Daten im Anwendungsbereich sind. Alle Anfragen aller Benutzer benötigen sie. Deshalb platzieren wir diese Referenz im [Config]-Objekt. Zusätzlich enthält die Konfiguration des Config-Beans in Zeile 1 ein init-method-Attribut. Dieses Attribut gibt die Bean-Methode an, die nach der Instanziierung des Beans ausgeführt werden soll. Hier legen wir fest, dass nach der Instanziierung der [web.Config]-Klasse deren init-Methode ausgeführt werden muss. Diese Methode lautet wie folgt:
// business layer initialized by Spring
private IMetier metier;
// list of employees
private List<Employe> employes;
// errors
private Exception initException;
// manufacturer
public Config() {
}
// spring method for object initialization
public void init() {
// we ask for the list of employees
try {
employes = metier.findAllEmployes();
} catch (Exception ex) {
initException = ex;
}
}
Wenn die init-Methode ausgeführt wird, wurde das Geschäftsfeld der Klasse von Spring instanziiert. Die init-Methode hat daher Zugriff auf die Schnittstelle der Geschäftsschicht [IMetier] (Zeile 2):
package metier;
import java.util.List;
import jpa.Employe;
public interface IMetier {
// get your payslip
FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés );
// list of employees
List<Employe> findAllEmployes();
}
- Zeile 8: Die Methode berechnet das Gehalt eines Mitarbeiters
- Zeile 10: Die Methode ruft die Liste der Mitarbeiter ab
Wir sehen, dass die init-Methode die Liste der Mitarbeiter von der [business]-Schicht anfordert. Diese Liste wird im Feld in Zeile 4 gespeichert. Tritt eine Ausnahme auf, wird diese im Feld in Zeile 6 gespeichert.
Zusammenfassend enthält das einzelne [Config]-Objekt:
- einen Verweis auf die [business]-Schicht
- die Liste der Mitarbeiter
19.4. Erstellen von Kachelansichten
Wie wir in der Struts-Konfigurationsdatei gesehen haben, werden Ansichten vom Tiles-Framework generiert. Wir werden nur das erläutern, was für das Schreiben unserer Anwendung unbedingt notwendig ist.
Tiles ermöglicht es Ihnen, Ansichten aus einer Master-Seite zu generieren. Diese Seite, hier [MasterPage.jsp] genannt, wird aus den folgenden JSP-Fragmenten zusammengesetzt:
Diese JSP-Fragmente sind im NetBeans-Projekt definiert:

Das Tiles-Framework ermöglicht es uns, festzulegen, welche Fragmente in die Master-Seite eingefügt werden sollen.
Die Master-Seite [MasterPage.jsp] sieht wie folgt aus:
<%@ 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>
Die Master-Seite ist ein Container für JSP-Fragmente. Hier handelt es sich um eine Zusammenstellung von sechs Fragmenten, nämlich denen in den Zeilen 17 bis 23. Bei der Generierung können zwischen 0 und 6 Fragmente in der Master-Seite zusammengestellt werden. Diese Generierung wird durch die Datei [WEB-INF/tiles.xml] gesteuert, die die Tiles-Ansichten definiert:
<?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>
<!-- the master page -->
<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>
<!-- input view -->
<definition name="saisie" extends="masterPage">
<put-attribute name="saisie" value="/Saisie.jsp"/>
</definition>
<!-- simulation view -->
<definition name="simulation" extends="saisie">
<put-attribute name="simulation" value="/Simulation.jsp"/>
</definition>
<!-- the simulations view -->
<definition name="simulations" extends="masterPage">
<put-attribute name="simulations" value="/Simulations.jsp"/>
</definition>
<!-- the exceptional view -->
<definition name="exception" extends="masterPage">
<put-attribute name="exception" value="/Exception.jsp"/>
</definition>
<!-- error view -->
<definition name="erreur" extends="masterPage">
<put-attribute name="erreur" value="/Erreur.jsp"/>
</definition>
</tiles-definitions>
- Die obige Datei definiert sechs Tiles-Ansichten mit den Namen: masterPage (Zeile 9), input (Zeile 20), simulation (Zeile 25), simulations (Zeile 30), exception (Zeile 35), error (Zeile 40).
- Zeilen 9–17: Definieren eine Ansicht namens masterPage (name), die der Master-Seite [MasterPage.jsp] (template) zugeordnet ist. Wir haben gesehen, dass diese JSP-Seite sechs Unteransichten definiert. Eine mit der Master-Seite verknüpfte Tiles-Ansicht muss das JSP-Fragment angeben, das mit jeder der sechs Unteransichten verknüpft ist. Wir sehen, dass einigen Unteransichten die leere Zeichenfolge als Wert zugewiesen ist. Diese Unteransichten werden nicht in die Master-Seite [MasterPage.jsp] aufgenommen. Die Tiles-Ansicht mit dem Namen masterPage besteht daher ausschließlich aus dem Unterfragment [Entete.jsp].
- Zeilen 20–22: Definieren Sie eine Ansicht namens `saisie`, die die zuvor definierte Ansicht `masterPage` erweitert. Das bedeutet, dass sie alle Definitionen aus der Ansicht `masterPage` erbt. Ihre Definition entspricht dem Folgenden:
<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>
Wir sehen, dass sie mit der JSP-Seite [MasterPage.jsp] verknüpft ist und daher die sechs Unteransichten dieser Seite definieren muss. Wir sehen, dass die Definition in Zeile 9 diejenige in Zeile 4 überschreibt. Die Tiles-Ansicht mit dem Namen „saisie“ setzt sich somit aus den JSP-Fragmenten [Entete.jsp, Saisie.jsp] zusammen
Wenn wir diesen Gedankengang fortsetzen, erhalten wir die folgende Tabelle:
Tiles-Ansicht | JSP-Seiten |
19.5. Meldungsdateien
Die Anwendung wurde internationalisiert. Die Meldungen befinden sich in den Dateien [messages.properties] und [Formulaire.properties].
Die Datei [messages.properties] sieht wie folgt aus:
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
# formats
Format.heure = {0,time}
Format.nombre = {0,number,#0.0##}
Format.pourcent = {0,number,##0.00' %'}
Format.monnaie={0,number,##0.00' \u20ac'}
# liste des simulations
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
# msg d'erreur
Erreur.sessionexpiree=La session a expir\u00e9
Erreur.numSimulation=N\u00b0 de simulation incorrect
# erreur de conversion
xwork.default.invalid.fieldvalue=Valeur invalide pour le champ "{0}".
Die Datei [Form.properties] sieht wie folgt aus:
# pour que les doubles soient au format local
double.format={0,number,#0.00##}
# msg d'erreur
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. Das Stylesheet
Kachelansichten verwenden das folgende 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. Die Startansicht
Um die Anwendung zu erkunden, werden wir sie anhand der verschiedenen Aktionen des Benutzers untersuchen. Für jede Aktion betrachten wir die Struts-Aktion, die sie ausführt, und die Tiles-Ansicht, die als Antwort zurückgegeben wird.
In [struts.xml] haben wir die folgenden Aktionen:
<!-- action par défaut -->
<default-action-ref name="index" />
<action name="index">
<result type="redirectAction">
<param name="actionName">Formulaire!input</param>
<param name="namespace">/</param>
</result>
</action>
<!-- action Formulaire -->
<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>
- Zeilen 2–8: Die Standardaktion der Anwendung ist [Form!input].
Die Klasse [Form] sieht wie folgt aus:
package web.actions;
...
public class Formulaire extends ActionSupport implements Preparable, SessionAware {
// configuration initialized by Spring
private Config config;
// list of employees
private List<Employe> employes;
// error list
private List<Erreur> erreurs;
// payslip
private FeuilleSalaire feuilleSalaire;
// foreclosures
private String comboEmployesValue;
private Double heuresTravaillees;
private Integer joursTravailles;
// session
private Map<String, Object> session;
// menu
private Menu menu;
@Override
public void prepare() throws Exception {
...
}
@Override
public String input() {
....
}
// wage calculation
public String calculSalaire() {
...
}
}
@Override
public void validate() {
...
}
@Override
public void setSession(Map<String, Object> map) {
session = map;
}
// getters and setters
...
}
- Zeile 4: Die Aktion [Form] implementiert die Schnittstelle „Preparable“. Diese Schnittstelle verfügt nur über eine einzige Methode, nämlich die Methode „prepare“ in Zeile 24. Diese Methode wird einmalig vor allen anderen Methoden der Aktion ausgeführt. Sie dient in der Regel dazu, das Modell der Aktion zu initialisieren.
Das Aktionsmodell ist in den Zeilen 6–21 definiert:
- Zeile 7: Das Feld „config“ wird, wie bereits erläutert, von Spring initialisiert. Es bietet Zugriff auf Daten im Anwendungsbereich:
- einen Verweis auf die [business]-Schicht
- einen Verweis auf die Liste der Mitarbeiter
- einen Verweis auf die Ausnahme, die möglicherweise während der Instanziierung des [Config]-Objekts aufgetreten ist
- Zeile 9: eine Liste der Mitarbeiter. Diese füllt das Mitarbeiter-Dropdown-Menü im Fragment [Saisie.jsp].
- Zeile 11: eine Liste von Fehlern. Diese füllt das Fragment [Error.jsp].
- Zeile 21: die Liste der Menüoptionen für das [Entete.jsp]-Fragment
![]() |
In [1] werden die Links im angezeigten Menü durch das Menüfeld der [Form]-Aktion gesteuert.
Die prepare-Methode wird vor der input-Methode ausgeführt. Sie lautet wie folgt:
@Override
public void prepare() throws Exception {
// configuration error?
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();
}
}
- Zeile 4: Wir rufen die Ausnahme aus dem von Spring instanziierten [Config]-Objekt ab
- Zeile 5: Wenn während der Instanziierung des [Config]-Objekts eine Ausnahme aufgetreten ist, initialisieren wir die Fehlerliste in Zeile 11. Die [Error]-Klasse sieht wie folgt aus:
package web.entities;
import java.io.Serializable;
public class Erreur implements Serializable{
public Erreur() {
}
// fields
private String classe;
private String message;
// manufacturer
public Erreur(String classe, String message){
this.setClasse(classe);
this.message=message;
}
// getters and setters
...
}
Die Klasse dient zur Speicherung des Ausnahmestapels:
- Zeile 11: die Ausnahmeklasse
- Zeile 12: die Ausnahmemeldung
Kehren wir zur Methode „prepare“ zurück:
- Zeile 13: Die Liste der Mitarbeiter aus dem [Config]-Objekt wird im Feld „employees“ der Aktion gespeichert.
Sobald die Methode „prepare“ ausgeführt wurde, wird als Nächstes die Methode „input“ ausgeführt. Sie sieht wie folgt aus:
@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";
}
}
Die Eingabemethode legt lediglich die Liste der anzuzeigenden Menüoptionen fest. Die Klasse [Menu] sieht wie folgt aus:
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 and setters
...
}
- Zeilen 8–13: Es gibt 6 mögliche Links im Menü
- Zeilen 18–25: Der Klassenkonstruktor ermöglicht es dir, festzulegen, welche Links angezeigt werden sollen und welche nicht.
Die Menü-Links werden in [Entete.jsp] angezeigt, einem JSP-Fragment, das in allen Tiles-Ansichten vorhanden ist. Jede Aktion verfügt über ein Menüfeld, um die Anzeige des Menüs in [Entete.jsp] zu steuern.
Kehren wir zur Eingabemethode zurück:
@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";
}
}
- Zeilen 3–6: Ist die Fehlerliste leer, wird das Menü [Simulation ausführen, Simulationen anzeigen, Sitzung beenden] angezeigt und die Eingabetaste zurückgegeben.
- Zeilen 9–10: Ist die Fehlerliste nicht leer, bleibt das Menü leer und die Ausnahmetaste wird zurückgegeben.
Kehren wir zur Konfiguration der Aktion [Form] in [struts.xml] zurück:
<!-- action Formulaire -->
<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>
- Zeile 5: Der Schlüssel „input“ zeigt die Kachelansicht mit dem Namen „input“ an
- Zeile 4: Der Schlüssel „exception“ zeigt die Tiles-Ansicht mit dem Namen „exception“ an
Beginnen wir mit der Tiles-Ansicht namens „input“. Sie besteht aus den JSP-Fragmenten [Entete.jsp] und [Saisie.jsp].
Das Fragment [Entete.jsp] sieht wie folgt aus:

Sein Code lautet wie folgt:
<%@ 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>
- Zeilen 8–25: Anzeige der sechs Menü-Links [Simulation ausführen (Zeilen 8–10), Simulation löschen (Zeilen 11–13), Simulationen anzeigen (Zeilen 14–16), Zurück zum Formular (Zeilen 17–19), Simulation speichern (Zeilen 20–22), Sitzung beenden (Zeilen 23–25).
- Zeilen 8, 11, 14, 17, 20, 23: Die Anzeige der Links wird durch das Menüfeld der aktuellen Aktion gesteuert.
Beachten Sie, dass das Fragment [Entete.jsp] eine HTML-Tabelle (Zeilen 4–28) anzeigt, aber keine vollständige HTML-Seite ist. Beachten Sie, dass alle Ansichten der Anwendung in die folgende Master-Seite [MasterPage.jsp] eingebettet sind:
<%@ 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>
Das Fragment [Entete.jsp] wird in Zeile 17 in eine reguläre HTML-Seite eingefügt.
Das Fragment [Saisie.jsp] wird in Zeile 19 eingefügt. Dies ist die resultierende Ansicht:

Der Code für das Fragment [Saisie.jsp] lautet wie folgt:
<%@ 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(){
// mail the form
document.forms['Saisie'].elements['action'].name='action:Formulaire!calculSalaire'
document.forms['Saisie'].submit();
}
</script>
<!-- data entry -->
<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>
- Zeile 17: Das Formular hat kein action-Attribut. Standardmäßig ist action='Form'.
- Zeile 18: Anzeige des Mitarbeiter-Dropdowns. Der Inhalt des Dropdowns (list-Attribut) wird vom Feld „employees“ der aktuellen Aktion bereitgestellt. Das value-Attribut der Optionen ist die Sozialversicherungsnummer des Mitarbeiters (listKey-Attribut). Die für jede Option angezeigte Bezeichnung ist der Vor- und Nachname des Mitarbeiters (listValue-Attribut). Die Sozialversicherungsnummer des im Kombinationsfeld ausgewählten Mitarbeiters wird an das Feld [Form].comboEmployeesValue (Attribut „name“) übermittelt.
- Zeile 19: Eingabefeld für geleistete Arbeitsstunden. Der angezeigte Wert (Attribut „value“) entspricht dem Wert des Felds „heuresTravaillees“ in der Aktion [Form] im folgenden Format (Form.properties):
double.format={0,number,#0.00##}
Der Wert wird in das Feld [Form].hoursWorked (Namensattribut) geschrieben.
- Zeile 20: Feld zur Eingabe der gearbeiteten Tage. Der angezeigte Wert (Attribut „value“) entspricht dem Wert des Feldes „daysWorked“ in der Aktion [Form].
Der Wert wird an das Feld [Form].daysWorked (Attribut „name“) übermittelt.
Letztendlich sieht die Kachelansicht, die beim Start angezeigt wird, wenn keine Fehler vorliegen, wie folgt aus:

Kehren wir zur Konfiguration der Aktion [Form] zurück:
<!-- action Formulaire -->
<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>
Wir haben gesehen, dass die Aktion [Form].input in Zeile 4 auch den Schlüssel „exception“ auslösen kann. In diesem Fall wird die Tiles-Ansicht mit dem Namen „exception“ angezeigt. Diese Ansicht besteht aus den Fragmenten [Header.jsp] und [Exception.jsp]. Das Fragment [Header.jsp] haben wir bereits vorgestellt. Das Fragment [Exception.jsp] sieht wie folgt aus:

Dies ist die Startseite von Version 2 der Anwendung, wenn das DBMS noch nicht gestartet wurde. Der JSP-Code für das Fragment [Error.jsp] lautet wie folgt:
<%@ 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>
- Zeilen 10–14: Ein Iterator über die List<Error>-Sammlung von Fehlern aus der [Form]-Aktion. Erinnern Sie sich daran, dass dort im Falle eines Fehlers ein Stapel von Ausnahmen gespeichert wurde.
19.8. Simulation ausführen
Sobald die Startansicht angezeigt wird, können Sie über den Link [Simulation ausführen] ein Gehalt berechnen.
19.8.1. Überprüfung der Eingaben
Betrachten Sie die folgende Abfolge:
![]() |
- in [1], eine falsche Eingabe
- in [2] die gesendete Antwort.
Betrachten wir die Konfiguration der [Form]-Aktion in [struts.xml]:
<!-- action Formulaire -->
<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>
Wir wissen, dass der Validierungs-Interceptor im Falle eines Validierungsfehlers den Schlüssel „input“ zurückgibt. Daher wird die Tiles-Eingabeansicht zurückgegeben. Der Validierungsprozess stellt sicher, dass Felder mit Fehlern mit Fehlermeldungen versehen werden.
Die Validierung der [Form]-Aktion wird durch die folgende [Form-validation.xml]-Datei abgewickelt:
<!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>
- Die Zeilen 6–20 überprüfen, ob das Feld `hoursWorked` eine reelle Zahl im Bereich [0,300] ist.
- Die Zeilen 22–36 überprüfen, ob das Feld `joursTravaillés` eine ganze Zahl im Bereich [0,31] ist.
Kehren wir zur Konfiguration der [Form]-Aktion in [struts.xml] zurück:
<!-- action Formulaire -->
<action name="Formulaire" class="web.actions.Formulaire">
...
<result name="input" type="tiles">saisie</result>
</action>
Wir wissen, dass der Validierungs-Interceptor im Falle eines Validierungsfehlers den Schlüssel „input“ zurückgibt. Daher wird die Tiles-Ansicht „Input“ zurückgegeben.
Zur Erinnerung: Diese Ansicht besteht aus den Fragmenten [Header.jsp] und [Input.jsp], wobei [Header.jsp] einen Titel und eine Liste von Optionen enthält und [Input.jsp] das Eingabeformular enthält. Im Falle von Eingabefehlern stellt der Validierungsprozess sicher, dass die fehlerhaften Felder mit Fehlermeldungen versehen werden und auch ihre falschen Werte anzeigen. Das Fragment [Entete.jsp] spielt im Validierungsprozess keine Rolle. Sehen wir uns dessen Code an:
<%@ 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>
Die sechs Links werden über das Menüfeld der Vorlage konfiguriert (Zeilen 8, 11, 14, 17, 20, 23). Wenn ein Fehler auftritt, wird diese Vorlage durch die Aktion nicht aktualisiert, was zu einer Seite ohne Menü führt. Um dieses Problem zu beheben, verfügt die Klasse [Form] über die folgende Validierungsmethode:
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() {
...
}
// wage calculation
public String calculSalaire() {
...
}
@Override
public void validate() {
// mistakes?
if (!getFieldErrors().isEmpty()) {
// menu
menu = new Menu(true, false, false, true, false, true);
}
}
// getters and setters
...
}
- Zeile 27: Wir wissen, dass bei Vorhandensein dieser Anweisung die Methode `validate` im Rahmen des Validierungsprozesses ausgeführt wird. Wir nutzen diese Gelegenheit, um das Menü in Zeile 4 zu aktualisieren, das Teil der Fragmentvorlage [Entete.jsp] ist.
- Zeilen 29–32: Wenn Validierungsfehler aufgetreten sind, setzen wir das Menü so, dass die Tiles-Eingabeansicht erneut angezeigt wird. Wenn keine Validierungsfehler aufgetreten sind, führen wir keine Aktion aus. Die Methode `calculSalaire` ist dann für die Erstellung des anzuzeigenden View-Modells zuständig.
19.8.2. Gehaltsberechnung
Kehren wir zum JSP-Code im Header zurück:
<%@ 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>
- Zeile 9: Wenn der Benutzer auf den Link [Simulation ausführen] klickt, wird die JavaScript-Funktion doSimulation ausgeführt. Diese Funktion ist im Fragment [Saisie.jsp] definiert:
<script language="javascript" type="text/javascript">
function doSimulation(){
// mail the form
document.forms['Saisie'].elements['action'].name='action:Formulaire!calculSalaire'
document.forms['Saisie'].submit();
}
</script>
<!-- data entry -->
<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>
- Zeile 14: Ein verstecktes Feld namens „action“ wird an die [Form]-Aktion gesendet. Mit diesem Feld können wir die Aktion und die Methode festlegen, die beim Absenden des Formulars ausgeführt werden sollen. Wie wir vielleicht noch aus den ersten Beispielen wissen, können diese in einem Parameter namens „action:Action!method“ angegeben werden. Der Wert dieses Parameters spielt keine Rolle; er muss lediglich vorhanden sein.
- Zeilen 2–6: Die JavaScript-Funktion, die ausgeführt wird, wenn der Benutzer im Fragment [Header.jsp] auf den Link [Run Simulation] klickt.
- Zeile 4: Wir ändern das name-Attribut des versteckten Aktionsfelds. Wir stellen sicher, dass es in der von Struts erwarteten Form action:Action!method vorliegt.
- Zeile 5: Das Formular mit dem Namen „Saisie de la ligne 5“ wird übermittelt. Dadurch wird die folgende Parameterzeichenfolge übermittelt:
SS1: INSEE-Nummer des im Kombinationsfeld ausgewählten Mitarbeiters
heuresTravaillees: Anzahl der geleisteten Arbeitsstunden
daysWorked: Anzahl der gearbeiteten Tage
action:Form!calculateSalary: Die oben genannten Elemente werden an die Aktion [Form] übergeben, woraufhin die Methode calculateSalary dieser Aktion ausgeführt wird.
Die Methode [Form].calculateSalary lautet wie folgt:
// wage calculation
public String calculSalaire() {
try {
// salary calculation
feuilleSalaire = config.getMetier().calculerFeuilleSalaire(comboEmployesValue, heuresTravaillees, joursTravailles);
// put the simulation in the session
session.put("simulation", new Simulation(0, "" + heuresTravaillees, "" + joursTravailles, feuilleSalaire));
// menu
menu = new Menu(true, true, true, true, false, true);
// finish
return "simulation";
} catch (Throwable th) {
...
}
}
- Zeile 5: Die Lohnabrechnung wird von der [business]-Schicht angefordert
- Zeile 7: Ein Simulation-Objekt wird der Sitzung des Benutzers hinzugefügt. Dies geschieht, da es für eine nachfolgende Anfrage benötigt werden könnte. Die [Simulation]-Klasse sieht wie folgt aus:
package web.entities;
import java.io.Serializable;
import metier.FeuilleSalaire;
public class Simulation implements Serializable{
public Simulation() {
}
// simulation fields
private Integer num;
private FeuilleSalaire feuilleSalaire;
private String heuresTravaillées;
private String joursTravaillés;
// manufacturer
public Simulation(Integer num,String heuresTravaillées, String joursTravaillés, FeuilleSalaire feuilleSalaire){
this.setNum(num);
this.setFeuilleSalaire(feuilleSalaire);
this.setHeuresTravaillées(heuresTravaillées);
this.setJoursTravaillés(joursTravaillés);
}
public double getIndemnites(){
return feuilleSalaire.getElementsSalaire().getIndemnitesEntretien()+ feuilleSalaire.getElementsSalaire().getIndemnitesRepas();
}
// getters and setters
...
}
- Zeile 12: die Simulationsnummer. Sie wird bei jeder neuen gespeicherten Simulation erhöht.
- Zeile 13: die Gehaltsabrechnung des Mitarbeiters
- Zeile 14: die Anzahl der gearbeiteten Stunden
- Zeile 15: die Anzahl der gearbeiteten Tage
- Zeile 25: Die Methode „getIndemnites“ gibt die Gesamtvergütung des Mitarbeiters zurück
Wir werden sehen, dass die Klasse [Simulation] das Modell für das Fragment [Simulations.jsp] ist, das alle durchgeführten Simulationen anzeigt.
Zurück zur Methode [Form].calculateSalary:
// wage calculation
public String calculSalaire() {
try {
// salary calculation
feuilleSalaire = config.getMetier().calculerFeuilleSalaire(comboEmployesValue, heuresTravaillees, joursTravailles);
// put the simulation in the session
session.put("simulation", new Simulation(0, "" + heuresTravaillees, "" + joursTravailles, feuilleSalaire));
// menu
menu = new Menu(true, true, true, true, false, true);
// finish
return "simulation";
} catch (Throwable th) {
...
}
- Zeile 9: Menü aktualisieren
- Zeile 11: Die Navigationstaste „Simulation“ zurückgeben.
Zurück zur Konfiguration der [Form]-Aktion:
<!-- action Formulaire -->
<action name="Formulaire" class="web.actions.Formulaire">
...
<result name="simulation" type="tiles">simulation</result>
</action>
Zeile 4 zeigt, dass die Navigationsschaltfläche „simulation“ die Tiles-Ansicht mit dem Namen „simulation“ anzeigt. Diese Ansicht besteht aus den folgenden JSP-Fragmenten: [Header, Input, Simulation].
Die gerenderte Ansicht sieht wie folgt aus:
![]() |
- in [1], das Fragment [Header.jsp]
- in [2] das Fragment [Input.jsp]
- unter [3] das Fragment [Simulation.jsp]. Beachten Sie, dass es sich bei der angezeigten Gehaltsabrechnung um eine fiktive Gehaltsabrechnung handelt, die von der [business]-Schicht gerendert wird.
Die ersten beiden Fragmente wurden bereits vorgestellt. Das Fragment [Simulation.jsp] lautet wie folgt:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<hr/>
<!-- information Employees -->
<span class="titreInfos">
<s:text name="Simulation.Infos.employe"/>
</span>
<br/><br/>
<table>
<!-- line 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>
<!-- line 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>
<!-- line 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>
<!-- line 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>
<!-- information Contributions -->
<br/>
<span class="titreInfos">
<s:text name="Simulation.Infos.cotisations"/>
</span>
<br/><br/>
<table>
<!-- line 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>
<!-- line 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>
<!-- information Indemnities -->
<br/>
<span class="titreInfos">
<s:text name="Form.Infos.indemnites"/>
</span>
<br/><br/>
<table>
<!-- line 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>
<!-- line 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>
<!-- salary information -->
<br/>
<span class="titreInfos">
<s:text name="Simulation.Infos.Salaire"/>
</span>
<br/><br/>
<table>
<!-- line 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>
<!-- line 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>
<!-- Net salary-->
<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>
Es ist lang… aber funktional einfach. Dieser Ausschnitt zeigt die verschiedenen Eigenschaften des Feldes [Form].paystub an, das die Gehaltsabrechnung des Mitarbeiters darstellt.
Zurück zur Methode [Form].calculatePay:
// wage calculation
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";
}
}
Die Gehaltsberechnung kann fehlschlagen. Dies wäre beispielsweise der Fall, wenn die Verbindung zum DBMS ausfallen würde. In diesem Fall behandeln wir die auftretende Ausnahme. Wir sind diesem Szenario bereits bei der Betrachtung der Methode [Form].input begegnet.
- Zeilen 7–11: Wir erstellen eine Liste von Error-Objekten aus dem Ausnahmestapel
- Zeile 13: Wir legen das Menü fest
- Zeile 14: Wir setzen den Ausnahmeschlüssel.
Der Ausnahmeschlüssel zeigt die Tiles-Ausnahmeansicht an:
<!-- action Formulaire -->
<action name="Formulaire" class="web.actions.Formulaire">
<result name="exception" type="tiles">exception</result>
...
</action>
Diese Tiles-Ansicht wurde bereits vorgestellt. Sie sieht wie folgt aus:

19.9. Simulation speichern
Nach dem Ausführen einer Simulation möchte der Benutzer diese möglicherweise in der Sitzung speichern.
![]() |
![]() |
- In [1] wird die Simulation gespeichert
- In [2] zeigt die Antwort die Liste der bereits durchgeführten Simulationen an, zu der die neue Simulation hinzugefügt wird
Der Link [Simulation speichern] befindet sich im 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>
Wir sehen, dass das Klicken auf den Link die Aktion [SaveSimulation] auslöst. Diese Aktion ist in der Datei [struts.xml] wie folgt konfiguriert:
<!-- action 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>
- Zeile 1: Die Aktion [SaveSimulation] ist mit der Klasse [Save] und deren Methode „execute“ verknüpft.
Die Klasse [Save] sieht wie folgt aus:
package web.actions;
...
public class Enregistrer extends ActionSupport implements SessionAware {
// session
private Map<String, Object> session;
// menu
private Menu menu;
@Override
public void setSession(Map<String, Object> session) {
this.session = session;
}
// action execution
public String execute() {
// retrieve the last simulation in the session
Simulation simulation = (Simulation) session.get("simulation");
if (simulation == null) {
return ERROR;
}
...
}
// getters and setters
...
}
- Zeile 4: Da die Aktion Zugriff auf die Sitzung benötigt, implementiert sie die Schnittstelle „SessionAware“.
- Zeile 7: Die Sitzung
- Zeile 9: das Menü
Wenn die Aktion [Save] instanziiert wird, wird ihre Methode execute aufgerufen. Zur Erinnerung: Ihre Aufgabe ist es, die neueste Simulation in die Sitzung einzufügen. Diese Simulation wird der Liste der abgeschlossenen Simulationen hinzugefügt, die ebenfalls in der Sitzung gespeichert ist.
- Zeile 19: Wir rufen die letzte in der Sitzung gespeicherte Simulation ab.
- Zeilen 20–22: Wenn sie nicht gefunden wird, ist die Sitzung wahrscheinlich abgelaufen. Tatsächlich dauert die Sitzung nur eine bestimmte Zeit, die in der Datei [web.xml] festgelegt werden kann, die die Anwendung konfiguriert.
- Zeile 21: Wir geben den Fehlerschlüssel zurück.
Zurück zur Konfiguration der Aktion [SaveSimulation]:
<!-- action 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>
Wir sehen, dass der Schlüssel „error“ (Zeile 3) die Anzeige der Tiles-Ansicht mit dem Namen „error“ auslöst. Diese Ansicht setzt sich aus den Fragmenten [Entete.jsp] und [Erreur.jsp] zusammen und sieht wie folgt aus:

Das Fragment [Erreur.jsp] sieht wie folgt aus:
<%@ 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>
Zurück zur Methode [Save].execute:
// action execution
public String execute() {
// retrieve the last simulation in the session
Simulation simulation = (Simulation) session.get("simulation");
if (simulation == null) {
return ERROR;
}
// retrieve the number of the last simulation
Integer numDerniereSimulation = (Integer) session.get("numDerniereSimulation");
if (numDerniereSimulation == null) {
numDerniereSimulation = 0;
}
// increment it
numDerniereSimulation++;
// we give it the new number in the session
session.put("numDerniereSimulation", numDerniereSimulation);
// retrieve the list of simulations
List<Simulation> simulations = (List<Simulation>) session.get("simulations");
if (simulations == null) {
simulations = new ArrayList<Simulation>();
session.put("simulations", simulations);
}
// we add the current simulation
simulation.setNum(numDerniereSimulation);
simulations.add(simulation);
// the list of simulations is displayed
menu = new Menu(false, false, false, false, true, true);
return "simulations";
}
- Zeilen 9–16: Die verschiedenen Simulationen werden beginnend mit 1 nummeriert. Die zuletzt zugewiesene Nummer wird in der Sitzung unter dem Schlüssel numDerniereSimulation gespeichert. Der Code in den Zeilen 9–16 ruft diesen Schlüssel ab und erhöht den damit verbundenen Wert.
- Zeilen 18–22: Die Liste der Simulationen wird in der Sitzung unter dem Schlüssel simulations gespeichert. Die Zeilen 18–22 rufen diese Liste ab, falls sie existiert, oder erstellen sie, falls sie nicht existiert.
- Zeilen 24–25: Sobald die Liste der Simulationen vorliegt, wird die aktuelle Simulation hinzugefügt (Zeile 25). Zuvor wurde der aktuellen Simulation eine Nummer zugewiesen (Zeile 24).
- Zeile 27: Das anzuzeigende Menü wird festgelegt
- Zeile 28: Wir geben den Navigationsschlüssel „simulations“ zurück.
Zurück zur Konfiguration der Aktion [EnregistrerSimulation] in [struts.xml]:
<!-- action 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>
Zeile 4: Der Schlüssel „simulations“ löst die Anzeige der Tiles-Ansicht mit dem Namen „simulations“ aus. Diese Ansicht besteht aus den Fragmenten [Entete.jsp] und [Simulations.jsp]. Die angezeigte Ansicht sieht wie folgt aus:
![]() |
- in [1] das Fragment [Entete.jsp], das wir inzwischen kennen.
- unter [2] das Fragment [Simulations.jsp]
Das Fragment [Simulations.jsp] sieht wie folgt aus:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!-- empty simulation list -->
<s:if test="#session['simulations']==null || #session['simulations'].size()==0">
<h2><s:text name="Pam.SimulationsVides.titre"/></h2>
</s:if>
<!-- non-empty simulation list -->
<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"/>">Retirer</a></td>
</tr>
</s:iterator>
</table>
</s:if>
- Zeilen 5–7: Wenn in der Sitzung keine Simulationen vorhanden sind, wird die folgende Ansicht angezeigt:

- Zeilen 13–21: Anzeige der Spaltenüberschriften der Tabelle
![]()
- Zeilen 23–55: Durchlaufen der Liste der in der Sitzung gefundenen Simulationen
- Zeilen 24–26: Erstellung einer URL mit dem Namen url (Attribut id). Der durch diese URL generierte HTML-Link lautet wie folgt:
<a href="<a href="view-source:http://localhost:8084/pam/SupprimerSimulation.action?id=1">/pam/SupprimerSimulation.action?id=1</a>">Retirer</a>
Wir sehen, dass der Link auf die Aktion [DeleteSimulation] mit dem Parameter „id“ verweist, der die Nummer der Simulation angibt, die aus der Liste entfernt werden soll.
- Zeilen 28–54: Bei jedem Durchlauf durch die Liste der Simulationen werden die Eigenschaften der aktuellen Simulation angezeigt.

19.10. Eine Simulation entfernen
Der Benutzer möchte möglicherweise eine Simulation aus der Liste der Simulationen entfernen:
![]() |
![]() |
- In [1] wurde Simulation Nr. 1 entfernt
- in [2] wurde Simulation Nr. 1 entfernt
Der Link [Entfernen] befindet sich in dem Fragment [Simulations.jsp], das wir bereits untersucht haben:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<!-- empty simulation list -->
<s:if test="#session['simulations']==null || #session['simulations'].size()==0">
<h2><s:text name="Pam.SimulationsVides.titre"/></h2>
</s:if>
<!-- non-empty simulation list -->
<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"/>">Retirer</a></td>
</tr>
</s:iterator>
</table>
</s:if>
- Zeilen 16–18: Erstellen des HTML-Links
<a href="<a href="view-source:http://localhost:8084/pam-01/SupprimerSimulation.action?id=2">/pam-01/SupprimerSimulation.action?id=</a>num">Retirer</a>
wobei num die Nummer der zu löschenden Simulation ist.
Die Aktion [DeleteSimulation] ist in der Datei [struts.xml] wie folgt definiert:
<?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>
<!-- internationalization -->
<constant name="struts.custom.i18n.resources" value="messages" />
<!-- spring integration -->
<constant name="struts.objectFactory.spring.autoWire" value="name" />
<!-- struts /Tiles actions -->
<package name="default" namespace="/" extends="tiles-default">
...
<!-- action RetirerSimulation -->
<action name="SupprimerSimulation" class="web.actions.Supprimer">
<result name="erreur" type="tiles">erreur</result>
<result name="simulations" type="tiles">simulations</result>
</action>
...
</package>
<!-- Add packages here -->
</struts>
- Zeile 16: Die Aktion [SupprimerSimulation] ist der Klasse [Supprimer] zugeordnet. Da keine Methode angegeben ist, wird deren Methode „execute“ ausgeführt. Die Klasse [Supprimer] sieht wie folgt aus:
package web.actions;
...
public class Supprimer extends ActionSupport implements SessionAware {
// session
private Map<String, Object> session;
// id of simulation to be deleted
private String id;
// menu
private Menu menu;
@Override
public void setSession(Map<String, Object> session) {
this.session = session;
}
// action execution
public String execute() {
// simulations are retrieved from the
List<Simulation> simulations = (List<Simulation>) session.get("simulations");
if (simulations == null) {
// abnormal case - session must have expired
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) {
// abnormal
erreur = true;
}
// mistake?
if (erreur) {
menu = new Menu(false, false, false, false, true, true);
return "erreur";
}
// search for the simulation to be deleted
for (int i = 0; i < simulations.size(); i++) {
if (num == simulations.get(i).getNum()) {
simulations.remove(i);
break;
}
}
// the list of simulations is displayed
menu = new Menu(false, false, false, false, true, true);
return "simulations";
}
// getters and setters
...
}
- Zeile 4: Die Aktion [Delete] implementiert die Schnittstelle [SessionAware], um auf die Sitzung zuzugreifen.
- Zeile 7: Die Session
- Zeile 9: Die Nummer der zu löschenden Simulation. Zur Erinnerung: Wir instanziieren die Klasse [Delete] über die HTML-URL:
<a href="<a href="view-source:http://localhost:8084/pam-01/SupprimerSimulation.action?id=2">/pam-01/SupprimerSimulation.action?id=</a>num">Retirer</a>
wobei num die Nummer der zu löschenden Simulation ist. Diese Nummer wird im Feld id in Zeile 9 gespeichert.
- Zeile 11: Das Menü für die Ansicht, das als Antwort auf die Anfrage angezeigt wird
- Zeile 19: Die Methode `execute`, die die Antwort auf die Anfrage generiert.
- Zeile 21: Wir rufen die Liste der in der Sitzung bereits durchgeführten Simulationen ab
- Zeilen 22–26: Wenn diese Liste nicht aus der Sitzung abgerufen werden kann, ist die Sitzung wahrscheinlich abgelaufen. Dieses Szenario ist uns bereits bekannt. Wir geben den Fehler-Schlüssel zurück, der die Tiles-Fehleransicht anzeigt:
<!-- action RetirerSimulation -->
<action name="SupprimerSimulation" class="web.actions.Supprimer">
<result name="erreur" type="tiles">erreur</result>
...
</action>
Die Fehleransicht von Tiles wurde in Abschnitt 19.9 vorgestellt.
- Zeilen 28–36: Wir überprüfen, ob die ID-Zeichenkette in Zeile 9 tatsächlich eine ganze Zahl größer als 0 ist.
- Zeilen 38–40: Ist dies nicht der Fall, wird der Fehler-Schlüssel erneut zurückgegeben, wodurch die Fehleransicht „Tiles“ angezeigt wird
- Zeilen 43–48: Die zu entfernende Simulation wird in der Liste der Simulationen gesucht. Wird sie gefunden, wird sie gelöscht.
- Zeile 50: Das Menü für die Ansicht „Tiles-Simulationen“ wird aktualisiert.
- Zeile 51: Der Schlüssel `simulations` wird zurückgegeben. Dadurch wird die Ansicht „Tiles-Simulationen“ angezeigt:
<!-- action RetirerSimulation -->
<action name="SupprimerSimulation" class="web.actions.Supprimer">
...
<result name="simulations" type="tiles">simulations</result>
</action>
Die Ansicht „Simulationskacheln“ wurde in Abschnitt 19.9 vorgestellt.
19.11. Zurück zum Formular
Von der Ansicht „Simulationskacheln“ aus kann der Benutzer zum Formular zurückkehren:
![]() |
![]() |
- Klicken Sie in [1] auf den Link, um zum Formular zurückzukehren
- in [2] erscheint ein leeres Formular
Der Link [Zurück zum Simulationsformular] ist im Fragment [Entete.jsp] definiert:
<%@ 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>
- Zeile 10: Der Link verweist auf die Aktion [ReturnForm]. Diese ist in der Datei [struts.xml] wie folgt definiert:
<!-- action RetourFormulaire -->
<action name="RetourFormulaire" >
<result type="redirectAction">
<param name="actionName">Formulaire!input</param>
<param name="namespace">/</param>
</result>
</action>
Wir sehen, dass diese Aktion keiner Klasse zugeordnet ist. Sie leitet den Browser des Clients lediglich zur Aktion [/Form!input] weiter. Wir befinden uns daher in derselben Situation wie bei der Anzeige der in Abschnitt 19.7 erläuterten Startansicht. Wir kehren somit zu dieser Startansicht [2] zurück.
19.12. Liste der Simulationen anzeigen
Über die Ansichten „Simulationskacheln“ oder „Eingabe“ kann der Benutzer die Anzeige der Simulationen anfordern:
![]() |
![]() |
- Klicken Sie in [1] auf den Link [Simulationen anzeigen]
- In [2] wird die Liste der Simulationen angezeigt
Der Link [Simulationen anzeigen] ist im Fragment [Entete.jsp] definiert:
<%@ 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>
- Zeile 10: Der Link [Simulationen anzeigen] ruft die Aktion [ViewSimulations] auf. Diese ist in der Datei [struts.xml] wie folgt definiert:
<!-- action VoirSimulations -->
<action name="VoirSimulations" class="web.actions.Voir">
<result name="success" type="tiles">simulations</result>
</action>
Die Aktion [ViewSimulations] ist der Klasse [View] zugeordnet, ohne dass eine Methode angegeben wird. Daher wird die Methode [View].execute ausgeführt. Die Klasse [View] sieht wie folgt aus:
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 and setters
public Menu getMenu() {
return menu;
}
public void setMenu(Menu menu) {
this.menu = menu;
}
}
Die Aktion [View] führt nur eine einzige Aufgabe aus: Sie legt das Menü für die Tiles-Simulationsansicht fest (Zeile 8). Es gibt keine execute-Methode. Daher wird diejenige aus der übergeordneten Klasse [ActionSupport] ausgeführt. Wir wissen, dass diese nichts anderes tut, als das Erfolgsflag zurückzugeben.
Zurück zur Aktion in [struts.xml]:
<!-- action VoirSimulations -->
<action name="VoirSimulations" class="web.actions.Voir">
<result name="success" type="tiles">simulations</result>
</action>
In Zeile 3 sehen wir, dass der Schlüssel „success“ die Anzeige der Tiles-Simulationsansicht auslöst. Dies wurde auf Seite 157 beschrieben.
19.13. Aktuelle Simulation löschen
In der Ansicht „Tiles-Simulation“ kann der Benutzer das Löschen der aktuellen Simulation anfordern:
![]() |
![]() |
- in [1] wird die aktuelle Simulation gelöscht
- in [2] wird das Eingabeformular gelöscht
Der Link [Simulation löschen] ist im Fragment [Entete.jsp] definiert:
<%@ 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>
In Zeile 10 sehen wir, dass der Link [Clear Simulation] die Aktion [Form!input] auslöst. Wir wissen, dass diese Aktion zur Startansicht [2] führt.
19.14. Beenden Sie die aktuelle Sitzung
Von jeder Kachelansicht aus kann der Benutzer das Beenden der Sitzung anfordern:
![]() |
![]() |
![]() |
- In [1] beginnen wir in der Simulationsansicht und beenden die Sitzung
- In [2] ist das Eingabeformular leer. Wir fordern die Anzeige der Simulationen an.
- In [3] ist die Liste der Simulationen nun leer.
Der Link [Sitzung beenden] ist im Fragment [Entete.jsp] definiert:
<%@ 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>
In Zeile 10 sehen wir, dass der Link [End Session] die Aktion [EndSession] auslöst. Diese ist in der Datei [struts.xml] wie folgt definiert:
<action name="TerminerSession" class="web.actions.Terminer">
<result name="success" type="redirectAction">
<param name="actionName">Formulaire!input</param>
<param name="namespace">/</param>
</result>
</action>
- Zeile 1: Wir sehen, dass die Klasse [Terminer] instanziiert und ihre Methode „execute“ aufgerufen wird.
- Zeilen 2–5: Nach Ausführung der Methode [Terminer].execute wird der Benutzer zur ursprünglichen Eingabemaske weitergeleitet. Dies erklärt Bildschirm 2.
Die Klasse [Terminer] sieht wie folgt aus:
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 {
// session
private Map<String, Object> session;
@Override
public String execute() {
// quit current session
session.clear();
return SUCCESS;
}
@Override
public void setSession(Map<String, Object> session) {
this.session = session;
}
}
Die Aktion [Finish] dient dazu, die Attribute der aktuellen Sitzung zu löschen.
- Zeile 7: Die Aktion [Terminate] implementiert die Schnittstelle [SessionAware], um auf die Sitzung zuzugreifen.
- Zeile 10: Das Sitzungswörterbuch
- Zeile 13: Die Methode „execute“ wird aufgerufen
- Zeile 15: Das Sitzungswörterbuch wird gelöscht. Dadurch verschwindet die Liste der derzeit in der Sitzung befindlichen Simulationen. Dies erklärt Bildschirm Nr. 3.
- Zeile 16: Es wird der Schlüssel „success“ zurückgegeben, der, wie wir gesehen haben, die Kachelansicht mit dem Wert [2] anzeigt.
19.15. Fazit
Wir haben Version 1 unserer Fallstudie, die mit einer simulierten [Business]-Schicht arbeitet, vollständig kommentiert:
![]() |
Nun muss nur noch die echte Geschäftsschicht mit der [Web]-Schicht „verbunden“ werden.






















