19. Estudo de caso – Versão 1
19.1. A camada [de negócios] simulada
Vamos rever a arquitetura da aplicação que estamos a construir:
![]() |
Temos os arquivos da camada [negócio, DAO, JPA] e apresentámos os elementos dessas camadas que a camada [web] precisa de conhecer. Estamos prontos para escrever a camada [web] utilizando o framework Struts.
Para simplificar os testes da nossa aplicação durante o desenvolvimento, iremos criar uma camada de negócios simulada que irá implementar a interface da camada [negócios]. A arquitetura ficará da seguinte forma:
![]() |
Iremos desenvolver a camada [web] utilizando a camada [negócio] simulada. Os testes serão mais simples, uma vez que já não existe uma base de dados na arquitetura. Graças ao Spring e à utilização de interfaces, a substituição da camada [business] simulada pela arquitetura real [business, DAO, JPA] numa fase posterior não terá qualquer impacto no código da camada [web / Struts2]. A camada [web / Struts2] que estamos prestes a desenvolver pode ser utilizada tal como está.
A camada [de negócios] simulada que iremos utilizar é a seguinte:
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;
}
}
- linha 11: a classe [MetierSimule] implementa a interface [IMetier] que a camada [business] real implementa.
- linha 14: um dicionário de funcionários indexado pelo seu número INSEE
- linha 15: a lista de funcionários
- linhas 27–39: implementação do método findAllEmployees da interface [IMetier].
- linhas 30–33: criação de uma lista de dois funcionários
- Linhas 34–36: criação do dicionário de funcionários indexado pelo número INSEE
- Linhas 18–24: Implementação do método `calculerSalaire` da interface [IMetier]. Aqui, devolvemos um recibo de vencimento fictício.
19.2. O projeto NetBeans
O projeto NetBeans é o seguinte:
![]() |
- em [1]:
- [applicationContext.xml] é o ficheiro de configuração do Spring
- [tiles.xml] é o ficheiro de configuração de um framework chamado Tiles.
- [web.xml] é o ficheiro de configuração da aplicação web
- em [2]: as diferentes vistas da aplicação
- em [3]:
- [messages.properties]: o ficheiro de mensagens
- [struts.xml]: o ficheiro de configuração do Struts
![]() |
- em [4]: o código-fonte da aplicação. As ações Struts encontram-se no pacote [web.actions].
- em [5]: a camada [business] simulada
- em [6]: os arquivos utilizados. Aqui estão os arquivos das várias ferramentas utilizadas: Spring, Tiles, Struts 2, o plugin de integração Struts 2/Spring e o plugin de integração Struts 2/Tiles.
- em [7]: o arquivo da camada [business, DAO, JPA] real. Dá-nos acesso às entidades JPA, à interface [IMetier] e às classes [PayrollSheet] e [PayrollItems]. Todos estes elementos são, de facto, utilizados pela nossa classe [SimulatedBusiness].
19.3. Configuração do projeto
O projeto é configurado por vários ficheiros:
- [web.xml], que configura a aplicação web
- [struts.xml], que configura o framework Struts
- [applicationContext.xml], que configura o framework Spring
- [tiles.xml], que configura o framework Tiles
19.3.1. Configuração da aplicação web
O ficheiro [web.xml] é o seguinte:
<?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>
- linhas 13–20: configurar o filtro Struts 2 – já visto
- linhas 22–24: configurar o ouvinte Spring – já visto
- linhas 9–11: configurar o ouvinte do Tiles. A classe [org.apache.struts2.tiles.StrutsTilesListener] será instanciada quando a aplicação web for iniciada. Em seguida, utilizará o seu ficheiro de configuração. Este é definido pelas linhas 5–8. O ficheiro de configuração do Tiles é, portanto, o ficheiro [WEB-INF/tiles.xml].
Por fim, quando a aplicação Struts é iniciada, são instanciadas três classes:
- uma para o filtro Struts 2. Este é o componente que lida com o «C» em MVC.
- outra para o ouvinte Spring. O Spring utilizará o ficheiro [applicationContext.xml] para instanciar as camadas [business, DAO, JPA] da aplicação. O Spring também instanciará, tal como no exemplo anterior, uma classe [Config] que conterá dados no âmbito da aplicação. Por fim, o Spring injetará uma referência a esta única instância [Config] em todas as ações Struts que dela necessitem.
- Outro para o ouvinte Tiles. Esta estrutura irá tratar da gestão de vistas. Voltaremos a este assunto em breve.
19.3.2. Configuração da estrutura Struts
A estrutura Struts é configurada pelo seguinte ficheiro [struts.xml]:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<!-- 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>
Iremos discutir as várias ações do Struts à medida que as formos abordando. Por enquanto, tenha em conta os seguintes pontos:
- linha 8: define o ficheiro de mensagens
- linha 10: define como os beans do Spring são injetados nas ações do Struts. A injeção é baseada no nome do bean. O campo da ação do Struts que precisa ser inicializado pelo Spring deve ter o mesmo nome do bean a ser injetado.
- linha 25: define a vista a apresentar para a chave de navegação «success» da ação [Form]. Vemos que o elemento <result> tem um atributo type='tiles' com o qual não estamos familiarizados. Estávamos familiarizados com o tipo redirect, que permite que o cliente seja redirecionado para uma vista. Aqui, a vista do tipo tiles é gerida pela estrutura Tiles. O tipo tiles é definido no ficheiro [struts-plugin.xml] dentro do arquivo [struts2-tiles-plugin-2.2.3.1.jar]:
<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>
- Linhas 3–5: A definição do tipo de resultado tiles.
- linha 2: este tipo é definido no pacote [tiles-default], que estende o pacote [struts-default].
- linha 14: define o pacote [default], que conterá todas as ações da aplicação. Para tirar partido da definição do tipo de vista tiles, o pacote estende [tiles-default].
19.3.3. Configuração do Spring Framework
O Spring Framework é configurado pelo seguinte ficheiro [WEB-INF/applicationContext.xml]:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<!-- 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>
- linha 13: a camada [business] simulada instanciada pela classe [business.SimulatedBusiness]
- linhas 9–11: configurar um bean denominado config. Tal como num exemplo anteriormente estudado, este bean será utilizado para encapsular informações do âmbito da aplicação. A classe associada a este bean é a seguinte classe [Config]:
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
...
}
Voltemos à configuração do bean de configuração:
<bean id="config" class="web.Config" init-method="init">
<property name="metier" ref="metier"/>
</bean>
<!-- métier -->
<bean id="metier" class="metier.Metier">
...
</bean>
Na linha 2, podemos ver que o bean de negócio da linha 5 é injetado (ref) no campo denominado business (name) do objeto [Config]. O bean de negócio é uma referência à camada [business]:
![]() |
Para interagir com a camada [business], todas as ações Struts na camada [web] precisarão de uma referência a ela. Podemos dizer que a referência à camada [business] é um dado com âmbito de aplicação. Todas as solicitações de todos os utilizadores precisarão dela. É por isso que colocamos esta referência no objeto [Config]. Além disso, na linha 1, a configuração do bean de configuração inclui um atributo init-method. Este atributo especifica o método do bean a ser executado após a instância do bean. Aqui, especificamos que, após a instância da classe [web.Config], o seu método init deve ser executado. Este método é o seguinte:
// 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;
}
}
Quando o método init é executado, o campo de negócio da classe já foi instanciado pelo Spring. O método init tem, portanto, acesso à interface da camada de negócio [IMetier] (linha 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();
}
- Linha 8: O método calcula o salário de um funcionário
- linha 10: o método recupera a lista de funcionários
Podemos ver que o método init solicita a lista de funcionários da camada [business]. Esta lista é armazenada no campo da linha 4. Se ocorrer uma exceção, esta é armazenada no campo da linha 6.
Em conclusão, o único objeto [Config] contém:
- uma referência à camada [business]
- a lista de funcionários
19.4. Gerar vistas de mosaicos
Como vimos no ficheiro de configuração do Struts, as vistas serão geradas pela estrutura Tiles. Iremos explicar apenas o que é estritamente necessário para escrever a nossa aplicação.
O Tiles permite gerar vistas a partir de uma página mestre. Esta página, aqui denominada [MasterPage.jsp], será uma compilação dos seguintes fragmentos JSP:
Estes fragmentos JSP estão definidos no projeto NetBeans:

