19. Estudo de caso – versão 1
19.1. A camada [metier] simulada
Voltemos à arquitetura da aplicação que estamos a construir:
![]() |
Dispomos do arquivo das camadas [metier, dao, jpa] e apresentámos os elementos dessas camadas que a camada [web] deveria conhecer. Estamos prontos para escrever esta camada utilizando o framework Struts.
Para simplificar os testes da nossa aplicação em desenvolvimento, vamos criar uma camada de negócio simulada que respeitará a interface da camada [metier]. A arquitetura ficará da seguinte forma:
![]() |
Vamos desenvolver a camada [web] com a camada [métier] simulada. Os testes serão mais simples de realizar, 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 posterior da camada simulada [metier] pela verdadeira arquitetura [metier, dao, jpa] não terá qualquer impacto no código da camada [web / struts2]. A camada [web / struts2] que vamos desenvolver agora poderá ser utilizada tal como está.
A camada simulada [metier] que vamos 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 {
// lista de funcionários
private Map<String, Employe> hashEmployes = new HashMap<String, Employe>();
private List<Employe> listEmployes;
// obter a folha de vencimento
public FeuilleSalaire calculerFeuilleSalaire(String SS,
double nbHeuresTravaillées, int nbJoursTravaillés) {
// recuperar o funcionário com o n.º SS
Employe e = hashEmployes.get(SS);
// emitir uma folha de vencimento fictícia
return new FeuilleSalaire(e, new Cotisation(3.49, 6.15, 9.39, 7.88), new ElementsSalaire(100, 100, 100, 100, 100));
}
// lista de funcionários
public List<Employe> findAllEmployes() {
if (listEmployes == null) {
// criar uma lista de dois funcionários
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)));
// dicionário de funcionários
for (Employe e : listEmployes) {
hashEmployes.put(e.getSS(), e);
}
}
// apresenta a lista de funcionários
return listEmployes;
}
}
- linha 11: a classe [MetierSimule] implementa a interface [IMetier] que a camada real [metier] implementa.
- linha 14: um dicionário de funcionários indexados pelo seu n.º INSEE
- linha 15: a lista de funcionários
- linhas 27-39: implementação do método findAllEmployes da interface [IMetier].
- linhas 30-33: criação de uma lista com dois funcionários
- linhas 34-36: criação do dicionário de funcionários indexado pelo n.º INSEE
- linhas 18-24: implementação do método calculerSalaire da interface [IMetier]. Aqui, é apresentada uma folha de salário fictícia.
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]: os códigos-fonte da aplicação. As ações Struts encontram-se no pacote [web.actions].
- em [5]: a camada [metier] simulada
- em [6]: os arquivos utilizados. Encontram-se aqui os arquivos das diferentes ferramentas utilizadas: Spring, Tiles, Struts 2, o plugin de integração Struts 2 / Spring, o plugin de integração Struts 2 / Tiles.
- em [7]: o arquivo da camada [metier, dao, jpa] real. Este permite-nos aceder às entidades JPA, à interface [IMetier] e às classes [FeuilleSalaire] e [ElementsSalaire]. Todos estes elementos são, de facto, utilizados pela nossa classe [MetierSimule].
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: configuram o filtro Struts 2 – já visto
- linhas 22-24: configuram o listener do Spring – já visto
- linhas 9-11: configuram o listener do Tiles. A classe [org.apache.struts2.tiles.StrutsTilesListener] será instanciada no arranque da aplicação web. Em seguida, irá 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, ao iniciar a aplicação Struts, são instanciadas três classes:
- uma para o filtro Struts 2. É esta que corresponde ao «C» de MVC.
- outra para o listener do Spring. O Spring irá utilizar o ficheiro [applicationContext.xml] para instanciar as camadas [métier, dao, jpa] da aplicação. O Spring irá também instanciar, tal como num exemplo anterior, uma classe [Config] que conterá os dados de âmbito Application. Por fim, o Spring irá injetar em cada ação Struts que necessite disso uma referência a esta instância única [Config].
- Outra para o listener Tiles. Este framework irá assegurar a gestão das vistas. Voltaremos a este assunto em breve.
19.3.2. Configuração do framework Struts
O framework Struts é configurado 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>
<!-- internacionalização -->
<constant name="struts.custom.i18n.resources" value="messages" />
<!-- integração com o Spring -->
<constant name="struts.objectFactory.spring.autoWire" value="name" />
<!-- ações Struts/Tiles -->
<package name="default" namespace="/" extends="tiles-default">
<!-- ação por predefinição -->
<default-action-ref name="index" />
<action name="index">
<result type="redirectAction">
<param name="actionName">Formulaire</param>
<param name="namespace">/</param>
</result>
</action>
<!-- ação de formulário -->
<action name="Formulaire" class="web.actions.Formulaire" method="input">
<result name="success" type="tiles">saisie</result>
<result name="exception" type="tiles">exception</result>
</action>
<!-- ação 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>
<!-- ação 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>
<!-- ação RetourFormulaire -->
<action name="RetourFormulaire" >
<result type="redirectAction">
<param name="actionName">Formulaire</param>
<param name="namespace">/</param>
</result>
</action>
<!-- ação VoirSimulations -->
<action name="VoirSimulations" class="web.actions.Voir">
<result name="success" type="tiles">simulations</result>
</action>
<!-- ação 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>
<!-- ação 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 comentar as diferentes ações do Struts à medida que as formos estudando. Por enquanto, podemos destacar os seguintes pontos:
- linha 8: define o ficheiro de mensagens
- linha 10: define o modo de injeção dos beans Spring nas ações Struts. A injeção é feita com base no nome do bean. O campo da ação Struts que deve ser inicializado pelo Spring tem de ter o nome do bean a ser injetado.
- linha 25: define a vista a apresentar para a chave de navegação success da ação [Formulaire]. Vemos que o resultado <result> tem um atributo type='tiles' que não conhecemos. Conhecíamos o tipo redirect, que permite redirecionar o cliente para uma vista. Aqui, a vista do tipo tiles é gerida pelo framework Tiles. O tipo tiles está definido no ficheiro [struts-plugin.xml] do arquivo [struts2-tiles-plugin-2.2.3.1.jar]:
- linhas 3-5: a definição do tipo de resultado tiles.
- linha 2: este tipo está definido no pacote [tiles-default], que estende o pacote [struts-default].
- linha 14: define o pacote [default], no qual estarão todas as ações da aplicação. Para tirar partido da definição do tipo de vista tiles, o pacote estende o [tiles-default].
19.3.3. Configuração do framework Spring
O framework Spring é 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">
<!-- camadas de aplicação -->
<!-- web -->
<bean id="config" class="web.Config" init-method="init">
<property name="metier" ref="metier"/>
</bean>
<!-- negócio -->
<bean id="metier" class="metier.MetierSimule"/>
</beans>
- linha 13: a camada [metier] simulada, instanciada pela classe [metier.MetierSimule]
- linhas 9-11: definem um bean denominado config. Tal como num exemplo analisado anteriormente, este bean servirá para encapsular as informações do âmbito Application. 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 {
// camada de negócio inicializada pelo Spring
private IMetier metier;
// lista de funcionários
private List<Employe> employes;
// erros
private Exception initException;
// construtor
public Config() {
}
// método do Spring para inicialização do objeto
public void init() {
// solicita-se a lista de funcionários
try {
employes = metier.findAllEmployes();
} catch (Exception ex) {
initException = ex;
}
}
// getters e setters
...
}
Voltemos à configuração do bean config:
<bean id="config" class="web.Config" init-method="init">
<property name="metier" ref="metier"/>
</bean>
<!-- logística -->
<bean id="metier" class="metier.Metier">
...
</bean>
Na linha 2, pode-se ver que o bean metier da linha 5 é injetado (ref) no campo denominado metier (name) do objeto [Config]. O bean metier é uma referência na camada [metier]:
![]() |
Para interagir com a camada [metier], todas as ações Struts da camada [web] necessitarão de uma referência a esta. Pode dizer-se que a referência à camada [metier] é um dado de âmbito Application. Todas as solicitações de todos os utilizadores irão necessitar dela. É por isso que colocamos essa referência no objeto [Config]. Além disso, na linha 1, a configuração do bean config possui um atributo init-method. Este atributo indica o método do bean a ser executado após a instanciação do bean. Aqui, indica-se que, após a instanciação da classe [web.Config], deve ser executado o seu método init. Este é o seguinte:
// camada de lógica de negócio inicializada pelo Spring
private IMetier metier;
// lista de funcionários
private List<Employe> employes;
// erros
private Exception initException;
// construtor
public Config() {
}
// método do Spring para inicialização do objeto
public void init() {
// solicita-se a lista de funcionários
try {
employes = metier.findAllEmployes();
} catch (Exception ex) {
initException = ex;
}
}
Quando o método init é executado, o campo metier da classe foi instanciado pelo Spring. O método init tem, portanto, acesso à camada de negócio da interface [IMetier] (linha 2):
package metier;
import java.util.List;
import jpa.Employe;
public interface IMetier {
// obter a folha de vencimentos
FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés );
// lista de funcionários
List<Employe> findAllEmployes();
}
- linha 8: o método permite calcular o salário de um funcionário
- linha 10: o método permite obter a lista de funcionários
Vemos que o método init solicita a lista de funcionários à camada [métier]. Esta lista é registada no campo da linha 4. Se ocorrer uma exceção, esta é armazenada no campo da linha 6.
Em conclusão, o objeto único [Config] contém:
- uma referência à camada [métier]
- a lista de funcionários
19.4. Geração das vistas Tiles
Como vimos no ficheiro de configuração do Struts, as vistas serão geradas pelo framework Tiles. Iremos explicar apenas o estritamente necessário para a criação da nossa aplicação.
O Tiles permite gerar vistas a partir de uma página-mestre. Esta, aqui denominada [MasterPage.JSP], será a combinação dos seguintes fragmentos JSP:
Estes fragmentos JSP estão definidos no projeto NetBeans:
![]() |
O framework Tiles permite-nos definir quais os fragmentos que serão inseridos na página-mestre.
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 de fragmentos JSP. Neste caso, é composta por seis fragmentos, os das linhas 17 a 23. Durante a geração, podem existir entre 0 e 6 fragmentos reunidos na página-mestre. Esta geração é controlada 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>
<!-- a página principal -->
<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>
<!-- a vista de introdução de dados -->
<definition name="saisie" extends="masterPage">
<put-attribute name="saisie" value="/Saisie.JSP"/>
</definition>
<!-- a vista de simulação -->
<definition name="simulation" extends="saisie">
<put-attribute name="simulation" value="/Simulation.JSP"/>
</definition>
<!-- a vista de simulações -->
<definition name="simulations" extends="masterPage">
<put-attribute name="simulations" value="/Simulations.JSP"/>
</definition>
<!-- a vista de exceção -->
<definition name="exception" extends="masterPage">
<put-attribute name="exception" value="/Exception.JSP"/>
</definition>
<!-- a vista de erro -->
<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), introdução (linha 20), simulação (linha 25), simulações (linha 30), exceção (linha 35), erro (linha 40).
- linhas 9-17: definem uma vista denominada masterPage (name) e associada à página mestre [MasterPage.JSP] (template). Vimos que esta página JSP definia seis subvistas. Uma vista Tiles associada à página-mãe deve indicar o fragmento JSP associado a cada uma das seis subvistas. Verifica-se que algumas subvistas recebem como valor (value) a cadeia vazia. Estas subvistas não serão incluídas na página principal [MasterPage.JSP]. A vista Tiles, denominada masterPage, é, portanto, constituída apenas pelo subfragmento [Entete.JSP].
- linhas 20-22: definem uma vista denominada saisie (name) e que estende (extends) a vista denominada masterPage, vista anteriormente. Isto significa que ela herda todas as definições da vista masterPage. A sua definição é equivalente à 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>
Vemos que está associada à página JSP [MasterPage.JSP] e que, como tal, deve definir as seis subvistas dessa página. Verifica-se que a definição da linha 9 anula a da linha 4. A vista Tiles, denominada saisie, é, portanto, constituída pelos fragmentos JSP e [Entete.JSP, Saisie.JSP]
Se continuarmos este raciocínio, obtemos a seguinte tabela:
vista Tiles | páginas JSP |
19.5. Os ficheiros de mensagens
A aplicação foi internacionalizada. As mensagens encontram-se nos ficheiros [messages.properties] e [Formulaire.properties].
O ficheiro [messages.properties] é o seguinte:
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
# formatos
Format.heure = {0,time}
Format.nombre = {0,number,#0.0##}
Format.pourcent = {0,number,##0.00' %'}
Format.monnaie={0,number,##0.00' \u20ac'}
# lista de simulações
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
# mensagem de erro
Erreur.sessionexpiree=La session a expir\u00e9
Erreur.numSimulation=N\u00b0 de simulation incorrect
# erro de conversão
xwork.default.invalid.fieldvalue=Valeur invalide pour le champ "{0}".
O ficheiro [Formulaire.properties] é o seguinte:
# para que os valores duplos fiquem no formato local
double.format={0,number,#0.00##}
# mensagem de erro
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 Tiles 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 vista inicial
Para analisar a aplicação, iremos apresentá-la com base nas diferentes ações do utilizador. Em cada caso, analisaremos a ação Struts que executa essa ação e a vista Tiles que é enviada em resposta.
Em [struts.xml], temos as seguintes ações:
<!-- ação por predefinição -->
<default-action-ref name="index" />
<action name="index">
<result type="redirectAction">
<param name="actionName">Formulaire!input</param>
<param name="namespace">/</param>
</result>
</action>
<!-- ação do formulário -->
<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 por predefinição da aplicação é [Formulaire!input].
A classe [Formulaire] é a seguinte:
package web.actions;
...
public class Formulaire extends ActionSupport implements Preparable, SessionAware {
// configuração inicializada pelo Spring
private Config config;
// lista de funcionários
private List<Employe> employes;
// lista de erros
private List<Erreur> erreurs;
// folha de salário
private FeuilleSalaire feuilleSalaire;
// entradas
private String comboEmployesValue;
private Double heuresTravaillees;
private Integer joursTravailles;
// sessão
private Map<String, Object> session;
// menu
private Menu menu;
@Override
public void prepare() throws Exception {
...
}
@Override
public String input() {
....
}
// cálculo do salário
public String calculSalaire() {
...
}
}
@Override
public void validate() {
...
}
@Override
public void setSession(Map<String, Object> map) {
session = map;
}
// getters e setters
...
}
- linha 4: a ação [Formulaire] implementa a interface Preparable. Esta tem apenas um método, o método prepare da linha 24. Este método é executado uma vez antes de qualquer método da ação. Serve geralmente para inicializar o modelo da ação.
O modelo da ação é constituído pelas linhas 6 a 21:
- linha 7: o campo config é inicializado pelo Spring, tal como foi explicado. Este campo dá acesso aos dados de âmbito da aplicação:
- uma referência na camada [métier]
- uma referência à lista de funcionários.
- uma referência à exceção que possa ter ocorrido durante a instanciação do objeto [Config]
- linha 9: uma lista de funcionários. Esta lista irá alimentar a lista suspensa de funcionários no fragmento [Saisie.JSP].
- linha 11: uma lista de erros. Esta irá alimentar o fragmento [Erreur.JSP].
- linha 21: a lista de opções do menu do fragmento [Entete.JSP]
![]() |
No [1], os links do menu apresentado são controlados pelo campo menu da ação [Formulaire].
O método prepare é executado antes do método input. É o seguinte:
@Override
public void prepare() throws Exception {
// erro de configuração?
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: recupera-se a exceção no objeto [Config] instanciado pelo Spring
- linha 5: se tiver ocorrido uma exceção na instanciação do objeto [Config], então inicializa-se a lista de erros da linha 11. A classe [Erreur] é a seguinte:
package web.entities;
import java.io.Serializable;
public class Erreur implements Serializable{
public Erreur() {
}
// campos
private String classe;
private String message;
// construtor
public Erreur(String classe, String message){
this.setClasse(classe);
this.message=message;
}
// getters e setters
...
}
A classe serve para armazenar a pilha de exceções:
- linha 11: a classe da exceção
- linha 12: a mensagem da exceção
Voltemos ao método prepare:
- linha 13: a lista de funcionários do objeto [Config] é armazenada no campo employes 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 input limita-se a definir 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 {
// elementos do 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 e setters
...
}
- linhas 8-13: existem 6 links possíveis no menu
- linhas 18-25: o construtor da classe permite definir os links que devem ser exibidos e aqueles que não devem.
Os links do menu são apresentados em [Entete.JSP], fragmento JSP presente em todas as vistas Tiles. Cada ação terá um campo menu para controlar a exibição do menu de [Entete.JSP].
Voltemos ao método input:
@Override
public String input() {
if (erreurs == null) {
// menu
menu = new Menu(true, false, false, true, false, true);
return SUCCESS;
} else {
// menu
menu = new Menu(false, false, false, false, false, false);
return "exception";
}
}
- linhas 3-6: se a lista de erros estiver vazia, o menu [Faire la simulation, Voir les simulations, Terminer la session] será exibido e a chave input será devolvida.
- linhas 9-10: se a lista de erros não estiver vazia, o menu ficará vazio e a chave exception será devolvida.
Voltemos à configuração da ação [Formulaire] em [struts.xml]:
<!-- ação Formulário -->
<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 apresenta a vista Tiles denominada saisie
- linha 4: a chave exception apresenta a vista Tiles denominada exception
Comecemos pela vista Tiles denominada saisie. 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 [Fazer a simulação (linhas 8-10), Apagar a simulação (linhas 11-13), Ver simulações (linhas 14-16), Voltar ao formulário (linhas 17-19), Guardar a simulação (linhas 20-22), Terminar a sessão (linhas 23-25).
- linhas 8, 11, 14, 17, 20, 23: a exibição dos links é controlada pelo campo menu da ação atual.
Note-se que o fragmento [Entete.JSP] apresenta uma tabela HTML (linhas 4-28), mas não é uma página HTML completa. Não se deve esquecer que todas as vistas da aplicação são inseridas 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. Trata-se da seguinte vista:
![]() |
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(){
// enviar o formulário
document.forms['Saisie'].elements['action'].name='action:Formulaire!calculSalaire'
document.forms['Saisie'].submit();
}
</script>
<!-- introdução de informações -->
<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 o atributo action. Por predefinição, temos action='Formulaire'.
- linha 18: exibição da lista suspensa dos funcionários. O conteúdo da lista suspensa (atributo list) é fornecido pelo campo employes da ação atual. O atributo value das opções corresponderá ao n.º SS dos funcionários (atributo listKey). O texto apresentado para cada opção será o nome próprio e o apelido do funcionário (atributo listValue). O n.º SS do colaborador selecionado na lista suspensa será inserido no campo [Formulaire].comboEmployesvalue (atributo name).
- linha 19: campo de introdução das horas trabalhadas. O valor apresentado (atributo value) corresponde ao do campo heuresTravaillees da ação [Formulaire], no seguinte formato (Formulaire.properties):
double.format={0,number,#0.00##}
O valor será lançado no campo [Formulaire].heuresTravaillees (atributo name).
- linha 20: campo de introdução dos dias trabalhados. O valor apresentado (atributo value) é o do campo joursTravailles da ação [Formulaire].
O valor será lançado no campo [Formulaire].joursTravailles (atributo name).
Por fim, a vista Tiles saisie apresentada no arranque, quando não existem erros, é a seguinte:
![]() |
Voltemos à configuração da ação [Formulaire]:
<!-- ação Formulário -->
<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 [Formulaire].input também pode gerar a chave exception da linha 4. Neste caso, é apresentada a vista Tiles denominada exception. Esta é composta pelos fragmentos [Entete.JSP] e [Exception.JSP]. Já apresentámos o fragmento [Entete.JSP]. O fragmento [Exception.JSP] é o seguinte:
![]() |
Esta é a página inicial da versão 2 da aplicação quando o Sgbd não foi iniciado. O código JSP do 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.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<Erreur> erros da ação [Formulaire]. Recorde-se que, em caso de erro, tinha sido armazenada aí uma pilha de exceções.
19.8. Fazer uma simulação
Depois de obter a vista inicial, é possível efetuar um cálculo de salário através do link [Faire une simulation].
19.8.1. Validação dos dados introduzidos
Consideremos a seguinte sequência:
![]() |
- em [1], uma entrada errada
- em [2], a resposta enviada.
Consideremos a configuração da ação [Formulaire] em [struts.xml]:
<!-- ação do formulário -->
<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>
Sabe-se que, em caso de erro de validação, o interceptor de validação devolve a chave input. É, portanto, a vista Tiles saisie que é devolvida. O processo de validação faz com que os campos com erros sejam acompanhados de mensagens de erro.
A validação da ação [Formulaire] é assegurada pelo seguinte ficheiro [Formulaire-validation.xml]:
<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
<validators>
<field name="heuresTravaillees" >
<field-validator type="required" short-circuit="true">
<message key="heuresTravaillées.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="heuresTravaillées.error"/>
</field-validator>
<field-validator type="double" short-circuit="true">
<param name="minInclusive">0</param>
<param name="maxInclusive">300</param>
<message key="heuresTravaillées.error"/>
</field-validator>
</field>
<field name="joursTravailles" >
<field-validator type="required" short-circuit="true">
<message key="joursTravaillés.error"/>
</field-validator>
<field-validator type="conversion" short-circuit="true">
<message key="joursTravaillés.error"/>
</field-validator>
<field-validator type="int" short-circuit="true">
<param name="min">0</param>
<param name="max">31</param>
<message key="joursTravaillés.error"/>
</field-validator>
</field>
</validators>
- as linhas 6-20 verificam se o campo heuresTravaillees é um número real no intervalo [0,300].
- As linhas 22 a 36 verificam se o campo joursTravailles é um número inteiro no intervalo [0,31].
Voltemos à configuração da ação [Formulaire] em [struts.xml]:
<!-- ação Formulário -->
<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. É, portanto, a vista Tiles saisie que é devolvida.
Recorde-se que esta é composta pelos fragmentos [Entete.JSP] e [Saisie.JSP], em que [Entete.JSP] contém um título e um conjunto de opções e [Saisie.JSP] o formulário de preenchimento. Em caso de erros de introdução de dados, o processo de validação faz com que os campos com erros sejam acompanhados por mensagens de erro e, além disso, exibam o seu valor incorreto. O fragmento [Entete.JSP] não intervém de forma alguma 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 menu do modelo (linhas 8, 11, 14, 17, 20, 23). Quando ocorre um erro, este modelo não é atualizado pela ação, pelo que se obtém uma página sem menu. Para resolver este problema, a classe [Formulaire] dispõe do seguinte método validate:
package web.actions;
import com.opensymphony.xwork2.ActionSupport;
...
public class Formulaire extends ActionSupport implements Preparable, SessionAware {
...
// menu
private Menu menu;
@Override
public void prepare() throws Exception {
...
}
@Override
public String input() {
...
}
// cálculo do salário
public String calculSalaire() {
...
}
@Override
public void validate() {
// erros?
if (!getFieldErrors().isEmpty()) {
// menu
menu = new Menu(true, false, false, true, false, true);
}
}
// getters e setters
...
}
- linha 27: sabe-se que, quando está presente, o método validate é executado pelo processo de validação. Aproveita-se esta oportunidade para atualizar o menu da linha 4, que faz parte do modelo do fragmento [Entete.JSP].
- linhas 29-32: se tiverem ocorrido erros de validação, posiciona-se o menu para voltar a apresentar a vista Tiles saisie. Se não tiverem ocorrido erros de validação, não se faz nada. É o método calculSalaire que fica então encarregado de criar o modelo da vista a apresentar.
19.8.2. O cálculo do salário
Voltemos ao código JSP do 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 [Faire la simulation], é executada a função JavaScript doSimulation. Esta está definida no fragmento [Saisie.JSP]:
<script language="javascript" type="text/javascript">
function doSimulation(){
// enviar o formulário
document.forms['Saisie'].elements['action'].name='action:Formulaire!calculSalaire'
document.forms['Saisie'].submit();
}
</script>
<!-- introdução de informações -->
<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 [Formulaire]. Este campo permitir-nos-á especificar a ação e o método que devem ser executados no POST do formulário. Talvez nos lembremos dos primeiros exemplos, em que estes podem ser especificados num parâmetro denominado action:Action!método. 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 [Faire la simulation] do fragmento [Entete.JSP].
- linha 4: altera-se o atributo name do campo oculto action. Assegura-se que este tenha o formato action:Action!método, tal como esperado pelo Struts.
- linha 5: o formulário denominado Saisie da linha 5 é enviado. Consequentemente, é enviada a seguinte cadeia de parâmetros:
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.
O método [Formulaire].calculSalaire é o seguinte:
// cálculo do salário
public String calculSalaire() {
try {
// cálculo do salário
feuilleSalaire = config.getMetier().calculerFeuilleSalaire(comboEmployesValue, heuresTravaillees, joursTravailles);
// a simulação é colocada na sessão
session.put("simulation", new Simulation(0, "" + heuresTravaillees, "" + joursTravailles, feuilleSalaire));
// menu
menu = new Menu(true, true, true, true, false, true);
// concluído
return "simulation";
} catch (Throwable th) {
...
}
}
- linha 5: o cálculo da folha de pagamento é solicitado à camada [métier]
- linha 7: insere-se um objeto do tipo Simulation na sessão do utilizador. Com efeito, pode ser necessário para uma consulta posterior. A classe [Simulation] é a seguinte:
package web.entities;
import java.io.Serializable;
import metier.FeuilleSalaire;
public class Simulation implements Serializable{
public Simulation() {
}
// campos de uma simulação
private Integer num;
private FeuilleSalaire feuilleSalaire;
private String heuresTravaillées;
private String joursTravaillés;
// gerador
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 e setters
...
}
- linha 12: o número da simulação. É incrementado a cada nova simulação registada.
- linha 13: a folha 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 apresenta o total das indemnizações do empregado
Veremos que a classe [Simulation] é o modelo do fragmento [Simulations.JSP], que apresenta todas as simulações realizadas.
Voltar ao método [Formulaire].calculSalaire:
// cálculo do salário
public String calculSalaire() {
try {
// cálculo do salário
feuilleSalaire = config.getMetier().calculerFeuilleSalaire(comboEmployesValue, heuresTravaillees, joursTravailles);
// colocamos a simulação na sessão
session.put("simulation", new Simulation(0, "" + heuresTravaillees, "" + joursTravailles, feuilleSalaire));
// menu
menu = new Menu(true, true, true, true, false, true);
// concluído
return "simulation";
} catch (Throwable th) {
...
}
- linha 9: atualização do menu
- linha 11: reenvio da chave de navegação simulation.
Voltar à configuração da ação [Formulaire]:
<!-- ação Formulário -->
<action name="Formulaire" class="web.actions.Formulaire">
...
<result name="simulation" type="tiles">simulation</result>
</action>
A linha 4 mostra que a chave de navegação simulation faz com que seja apresentada a vista Tiles denominada simulation. Esta é composta pelos seguintes fragmentos JSP: [Entete, Saisie, Simulation].
A vista apresentada é a seguinte:
![]() |
- em [1], o fragmento [Entete.JSP]
- em [2], o fragmento [Saisie.JSP]
- em [3], o fragmento [Simulation.JSP]. Recorde-se que a folha de vencimento apresentada é a folha de vencimento fictícia gerada pela camada [metier].
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/>
<!-- informações do funcionário -->
<span class="titreInfos">
<s:text name="Simulation.Infos.employe"/>
</span>
<br/><br/>
<table>
<!-- linha 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>
<!-- linha 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>
<!-- linha 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>
<!-- linha 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>
<!-- informações sobre contribuições -->
<br/>
<span class="titreInfos">
<s:text name="Simulation.Infos.cotisations"/>
</span>
<br/><br/>
<table>
<!-- linha 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>
<!-- linha 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>
<!-- informações sobre indemnizações -->
<br/>
<span class="titreInfos">
<s:text name="Form.Infos.indemnites"/>
</span>
<br/><br/>
<table>
<!-- linha 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>
<!-- linha 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>
<!-- informações sobre o salário -->
<br/>
<span class="titreInfos">
<s:text name="Simulation.Infos.Salaire"/>
</span>
<br/><br/>
<table>
<!-- linha 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>
<!-- linha 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>
<!-- Salário líquido-->
<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 fragmento apresenta as diferentes propriedades do campo [Formulaire].feuilleSalaire, que representa a folha de vencimento do funcionário.
Voltar ao método [Formulaire].calculSalaire:
// cálculo do salário
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. Tal aconteceria, nomeadamente, se a ligação com o Sgbd fosse interrompida. Nesse caso, trata-se da exceção que ocorre. Já nos deparámos com este caso durante a análise do método [Formulaire].input.
- linhas 7-11: cria-se uma lista de objetos Erreur a partir da pilha de exceções
- linha 13: define-se o menu
- linha 14: devolvemos a chave exception.
A chave exception irá apresentar a vista Tiles exception:
<!-- ação Formulário -->
<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
Depois de realizar uma simulação, o utilizador pode querer guardá-la na sessão.
![]() |
![]() |
- em [1], regista-se a simulação
- em [2], a resposta que apresenta a lista das simulações já realizadas, à qual é adicionada a nova simulação
O link [Enregistrer la simulation] encontra-se 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>
Vê-se que um clique no link provoca a execução da ação [EnregistrerSimulation]. Esta está configurada no ficheiro [struts.xml] da seguinte forma:
<!-- ação 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 [EnregistrerSimulation] está associada à classe [Enregistrer] e ao seu método execute.
A classe [Enregistrer] é a seguinte:
package web.actions;
...
public class Enregistrer extends ActionSupport implements SessionAware {
// sessão
private Map<String, Object> session;
// menu
private Menu menu;
@Override
public void setSession(Map<String, Object> session) {
this.session = session;
}
// execução da ação
public String execute() {
// recupera-se a última simulação da sessão
Simulation simulation = (Simulation) session.get("simulation");
if (simulation == null) {
return ERROR;
}
...
}
// getters e setters
...
}
- linha 4: uma vez que a ação precisa de ter acesso à sessão, implementa a interface SessionAware.
- linha 7: a sessão
- linha 9: o menu
Quando a ação [Enregistrer] foi instanciada, o seu método execute é executado. Recorde-se que a sua função é colocar na sessão a última simulação. Esta será adicionada à lista de simulações já realizadas, também ela guardada na sessão.
- linha 19: recupera-se da sessão a última simulação que aí foi colocada.
- linhas 20-22: se não a encontrarmos, é provável que a sessão tenha expirado. Com efeito, esta tem uma duração limitada, que pode ser definida no ficheiro [web.xml], que configura a aplicação.
- linha 21: devolve-se a chave error.
Voltar à configuração da ação [EnregistrerSimulation]:
<!-- ação 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>
Vê-se que a chave error (linha 3) faz com que seja apresentada a vista « Tiles» denominada erreur. Esta é 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>
Voltar ao método [Enregistrer].execute:
// execução da ação
public String execute() {
// recupera-se a última simulação da sessão
Simulation simulation = (Simulation) session.get("simulation");
if (simulation == null) {
return ERROR;
}
// recupera-se o n.º da última simulação
Integer numDerniereSimulation = (Integer) session.get("numDerniereSimulation");
if (numDerniereSimulation == null) {
numDerniereSimulation = 0;
}
// incrementa-se esse número
numDerniereSimulation++;
// atribui-se o novo número à sessão
session.put("numDerniereSimulation", numDerniereSimulation);
// recupera-se a lista de simulações
List<Simulation> simulations = (List<Simulation>) session.get("simulations");
if (simulations == null) {
simulations = new ArrayList<Simulation>();
session.put("simulations", simulations);
}
// adiciona-se a simulação atual
simulation.setNum(numDerniereSimulation);
simulations.add(simulation);
// exibe-se a lista de simulações
menu = new Menu(false, false, false, false, true, true);
return "simulations";
}
- linhas 9-16: as diferentes simulações são numeradas a partir de 1. O último número atribuído é guardado na sessão sob a chave numDerniereSimulation. O código das linhas 9-16 consiste em recuperar essa chave e incrementar o valor que lhe está associado.
- linhas 18-22: a lista de simulações é guardada na sessão associada à chave simulations. As linhas 18-22 consistem em recuperar essa lista, caso exista, ou criá-la, caso não exista.
- linhas 24-25: uma vez obtida a lista de simulações, adiciona-se a ela a simulação atual (linha 25). Anteriormente, a simulação atual recebeu um número (linha 24).
- linha 27: define-se o menu a apresentar
- linha 28: define-se a chave de navegação simulations.
Voltar à configuração da ação [EnregistrerSimulation] em [struts.xml]:
<!-- ação 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>
Na linha 4, a chave simulations faz com que seja apresentada a vista Tiles denominada simulations. Esta vista é composta pelos fragmentos [Entete.JSP] e [Simulations.JSP]. A vista apresentada é a seguinte:
![]() |
- em [1], o fragmento [Entete.JSP] que já conhecemos bem.
- 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" %>
<!-- lista de simulações vazia -->
<s:if test="#session['simulations']==null || #session['simulations'].size()==0">
<h2><s:text name="Pam.SimulationsVides.titre"/></h2>
</s:if>
<!-- lista de simulações não vazia -->
<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"/>">Remover</a></td>
</tr>
</s:iterator>
</table>
</s:if>
- linhas 5-7: se não houver simulações na sessão, então é apresentada a seguinte vista:
![]() |
- linhas 13-21: exibição dos cabeçalhos das colunas da tabela
- linhas 23-55: iteração sobre 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>
Vê-se que o link aponta para a ação [SupprimerSimulation] com o parâmetro id, que representa o número da simulação a remover da lista.
- linhas 28-54: a cada iteração na 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], elimina-se a simulação n.º 1
- em [2], a simulação n.º 1 foi eliminada
A ligação [Retirer] 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" %>
<!-- lista de simulações vazia -->
<s:if test="#session['simulations']==null || #session['simulations'].size()==0">
<h2><s:text name="Pam.SimulationsVides.titre"/></h2>
</s:if>
<!-- lista de simulações não vazia -->
<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"/>">Remover</a></td>
</tr>
</s:iterator>
</table>
</s:if>
- linhas 16-18: geram a ligação 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 remover.
A ação [SupprimerSimulation] está 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>
<!-- internacionalização -->
<constant name="struts.custom.i18n.resources" value="messages" />
<!-- integração com o Spring -->
<constant name="struts.objectFactory.spring.autoWire" value="name" />
<!-- ações Struts /Tiles -->
<package name="default" namespace="/" extends="tiles-default">
...
<!-- ação RetirerSimulation -->
<action name="SupprimerSimulation" class="web.actions.Supprimer">
<result name="erreur" type="tiles">erreur</result>
<result name="simulations" type="tiles">simulations</result>
</action>
...
</package>
<!-- Adicionar pacotes aqui -->
</struts>
- linha 16: a ação [SupprimerSimulation] está associada à classe [Supprimer]. Como não é especificado nenhum método, será executado o seu método execute. A classe [Supprimer] é a seguinte:
package web.actions;
...
public class Supprimer extends ActionSupport implements SessionAware {
// sessão
private Map<String, Object> session;
// ID da simulação a eliminar
private String id;
// menu
private Menu menu;
@Override
public void setSession(Map<String, Object> session) {
this.session = session;
}
// execução da ação
public String execute() {
// recuperam-se as simulações da sessão
List<Simulation> simulations = (List<Simulation>) session.get("simulations");
if (simulations == null) {
// situação anómala — a sessão deve ter expirado
menu = new Menu(false, false, false, false, true, true);
return "erreur";
}
// teste de ID
int num = 0;
boolean erreur = false;
try {
num = Integer.parseInt(id);
erreur = num <= 0;
} catch (NumberFormatException ex) {
// anormal
erreur = true;
}
// erro?
if (erreur) {
menu = new Menu(false, false, false, false, true, true);
return "erreur";
}
// procura-se a simulação a eliminar
for (int i = 0; i < simulations.size(); i++) {
if (num == simulations.get(i).getNum()) {
simulations.remove(i);
break;
}
}
// a lista de simulações está a ser apresentada
menu = new Menu(false, false, false, false, true, true);
return "simulations";
}
// getters e setters
...
}
- linha 4: a ação [Supprimer] implementa a interface [SessionAware] para ter acesso à sessão.
- linha 7: a sessão
- linha 9: o número da simulação a eliminar. Recorde-se, de facto, que se chega à instanciação da classe [Supprimer] através da 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 remover. Este número será armazenado no campo id da linha 9.
- linha 11: o menu para a vista que será apresentada em resposta ao pedido
- linha 19: o método execute que irá gerar a resposta à solicitação.
- linha 21: recupera-se a lista das simulações já realizadas na sessão
- linhas 22-26: o facto de não ser possível recuperar esta lista na sessão significa provavelmente que a sessão expirou. Já nos deparámos com este caso. Devolvemos a chave erreur, que faz com que seja apresentada a vista Tiles erreur:
<!-- ação RetirerSimulation -->
<action name="SupprimerSimulation" class="web.actions.Supprimer">
<result name="erreur" type="tiles">erreur</result>
...
</action>
A vista Tiles erreur foi apresentada no parágrafo 19.9.
- linhas 28-36: verifica-se se a cadeia id da linha 9 representa efetivamente um número inteiro >0.
- linhas 38-40: se não for esse o caso, devolve-se novamente a chave erreur, que fará com que seja apresentada a vista Tiles erreur
- linhas 43-48: procura-se a simulação a remover na lista de simulações. Se for encontrada, é eliminada.
- linha 50: atualiza-se o menu para a vista Tiles simulations.
- linha 51: é devolvida a chave simulations. Esta irá apresentar a vista Tiles simulations:
<!-- ação RetirerSimulation -->
<action name="SupprimerSimulation" class="web.actions.Supprimer">
...
<result name="simulations" type="tiles">simulations</result>
</action>
A vista Tiles simulations foi apresentada no parágrafo 19.9.
19.11. Voltar ao formulário
A partir da vista Tiles simulations, o utilizador pode regressar ao formulário:
![]() |
![]() |
- em [1], clica-se na ligação para regressar ao formulário
- em [2], é apresentado um formulário vazio
O link [Retour au formulaire de simulation] 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 [RetourFormulaire]. Esta está definida da seguinte forma no ficheiro [struts.xml]:
<!-- ação RetourFormulaire -->
<action name="RetourFormulaire" >
<result type="redirectAction">
<param name="actionName">Formulaire!input</param>
<param name="namespace">/</param>
</result>
</action>
Vê-se que esta ação não está associada a nenhuma classe. Limita-se a redirecionar o navegador do cliente para a ação [/Formulaire!input]. Estamos, portanto, na mesma situação que durante a exibição da vista inicial explicada no parágrafo 19.7. Encontramos, assim, esta vista inicial [2].
19.12. Ver a lista de simulações
A partir das vistas Tiles simulation ou saisie, o utilizador pode solicitar a visualização das simulações:
![]() |
![]() |
- em [1], clica-se na ligação [Voir les simulations]
- em [2], encontra-se a lista de simulações
O link [Voir les simulations] 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 [Voir les simulations] chama a ação [VoirSimulations]. Esta está definida da seguinte forma no ficheiro [struts.xml]:
<!-- ação VoirSimulations -->
<action name="VoirSimulations" class="web.actions.Voir">
<result name="success" type="tiles">simulations</result>
</action>
A ação [VoirSimulations] está associada à classe [Voir] sem especificação do método. Por conseguinte, será executado o método [Voir].execute. A classe [Voir] é 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 e setters
public Menu getMenu() {
return menu;
}
public void setMenu(Menu menu) {
this.menu = menu;
}
}
A ação [Voir] faz apenas uma coisa: posicionar o menu para a vista «Tiles simulations» (linha 8). Não existe nenhum método execute. Por conseguinte, será executado o método da classe pai [ActionSupport]. Sabemos que esta não faz nada além de devolver a chave success.
De volta à ação em [struts.xml]:
<!-- ação VoirSimulations -->
<action name="VoirSimulations" class="web.actions.Voir">
<result name="success" type="tiles">simulations</result>
</action>
Na linha 3, vemos que a chave success leva à exibição da vista Tiles simulations. Esta foi descrita na página 161.
19.13. Apagar a simulação atual
A partir da vista Tiles simulation, o utilizador pode solicitar a eliminação da simulação atual:
![]() |
![]() |
- em [1], apaga-se a simulação atual
- em [2], o formulário de introdução de dados aparece vazio
A ligação [Effacer la simulation] está definida 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, verifica-se que a ligação [Effacer la simulation] aciona a ação [Formulaire!input]. Sabe-se que esta ação conduz à vista inicial [2].
19.14. Terminar a sessão atual
A partir de todas as vistas Tiles, o utilizador pode solicitar o encerramento da sessão:
![]() |
![]() |
![]() |
- em [1], a partir da vista das simulações, encerra-se a sessão
- em [2], o formulário de introdução de dados aparece vazio. Solicita-se a visualização das simulações.
- em [3], a lista de simulações está agora vazia.
O link [Terminer la session] 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, verifica-se que a ligação [Terminer la session] aciona a ação [TerminerSession]. 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: verifica-se que a classe [Terminer] vai ser instanciada e o seu método execute executado.
- linhas 2-5: após a execução do método [Terminer].execute, haverá um redirecionamento para a vista inicial de introdução de dados. É isso que explica o ecrã n.º 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 {
// sessão
private Map<String, Object> session;
@Override
public String execute() {
// encerramento da sessão atual
session.clear();
return SUCCESS;
}
@Override
public void setSession(Map<String, Object> session) {
this.session = session;
}
}
A função da ação [Terminer] é esvaziar a sessão atual dos seus atributos.
- linha 7: a ação [Terminer] implementa a interface [SessionAware] para ter acesso à sessão.
- linha 10: o dicionário da sessão
- linha 13: o método execute que é executado
- linha 15: esvazia o dicionário da sessão. Consequentemente, a lista de simulações que se encontra na sessão irá desaparecer. É isto que explica o ecrã n.º 3.
- linha 16: devolve a chave success, que, como vimos, irá apresentar a vista Tiles saisie [2].
19.15. Conclusion
Comentámos na íntegra a versão 1 do nosso estudo de caso, que trabalha com uma camada simulada [metier]:
![]() |
Resta-nos apenas «ligar» a verdadeira camada de negócio à camada [web].






























