XIX. Etude de cas - version 1▲
XIX-A. La couche [metier] simulée▲
Revenons sur l'architecture de l'application que nous construisons :
Nous disposons de l'archive des couches [metier, dao, jpa] et nous avons présenté les éléments de ces couches que la couche [web] devait connaître. Nous sommes prêts pour écrire celle-ci à l'aide du framework Struts.
Pour simplifier les tests de notre application en cours de développement, nous allons créer une couche métier simulée qui respectera l'interface de la couche [metier]. L'architecture va devenir la suivante :
Nous allons développer la couche [web] avec la couche [métier] simulée. Les tests seront plus simples à faire car il n'y a plus de base de données dans l'architecture. Grâce à Spring et à l'utilisation d'interfaces, remplacer ultérieurement la couche [metier] simulée par la véritable architecture [metier, dao, jpa] aura un impact nul sur le code de la couche [web / struts2]. La couche [web / struts2] que nous allons développer maintenant pourra être utilisée telle quelle.
La couche [metier] simulée que nous allons utiliser est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
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 {
//
liste
des
employes
private
Map<
String, Employe>
hashEmployes =
new
HashMap<
String, Employe>
(
);
private
List<
Employe>
listEmployes;
//
obtenir
la
feuille
de
salaire
public
FeuilleSalaire calculerFeuilleSalaire
(
String SS,
double
nbHeuresTravaillées, int
nbJoursTravaillés) {
//
on
récupère
l'employé
de
n°
SS
Employe e =
hashEmployes.get
(
SS);
//
on
rend
une
feuille
de
salaire
fictive
return
new
FeuilleSalaire
(
e, new
Cotisation
(
3
.49
, 6
.15
, 9
.39
, 7
.88
), new
ElementsSalaire
(
100
, 100
, 100
, 100
, 100
));
}
//
liste
des
employés
public
List<
Employe>
findAllEmployes
(
) {
if
(
listEmployes =
=
null
) {
//
on
crée
une
liste
de
deux
employés
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
)));
//
dictionnaire
des
employes
for
(
Employe e : listEmployes) {
hashEmployes.put
(
e.getSS
(
), e);
}
}
//
on
rend
la
liste
des
employés
return
listEmployes;
}
}
- ligne 11 : la classe [MetierSimule] implémente l'interface [IMetier] que la véritable couche [metier] implémente.
- ligne 14 : un dictionnaire des employés indexés par leur n° INSEE
- ligne15 : la liste des employes
- lignes 27-39 : implémentation de la méthode findAllEmployes de l'interface [IMetier].
- lignes 30-33 : création d'une liste de deux employés
- lignes 34-36 : création du dictionnaire des employés indexé par le n° INSEE
- lignes 18-24 : implémentation de la méthode calculerSalaire de l'interface [IMetier]. Ici on rend une feuille de salaire fictive.
XIX-B. Le projet Netbeans▲
Le projet Netbeans est le suivant :
- en [1] :
- [applicationContext.xml] est le fichier de configuration de Spring
- [tiles.xml] est le fichier de configuration d'un framework appelé Tiles.
- [web.xml] est le fichier de configuration de l'application web
- en [2] : les différentes vues de l'application
- en [3] :
- [messages.properties] : le fichier des messages
- [struts.xml] : le fichier de configuration de Struts
- en [4] : les codes source de l'application. Les actions Struts sont dans le package [web.actions].
- en [5] : la couche [metier] simulée
- en [6] : les archives utilisées. On trouve les archives des différents outils utilisés : Spring, Tiles, Struts 2, le plugin d'intégration Struts 2 / Spring, le plugin d'intégration Struts 2 / Tiles.
- en [7] : l'archive de la couche [metier, dao, jpa] véritable. Elle nous permet d'avoir accès aux entités Jpa, à l'interface [IMetier], aux classes [FeuilleSalaire] et [ElementsSalaire]. Tous ces éléments sont en effet utilisés par notre classe [MetierSimule].
XIX-C. Configuration du projet▲
Le projet est configuré par différents fichiers :
- [web.xml] qui configure l'application web
- [struts.xml] qui configure le framework Struts
- [applicationContext.xml] qui configure le framework Spring
- [tiles.xml] qui configure le framework Tiles
XIX-C-1. Configuration de l'application web▲
Le fichier [web.xml] est le suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
<?
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
>
- lignes 13-20 : configurent le filtre Struts 2 - déjà vu
- lignes 22-24 : configurent le listener de Spring - déjà vu
- lignes 9-11 : configurent le listener Tiles. La classe [org.apache.struts2.tiles.StrutsTilesListener] va être instanciée au démarrage de l'application web. Elle va alors exploiter son fichier de configuration. Celui-ci est défini par les lignes 5-8. Le fichier de configuration de Tiles est donc le fichier [WEB-INF/tiles.xml].
Au final, au démarrage de l'application Struts, trois classes sont instanciées :
- l'une pour le filtre Struts 2. C'est elle qui assure le C de MVC.
- une autre pour le listener Spring. Spring va exploiter le fichier [applicationContext.xml] pour instancier les couches [métier, dao, jpa] de l'application. Spring va également instancier, comme dans un exemple précédent, une classe [Config] qui contiendra les données de portée Application. Enfin Spring injectera dans chaque action Struts qui en a besoin, une référence sur cette unique instance [Config].
- une autre pour le listener Tiles. Ce framework va assurer la gestion des vues. Nous y reviendrons bientôt.
XIX-C-2. Configuration du framework Struts▲
Le framework Struts est configuré par le fichier [struts.xml] suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
<?
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
>
<!--
internationalisation
-->
<
constant
name
=
"
struts.custom.i18n.resources
"
value
=
"
messages
"
/
>
<!--
intégration
Spring
-->
<
constant
name
=
"
struts.objectFactory.spring.autoWire
"
value
=
"
name
"
/
>
<!--
actions
Struts
/Tiles
-->
<
package
name
=
"
default
"
namespace
=
"
/
"
extends
=
"
tiles-default
"
>
<!--
action
par
défaut
-->
<
default-action-ref
name
=
"
index
"
/
>
<
action
name
=
"
index
"
>
<
result
type
=
"
redirectAction
"
>
<
param
name
=
"
actionName
"
>
Formulaire<
/
param
>
<
param
name
=
"
namespace
"
>
/<
/
param
>
<
/
result
>
<
/
action
>
<!--
action
Formulaire
-->
<
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
>
Nous commenterons les différentes actions Struts au fur et à mesure où nous les étudierons. Pour l'instant, on peut noter les points suivants :
- ligne 8 : définit le fichier des messages
- ligne 10 : définit le mode d'injection des beans Spring dans les actions Struts. L'injection se fait selon le nom du bean. Il faut que le champ de l'action Struts qui doit être initialisé par Spring porte le nom du bean à injecter.
- ligne 25 : définit la vue à afficher pour la clé de navigation success de l'action [Formulaire]. On voit que le résultat <result> a un attribut type='tiles' que nous ne connaissons pas. Nous connaissions le type redirect qui permet de rediriger le client vers une vue. Ici la vue de type tiles est gérée par le framework Tiles . Le type tiles est défini dans le fichier [struts-plugin.xml] de l'archive [struts2-tiles-plugin-2.2.3.1.jar] :
2.
3.
4.
5.
6.
7.
<
struts
>
<
package
name
=
"
tiles-default
"
extends
=
"
struts-default
"
>
<
result-types
>
<
result-type
name
=
"
tiles
"
class
=
"
org.apache.struts2.views.tiles.TilesResult
"
/
>
<
/
result-types
>
<
/
package
>
<
/
struts
>
- lignes 3-5 : la définition du type de résultat tiles .
- ligne 2 : ce type est défini dans le package [tiles-default] qui étend le package [struts-default].
ligne 14 : définit le package [default] dans lequel seront toutes les actions de l'application. Pour profiter de la définition du type de vue tiles , le package étend [tiles-default].
XIX-C-3. Configuration du framework Spring▲
Le framework Spring est configuré par le fichier [WEB-INF/applicationContext.xml] suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
<?
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
"
>
<!--
couches
applicatives
-->
<!--
web
-->
<
bean
id
=
"
config
"
class
=
"
web.Config
"
init-method
=
"
init
"
>
<
property
name
=
"
metier
"
ref
=
"
metier
"
/
>
<
/
bean
>
<!--
métier
-->
<
bean
id
=
"
metier
"
class
=
"
metier.MetierSimule
"
/
>
<
/
beans
>
- ligne 13 : la couche [metier] simulée instanciée par la classe [metier.MetierSimule]
- lignes 9-11 : configurent un bean nommé config . Comme dans un exemple précédemment étudié, ce bean servira à encapsuler les informations de portée Application . La classe associée à ce bean est la classe [Config] suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
package
web;
import
java.util.List;
import
jpa.Employe;
import
metier.IMetier;
public
class
Config {
//
couche
métier
initialisée
par
Spring
private
IMetier metier;
//
liste
des
employés
private
List<
Employe>
employes;
//
erreurs
private
Exception initException;
//
constructeur
public
Config
(
) {
}
//
méthode
Spring
d'initialisation
de
l'objet
public
void
init
(
) {
//
on
demande
la
liste
des
employés
try
{
employes =
metier.findAllEmployes
(
);
}
catch
(
Exception ex) {
initException =
ex;
}
}
//
getters
et
setters
...
}
Revenons à la configuration du bean config :
2.
3.
4.
5.
6.
7.
<
bean
id
=
"
config
"
class
=
"
web.Config
"
init-method
=
"
init
"
>
<
property
name
=
"
metier
"
ref
=
"
metier
"
/
>
<
/
bean
>
<!--
métier
-->
<
bean
id
=
"
metier
"
class
=
"
metier.Metier
"
>
...
<
/
bean
>
Ligne 2, on peut voir que le bean metier de la ligne 5, est injecté (ref) dans le champ nommé metier (name) de l'objet [Config]. Le bean metier est une référence sur la couche [metier] :
Pour dialoguer avec la couche [metier], toutes les actions Struts de la couche [web] auront besoin d'une référence sur celle-ci. On peut dire que la référence sur la couche [metier] est une donnée de portée Application. Toutes les requêtes de tous les utilisateurs en auront besoin. C'est pourquoi, nous mettons cette référence dans l'objet [Config]. Par ailleurs, ligne 1, la configuration du bean config a un attribut init-method. Cet attribut désigne la méthode du bean à exécuter après instanciation du bean. Ici, on indique qu'après instanciation de la classe [web.Config], on doit exécuter sa méthode init. Celle-ci est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
//
couche
métier
initialisée
par
Spring
private
IMetier metier;
//
liste
des
employés
private
List<
Employe>
employes;
//
erreurs
private
Exception initException;
//
constructeur
public
Config
(
) {
}
//
méthode
Spring
d'initialisation
de
l'objet
public
void
init
(
) {
//
on
demande
la
liste
des
employés
try
{
employes =
metier.findAllEmployes
(
);
}
catch
(
Exception ex) {
initException =
ex;
}
}
Lorsque la méthode init est exécutée, le champ metier de la classe a été instancié par Spring. La méthode init a donc accès à la couche metier d'interface [IMetier] (ligne 2) :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
package
metier;
import
java.util.List;
import
jpa.Employe;
public
interface
IMetier {
//
obtenir
la
feuille
de
salaire
FeuilleSalaire calculerFeuilleSalaire
(
String SS, double
nbHeuresTravaillées, int
nbJoursTravaillés );
//
liste
des
employés
List<
Employe>
findAllEmployes
(
);
}
- ligne 8 : la méthode permet de calculer le salaire d'un employé
- ligne 10 : la méthode permet d'obtenir la liste des employés
On voit que la méthode init demande la liste des employés à la couche [métier]. Cette liste est enregistrée dans le champ de la ligne 4. S'il se produit une exception, celle-ci est mémorisée dans le champ de la ligne 6.
Pour conclure, l'objet unique [Config] contient :
- une référence sur la couche [métier]
- la liste des employés
XIX-D. Génération des vues Tiles▲
On l'a vu dans le fichier de configuration de Struts, les vues vont être générées par le framework Tiles. Nous n'allons expliquer que le strict nécessaire utile à l'écriture de notre application.
Tiles permet de générer des vues à partir d'une page maître. Celle-ci appelée ici [MasterPage.jsp] sera l'assemblage des fragments Jsp suivants :
Entete.jsp | |
Saisie.jsp | |
Simulation.jsp | |
Simulations.jsp | |
Exception.jsp | |
Erreur.jsp |
Ces framents Jsp sont définis dans le projet Netbeans :
Le framework Tiles nous permet de définir quels fragments seront insérés dans la page maître.
La page maître [MasterPage.jsp] est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
<%@
page
contentType
=
"
text
/
html
;
charset
=
UTF
-
8
"
pageEncoding
=
"
UTF
-
8
"
%>
<%@
taglib
prefix
=
"
s
"
uri
=
"
/
struts
-
tags
"
%>
<%@
taglib
uri
=
"
http
:
/
/
tiles
.
apache
.
org
/
tags
-
tiles
"
prefix
=
"
tiles
"
%>
<!
DOCTYPE
HTML
PUBLIC
"
-//W3C//DTD
HTML
4.01
Transitional//EN
"
"
http://www.w3.org/TR/html4/loose.dtd
"
>
<
html
>
<
head
>
<
meta
http
-
equiv
=
"
Content
-
Type
"
content
=
"
text
/
html
;
charset
=
UTF
-
8
"
>
<
link
href
=
"
styles
.
css
"
rel
=
"
stylesheet
"
type
=
"
text
/
css
"
/
>
<
title
>
<
tiles
:
insertAttribute
name
=
"
titre
"
ignore
=
"
true
"
/
>
<
/
title
>
<
s
:
head
/
>
<
/
head
>
<
body
background
=
"
<
s
:
url
value
=
"
/
ressources
/
standard
.
jpg
"
/
>
"
>
<
tiles
:
insertAttribute
name
=
"
entete
"
/
>
<
hr
/
>
<
tiles
:
insertAttribute
name
=
"
saisie
"
/
>
<
tiles
:
insertAttribute
name
=
"
simulation
"
/
>
<
tiles
:
insertAttribute
name
=
"
exception
"
/
>
<
tiles
:
insertAttribute
name
=
"
erreur
"
/
>
<
tiles
:
insertAttribute
name
=
"
simulations
"
/
>
<
/
body
>
<
/
html
>
La page maître est un conteneur de fragments Jsp. Ici, elle est l'assemblage de six fragments, ceux des lignes 17 à 23. A la génération, il peut y avoir de 0 à 6 fragments assemblés dans la page maître. Cette génération est gouvernée par le fichier [WEB-INF/tiles.xml] qui définit les vues Tiles :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
<?
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
>
<!--
la
page
maître
-->
<
definition
name
=
"
masterPage
"
template
=
"
/MasterPage.jsp
"
>
<
put-attribute
name
=
"
entete
"
value
=
"
/Entete.jsp
"
/
>
<
put-attribute
name
=
"
titre
"
value
=
"
Pam
"
/
>
<
put-attribute
name
=
"
saisie
"
value
=
"
"
/
>
<
put-attribute
name
=
"
simulation
"
value
=
"
"
/
>
<
put-attribute
name
=
"
simulations
"
value
=
"
"
/
>
<
put-attribute
name
=
"
exception
"
value
=
"
"
/
>
<
put-attribute
name
=
"
erreur
"
value
=
"
"
/
>
<
/
definition
>
<!--
la
vue
saisie
-->
<
definition
name
=
"
saisie
"
extends
=
"
masterPage
"
>
<
put-attribute
name
=
"
saisie
"
value
=
"
/Saisie.jsp
"
/
>
<
/
definition
>
<!--
la
vue
simulation
-->
<
definition
name
=
"
simulation
"
extends
=
"
saisie
"
>
<
put-attribute
name
=
"
simulation
"
value
=
"
/Simulation.jsp
"
/
>
<
/
definition
>
<!--
la
vue
simulations
-->
<
definition
name
=
"
simulations
"
extends
=
"
masterPage
"
>
<
put-attribute
name
=
"
simulations
"
value
=
"
/Simulations.jsp
"
/
>
<
/
definition
>
<!--
la
vue
exception
-->
<
definition
name
=
"
exception
"
extends
=
"
masterPage
"
>
<
put-attribute
name
=
"
exception
"
value
=
"
/Exception.jsp
"
/
>
<
/
definition
>
<!--
la
vue
erreur
-->
<
definition
name
=
"
erreur
"
extends
=
"
masterPage
"
>
<
put-attribute
name
=
"
erreur
"
value
=
"
/Erreur.jsp
"
/
>
<
/
definition
>
<
/
tiles-definitions
>
- le fichier ci-dessus définit six vues Tiles nommées : masterPage (ligne 9), saisie (ligne 20), simulation (ligne 25), simulations (ligne 30), exception (ligne 35), erreur (ligne 40).
- lignes 9-17 : définissent une vue appelée masterPage (name) et associée à la page maître [MasterPage.jsp] (template). On a vu que cette page Jsp définissait six sous-vues. Une vue Tiles associée à la page maître doit indiquer le fragment Jsp associé à chacune des six sous-vues. On voit que certaines sous-vues reçoivent pour valeur (value) la chaîne vide. Ces sous-vues ne seront pas incluses dans la page maître [MasterPage.jsp]. La vue Tiles nommée masterPage est donc constituée du seul sous-fragment [Entete.jsp].
- lignes 20-22 : définissent une vue appelée saisie (name) et qui étend (extends) la vue nommée masterPage vue précédemment. Cela signifie qu'elle reprend toutes les définitions de la vue masterPage. Sa définition est équivalente à la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
<
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
>
On voit qu'elle est associée à la page Jsp [MasterPage.jsp] et qu'à ce titre elle doit définir les six sous-vues de cette page. On voit que la déinition de la ligne 9 annule celle de la ligne 4. La vue Tiles nommée saisie est donc constituée des fragments Jsp [Entete.jsp, Saisie.jsp]
Si nous continuons ce raisonnement, nous obtenons le tableau suivant :
vue Tiles | pages Jsp |
masterPage | Entete.jsp |
saisie | Entete.jsp, Saisie.jsp |
simulation | Entete.jsp, Saisie.jsp, Simulation.jsp |
simulations | Entete.jsp, Simulations.jsp |
exception | Entete.jsp, Exception.jsp |
erreur | Entete.jsp, Erreur.jsp |
XIX-E. Les fichiers de messages▲
L'application a été internationalisée. On trouve les messages dans les fichiers [messages.properties] et [Formulaire.properties].
Le fichier [messages.properties] est le suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
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}".
Le fichier [Formulaire.properties] est le suivant :
2.
3.
4.
5.
# 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
XIX-F. La feuille de style▲
Les vues Tiles utilisent la feuille de style [styles.css] suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
.
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
}
XIX-G. La vue initiale▲
Pour étudier l'application, nous allons la présenter à partir des différentes actions de l'utilisateur. Nous étudierons à chaque fois, l'action Struts qui exécute cette action et la vue Tiles qui est envoyée en réponse.
Dans [struts.xml] nous avons les actions suivantes :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
<!--
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
>
- lignes 2-8 : l'action par défaut de l'application est [Formulaire!input].
La classe [Formulaire] est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
package
web.actions;
...
public
class
Formulaire extends
ActionSupport implements
Preparable, SessionAware {
//
configuration
initialisée
par
Spring
private
Config config;
//
liste
des
employés
private
List<
Employe>
employes;
//
liste
des
erreurs
private
List<
Erreur>
erreurs;
//
feuille
de
salaire
private
FeuilleSalaire feuilleSalaire;
//
saisies
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
(
) {
....
}
//
calcul
du
salaire
public
String calculSalaire
(
) {
...
}
}
@
Override
public
void
validate
(
) {
...
}
@
Override
public
void
setSession
(
Map<
String, Object>
map) {
session =
map;
}
//
getters
et
setters
...
}
- ligne 4 : l'action [Formulaire] implémente l'interface Preparable. Celle-ci n'a qu'une méthode, la méthode prepare de la ligne 24. Cette méthode est exécutée une fois avant toute méthode de l'action. Elle sert généralement à initialiser le modèle de l'action.
Le modèle de l'action est formé des lignes 6-21 :
- ligne 7 : le champ config est initialisé par Spring comme il a été expliqué. Il donne accès aux données de portée application :
- une référence sur la couche [métier]
- une référence sur la liste des employés.
- une référence sur l'exception qui a pu éventuellement se produire lors de l'instanciation de l'objet [Config]
- ligne 9 : une liste des employés. Celle-ci va alimenter le combo des employés dans le fragment [Saisie.jsp].
- ligne 11 : une liste d'erreurs. Celle-ci va alimenter le fragment [Erreur.jsp].
- ligne 21 : la liste des options du menu du fragment [Entete.jsp]
En [1], les liens du menu affiché sont contrôlés par le champ menu de l'action [Formulaire].
La méthode prepare est exécutée avant la méthode input. C'est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
@
Override
public
void
prepare
(
) throws
Exception {
//
erreur
de
configuration
?
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
(
);
}
}
- ligne 4 : on récupère l'exception dans l'objet [Config] instancié par Spring
- ligne 5 : s'il y a eu exception dans l'instanciation de l'objet [Config] alors on initialise la liste d'erreurs de la ligne 11. La classe [Erreur] est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
package
web.entities;
import
java.io.Serializable;
public
class
Erreur implements
Serializable{
public
Erreur
(
) {
}
//
champs
private
String classe;
private
String message;
//
constructeur
public
Erreur
(
String classe, String message){
this
.setClasse
(
classe);
this
.message=
message;
}
//
getters
et
setters
...
}
La classe sert à mémoriser la pile des exceptions :
- ligne 11 : la classe de l'exception
- ligne 12 : le message de l'exception
Revenons à la méthode prepare :
- ligne 13 : la liste des employés de l'objet [Config] est mémorisée dans le champ employes de l'action.
Une fois la méthode prepare exécutée, la méthode input va l'être à son tour. C'est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
@
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
"
;
}
}
La méthode input se contente de positionner la liste des options de menu à afficher. La classe [Menu] est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
package
web.entities;
import
java.io.Serializable;
public
class
Menu implements
Serializable {
//
éléments
du
menu
private
boolean
faireSimulation;
private
boolean
effacerSimulation;
private
boolean
enregistrerSimulation;
private
boolean
voirSimulations;
private
boolean
retourFormulaire;
private
boolean
terminerSession;
public
Menu
(
) {
}
public
Menu
(
boolean
faireSimulation, boolean
effacerSimulation, boolean
enregistrerSimulation, boolean
voirSimulations, boolean
retourFormulaire, boolean
terminerSession) {
this
.faireSimulation =
faireSimulation;
this
.effacerSimulation =
effacerSimulation;
this
.enregistrerSimulation =
enregistrerSimulation;
this
.voirSimulations =
voirSimulations;
this
.retourFormulaire =
retourFormulaire;
this
.terminerSession =
terminerSession;
}
//
getters
et
setters
...
}
- lignes 8-13 : il y a 6 liens possibles dans le menu
- lignes 18-25 : le constructeur de la classe permet de fixer les liens qui doivent être affichés et ceux qui ne le doivent pas.
Les liens du menu sont affichés dans [Entete.jsp], fragment jsp présent dans toutes les vues Tiles. Chaque action aura un champ menu pour contrôler l'affichage du menu de [Entete.jsp].
Revenons à la méthode input :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
@
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
"
;
}
}
- lignes 3-6 : si la liste des erreurs est vide, le menu [Faire la simulation, Voir les simulations, Terminer la session] sera affiché et la clé input retournée.
- lignes 9-10 : si la liste des erreurs n'est pas vide, le menu sera vide et la clé exception sera retournée.
Revenons à la configuration de l'action [Formulaire] dans [struts.xml] :
2.
3.
4.
5.
6.
7.
<!--
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
>
- ligne 5 : la clé input affiche la vue Tiles nommée saisie
- ligne 4 : la clé exception affiche la vue Tiles nommée exception
Commençons par la vue Tiles nommée saisie. Elle est composée des framents Jsp [Entete.jsp] et [Saisie.jsp].
Le fragment [Entete.jsp] est le suivant :
Son code est le suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
<%@
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
>
- lignes 8-25 : affichage des six liens du menu [Faire la simulation (lignes 8-10), Effacer la simulation (lignes 11-13), Voir les simulations (lignes 14-16), Retour au formulaire (lignes 17-19), Enregistrer la simulation (lignes 20-22), Terminer la session (lignes 23-25).
- lignes 8, 11, 14, 17, 20, 23 : l'affichage des liens est contrôlé par le champ menu de l'action courante.
On remarquera que le fragment [Entete.jsp] affiche une table Html (lignes 4-28) mais n'est pas une page Html complète. Il ne faut pas oublier ici que toutes les vues de l'application s'insèrent dans la page maître [MasterPage.jsp] suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
<%@
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
>
Le fragment [Entete.jsp] s'insère en ligne 17, à l'intérieur d'une page Html régulière.
Le fragment [Saisie.jsp] s'insère ligne 19. C'est la vue suivante :
Le code du fragment [Saisie.jsp] est le suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
<%@
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
(
){
//
on
poste
le
formulaire
document.
forms[
'
Saisie
'
]
.
elements[
'
action
'
]
.
name=
'
action:Formulaire!calculSalaire
'
document.
forms[
'
Saisie
'
]
.
submit
(
);
}
</script>
<!--
saisie
des
informations
-->
<
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
>
- ligne 17 : le formulaire n'a pas d'attribut action. Par défaut, on a action='Formulaire'.
- ligne 18 : affichage du combo des employés. Le contenu du combo (attribut list)est fourni par le champ employes de l'action courante. L'attribut value des options sera le n° SS des employés (attribut listKey). Le libellé affiché pour chaque option sera le prénom et le nom de l'employé (attribut listValue). Le n° SS de l'employé sélectionné dans le combo sera posté au champ [Formulaire].comboEmployesvalue (attribut name).
- ligne 19 : champ de saisie des heures travaillées. La valeur affichée (attribut value) est celle du champ heuresTravaillées de l'action [Formulaire] au format suivant (Formulaire.properties) :
La valeur sera postée au champ [Formulaire].heuresTravaillees (attribut name).
double.format= {0,number,#0.00##}
- ligne 20 : champ de saisie des jours travaillés. La valeur affichée (attribut value) est celle du champ joursTravaillés de l'action [Formulaire].
La valeur sera postée au champ [Formulaire].joursTravailles (attribut name).
Au final, la vue Tiles saisie affichée au démarrage lorsqu'il n'y a pas d'erreurs est la suivante :
Revenons à la configuration de l'action [Formulaire] :
2.
3.
4.
5.
6.
7.
<!--
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
>
On a vu que l'action [Formulaire].input pouvait également rendre la clé exception de la ligne 4. Dans ce cas, c'est la vue Tiles nommée exception qui est affichée. Celle-ci est composée des fragments [Entete.jsp] et [Exception.jsp]. Nous avons déjà présenté le fragment [Entete.jsp]. Le fragment [Exception.jsp] est le suivant :
Ceci est la page de démarrage de la version 2 de l'application lorsque le Sgbd n'a pas été lancé . Le code Jsp du fragment [Erreur.jsp] est le suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
<%@
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
>
- lignes 10-14 : un itérateur sur la collection List<Erreur> erreurs de l'action [Formulaire]. On se rappelle qu'en cas d'erreur on y avait stocké une pile d'exceptions.
XIX-H. Faire une simulation▲
Une fois la vue de démarrage obtenue, on peut faire un calcul de salaire via le lien [Faire une simulation].
XIX-H-1. Validation des saisies▲
Considérons la séquence suivante :
- en [1], une saisie erronée
- en [2], la réponse envoyée.
Considérons la configuration de l'action [Formulaire] dans [struts.xml] :
2.
3.
4.
5.
6.
7.
<!--
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
>
On sait qu'en cas d'erreur de validation, l'intercepteur de validation renvoie la clé input. C'est donc la vue Tiles saisie qui est renvoyée. Le processus de validation fait que les champs erronés sont accompagnés de messages d'erreur.
La validation de l'action [Formulaire] est assurée par le fichier [Formulaire-validation.xml] suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
<!
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
>
- les lignes 6-20 vérifient que le champ heuresTravaillees est un nombre réel dans l'intervalle [0,300].
- les lignes 22-36 vérifient que le champ joursTravailles est un nombre entier dans l'intervalle [0,31].
Revenons la configuration de l'action [Formulaire] dans [struts.xml] :
2.
3.
4.
5.
<!--
action
Formulaire
-->
<
action
name
=
"
Formulaire
"
class
=
"
web.actions.Formulaire
"
>
...
<
result
name
=
"
input
"
type
=
"
tiles
"
>
saisie<
/
result
>
<
/
action
>
On sait qu'en cas d'erreur de validation, l'intercepteur de validation renvoie la clé input. C'est donc la vue Tiles saisie qui est renvoyée.
Rappelons que celle-ci est composée des fragments [Entete.jsp] et [Saisie.jsp] où [Entete.jsp] comporte un titre et un choix d'options et [Saisie.jsp] le formulaire de saisie. En cas d'erreurs de saisie, le processus de validation fait que les champs erronés sont accompagnés de messages d'erreur et affichent de plus leur valeur erronée. Le fragment [Entete.jsp] n'intervient en rien dans le processus de validation. Regardons son code :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
<%@
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
>
Les six liens sont configurés par le champ menu du modèle (lignes 8,11, 14, 17, 20, 23). Lorsqu'il y a une erreur, ce modèle n'est pas mis à jour par l'action, et on retrouve alors une page sans menu. Pour remédier à ce problème, la classe [Formulaire] a la méthode validate suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
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
(
) {
...
}
//
calcul
du
salaire
public
String calculSalaire
(
) {
...
}
@
Override
public
void
validate
(
) {
//
des
erreurs
?
if
(
!
getFieldErrors
(
).isEmpty
(
)) {
//
menu
menu =
new
Menu
(
true
, false
, false
, true
, false
, true
);
}
}
//
getters
et
setters
...
}
- ligne 27 : on sait que lorsqu'elle est présente, la méthode validate est exécutée par le processus de validation. On en profite pour mettre à jour le menu de la ligne 4 qui fait partie du modèle du fragment [Entete.jsp].
- lignes 29-32 : s'il y a eu des erreurs de validation, alors on positionne le menu pour réafficher la vue Tiles saisie. S'il n'y a pas eu d'erreurs de validation, alors on ne fait rien. C'est la méthode calculSalaire qui est alors chargée de créer le modèle de la vue à afficher.
XIX-H-2. Le calcul du salaire▲
Revenons au code Jsp de l'entête :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
<%@
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
>
- ligne 9 : lorsque l'utilisateur clique sur le lien [Faire la simulation], la fonction Javascript doSimulation est exécutée. Celle-ci est défini dans le fragment [Saisie.jsp] :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
<
script
language
=
"
javascript
"
type
=
"
text/javascript
"
>
function doSimulation(){
// on poste le formulaire
document.forms['Saisie'].elements['action'].name='action:Formulaire!calculSalaire'
document.forms['Saisie'].submit();
}
<
/
script
>
<!--
saisie
des
informations
-->
<
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
>
- ligne 14 : un champ caché nommé action sera posté à l'action [Formulaire]. Ce champ va nous permettre de préciser l'action et la méthode qui doivent être exécutées au POST du formulaire. On se rappelle peut-être des premiers exemples, que celles-ci peuvent être précisées dans un paramètre nommé action:Action!méthode. Peu importe la valeur de ce paramètre. Il suffit qu'il soit présent.
- lignes 2-6 : la fonction Javascript qui est exécutée lorsque l'utilisateur clique sur le lien [Faire la simulation] du fragment [Entete.jsp].
- ligne 4 : on change l'attribut name du champ caché action. On fait en sorte qu'il soit de la forme action:Action!méthode attendue par Struts.
- ligne 5 : le formulaire nommé Saisie de la ligne 5 est posté. Du coup est postée la chaîne de paramètres suivante :
comboEmployesValue=SS1&heuresTravaillees=xx&joursTravailles=yy&action:Formulaire!calculSalaire
SS1 : n° INSEE de l'employé sélectionné dans le combo
heuresTravaillees : nombre d'heures travaillées
joursTravailles : nombre de jours travaillés
action:Formulaire!calculSalaire : les éléments ci-dessus seront postés à l'action [Formulaire] puis la méthode calculSalaire de cette action sera exécutée.
La méthode [Formulaire].calculSalaire est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
//
calcul
du
salaire
public
String calculSalaire
(
) {
try
{
//
calcul
salaire
feuilleSalaire =
config.getMetier
(
).calculerFeuilleSalaire
(
comboEmployesValue, heuresTravaillees, joursTravailles);
//
on
met
la
simulation
dans
la
session
session.put
(
"
simulation
"
, new
Simulation
(
0
, "
"
+
heuresTravaillees, "
"
+
joursTravailles, feuilleSalaire));
//
menu
menu =
new
Menu
(
true
, true
, true
, true
, false
, true
);
//
fini
return
"
simulation
"
;
}
catch
(
Throwable th) {
...
}
}
- ligne 5 : le calcul de la feuille de salaire est demandée à la couche [métier]
- ligne 7 : on met un objet de type Simulation dans la session de l'utilisateur. En effet, on peut en avoir besoin lors d'une requête ultérieure. La classe [Simulation] est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
package
web.entities;
import
java.io.Serializable;
import
metier.FeuilleSalaire;
public
class
Simulation implements
Serializable{
public
Simulation
(
) {
}
//
champs
d'une
simulation
private
Integer num;
private
FeuilleSalaire feuilleSalaire;
private
String heuresTravaillées;
private
String joursTravaillés;
//
constructeur
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
et
setters
...
}
- ligne 12 : le n° de la simulation. Est incrémenté à chaque nouvelle simulation enregistrée.
- ligne 13 : la feuille de salaire de l'employé
- ligne 14 : son nombre d'heures travaillées
- ligne 15 : son nombre de jours travaillés
- ligne 25 : la méthode getIndemnites rend le total des indmnités de l'employé
Nous verrons que la classe [Simulation] est le modèle du fragment [Simulations.jsp] qui présente toutes les simulations faites.
Retour à la méthode [Formulaire].calculSalaire :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
//
calcul
du
salaire
public
String calculSalaire
(
) {
try
{
//
calcul
salaire
feuilleSalaire =
config.getMetier
(
).calculerFeuilleSalaire
(
comboEmployesValue, heuresTravaillees, joursTravailles);
//
on
met
la
simulation
dans
la
session
session.put
(
"
simulation
"
, new
Simulation
(
0
, "
"
+
heuresTravaillees, "
"
+
joursTravailles, feuilleSalaire));
//
menu
menu =
new
Menu
(
true
, true
, true
, true
, false
, true
);
//
fini
return
"
simulation
"
;
}
catch
(
Throwable th) {
...
}
- ligne 9 : mise à jour du menu
- ligne 11 : renvoi de la clé de navigation simulation.
Retour à la configuration de l'action [Formulaire] :
2.
3.
4.
5.
<!--
action
Formulaire
-->
<
action
name
=
"
Formulaire
"
class
=
"
web.actions.Formulaire
"
>
...
<
result
name
=
"
simulation
"
type
=
"
tiles
"
>
simulation<
/
result
>
<
/
action
>
La ligne 4, montre que la clé de navigation simulation fait afficher la vue Tiles nommée simulation. Celle-ci est composée des fragments Jsp suivants : [Entete, Saisie, Simulation].
La vue rendue est la suivante :
- en [1], le fragment [Entete.jsp]
- en [2], le fragment [Saisie.jsp]
- en [3], le fragment [Simulation.jsp]. On rappelle que la feuille de salaire présentée est la feuille de salaire fictive rendue par la couche [metier].
Les deux premiers fragments ont déjà été présentés. Le fragment [Simulation.jsp] est le suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
193.
194.
195.
196.
197.
198.
199.
200.
201.
202.
203.
204.
205.
206.
207.
208.
209.
210.
211.
<%@
page
contentType
=
"
text
/
html
;
charset
=
UTF
-
8
"
pageEncoding
=
"
UTF
-
8
"
%>
<%@
taglib
prefix
=
"
s
"
uri
=
"
/
struts
-
tags
"
%>
<
hr
/
>
<!--
informations
Employé
-->
<
span
class
=
"
titreInfos
"
>
<
s
:
text
name
=
"
Simulation
.
Infos
.
employe
"
/
>
<
/
span
>
<
br
/
>
<
br
/
>
<
table
>
<!--
ligne
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
>
<!--
ligne
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
>
<!--
ligne
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
>
<!--
ligne
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
>
<!--
informations
Cotisations
-->
<
br
/
>
<
span
class
=
"
titreInfos
"
>
<
s
:
text
name
=
"
Simulation
.
Infos
.
cotisations
"
/
>
<
/
span
>
<
br
/
>
<
br
/
>
<
table
>
<!--
ligne
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
>
<!--
ligne
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
>
<!--
informations
Indemnités
-->
<
br
/
>
<
span
class
=
"
titreInfos
"
>
<
s
:
text
name
=
"
Form
.
Infos
.
indemnites
"
/
>
<
/
span
>
<
br
/
>
<
br
/
>
<
table
>
<!--
ligne
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
>
<!--
ligne
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
>
<!--
informations
Salaire
-->
<
br
/
>
<
span
class
=
"
titreInfos
"
>
<
s
:
text
name
=
"
Simulation
.
Infos
.
Salaire
"
/
>
<
/
span
>
<
br
/
>
<
br
/
>
<
table
>
<!--
ligne
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
>
<!--
ligne
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
>
<!--
Salaire
net
-->
<
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
>
C'est long ... mais c'est fonctionnellement simple. Ce fragment affiche les différentes propriétés du champ [Formulaire].feuilleSalaire qui représente la feuille de salaire de l'employé.
Retour à la méthode [Formulaire].calculSalaire :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
//
calcul
du
salaire
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
"
;
}
}
La calcul du salaire peut mal se passer. Ce serait le cas notamment si le lien avec le Sgbd se cassait. Dans ce cas, on gère l'exception qui se produit. Nous avons déjà rencontré ce cas lors de l'étude de la méthode [Formulaire].input.
- lignes 7-11 : on crée une liste d'objets Erreur à partir de la pile des exceptions
- ligne 13 : on fixe le menu
- ligne 14 : on rend la clé exception.
La clé exception va faire afficher la vue Tiles exception :
2.
3.
4.
5.
<!--
action
Formulaire
-->
<
action
name
=
"
Formulaire
"
class
=
"
web.actions.Formulaire
"
>
<
result
name
=
"
exception
"
type
=
"
tiles
"
>
exception<
/
result
>
...
<
/
action
>
Cette vue Tiles a déjà été présentée. Elle a l'allure suivante :
XIX-I. Enregistrer une simulation▲
Après avoir fait une simulation, l'utilisateur peut vouloir l'enregistrer dans la session.
- en [1], on enregistre la simulation
- en [2], la réponse qui présente la liste des simulations déjà faites à laquelle est ajoutée la nouvelle simulation
Le lien [Enregistrer la simulation] se trouve dans le fragment [Entete.jsp] :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
<%@
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
>
On voit qu'un clic sur le lien provoque l'exécution de l'action [EnregistrerSimulation]. Celle-ci est configurée dans le fichier [struts.xml] de la façon suivante :
2.
3.
4.
5.
<!--
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
>
- ligne 1 : l'action [EnregistrerSimulation] est associée à la classe [Enregistrer] et à sa méthode execute.
La classe [Enregistrer] est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
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;
}
//
exécution
de
l'action
public
String execute
(
) {
//
on
récupère
la
dernière
simulation
dans
la
session
Simulation simulation =
(
Simulation) session.get
(
"
simulation
"
);
if
(
simulation =
=
null
) {
return
ERROR;
}
...
}
//
getters
et
setters
...
}
- ligne 4 : parce que l'action doit avoir accès à la session, elle implémente l'interface SessionAware.
- ligne 7 : la session
- ligne 9 : le menu
Lorsque l'action [Enregistrer] a été instanciée, sa méthode execute est exécutée. On rappelle que son travail est de mettre dans la session la dernière simulation. Celle-ci sera ajoutée à la liste de simulations déjà faites, elle-aussi conservée dans la session.
- ligne 19 : on récupère dans la session la dernière simulation qui y a été placée.
- lignes 20-22 : si on ne la trouve pas, alors c'est que probablement la session a expiré. En effet, celle-ci ne dure qu'un certain temps qu'on peut fixer dans le fichier [web.xml] qui configure l'application.
- ligne 21 : on rend la clé error.
Retour à la configuration de l'action [EnregistrerSimulation] :
2.
3.
4.
5.
<!--
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
>
On voit que la clé error (ligne 3) amène l'affichage de la vue Tiles nommée erreur. Celle-ci est composée des fragments [Entete.jsp] et [Erreur.jsp] et à l'aspect suivant :
Le frament [Erreur.jsp] est le suivant :
2.
3.
4.
5.
<%@
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
>
Retour à la méthode [Enregistrer].execute :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
//
exécution
de
l'action
public
String execute
(
) {
//
on
récupère
la
dernière
simulation
dans
la
session
Simulation simulation =
(
Simulation) session.get
(
"
simulation
"
);
if
(
simulation =
=
null
) {
return
ERROR;
}
//
on
récupère
le
N°
de
la
dernière
simulation
Integer numDerniereSimulation =
(
Integer) session.get
(
"
numDerniereSimulation
"
);
if
(
numDerniereSimulation =
=
null
) {
numDerniereSimulation =
0
;
}
//
on
l'incrémente
numDerniereSimulation+
+
;
//
on
la
remet
le
nouveau
n°
dans
la
session
session.put
(
"
numDerniereSimulation
"
, numDerniereSimulation);
//
on
récupère
la
liste
des
simulations
List<
Simulation>
simulations =
(
List<
Simulation>
) session.get
(
"
simulations
"
);
if
(
simulations =
=
null
) {
simulations =
new
ArrayList<
Simulation>
(
);
session.put
(
"
simulations
"
, simulations);
}
//
on
lui
ajoute
la
simulation
courante
simulation.setNum
(
numDerniereSimulation);
simulations.add
(
simulation);
//
on
affiche
la
liste
des
simulations
menu =
new
Menu
(
false
, false
, false
, false
, true
, true
);
return
"
simulations
"
;
}
- lignes 9-16 : les différentes simulations sont numérotées à partir de 1. Le dernier n° attribué est mis dans la session sous la clé numDerniereSimulation. Le code des lignes 9-16 consiste à récupérer cette clé et à incrémenter la valeur qui lui est associée.
- lignes 18-22 : la liste des simulations est conservée dans la session associée à la clé simulations. Les lignes 18-22 consistent à retrouver cette liste si elle existe ou à la créer si elle n'existe pas.
- lignes 24-25 : une fois la liste des simulations obtenues, on lui ajoute la simulation courante (ligne 25). Auparavant, la simulation courante a reçu un numéro (ligne 24).
- ligne 27 : on fixe le menu à afficher
- ligne 28 : on rend la clé de navigation simulations.
Retour à la configuration de l'action [EnregistrerSimulation] dans [struts.xml] :
2.
3.
4.
5.
<!--
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
>
Ligne 4, la clé simulations provoque l'affichage de la vue Tiles nommée simulations. Cette vue est composée des fragments [Entete.jsp] et [Simulations.jsp]. La vue affichée est la suivante :
- en [1], le fragment [Entete.jsp] que nous connaissons bien maintenant.
- en [2], le fragment [Simulations.jsp]
Le fragment [Simulations.jsp] est le suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
<%@
page
contentType
=
"
text
/
html
;
charset
=
UTF
-
8
"
pageEncoding
=
"
UTF
-
8
"
%>
<%@
taglib
prefix
=
"
s
"
uri
=
"
/
struts
-
tags
"
%>
<!--
liste
de
simulations
vide
-->
<
s
:
if
test
=
"
#
session
[
'
simulations
'
]
=
=
null
|
|
#
session
[
'
simulations
'
]
.
size
(
)
=
=
0
"
>
<
h2
>
<
s
:
text
name
=
"
Pam
.
SimulationsVides
.
titre
"
/
>
<
/
h2
>
<
/
s
:
if
>
<!--
liste
de
simulations
non
vide
-->
<
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
>
- lignes 5-7 : s'il n'y a pas de simulations dans la session, alors on affiche la vue suivante :
- lignes 13-21 : affichage des entêtes des colonnes du tableau
- lignes 23-55 : itérateur sur la liste des simulations trouvée dans la session
- lignes 24-26 : création d'une Url nommé url (attribut id). Le lien Html généré par cette Url est le suivant :
<a href="/pam/SupprimerSimulation.action?id=1">Retirer</a>
On voit que le lien cible l'action [SupprimerSimulation] avec le paramètre id qui représente le n° de la simulation à retirer de la liste. - lignes 28-54 : à chaque itération sur la liste des simulations, on affiche les propriétés de la simulation courante.
XIX-J. Retirer une simulation▲
L'utilisateur peut vouloir enlever une simulation de la liste des simulations :
- en [1], on supprime la simulation n° 1
- en [2], la simulation n° 1 a été supprimée
Le lien [Retirer] se trouve dans le fragment [Simulations.jsp] que nous avons déjà étudié :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
<%@
page
contentType
=
"
text
/
html
;
charset
=
UTF
-
8
"
pageEncoding
=
"
UTF
-
8
"
%>
<%@
taglib
prefix
=
"
s
"
uri
=
"
/
struts
-
tags
"
%>
<!--
liste
de
simulations
vide
-->
<
s
:
if
test
=
"
#
session
[
'
simulations
'
]
=
=
null
|
|
#
session
[
'
simulations
'
]
.
size
(
)
=
=
0
"
>
<
h2
>
<
s
:
text
name
=
"
Pam
.
SimulationsVides
.
titre
"
/
>
<
/
h2
>
<
/
s
:
if
>
<!--
liste
de
simulations
non
vide
-->
<
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
>
- lignes 16-18 : génèrent le lien Html
<a href="/pam-01/SupprimerSimulation.action?id=num">Retirer</a>
où num est le n° de la simulation à retirer.
L'action [SupprimerSimulation] est définie comme suit dans le fichier [struts.xml] :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
<?
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
>
<!--
internationalisation
-->
<
constant
name
=
"
struts.custom.i18n.resources
"
value
=
"
messages
"
/
>
<!--
intégration
Spring
-->
<
constant
name
=
"
struts.objectFactory.spring.autoWire
"
value
=
"
name
"
/
>
<!--
actions
Struts
/Tiles
-->
<
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
>
- ligne 16 : l'action [SupprimerSimulation] est associée à la classe [Supprimer]. Parce qu'aucune méthode n'est précisée, c'est sa méthode execute qui sera exécutée. La classe [Supprimer] est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
package
web.actions;
...
public
class
Supprimer extends
ActionSupport implements
SessionAware {
//
session
private
Map<
String, Object>
session;
//
id
de
la
simulation
à
supprimer
private
String id;
//
menu
private
Menu menu;
@
Override
public
void
setSession
(
Map<
String, Object>
session) {
this
.session =
session;
}
//
exécution
de
l'action
public
String execute
(
) {
//
on
récupère
les
simulations
dans
la
session
List<
Simulation>
simulations =
(
List<
Simulation>
) session.get
(
"
simulations
"
);
if
(
simulations =
=
null
) {
//
cas
anormal
-
la
session
a
du
expirer
menu =
new
Menu
(
false
, false
, false
, false
, true
, true
);
return
"
erreur
"
;
}
//
test
de
id
int
num =
0
;
boolean
erreur =
false
;
try
{
num =
Integer.parseInt
(
id);
erreur =
num <=
0
;
}
catch
(
NumberFormatException ex) {
//
anormal
erreur =
true
;
}
//
erreur
?
if
(
erreur) {
menu =
new
Menu
(
false
, false
, false
, false
, true
, true
);
return
"
erreur
"
;
}
//
on
recherche
la
simulation
à
supprimer
for
(
int
i =
0
; i <
simulations.size
(
); i+
+
) {
if
(
num =
=
simulations.get
(
i).getNum
(
)) {
simulations.remove
(
i);
break
;
}
}
//
on
affiche
la
liste
des
simulations
menu =
new
Menu
(
false
, false
, false
, false
, true
, true
);
return
"
simulations
"
;
}
//
getters
et
setters
...
}
- ligne 4 : l'action [Supprimer] implémente l'interface [SessionAware] pour avoir accès à la session.
- ligne 7 : la session
- ligne 9 : le n° de la simulation à supprimer. On se rappelle en effet qu'on arrive à l'instanciation de la classe [Supprimer] par l'Url Html :
<a href="/pam-01/SupprimerSimulation.action?id=num">Retirer</a>
où num est le n° de la simulation à retirer. Ce numéro sera stocké dans le champ id de la ligne 9. - ligne 11 : le menu pour la vue qui sera affichée en réponse à la requête
- ligne 19 : la méthode execute qui va générer la réponse à la requête.
- ligne 21 : on récupère la liste des simulations déjà faites dans la session
- lignes 22-26 : ne pas récupérer cette liste dans la session veut probablement dire que la session a expiré. On a déjà rencontré ce cas. On rend la clé erreur qui fait afficher la vue Tiles erreur :
2.
3.
4.
5.
<!--
action
RetirerSimulation
-->
<
action
name
=
"
SupprimerSimulation
"
class
=
"
web.actions.Supprimer
"
>
<
result
name
=
"
erreur
"
type
=
"
tiles
"
>
erreur<
/
result
>
...
<
/
action
>
La vue Tiles erreur a été présentée chapitre XIX.
- lignes 28-36 : on vérifie que la chaîne id de la ligne 9 représente bien un nombre entier >0.
- lignes 38-40 : si ce n'est pas le cas, on rend de nouveau la clé erreur qui fera afficher la vue Tiles erreur
- lignes 43-48 : la simulation à retirer est cherchée dans la liste des simulations. Si elle est trouvée, elle est supprimée.
- ligne 50 : on met le menu à jour pour la vue Tiles simulations.
- ligne 51 : on rend la clé simulations. Celle-ci va faire afficher la vue Tiles simulations :
2.
3.
4.
5.
<!--
action
RetirerSimulation
-->
<
action
name
=
"
SupprimerSimulation
"
class
=
"
web.actions.Supprimer
"
>
...
<
result
name
=
"
simulations
"
type
=
"
tiles
"
>
simulations<
/
result
>
<
/
action
>
La vue Tiles simulations a été présentée chapitre XIX.
XIX-K. Revenir au formulaire▲
A partir de la vue Tiles simulations, l'utilisateur peut revenir au formulaire :
- en [1], on clique sur le lien de retour au formulaire
- en [2], on retrouve un formulaire vide
Le lien [Retour au formulaire de simulation] est défini dans le fragment [Entete.jsp] :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
<%@
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
>
- ligne 10 : le lien pointe sur l'action [RetourFormulaire]. Celle-ci est définie de la façon suivante dans le fichier [struts.xml] :
2.
3.
4.
5.
6.
7.
<!--
action
RetourFormulaire
-->
<
action
name
=
"
RetourFormulaire
"
>
<
result
type
=
"
redirectAction
"
>
<
param
name
=
"
actionName
"
>
Formulaire!input<
/
param
>
<
param
name
=
"
namespace
"
>
/<
/
param
>
<
/
result
>
<
/
action
>
On voit que cette action n'est associée à aucune classe. Elle se contente de rediriger le navigateur client vers l'action [/Formulaire!input]. On est alors dans le même cas que lors de l'affichage de la vue initiale expliquée au chapitre XIX. On retrouve donc cette vue initiale [2].
XIX-L. Voir la liste des simulations▲
A partir des vues Tiles simulation ou saisie, l'utilisateur peut demander à voir les simulations :
- en [1], on clique sur le lien [Voir les simulations]
- en [2], on retrouve la liste des simulations
Le lien [Voir les simulations] est défini dans le fragment [Entete.jsp] :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
<%@
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
>
- ligne 10 : le lien [Voir les simulations] appelle l'action [VoirSimulations]. Celle-ci est définie de la façon suivante dans le fichier [struts.xml] :
2.
3.
4.
<!--
action
VoirSimulations
-->
<
action
name
=
"
VoirSimulations
"
class
=
"
web.actions.Voir
"
>
<
result
name
=
"
success
"
type
=
"
tiles
"
>
simulations<
/
result
>
<
/
action
>
L'action [VoirSimulations] est associée à la classe [Voir] sans précision de méthode. C'est donc la méthode [Voir].execute qui sera exécutée. La classe [Voir] est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
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
et
setters
public
Menu getMenu
(
) {
return
menu;
}
public
void
setMenu
(
Menu menu) {
this
.menu =
menu;
}
}
L'action [Voir] ne fait qu'une chose : positionner le menu pour la vue Tiles simulations (ligne 8). Il n'y a pas de méthode execute. C'est donc celle de la classe parent [ActionSupport] qui sera exécutée. On sait qu'elle ne fait rien si ce n'est rendre la clé success.
Retour à l'action dans [struts.xml] :
2.
3.
4.
<!--
action
VoirSimulations
-->
<
action
name
=
"
VoirSimulations
"
class
=
"
web.actions.Voir
"
>
<
result
name
=
"
success
"
type
=
"
tiles
"
>
simulations<
/
result
>
<
/
action
>
Ligne 3, on voit que la clé success conduit à l'affichage de la vue Tiles simulations. Celle-ci a été décrite dans le chapitre XIX.
XIX-M. Effacer la simulation courante▲
A partir de la vues Tiles simulation, l'utilisateur peut demander à effacer la simulation courante :
- en [1], on efface la simulation courante
- en [2], on retrouve le formulaire de saisie vide
Le lien [Effacer la simulation] est défini dans le fragment [Entete.jsp] :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
<%@
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
>
Ligne 10, on voit que le lien [Effacer la simulation] déclenche l'action [Formulaire!input]. On sait que cette action amène à la vue initiale [2].
XIX-N. Terminer la session courante▲
A partir de toutes les vues Tiles, l'utilisateur peut demander à terminer la session :
- en [1], on part de la vue des simulations et on termine la session
- en [2], on retrouve le formulaire de saisie vide. On demande à voir les simulations.
- en [3], la liste des simulations est désormais vide.
Le lien [Terminer la session] est défini dans le fragment [Entete.jsp] :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
<%@
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
>
Ligne 10, on voit que le lien [Terminer la session] déclenche l'action [TerminerSession]. Celle-ci est définie de la façon suivante dans le fichier [struts.xml] :
2.
3.
4.
5.
6.
<
action
name
=
"
TerminerSession
"
class
=
"
web.actions.Terminer
"
>
<
result
name
=
"
success
"
type
=
"
redirectAction
"
>
<
param
name
=
"
actionName
"
>
Formulaire!input<
/
param
>
<
param
name
=
"
namespace
"
>
/<
/
param
>
<
/
result
>
<
/
action
>
- ligne 1 : on voit que la classe [Terminer] va être instanciée et sa méthode execute exécutée.
- lignes 2-5 : après exécution de la méthode [Terminer].execute, il y aura redirection sur la vue initiale de saisie. C'est ce qui explique l'écran n° 2.
La classe [Terminer] est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
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
(
) {
//
abandon
de
la
session
courante
session.clear
(
);
return
SUCCESS;
}
@
Override
public
void
setSession
(
Map<
String, Object>
session) {
this
.session =
session;
}
}
Le rôle de l'action [Terminer] est de vider la session courante de ses attributs.
- ligne 7 : l'action [Terminer] implémente l'interface [SessionAware] pour avoir accès à la session.
- ligne 10 : le dictionnaire de la session
- ligne 13 : la méthode execute qui est exécutée
- ligne 15 : elle vide le dictionnaire de la session. Du coup, la liste des simulations qui est en session va disparaître. C'est ce qui explique l'écran n° 3.
- ligne 16 : elle rend la clé success, qui on l'a vu, va afficher la vue Tiles saisie [2].
XIX-O. Conclusion▲
Nous avons entièrement commenté la version 1 de notre étude de cas qui travaille avec une couche [metier] simulée :
Il nous reste plus qu'à " brancher " la vraie couche métier sur la couche [web].