A estrutura Tiles permite-nos definir quais os fragmentos que serão inseridos na página principal.
A página mestre [MasterPage.jsp] é a seguinte:
<%@ 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>
A página mestre é um contentor para fragmentos JSP. Aqui, trata-se de um conjunto de seis fragmentos, os que se encontram nas linhas 17 a 23. Após a geração, podem existir entre 0 e 6 fragmentos reunidos na página mestre. Esta geração é regida pelo ficheiro [WEB-INF/tiles.xml], que define as vistas Tiles:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"
"http://tiles.apache.org/dtds/tiles-config_2_0.dtd">
<tiles-definitions>
<!-- 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>
- O ficheiro acima define seis vistas Tiles denominadas: masterPage (linha 9), input (linha 20), simulation (linha 25), simulations (linha 30), exception (linha 35), error (linha 40).
- Linhas 9–17: definem uma vista denominada masterPage (nome) e associada à página principal [MasterPage.jsp] (modelo). Vimos que esta página JSP definiu seis subvisualizações. Uma visualização Tiles associada à página mestre deve especificar o fragmento JSP associado a cada uma das seis subvisualizações. Observamos que a algumas subvisualizações é atribuída a cadeia vazia como valor. Estas subvisualizações não serão incluídas na página mestre [MasterPage.jsp]. A visualização Tiles denominada masterPage consiste, portanto, exclusivamente no subfragmento [Entete.jsp].
- Linhas 20–22: definem uma vista denominada `saisie` que estende a vista `masterPage` definida anteriormente. Isto significa que herda todas as definições da vista `masterPage`. A sua definição é equivalente ao seguinte:
<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>
Podemos ver que está associada à página JSP [MasterPage.jsp] e que, como tal, deve definir as seis subvisualizações desta página. Podemos ver que a definição na linha 9 substitui a da linha 4. A visualização Tiles denominada «saisie» é, portanto, composta pelos fragmentos JSP [Entete.jsp, Saisie.jsp]
Se continuarmos esta linha de raciocínio, obtemos a seguinte tabela:
Vista Tiles | Páginas JSP |
19.5. Ficheiros de mensagens
A aplicação foi internacionalizada. As mensagens encontram-se nos ficheiros [messages.properties] e [Formulaire.properties].
O ficheiro [messages.properties] tem o seguinte conteúdo:
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}".
O ficheiro [Form.properties] é o seguinte:
# 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. A folha de estilo
As vistas de mosaicos utilizam a seguinte folha de estilo [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. A visualização inicial
Para explorar a aplicação, iremos analisá-la com base nas várias ações do utilizador. Para cada ação, iremos analisar a ação Struts que a executa e a vista Tiles que é devolvida em resposta.
No [struts.xml] temos as seguintes ações:
<!-- 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>
- linhas 2-8: a ação padrão da aplicação é [Form!input].
A classe [Form] é a seguinte:
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
...
}
- Linha 4: A ação [Form] implementa a interface Preparable. Esta interface possui apenas um método, o método prepare na linha 24. Este método é executado uma vez antes de qualquer outro método da ação. É geralmente utilizado para inicializar o modelo da ação.
O modelo da ação é definido nas linhas 6–21:
- linha 7: o campo config é inicializado pelo Spring, conforme explicado. Ele fornece acesso a dados no âmbito da aplicação:
- uma referência à camada [business]
- uma referência à lista de funcionários
- uma referência à exceção que pode ter ocorrido durante a instanciação do objeto [Config]
- linha 9: uma lista de funcionários. Isto irá preencher o menu suspenso de funcionários no fragmento [Saisie.jsp].
- linha 11: uma lista de erros. Isto irá preencher o fragmento [Error.jsp].
- Linha 21: a lista de opções de menu para o fragmento [Entete.jsp]
![]() |
Em [1], os links no menu exibido são controlados pelo campo de menu da ação [Form].
O método prepare é executado antes do método input. É o seguinte:
@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();
}
}
- linha 4: recuperamos a exceção do objeto [Config] instanciado pelo Spring
- linha 5: se ocorreu uma exceção durante a instanciação do objeto [Config], então inicializamos a lista de erros na linha 11. A classe [Error] é a seguinte:
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
...
}
A classe é utilizada para armazenar a pilha de exceções:
- linha 11: a classe de exceção
- linha 12: a mensagem de exceção
Voltemos ao método prepare:
- linha 13: a lista de funcionários do objeto [Config] é armazenada no campo employees da ação.
Assim que o método prepare for executado, o método input será executado a seguir. É o seguinte:
@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";
}
}
O método de entrada define simplesmente a lista de opções do menu a apresentar. A classe [Menu] é a seguinte:
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
...
}
- linhas 8–13: existem 6 links possíveis no menu
- linhas 18-25: o construtor da classe permite especificar quais os links que devem ser apresentados e quais os que não devem.
Os links do menu são exibidos em [Entete.jsp], um fragmento JSP presente em todas as visualizações do Tiles. Cada ação terá um campo de menu para controlar a exibição do menu em [Entete.jsp].
Voltemos ao método de entrada:
@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";
}
}
- linhas 3-6: se a lista de erros estiver vazia, será exibido o menu [Executar Simulação, Ver Simulações, Terminar Sessão] e será devolvida a tecla de entrada.
- linhas 9-10: se a lista de erros não estiver vazia, o menu ficará vazio e a chave de exceção será devolvida.
Voltemos à configuração da ação [Form] em [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>
- Linha 5: A chave input exibe a vista Tiles denominada input
- linha 4: a chave exception exibe a vista Tiles denominada exception
Comecemos pela vista Tiles denominada «input». Esta é composta pelos fragmentos JSP [Entete.jsp] e [Saisie.jsp].
O fragmento [Entete.jsp] é o seguinte:

O seu código é o seguinte:
<%@ 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>
- linhas 8-25: exibição dos seis links do menu [Executar Simulação (linhas 8-10), Limpar Simulação (linhas 11-13), Ver Simulações (linhas 14–16), Voltar ao Formulário (linhas 17–19), Guardar Simulação (linhas 20–22), Terminar Sessão (linhas 23–25).
- linhas 8, 11, 14, 17, 20, 23: a exibição dos links é controlada pelo campo de menu da ação atual.
Note que o fragmento [Entete.jsp] apresenta uma tabela HTML (linhas 4–28), mas não é uma página HTML completa. Tenha em mente que todas as visualizações da aplicação estão incorporadas na seguinte página mestre [MasterPage.jsp]:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link href="styles.css" rel="stylesheet" type="text/css"/>
<title>
<tiles:insertAttribute name="titre" ignore="true" />
</title>
<s:head/>
</head>
<body background="<s:url value="/ressources/standard.jpg"/>">
<tiles:insertAttribute name="entete" />
<hr/>
<tiles:insertAttribute name="saisie" />
<tiles:insertAttribute name="simulation" />
<tiles:insertAttribute name="exception" />
<tiles:insertAttribute name="erreur" />
<tiles:insertAttribute name="simulations" />
</body>
</html>
O fragmento [Entete.jsp] é inserido na linha 17, dentro de uma página HTML normal.
O fragmento [Saisie.jsp] é inserido na linha 19. Esta é a visualização resultante:

O código do fragmento [Saisie.jsp] é o seguinte:
<%@ 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>
- Linha 17: O formulário não tem atributo action. Por predefinição, action='Form'.
- Linha 18: Exibição da lista suspensa de funcionários. O conteúdo da lista suspensa (atributo list) é fornecido pelo campo employees da ação atual. O atributo value das opções será o número de segurança social do funcionário (atributo listKey). O rótulo exibido para cada opção será o nome e apelido do funcionário (atributo listValue). O número de segurança social do funcionário selecionado na caixa combinada será enviado para o campo [Form].comboEmployeesValue (atributo name).
- Linha 19: campo de entrada para horas trabalhadas. O valor exibido (atributo value) é o do campo heuresTravaillees na ação [Form], no seguinte formato (Form.properties):
double.format={0,number,#0.00##}
O valor será lançado no campo [Form].hoursWorked (atributo name).
- Linha 20: campo para introduzir os dias trabalhados. O valor apresentado (atributo value) é o do campo daysWorked na ação [Form].
O valor será enviado para o campo [Form].daysWorked (atributo name).
Por fim, a vista Tiles apresentada no arranque, quando não existem erros, é a seguinte:

Voltemos à configuração da ação [Form]:
<!-- 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>
Vimos que a ação [Form].input também pode acionar a chave exception na linha 4. Neste caso, é apresentada a vista Tiles denominada exception. Esta vista é composta pelos fragmentos [Header.jsp] e [Exception.jsp]. Já apresentámos o fragmento [Header.jsp]. O fragmento [Exception.jsp] é o seguinte:

Esta é a página inicial da versão 2 da aplicação quando o DBMS ainda não foi iniciado. O código JSP para o fragmento [Error.jsp] é o seguinte:
<%@ 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>
- Linhas 10–14: Um iterador sobre a coleção List<Error> de erros da ação [Form]. Recorde-se que, em caso de erro, uma pilha de exceções foi armazenada nesse local.
19.8. Execute uma simulação
Assim que a vista inicial for apresentada, pode calcular um salário através da ligação [Executar uma simulação].
19.8.1. Validação das entradas
Considere a seguinte sequência:
![]() |
- em [1], uma entrada incorreta
- em [2], a resposta enviada.
Vamos considerar a configuração da ação [Form] em [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>
Sabemos que, em caso de erro de validação, o interceptor de validação devolve a chave de entrada. Por conseguinte, é devolvida a vista de entrada Tiles. O processo de validação garante que os campos com erros sejam acompanhados por mensagens de erro.
A validação da ação [Form] é tratada pelo seguinte ficheiro [Form-validation.xml]:
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<validators>
<field name="heuresTravaillees" >
<field-validator type="required" short-circuit="true">
<message key="heuresTravaillées.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="heuresTravaillées.error"/>
</field-validator>
<field-validator type="double" short-circuit="true">
<param name="minInclusive">0</param>
<param name="maxInclusive">300</param>
<message key="heuresTravaillées.error"/>
</field-validator>
</field>
<field name="joursTravailles" >
<field-validator type="required" short-circuit="true">
<message key="joursTravaillés.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="joursTravaillés.error"/>
</field-validator>
<field-validator type="int" short-circuit="true">
<param name="min">0</param>
<param name="max">31</param>
<message key="joursTravaillés.error"/>
</field-validator>
</field>
</validators>
- As linhas 6–20 verificam se o campo hoursWorked é um número real no intervalo [0,300].
- As linhas 22–36 verificam se o campo `joursTravaillés` é um número inteiro no intervalo [0,31].
Voltemos à configuração da ação [Form] no [struts.xml]:
<!-- action Formulaire -->
<action name="Formulaire" class="web.actions.Formulaire">
...
<result name="input" type="tiles">saisie</result>
</action>
Sabemos que, em caso de erro de validação, o interceptor de validação devolve a chave «input». Por conseguinte, é devolvida a vista «Input» do Tiles.
Recorde-se que esta vista é composta pelos fragmentos [Header.jsp] e [Input.jsp], em que [Header.jsp] contém um título e uma lista de opções, e [Input.jsp] contém o formulário de entrada. Em caso de erros de entrada, o processo de validação garante que os campos errados sejam acompanhados por mensagens de erro e também exibam os seus valores incorretos. O fragmento [Entete.jsp] não desempenha qualquer papel no processo de validação. Vejamos o seu código:
<%@ 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>
Os seis links são configurados pelo campo de menu do modelo (linhas 8, 11, 14, 17, 20, 23). Quando ocorre um erro, este modelo não é atualizado pela ação, resultando numa página sem menu. Para resolver este problema, a classe [Form] possui o seguinte método de validação:
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
...
}
- linha 27: sabemos que, quando isto está presente, o método validate é executado pelo processo de validação. Aproveitamos esta oportunidade para atualizar o menu na linha 4, que faz parte do modelo de fragmento [Entete.jsp].
- linhas 29–32: se houver erros de validação, definimos o menu para voltar a apresentar a vista de entrada do Tiles. Se não houver erros de validação, não fazemos nada. O método `calculSalaire` é então responsável por criar o modelo de vista a ser apresentado.
19.8.2. Cálculo do salário
Voltemos ao código JSP no cabeçalho:
<%@ 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>
- Linha 9: Quando o utilizador clica na ligação [Executar Simulação], a função JavaScript doSimulation é executada. Esta função está definida no fragmento [Saisie.jsp]:
<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>
- linha 14: um campo oculto denominado action será enviado para a ação [Form]. Este campo permite-nos especificar a ação e o método a executar quando o formulário for enviado. Como nos podemos lembrar dos primeiros exemplos, estes podem ser especificados num parâmetro denominado action:Action!method. O valor deste parâmetro não importa; basta que esteja presente.
- Linhas 2–6: A função JavaScript que é executada quando o utilizador clica na ligação [Run Simulation] no fragmento [Header.jsp].
- Linha 4: Alteramos o atributo name do campo de ação oculto. Asseguramos que este está no formato action:Action!method esperado pelo Struts.
- Linha 5: O formulário denominado «Saisie de la ligne 5» é enviado. Como resultado, é enviada a seguinte cadeia de parâmetros:
SS1: número INSEE do funcionário selecionado na caixa combinada
heuresTravaillees: número de horas trabalhadas
diasTrabalhados: número de dias trabalhados
action:Form!calculateSalary: os elementos acima serão enviados para a ação [Form] e, em seguida, o método calculateSalary dessa ação será executado.
O método [Form].calculateSalary é o seguinte:
// 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) {
...
}
}
- Linha 5: O cálculo da folha de pagamento é solicitado à camada [business]
- linha 7: é adicionado um objeto Simulação à sessão do utilizador. Isto porque pode ser necessário para um pedido subsequente. A classe [Simulação] é a seguinte:
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
...
}
- linha 12: o número da simulação. É incrementado a cada nova simulação guardada.
- linha 13: o recibo de vencimento do funcionário
- linha 14: o número de horas trabalhadas
- linha 15: o número de dias trabalhados
- linha 25: o método getIndemnites devolve a remuneração total do funcionário
Veremos que a classe [Simulation] é o modelo para o fragmento [Simulations.jsp], que exibe todas as simulações realizadas.
De volta ao método [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) {
...
}
- linha 9: atualizar o menu
- linha 11: devolve a tecla de navegação «simulação».
Voltar à configuração da ação [Formulário]:
<!-- action Formulaire -->
<action name="Formulaire" class="web.actions.Formulaire">
...
<result name="simulation" type="tiles">simulation</result>
</action>
A linha 4 mostra que a tecla de navegação «simulation» apresenta a vista Tiles denominada «simulation». Esta vista é composta pelos seguintes fragmentos JSP: [Header, Input, Simulation].
A vista renderizada é a seguinte:
![]() |
- em [1], o fragmento [Header.jsp]
- em [2], o fragmento [Input.jsp]
- em [3], o fragmento [Simulation.jsp]. Note-se que o recibo de vencimento apresentado é o recibo de vencimento fictício gerado pela camada [business].
Os dois primeiros fragmentos já foram apresentados. O fragmento [Simulation.jsp] é o seguinte:
<%@ 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>
É longo... mas funcionalmente simples. Este trecho de código apresenta as várias propriedades do campo [Form].paystub, que representa o recibo de vencimento do funcionário.
De volta ao método [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";
}
}
O cálculo do salário pode falhar. Seria o caso, por exemplo, se a ligação ao SGBD falhasse. Neste caso, tratamos a exceção que ocorre. Já nos deparámos com este cenário ao estudar o método [Form].input.
- Linhas 7–11: Criamos uma lista de objetos Error a partir da pilha de exceções
- linha 13: definimos o menu
- linha 14: definimos a chave da exceção.
A chave de exceção irá apresentar a vista de exceções do Tiles:
<!-- action Formulaire -->
<action name="Formulaire" class="web.actions.Formulaire">
<result name="exception" type="tiles">exception</result>
...
</action>
Esta vista Tiles já foi apresentada. Tem o seguinte aspeto:

