Skip to content

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]:
1
2
3
4
5
6
7
<struts>
    <package name="tiles-default" extends="struts-default">
        <result-types>
            <result-type name="tiles" class="org.apache.struts2.views.tiles.TilesResult"/>
        </result-types>
    </package>
</struts>
  • 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:

Entete.JSP
Saisie.JSP
Simulation.JSP
Simulations.JSP
Exception.JSP
Erreur.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
masterPage
Entete.JSP
saisie
Entete.JSP, Saisie.JSP
simulation
Entete.JSP, Saisie.JSP, Simulation.JSP
simulations
Entete.JSP, Simulations.JSP
exception
Entete.JSP, Exception.JSP
erreur
Entete.JSP, Erreur.JSP

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:
comboEmployesValue=SS1&heuresTravaillees=xx&joursTravailles=yy&action:Formulaire!calculSalaire
SS1 : n° INSEE de l'employé sélectionné dans le combo
heuresTravaillees : nombre d'heures travaillées
joursTravailles : nombre de jours travaillés
action : Formulaire!calculSalaire : les éléments ci-dessus seront postés à l'action [Formulaire] puis la méthode calculSalaire de cette action sera exécutée.

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].