19.9. Guardar uma simulação
Após executar uma simulação, o utilizador poderá querer guardá-la na sessão.
![]() |
![]() |
- Em [1], a simulação é guardada
- Em [2], a resposta apresenta a lista de simulações já realizadas, à qual a nova simulação é adicionada
O link [Guardar Simulação] está localizado no fragmento [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>
Podemos ver que clicar no link aciona a ação [SaveSimulation]. Esta ação está configurada no ficheiro [struts.xml] da seguinte forma:
<!-- 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>
- Linha 1: A ação [SaveSimulation] está associada à classe [Save] e ao seu método execute.
A classe [Save] é a seguinte:
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
...
}
- linha 4: como a ação precisa de aceder à sessão, ela implementa a interface SessionAware.
- linha 7: a sessão
- linha 9: o menu
Quando a ação [Save] é instanciada, o seu método execute é chamado. Recorde-se que a sua função é colocar a simulação mais recente na sessão. Esta simulação será adicionada à lista de simulações concluídas, que também é armazenada na sessão.
- linha 19: recuperamos a última simulação colocada na sessão.
- linhas 20–22: se não for encontrada, então a sessão provavelmente expirou. Na verdade, a sessão dura apenas um determinado período de tempo, que pode ser definido no ficheiro [web.xml] que configura a aplicação.
- Linha 21: Devolvemos a chave de erro.
De volta à configuração da ação [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>
Podemos ver que a chave "error" (linha 3) aciona a exibição da vista Tiles denominada "error". Esta vista é composta pelos fragmentos [Entete.jsp] e [Erreur.jsp] e tem o seguinte aspeto:

O fragmento [Erreur.jsp] é o seguinte:
<%@ 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>
De volta ao método [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";
}
- linhas 9–16: As várias simulações são numeradas a partir de 1. O último número atribuído é armazenado na sessão sob a chave numDerniereSimulation. O código nas linhas 9–16 recupera esta chave e incrementa o valor a ela associado.
- linhas 18–22: A lista de simulações é armazenada na sessão sob a chave simulations. As linhas 18–22 recuperam esta lista, caso exista, ou criam-na, caso não exista.
- Linhas 24–25: Assim que a lista de simulações é obtida, a simulação atual é adicionada a ela (linha 25). Anteriormente, foi atribuído um número à simulação atual (linha 24).
- Linha 27: O menu a ser exibido é definido
- Linha 28: Devolvemos a chave de navegação «simulations».
Voltar à configuração da ação [EnregistrerSimulation] em [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>
Linha 4: A chave «simulations» aciona a exibição da vista Tiles denominada «simulations». Esta vista é composta pelos fragmentos [Entete.jsp] e [Simulations.jsp]. A vista exibida é a seguinte:
![]() |
- em [1], o fragmento [Entete.jsp], com o qual já estamos familiarizados.
- em [2], o fragmento [Simulations.jsp]
O fragmento [Simulations.jsp] é o seguinte:
<%@ 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>
- linhas 5-7: se não houver simulações na sessão, é apresentada a seguinte vista:

- linhas 13-21: exibem os cabeçalhos das colunas da tabela
![]()
- linhas 23-55: percorre a lista de simulações encontradas na sessão
- linhas 24-26: criação de um URL denominado url (atributo id). O link HTML gerado por este URL é o seguinte:
<a href="<a href="view-source:http://localhost:8084/pam/SupprimerSimulation.action?id=1">/pam/SupprimerSimulation.action?id=1</a>">Retirer</a>
Podemos ver que o link aponta para a ação [DeleteSimulation] com o parâmetro id, que representa o número da simulação a ser removida da lista.
- Linhas 28–54: Para cada iteração pela lista de simulações, são apresentadas as propriedades da simulação atual.

19.10. Remover uma simulação
O utilizador pode querer remover uma simulação da lista de simulações:
![]() |
![]() |
- em [1], a simulação n.º 1 foi eliminada
- em [2], a simulação n.º 1 foi removida
O link [Remover] encontra-se no fragmento [Simulations.jsp] que já analisámos:
<%@ 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>
- Linhas 16-18: geram o link HTML
<a href="<a href="view-source:http://localhost:8084/pam-01/SupprimerSimulation.action?id=2">/pam-01/SupprimerSimulation.action?id=</a>num">Retirer</a>
onde num é o número da simulação a ser removida.
A ação [DeleteSimulation] é definida da seguinte forma no ficheiro [struts.xml]:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<!-- 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>
- Linha 16: A ação [SupprimerSimulation] está associada à classe [Supprimer]. Como não foi especificado nenhum método, será executado o método execute. A classe [Supprimer] é a seguinte:
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
...
}
- Linha 4: A ação [Delete] implementa a interface [SessionAware] para aceder à sessão.
- linha 7: a sessão
- linha 9: o número da simulação a ser eliminada. Recorde-se que instanciamos a classe [Delete] através do URL HTML:
<a href="<a href="view-source:http://localhost:8084/pam-01/SupprimerSimulation.action?id=2">/pam-01/SupprimerSimulation.action?id=</a>num">Retirer</a>
onde num é o número da simulação a ser removida. Este número será armazenado no campo id na linha 9.
- Linha 11: o menu da vista que será exibido em resposta ao pedido
- Linha 19: O método `execute` que irá gerar a resposta ao pedido.
- Linha 21: Recuperamos a lista de simulações já realizadas na sessão
- linhas 22-26: a incapacidade de recuperar esta lista da sessão significa provavelmente que a sessão expirou. Já nos deparámos com este cenário anteriormente. Devolvemos a chave de erro, que apresenta a vista de erro do Tiles:
<!-- action RetirerSimulation -->
<action name="SupprimerSimulation" class="web.actions.Supprimer">
<result name="erreur" type="tiles">erreur</result>
...
</action>
A visualização de erros do Tiles foi apresentada na Secção 19.9.
- linhas 28–36: verificamos se a cadeia de caracteres id na linha 9 é, de facto, um número inteiro maior que 0.
- linhas 38-40: se não for esse o caso, a chave de erro é devolvida novamente, o que exibirá a vista de erro do Tiles
- linhas 43–48: A simulação a ser removida é procurada na lista de simulações. Se encontrada, é eliminada.
- Linha 50: Atualiza o menu da vista de simulações do Tiles.
- Linha 51: A chave `simulations` é devolvida. Isto irá apresentar a vista de simulações Tiles:
<!-- action RetirerSimulation -->
<action name="SupprimerSimulation" class="web.actions.Supprimer">
...
<result name="simulations" type="tiles">simulations</result>
</action>
A vista «Simulações em mosaico» foi apresentada na Secção 19.9.
19.11. Voltar ao formulário
A partir da vista «Simulações em mosaico», o utilizador pode regressar ao formulário:
![]() |
![]() |
- em [1], clique na ligação para voltar ao formulário
- em [2], aparece um formulário vazio
O link [Voltar ao formulário de simulação] está definido no fragmento [Entete.jsp]:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<table>
<tr>
<td><h1><s:text name="Pam.titre"/></h1></td>
<td>
...
<s:if test="menu.retourFormulaire">
|<a href="<s:url action="RetourFormulaire"/>"><s:text name="Menu.RetourFormulaire"/></a><br/>
</s:if>
...
</td>
</tr>
</table>
- Linha 10: O link aponta para a ação [ReturnForm]. Esta é definida da seguinte forma no ficheiro [struts.xml]:
<!-- action RetourFormulaire -->
<action name="RetourFormulaire" >
<result type="redirectAction">
<param name="actionName">Formulaire!input</param>
<param name="namespace">/</param>
</result>
</action>
Podemos ver que esta ação não está associada a nenhuma classe. Simplesmente redireciona o navegador do cliente para a ação [/Form!input]. Estamos, portanto, na mesma situação que ao apresentar a vista inicial explicada na secção 19.7. Assim, regressamos a esta vista inicial [2].
19.12. Ver a lista de simulações
A partir das vistas «Simulation Tiles» ou «Input», o utilizador pode solicitar a visualização das simulações:
![]() |
![]() |
- em [1], clique na ligação [Ver Simulações]
- em [2], é apresentada a lista de simulações
O link [Ver simulações] está definido no fragmento [Entete.jsp]:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<table>
<tr>
<td><h1><s:text name="Pam.titre"/></h1></td>
<td>
...
<s:if test="menu.voirSimulations">
|<a href="<s:url action="VoirSimulations"/>"><s:text name="Menu.VoirSimulations"/></a><br/>
</s:if>
...
</td>
</tr>
</table>
- Linha 10: O link [Ver Simulações] chama a ação [ViewSimulations]. Esta está definida da seguinte forma no ficheiro [struts.xml]:
<!-- action VoirSimulations -->
<action name="VoirSimulations" class="web.actions.Voir">
<result name="success" type="tiles">simulations</result>
</action>
A ação [ViewSimulations] está associada à classe [View] sem especificar um método. Por conseguinte, será executado o método [View].execute. A classe [View] é a seguinte:
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;
}
}
A ação [View] faz apenas uma coisa: define o menu para a vista de simulações do Tiles (linha 8). Não existe um método execute. Por isso, será executado o da classe pai [ActionSupport]. Sabemos que este não faz nada, exceto devolver o sinalizador de sucesso.
De volta à ação em [struts.xml]:
<!-- action VoirSimulations -->
<action name="VoirSimulations" class="web.actions.Voir">
<result name="success" type="tiles">simulations</result>
</action>
Na linha 3, vemos que a chave «success» aciona a exibição da vista de simulações do Tiles. Isto foi descrito na página 157.
19.13. Limpar a simulação atual
A partir da vista de simulação Tiles, o utilizador pode solicitar a limpeza da simulação atual:
![]() |
![]() |
- em [1], a simulação atual é apagada
- em [2], o formulário de entrada é apagado
O link [Limpar Simulação] está definido no fragmento [Entete.jsp]:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<table>
<tr>
<td><h1><s:text name="Pam.titre"/></h1></td>
<td>
...
<s:if test="menu.effacerSimulation">
|<a href="<s:url action="Formulaire!input"/>"><s:text name="Menu.EffacerSimulation"/></a><br/>
</s:if>
...
</td>
</tr>
</table>
Na linha 10, vemos que o link [Limpar simulação] aciona a ação [Form!input]. Sabemos que esta ação leva à vista inicial [2].
19.14. Terminar a sessão atual
A partir de qualquer vista de mosaicos, o utilizador pode solicitar o encerramento da sessão:
![]() |
![]() |
![]() |
- Em [1], partimos da vista de simulações e encerramos a sessão
- Em [2], o formulário de entrada está vazio. Solicitamos a visualização das simulações.
- Em [3], a lista de simulações está agora vazia.
O link [Terminar sessão] está definido no fragmento [Entete.jsp]:
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<table>
<tr>
<td><h1><s:text name="Pam.titre"/></h1></td>
<td>
...
<s:if test="menu.terminerSession">
|<a href="<s:url action="TerminerSession"/>"><s:text name="Menu.TerminerSession"/></a><br/>
</s:if>
</td>
</tr>
</table>
Na linha 10, vemos que o link [End Session] aciona a ação [EndSession]. Esta está definida da seguinte forma no ficheiro [struts.xml]:
<action name="TerminerSession" class="web.actions.Terminer">
<result name="success" type="redirectAction">
<param name="actionName">Formulaire!input</param>
<param name="namespace">/</param>
</result>
</action>
- Linha 1: Podemos ver que a classe [Terminer] será instanciada e o seu método execute será chamado.
- Linhas 2–5: Após a execução do método [Terminer].execute, o utilizador será redirecionado para a vista de entrada inicial. Isto explica o Ecrã 2.
A classe [Terminer] é a seguinte:
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;
}
}
A função da ação [Finish] é limpar os atributos da sessão atual.
- Linha 7: A ação [Terminate] implementa a interface [SessionAware] para aceder à sessão.
- linha 10: o dicionário da sessão
- linha 13: o método execute é chamado
- linha 15: limpa o dicionário da sessão. Como resultado, a lista de simulações atualmente na sessão desaparecerá. Isto explica a tela n.º 3.
- Linha 16: Retorna a chave «success», que, como vimos, exibirá a vista Tiles com o valor [2].
19.15. Conclusão
Comentámos exaustivamente a versão 1 do nosso estudo de caso, que funciona com uma camada [de negócios] simulada:
![]() |
Resta apenas «ligar» a camada de negócios real à camada [web].






















