Skip to content

19. Estudo de caso – Versão 1

19.1. A camada [de negócios] simulada

Vamos rever a arquitetura da aplicação que estamos a construir:

Temos os arquivos da camada [negócio, DAO, JPA] e apresentámos os elementos dessas camadas que a camada [web] precisa de conhecer. Estamos prontos para escrever a camada [web] utilizando o framework Struts.

Para simplificar os testes da nossa aplicação durante o desenvolvimento, iremos criar uma camada de negócios simulada que irá implementar a interface da camada [negócios]. A arquitetura ficará da seguinte forma:

Iremos desenvolver a camada [web] utilizando a camada [negócio] simulada. Os testes serão mais simples, uma vez que já não existe uma base de dados na arquitetura. Graças ao Spring e à utilização de interfaces, a substituição da camada [business] simulada pela arquitetura real [business, DAO, JPA] numa fase posterior não terá qualquer impacto no código da camada [web / Struts2]. A camada [web / Struts2] que estamos prestes a desenvolver pode ser utilizada tal como está.

A camada [de negócios] simulada que iremos utilizar é a seguinte:


package metier;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import jpa.Cotisation;
import jpa.Employe;
import jpa.Indemnite;
 
public class MetierSimule implements IMetier {
 
  // list of employees
  private Map<String, Employe> hashEmployes = new HashMap<String, Employe>();
  private List<Employe> listEmployes;
 
  // get your payslip
  public FeuilleSalaire calculerFeuilleSalaire(String SS,
          double nbHeuresTravaillées, int nbJoursTravaillés) {
    // we retrieve employee n° SS
    Employe e = hashEmployes.get(SS);
    // a fictitious payslip is returned
    return new FeuilleSalaire(e, new Cotisation(3.49, 6.15, 9.39, 7.88), new ElementsSalaire(100, 100, 100, 100, 100));
  }
 
  // list of employees
  public List<Employe> findAllEmployes() {
    if (listEmployes == null) {
      // create a list of two employees
      listEmployes = new ArrayList<Employe>();
      listEmployes.add(new Employe("254104940426058", "Jouveinal", "Marie", "5 rue des oiseaux", "St Corentin", "49203", new Indemnite(2, 2.1, 2.1, 3.1, 15)));
      listEmployes.add(new Employe("260124402111742", "Laverti", "Justine", "La br�lerie", "St Marcel", "49014", new Indemnite(1, 1.93, 2, 3, 12)));
      // employee dictionary
      for (Employe e : listEmployes) {
        hashEmployes.put(e.getSS(), e);
      }
    }
    // we return the list of employees
    return listEmployes;
  }
}
  • linha 11: a classe [MetierSimule] implementa a interface [IMetier] que a camada [business] real implementa.
  • linha 14: um dicionário de funcionários indexado pelo seu número INSEE
  • linha 15: a lista de funcionários
  • linhas 27–39: implementação do método findAllEmployees da interface [IMetier].
  • linhas 30–33: criação de uma lista de dois funcionários
  • Linhas 34–36: criação do dicionário de funcionários indexado pelo número INSEE
  • Linhas 18–24: Implementação do método `calculerSalaire` da interface [IMetier]. Aqui, devolvemos um recibo de vencimento fictício.

19.2. O projeto NetBeans

O projeto NetBeans é o seguinte:

  • em [1]:
  • [applicationContext.xml] é o ficheiro de configuração do Spring
  • [tiles.xml] é o ficheiro de configuração de um framework chamado Tiles.
  • [web.xml] é o ficheiro de configuração da aplicação web
  • em [2]: as diferentes vistas da aplicação
  • em [3]:
  • [messages.properties]: o ficheiro de mensagens
  • [struts.xml]: o ficheiro de configuração do Struts
  • em [4]: o código-fonte da aplicação. As ações Struts encontram-se no pacote [web.actions].
  • em [5]: a camada [business] simulada
  • em [6]: os arquivos utilizados. Aqui estão os arquivos das várias ferramentas utilizadas: Spring, Tiles, Struts 2, o plugin de integração Struts 2/Spring e o plugin de integração Struts 2/Tiles.
  • em [7]: o arquivo da camada [business, DAO, JPA] real. Dá-nos acesso às entidades JPA, à interface [IMetier] e às classes [PayrollSheet] e [PayrollItems]. Todos estes elementos são, de facto, utilizados pela nossa classe [SimulatedBusiness].

19.3. Configuração do projeto

O projeto é configurado por vários ficheiros:

  • [web.xml], que configura a aplicação web
  • [struts.xml], que configura o framework Struts
  • [applicationContext.xml], que configura o framework Spring
  • [tiles.xml], que configura o framework Tiles

19.3.1. Configuração da aplicação web

O ficheiro [web.xml] é o seguinte:


<?xml version="1.0" encoding="UTF-8"?>
<web-app id="pam_struts_01" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
  <display-name>Pam</display-name>
    <!-- Tiles -->
  <context-param>
    <param-name> org.apache.tiles.impl.BasicTilesContainer.DEFINITIONS_CONFIG </param-name>
    <param-value>/WEB-INF/tiles.xml</param-value>
  </context-param>
  <listener>
    <listener-class>org.apache.struts2.tiles.StrutsTilesListener</listener-class>
  </listener>
    <!-- Struts 2 -->
  <filter>
    <filter-name>struts2</filter-name>
    <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>struts2</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
     <!-- Spring -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
</web-app>
  • linhas 13–20: configurar o filtro Struts 2 – já visto
  • linhas 22–24: configurar o ouvinte Spring – já visto
  • linhas 9–11: configurar o ouvinte do Tiles. A classe [org.apache.struts2.tiles.StrutsTilesListener] será instanciada quando a aplicação web for iniciada. Em seguida, utilizará o seu ficheiro de configuração. Este é definido pelas linhas 5–8. O ficheiro de configuração do Tiles é, portanto, o ficheiro [WEB-INF/tiles.xml].

Por fim, quando a aplicação Struts é iniciada, são instanciadas três classes:

  • uma para o filtro Struts 2. Este é o componente que lida com o «C» em MVC.
  • outra para o ouvinte Spring. O Spring utilizará o ficheiro [applicationContext.xml] para instanciar as camadas [business, DAO, JPA] da aplicação. O Spring também instanciará, tal como no exemplo anterior, uma classe [Config] que conterá dados no âmbito da aplicação. Por fim, o Spring injetará uma referência a esta única instância [Config] em todas as ações Struts que dela necessitem.
  • Outro para o ouvinte Tiles. Esta estrutura irá tratar da gestão de vistas. Voltaremos a este assunto em breve.

19.3.2. Configuração da estrutura Struts

A estrutura Struts é configurada pelo seguinte ficheiro [struts.xml]:


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">
 
<struts>
  <!-- internationalization -->
  <constant name="struts.custom.i18n.resources" value="messages" />
  <!-- spring integration -->
  <constant name="struts.objectFactory.spring.autoWire" value="name" />
 
 
  <!-- struts /Tiles actions -->
  <package name="default" namespace="/" extends="tiles-default">
    <!-- default action -->
    <default-action-ref name="index" />
    <action name="index">
      <result type="redirectAction">
        <param name="actionName">Formulaire</param>
        <param name="namespace">/</param>
      </result>
    </action>
    <!-- action Form -->
    <action name="Formulaire" class="web.actions.Formulaire" method="input">
      <result name="success" type="tiles">saisie</result>
      <result name="exception" type="tiles">exception</result>
    </action>
    <!-- action FaireSimulation -->
    <action name="FaireSimulation" class="web.actions.Formulaire" method="calculSalaire">
      <result name="success" type="tiles">simulation</result>
      <result name="exception" type="tiles">exception</result>
      <result name="input" type="tiles">saisie</result>
    </action>
    <!-- action EnregistrerSimulation -->
    <action name="EnregistrerSimulation" class="web.actions.Enregistrer" method="execute">
      <result name="error" type="tiles">erreur</result>
      <result name="simulations" type="tiles">simulations</result>
    </action>
   <!-- action RetourFormulaire -->
    <action name="RetourFormulaire" >
      <result type="redirectAction">
        <param name="actionName">Formulaire</param>
        <param name="namespace">/</param>
      </result>
    </action>
   <!-- action VoirSimulations -->
    <action name="VoirSimulations" class="web.actions.Voir">
      <result name="success" type="tiles">simulations</result>
    </action>
   <!-- action RetirerSimulation -->
    <action name="SupprimerSimulation" class="web.actions.Supprimer" method="execute">
      <result name="erreur" type="tiles">erreur</result>
      <result name="simulations" type="tiles">simulations</result>
    </action>
   <!-- action TerminerSession -->
    <action name="TerminerSession" class="web.actions.Terminer" method="execute">
      <result name="success" type="redirectAction">
        <param name="actionName">Formulaire</param>
        <param name="namespace">/</param>
      </result>
    </action>
  </package>
 
</struts>

Iremos discutir as várias ações do Struts à medida que as formos abordando. Por enquanto, tenha em conta os seguintes pontos:

  • linha 8: define o ficheiro de mensagens
  • linha 10: define como os beans do Spring são injetados nas ações do Struts. A injeção é baseada no nome do bean. O campo da ação do Struts que precisa ser inicializado pelo Spring deve ter o mesmo nome do bean a ser injetado.
  • linha 25: define a vista a apresentar para a chave de navegação «success» da ação [Form]. Vemos que o elemento <result> tem um atributo type='tiles' com o qual não estamos familiarizados. Estávamos familiarizados com o tipo redirect, que permite que o cliente seja redirecionado para uma vista. Aqui, a vista do tipo tiles é gerida pela estrutura Tiles. O tipo tiles é definido no ficheiro [struts-plugin.xml] dentro do arquivo [struts2-tiles-plugin-2.2.3.1.jar]:


<struts>
    <package name="tiles-default" extends="struts-default">
        <result-types>
            <result-type name="tiles" class="org.apache.struts2.views.tiles.TilesResult"/>
        </result-types>
    </package>
</struts>

  • Linhas 3–5: A definição do tipo de resultado tiles.
  • linha 2: este tipo é definido no pacote [tiles-default], que estende o pacote [struts-default].

  • linha 14: define o pacote [default], que conterá todas as ações da aplicação. Para tirar partido da definição do tipo de vista tiles, o pacote estende [tiles-default].

19.3.3. Configuração do Spring Framework

O Spring Framework é configurado pelo seguinte ficheiro [WEB-INF/applicationContext.xml]:


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
 
  <!-- application layers -->
 
<!-- web -->
  <bean id="config" class="web.Config" init-method="init">
    <property name="metier" ref="metier"/>
  </bean>
  <!-- business -->
  <bean id="metier" class="metier.MetierSimule"/>
 
</beans>

  • linha 13: a camada [business] simulada instanciada pela classe [business.SimulatedBusiness]
  • linhas 9–11: configurar um bean denominado config. Tal como num exemplo anteriormente estudado, este bean será utilizado para encapsular informações do âmbito da aplicação. A classe associada a este bean é a seguinte classe [Config]:


package web;
 
import java.util.List;
import jpa.Employe;
import metier.IMetier;
 
public class Config {
 
  // business layer initialized by Spring
  private IMetier metier;
  // list of employees
  private List<Employe> employes;
  // errors
  private Exception initException;
 
  // manufacturer
  public Config() {
  }
 
  // spring method for object initialization
  public void init() {
    // we ask for the list of employees
    try {
      employes = metier.findAllEmployes();
    } catch (Exception ex) {
      initException = ex;
    }
  }
 
  // getters and setters
  ...
}

Voltemos à configuração do bean de configuração:


  <bean id="config" class="web.Config" init-method="init">
    <property name="metier" ref="metier"/>
  </bean>
  <!-- métier -->
  <bean id="metier" class="metier.Metier">
    ...  
</bean>

Na linha 2, podemos ver que o bean de negócio da linha 5 é injetado (ref) no campo denominado business (name) do objeto [Config]. O bean de negócio é uma referência à camada [business]:

Para interagir com a camada [business], todas as ações Struts na camada [web] precisarão de uma referência a ela. Podemos dizer que a referência à camada [business] é um dado com âmbito de aplicação. Todas as solicitações de todos os utilizadores precisarão dela. É por isso que colocamos esta referência no objeto [Config]. Além disso, na linha 1, a configuração do bean de configuração inclui um atributo init-method. Este atributo especifica o método do bean a ser executado após a instância do bean. Aqui, especificamos que, após a instância da classe [web.Config], o seu método init deve ser executado. Este método é o seguinte:


// business layer initialized by Spring
  private IMetier metier;
  // list of employees
  private List<Employe> employes;
  // errors
  private Exception initException;
 
  // manufacturer
  public Config() {
  }
 
  // spring method for object initialization
  public void init() {
    // we ask for the list of employees
    try {
      employes = metier.findAllEmployes();
    } catch (Exception ex) {
      initException = ex;
    }
  }

Quando o método init é executado, o campo de negócio da classe já foi instanciado pelo Spring. O método init tem, portanto, acesso à interface da camada de negócio [IMetier] (linha 2):


package metier;
 
import java.util.List;
import jpa.Employe;
 
public interface IMetier {
  // get your payslip
  FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés );
  // list of employees
  List<Employe> findAllEmployes();
}
  • Linha 8: O método calcula o salário de um funcionário
  • linha 10: o método recupera a lista de funcionários

Podemos ver que o método init solicita a lista de funcionários da camada [business]. Esta lista é armazenada no campo da linha 4. Se ocorrer uma exceção, esta é armazenada no campo da linha 6.

Em conclusão, o único objeto [Config] contém:

  • uma referência à camada [business]
  • a lista de funcionários

19.4. Gerar vistas de mosaicos

Como vimos no ficheiro de configuração do Struts, as vistas serão geradas pela estrutura Tiles. Iremos explicar apenas o que é estritamente necessário para escrever a nossa aplicação.

O Tiles permite gerar vistas a partir de uma página mestre. Esta página, aqui denominada [MasterPage.jsp], será uma compilação dos seguintes fragmentos JSP:

Header.jsp
 
Input.jsp
 
Simulation.jsp
 
Simulações.jsp
 
Exceção.jsp
 
Erro.jsp
 

Estes fragmentos JSP estão definidos no projeto NetBeans:

Image

A estrutura Tiles permite-nos definir quais os fragmentos que serão inseridos na página principal.

A página mestre [MasterPage.jsp] é a seguinte:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  "http://www.w3.org/TR/html4/loose.dtd">
 
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <link href="styles.css" rel="stylesheet" type="text/css"/>
    <title>
      <tiles:insertAttribute name="titre" ignore="true" />
    </title>
    <s:head/>
  </head>
  <body background="<s:url value="/ressources/standard.jpg"/>">
    <tiles:insertAttribute name="entete" />
    <hr/>
    <tiles:insertAttribute name="saisie" />
    <tiles:insertAttribute name="simulation" />
    <tiles:insertAttribute name="exception" />
    <tiles:insertAttribute name="erreur" />
    <tiles:insertAttribute name="simulations" />
  </body>
</html>

A página mestre é um contentor para fragmentos JSP. Aqui, trata-se de um conjunto de seis fragmentos, os que se encontram nas linhas 17 a 23. Após a geração, podem existir entre 0 e 6 fragmentos reunidos na página mestre. Esta geração é regida pelo ficheiro [WEB-INF/tiles.xml], que define as vistas Tiles:


<?xml version="1.0" encoding="UTF-8" ?>
 
<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 2.0//EN"
 "http://tiles.apache.org/dtds/tiles-config_2_0.dtd">
 
<tiles-definitions>
 
<!-- the master page -->
  <definition name="masterPage" template="/MasterPage.jsp">
    <put-attribute name="entete" value="/Entete.jsp"/>
    <put-attribute name="titre"  value="Pam"/>
    <put-attribute name="saisie" value=""/>     
    <put-attribute name="simulation" value=""/>
    <put-attribute name="simulations" value=""/>
    <put-attribute name="exception" value=""/>     
    <put-attribute name="erreur" value=""/>     
  </definition>
 
<!-- input view -->
  <definition name="saisie" extends="masterPage">
    <put-attribute name="saisie"   value="/Saisie.jsp"/>     
  </definition>

  <!-- simulation view -->
 <definition name="simulation" extends="saisie">
    <put-attribute name="simulation"   value="/Simulation.jsp"/>     
  </definition>
 
<!-- the simulations view -->
  <definition name="simulations" extends="masterPage">
    <put-attribute name="simulations"   value="/Simulations.jsp"/>     
  </definition>
 
<!-- the exceptional view -->
  <definition name="exception" extends="masterPage">
    <put-attribute name="exception"   value="/Exception.jsp"/>     
  </definition>
 
<!-- error view -->
 <definition name="erreur" extends="masterPage">
    <put-attribute name="erreur"   value="/Erreur.jsp"/>     
  </definition>
</tiles-definitions>
  • O ficheiro acima define seis vistas Tiles denominadas: masterPage (linha 9), input (linha 20), simulation (linha 25), simulations (linha 30), exception (linha 35), error (linha 40).
  • Linhas 9–17: definem uma vista denominada masterPage (nome) e associada à página principal [MasterPage.jsp] (modelo). Vimos que esta página JSP definiu seis subvisualizações. Uma visualização Tiles associada à página mestre deve especificar o fragmento JSP associado a cada uma das seis subvisualizações. Observamos que a algumas subvisualizações é atribuída a cadeia vazia como valor. Estas subvisualizações não serão incluídas na página mestre [MasterPage.jsp]. A visualização Tiles denominada masterPage consiste, portanto, exclusivamente no subfragmento [Entete.jsp].
  • Linhas 20–22: definem uma vista denominada `saisie` que estende a vista `masterPage` definida anteriormente. Isto significa que herda todas as definições da vista `masterPage`. A sua definição é equivalente ao seguinte:

<definition name="saisie" template="/MasterPage.jsp">
    <put-attribute name="entete" value="/Entete.jsp"/>
    <put-attribute name="titre"  value="Pam"/>
    <put-attribute name="saisie" value=""/>     
    <put-attribute name="simulation" value=""/>
    <put-attribute name="simulations" value=""/>
    <put-attribute name="exception" value=""/>     
    <put-attribute name="erreur" value=""/>
    <put-attribute name="saisie" value="/Saisie.jsp"/>     
  </definition>

Podemos ver que está associada à página JSP [MasterPage.jsp] e que, como tal, deve definir as seis subvisualizações desta página. Podemos ver que a definição na linha 9 substitui a da linha 4. A visualização Tiles denominada «saisie» é, portanto, composta pelos fragmentos JSP [Entete.jsp, Saisie.jsp]

Se continuarmos esta linha de raciocínio, obtemos a seguinte tabela:

Vista Tiles
Páginas JSP
masterPage
Header.jsp
Entrada
Header.jsp, Input.jsp
simulação
Header.jsp, Entry.jsp, Simulation.jsp
simulações
Header.jsp, Simulações.jsp
exceção
Header.jsp, Exception.jsp
erro
Header.jsp, Error.jsp

19.5. Ficheiros de mensagens

A aplicação foi internacionalizada. As mensagens encontram-se nos ficheiros [messages.properties] e [Formulaire.properties].

O ficheiro [messages.properties] tem o seguinte conteúdo:


Pam.titre=Calcul du salaire des assistantes maternelles
Pam.Erreurs.titre=Les erreurs suivantes se sont produites :
Pam.Erreurs.classe=Exception
Pam.Erreurs.message=Message
Pam.Erreur.libelle=L''erreur suivante s''est produite
Pam.Saisie.Heures.libell\u00e9=Heures travaill\u00e9es
Pam.Saisie.Jours.libell\u00e9=Jours travaill\u00e9s
Pam.Saisie.employ\u00e9=Employ\u00e9
Pam.BtnSalaire.libell\u00e9=Salaire
Pam.BtnEffacer.libell\u00e9=Effacer
Simulation.Infos.employe=Informations Employ\u00e9
Simulation.Employe.nom=Nom
Simulation.Employe.prenom=Pr\u00e9nom
Simulation.Employe.adresse=Adresse
Simulation.Employe.indice=Indice
Simulation.Employe.ville=Ville
Simulation.Employe.codePostal=Code Postal
Simulation.Infos.cotisations=Cotisations Sociales
Simulation.Cotisations.csgrds=CsgRds
Simulation.Cotisations.csgrds=Csgd
Simulation.Cotisations.retraite=Retraite
Simulation.Cotisations.secu=S\u00e9cu
Form.Infos.indemnites=Indemnit\u00e9s
Simulation.Indemnites.salaireHoraire=Salaire horaire
Simulation.Indemnites.entretienJour=Entretien/Jour
Simulation.Indemnites.repasJour=Repas/Jour
Simulation.Indemnites.cong\u00e9sPay\u00e9s=Cong\u00e9s pay\u00e9s
Simulation.Infos.Salaire=Salaire
Simulation.Salaire.salaireBase=Salaire de base
Simulation.Salaire.cotisationsSociales=Cotisations sociales
Simulation.Salaire.entretien=Indemnit\u00e9s d''entretien
Simulation.Salaire.repas=Indemnit\u00e9s de repas
Simulation.salaireNet=Salaire net
# formats
Format.heure = {0,time}
Format.nombre = {0,number,#0.0##}
Format.pourcent = {0,number,##0.00' %'}
Format.monnaie={0,number,##0.00' \u20ac'}
# liste des simulations
Pam.Simulations.titre=Liste des simulations
Pam.Simulations.num=Num\u00e9ro
Pam.Simulations.nom=Nom
Pam.Simulations.prenom=Pr\u00e9nom
Pam.Simulations.heures=Heures
Pam.Simulations.jours=Jours
Pam.Simulations.salairebase=Salaire de base
Pam.Simulations.indemnites=Indemnites
Pam.Simulations.cotisationsociales=Cotisations
Pam.Simulations.salairenet=Salaire
Pam.SimulationsVides.titre=La liste des simulations est vide
# menu
Menu.FaireSimulation=Faire la simulation
Menu.EffacerSimulation=Effacer la simulation
Menu.VoirSimulations=Voir les simulations
Menu.RetourFormulaire=Retour au formulaire de navigation
Menu.EnregistrerSimulation=Enregistrer la simulation
Menu.TerminerSession=Terminer la session
# msg d'erreur
Erreur.sessionexpiree=La session a expir\u00e9
Erreur.numSimulation=N\u00b0 de simulation incorrect
# erreur de conversion
xwork.default.invalid.fieldvalue=Valeur invalide pour le champ "{0}".

O ficheiro [Form.properties] é o seguinte:


# pour que les doubles soient au format local
double.format={0,number,#0.00##}
# msg d'erreur
joursTravaill\u00e9s.error=Tapez un nombre entier compris entre 1 et 31
heuresTravaill\u00e9es.error=Tapez un nombre r\u00e9el entre 0 et 300

19.6. A folha de estilo

As vistas de mosaicos utilizam a seguinte folha de estilo [styles.css]:


.libelle{
  background-color: #ccffff;
  font-family: 'Times New Roman',Times,serif;
  font-size: 14px;
  font-weight: bold;;
  padding-right: 5px;
  padding-left: 5px;
  padding-bottom: 5px;
  padding-top: 5px;
}
 
 
.info{
  background-color: #99cc00;;
  padding-right: 5px;
  padding-left: 5px;
  padding-bottom: 5px;
  padding-top: 5px;
}
 
.titreInfos{
  background-color: #ffcc00
}

19.7. A visualização inicial

Para explorar a aplicação, iremos analisá-la com base nas várias ações do utilizador. Para cada ação, iremos analisar a ação Struts que a executa e a vista Tiles que é devolvida em resposta.

No [struts.xml] temos as seguintes ações:


  <!-- action par défaut -->
    <default-action-ref name="index" />
    <action name="index">
      <result type="redirectAction">
        <param name="actionName">Formulaire!input</param>
        <param name="namespace">/</param>
      </result>
    </action>
    <!-- action Formulaire -->
    <action name="Formulaire" class="web.actions.Formulaire">
      <result name="success" type="tiles">saisie</result>
      <result name="exception" type="tiles">exception</result>
      <result name="input" type="tiles">saisie</result>
      <result name="simulation" type="tiles">simulation</result>
</action>

  • linhas 2-8: a ação padrão da aplicação é [Form!input].

A classe [Form] é a seguinte:


package web.actions;
 
...
public class Formulaire extends ActionSupport implements Preparable, SessionAware {
 
  // configuration initialized by Spring
  private Config config;
  // list of employees
  private List<Employe> employes;
  // error list
  private List<Erreur> erreurs;
  // payslip
  private FeuilleSalaire feuilleSalaire;
  // foreclosures
  private String comboEmployesValue;
  private Double heuresTravaillees;
  private Integer joursTravailles;
  // session
  private Map<String, Object> session;
  // menu
  private Menu menu;
 
  @Override
  public void prepare() throws Exception {
    ...
  }
 
  @Override
  public String input() {
  ....
  }
 
  // wage calculation
  public String calculSalaire() {
 ...
    }
  }
 
  @Override
  public void validate() {
  ...
  }
 
  @Override
  public void setSession(Map<String, Object> map) {
    session = map;
  }
 
  // getters and setters
  ...
}
  • Linha 4: A ação [Form] implementa a interface Preparable. Esta interface possui apenas um método, o método prepare na linha 24. Este método é executado uma vez antes de qualquer outro método da ação. É geralmente utilizado para inicializar o modelo da ação.

O modelo da ação é definido nas linhas 6–21:

  • linha 7: o campo config é inicializado pelo Spring, conforme explicado. Ele fornece acesso a dados no âmbito da aplicação:
  • uma referência à camada [business]
  • uma referência à lista de funcionários
  • uma referência à exceção que pode ter ocorrido durante a instanciação do objeto [Config]
  • linha 9: uma lista de funcionários. Isto irá preencher o menu suspenso de funcionários no fragmento [Saisie.jsp].
  • linha 11: uma lista de erros. Isto irá preencher o fragmento [Error.jsp].
  • Linha 21: a lista de opções de menu para o fragmento [Entete.jsp]

Em [1], os links no menu exibido são controlados pelo campo de menu da ação [Form].

O método prepare é executado antes do método input. É o seguinte:


  @Override
  public void prepare() throws Exception {
    // configuration error?
    Exception initException = config.getInitException();
    if (initException != null) {
      erreurs = new ArrayList<Erreur>();
      Throwable th = initException;
      while (th != null) {
        erreurs.add(new Erreur(th.getClass().getName(), th.getMessage()));
        th = th.getCause();
      }
    } else {
      employes = config.getEmployes();
    }
}
  • linha 4: recuperamos a exceção do objeto [Config] instanciado pelo Spring
  • linha 5: se ocorreu uma exceção durante a instanciação do objeto [Config], então inicializamos a lista de erros na linha 11. A classe [Error] é a seguinte:

package web.entities;
 
import java.io.Serializable;
 
public class Erreur implements Serializable{
 
  public Erreur() {
  }
 
  // fields
  private String classe;
  private String message;
 
  // manufacturer
  public Erreur(String classe, String message){
    this.setClasse(classe);
    this.message=message;
  }
 
  // getters and setters
...
}

A classe é utilizada para armazenar a pilha de exceções:

  • linha 11: a classe de exceção
  • linha 12: a mensagem de exceção

Voltemos ao método prepare:

  • linha 13: a lista de funcionários do objeto [Config] é armazenada no campo employees da ação.

Assim que o método prepare for executado, o método input será executado a seguir. É o seguinte:


  @Override
  public String input() {
    if (erreurs == null) {
      // menu
      menu = new Menu(true, false, false, true, false, true);
      return SUCCESS;
    } else {
      // menu
      menu = new Menu(false, false, false, false, false, false);
      return "exception";
    }
}

O método de entrada define simplesmente a lista de opções do menu a apresentar. A classe [Menu] é a seguinte:


package web.entities;
 
import java.io.Serializable;
 
public class Menu implements Serializable {
  // menu items
 
  private boolean faireSimulation;
  private boolean effacerSimulation;
  private boolean enregistrerSimulation;
  private boolean voirSimulations;
  private boolean retourFormulaire;
  private boolean terminerSession;
 
  public Menu() {
  }
 
  public Menu(boolean faireSimulation, boolean effacerSimulation, boolean enregistrerSimulation, boolean voirSimulations, boolean retourFormulaire, boolean terminerSession) {
    this.faireSimulation = faireSimulation;
    this.effacerSimulation = effacerSimulation;
    this.enregistrerSimulation = enregistrerSimulation;
    this.voirSimulations = voirSimulations;
    this.retourFormulaire = retourFormulaire;
    this.terminerSession = terminerSession;
  }
 
  // getters and setters
...  
}
  • linhas 8–13: existem 6 links possíveis no menu
  • linhas 18-25: o construtor da classe permite especificar quais os links que devem ser apresentados e quais os que não devem.

Os links do menu são exibidos em [Entete.jsp], um fragmento JSP presente em todas as visualizações do Tiles. Cada ação terá um campo de menu para controlar a exibição do menu em [Entete.jsp].

Voltemos ao método de entrada:


  @Override
  public String input() {
    if (erreurs == null) {
      // menu
      menu = new Menu(true, false, false, true, false, true);
      return SUCCESS;
    } else {
      // menu
      menu = new Menu(false, false, false, false, false, false);
      return "exception";
    }
}
  • linhas 3-6: se a lista de erros estiver vazia, será exibido o menu [Executar Simulação, Ver Simulações, Terminar Sessão] e será devolvida a tecla de entrada.
  • linhas 9-10: se a lista de erros não estiver vazia, o menu ficará vazio e a chave de exceção será devolvida.

Voltemos à configuração da ação [Form] em [struts.xml]:


    <!-- action Formulaire -->
    <action name="Formulaire" class="web.actions.Formulaire">
      <result name="success" type="tiles">saisie</result>
      <result name="exception" type="tiles">exception</result>
      <result name="input" type="tiles">saisie</result>
      <result name="simulation" type="tiles">simulation</result>
</action>
  • Linha 5: A chave input exibe a vista Tiles denominada input
  • linha 4: a chave exception exibe a vista Tiles denominada exception

Comecemos pela vista Tiles denominada «input». Esta é composta pelos fragmentos JSP [Entete.jsp] e [Saisie.jsp].

O fragmento [Entete.jsp] é o seguinte:

Image

O seu código é o seguinte:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
 
<table>
  <tr>
    <td><h1><s:text name="Pam.titre"/></h1></td>
    <td>
      <s:if test="menu.faireSimulation">
        |<a href="javascript:doSimulation()"><s:text name="Menu.FaireSimulation"/></a><br/>
      </s:if>
      <s:if test="menu.effacerSimulation">
        |<a href="<s:url action="Formulaire!input"/>"><s:text name="Menu.EffacerSimulation"/></a><br/>
      </s:if>
      <s:if test="menu.voirSimulations">
        |<a href="<s:url action="VoirSimulations"/>"><s:text name="Menu.VoirSimulations"/></a><br/>
      </s:if>
      <s:if test="menu.retourFormulaire">
        |<a href="<s:url action="RetourFormulaire"/>"><s:text name="Menu.RetourFormulaire"/></a><br/>
      </s:if>
      <s:if test="menu.enregistrerSimulation">
        |<a href="<s:url action="EnregistrerSimulation"/>"><s:text name="Menu.EnregistrerSimulation"/></a><br/>
      </s:if>
      <s:if test="menu.terminerSession">
        |<a href="<s:url action="TerminerSession"/>"><s:text name="Menu.TerminerSession"/></a><br/>
      </s:if>
    </td>
  </tr>
</table>
  • linhas 8-25: exibição dos seis links do menu [Executar Simulação (linhas 8-10), Limpar Simulação (linhas 11-13), Ver Simulações (linhas 14–16), Voltar ao Formulário (linhas 17–19), Guardar Simulação (linhas 20–22), Terminar Sessão (linhas 23–25).
  • linhas 8, 11, 14, 17, 20, 23: a exibição dos links é controlada pelo campo de menu da ação atual.

Note que o fragmento [Entete.jsp] apresenta uma tabela HTML (linhas 4–28), mas não é uma página HTML completa. Tenha em mente que todas as visualizações da aplicação estão incorporadas na seguinte página mestre [MasterPage.jsp]:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  "http://www.w3.org/TR/html4/loose.dtd">
 
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <link href="styles.css" rel="stylesheet" type="text/css"/>
    <title>
      <tiles:insertAttribute name="titre" ignore="true" />
    </title>
    <s:head/>
  </head>
  <body background="<s:url value="/ressources/standard.jpg"/>">
    <tiles:insertAttribute name="entete" />
    <hr/>
    <tiles:insertAttribute name="saisie" />
    <tiles:insertAttribute name="simulation" />
    <tiles:insertAttribute name="exception" />
    <tiles:insertAttribute name="erreur" />
    <tiles:insertAttribute name="simulations" />
  </body>
</html>

O fragmento [Entete.jsp] é inserido na linha 17, dentro de uma página HTML normal.

O fragmento [Saisie.jsp] é inserido na linha 19. Esta é a visualização resultante:

Image

O código do fragmento [Saisie.jsp] é o seguinte:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
<%@ taglib uri="http://tiles.apache.org/tags-tiles" prefix="tiles" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  "http://www.w3.org/TR/html4/loose.dtd">
 
 
<script language="javascript" type="text/javascript">
  function doSimulation(){
    // mail the form
    document.forms['Saisie'].elements['action'].name='action:Formulaire!calculSalaire'
    document.forms['Saisie'].submit();
  }  
</script>
 
<!-- data entry -->
<s:form name="Saisie" id="Saisie">
  <s:select name="comboEmployesValue" list="employes" listKey="SS" listValue="prenom+' ' +nom" key="Pam.Saisie.employé"/>
  <s:textfield name="heuresTravaillees" key="Pam.Saisie.Heures.libellé" value="%{#parameters['heuresTravaillees']!=null ? #parameters['heuresTravaillees'] : heuresTravaillees==null ? '' : getText('double.format',{heuresTravaillees})}"/>
  <s:textfield name="joursTravailles" key="Pam.Saisie.Jours.libellé" value="%{#parameters['joursTravailles']!=null ? #parameters['joursTravailles'] : joursTravailles==null ? '' : joursTravailles}"/>
  <input type="hidden" name="action"/>
</s:form>
  • Linha 17: O formulário não tem atributo action. Por predefinição, action='Form'.
  • Linha 18: Exibição da lista suspensa de funcionários. O conteúdo da lista suspensa (atributo list) é fornecido pelo campo employees da ação atual. O atributo value das opções será o número de segurança social do funcionário (atributo listKey). O rótulo exibido para cada opção será o nome e apelido do funcionário (atributo listValue). O número de segurança social do funcionário selecionado na caixa combinada será enviado para o campo [Form].comboEmployeesValue (atributo name).
  • Linha 19: campo de entrada para horas trabalhadas. O valor exibido (atributo value) é o do campo heuresTravaillees na ação [Form], no seguinte formato (Form.properties):

double.format={0,number,#0.00##}

O valor será lançado no campo [Form].hoursWorked (atributo name).

  • Linha 20: campo para introduzir os dias trabalhados. O valor apresentado (atributo value) é o do campo daysWorked na ação [Form].

O valor será enviado para o campo [Form].daysWorked (atributo name).

Por fim, a vista Tiles apresentada no arranque, quando não existem erros, é a seguinte:

Image

Voltemos à configuração da ação [Form]:


    <!-- action Formulaire -->
    <action name="Formulaire" class="web.actions.Formulaire">
      <result name="success" type="tiles">saisie</result>
      <result name="exception" type="tiles">exception</result>
      <result name="input" type="tiles">saisie</result>
      <result name="simulation" type="tiles">simulation</result>
</action>

Vimos que a ação [Form].input também pode acionar a chave exception na linha 4. Neste caso, é apresentada a vista Tiles denominada exception. Esta vista é composta pelos fragmentos [Header.jsp] e [Exception.jsp]. Já apresentámos o fragmento [Header.jsp]. O fragmento [Exception.jsp] é o seguinte:

Image

Esta é a página inicial da versão 2 da aplicação quando o DBMS ainda não foi iniciado. O código JSP para o fragmento [Error.jsp] é o seguinte:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
 
<h2><s:text name="Pam.Erreurs.titre"/></h2>
<table>
  <tr class="titreInfos">
    <th><s:text name="Pam.Erreurs.classe"/></th>
    <th><s:text name="Pam.Erreurs.message"/></th>
  </tr>
  <s:iterator value="erreurs">
    <tr>
      <td class="libelle"><s:property value="classe"/></td>
      <td class="info"><s:property value="message"/></td>
    </tr>
  </s:iterator>
</table>
  • Linhas 10–14: Um iterador sobre a coleção List<Error> de erros da ação [Form]. Recorde-se que, em caso de erro, uma pilha de exceções foi armazenada nesse local.

19.8. Execute uma simulação

Assim que a vista inicial for apresentada, pode calcular um salário através da ligação [Executar uma simulação].

19.8.1. Validação das entradas

Considere a seguinte sequência:

  • em [1], uma entrada incorreta
  • em [2], a resposta enviada.

Vamos considerar a configuração da ação [Form] em [struts.xml]:


    <!-- action Formulaire -->
    <action name="Formulaire" class="web.actions.Formulaire">
      <result name="success" type="tiles">saisie</result>
      <result name="exception" type="tiles">exception</result>
      <result name="input" type="tiles">saisie</result>
      <result name="simulation" type="tiles">simulation</result>
</action>

Sabemos que, em caso de erro de validação, o interceptor de validação devolve a chave de entrada. Por conseguinte, é devolvida a vista de entrada Tiles. O processo de validação garante que os campos com erros sejam acompanhados por mensagens de erro.

A validação da ação [Form] é tratada pelo seguinte ficheiro [Form-validation.xml]:


<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
 
 
<validators>
 
  <field name="heuresTravaillees" >
    <field-validator type="required" short-circuit="true">
      <message key="heuresTravaillées.error"/>
    </field-validator>
 
    <field-validator type="conversion" short-circuit="true">
      <message key="heuresTravaillées.error"/>
    </field-validator>
 
    <field-validator type="double" short-circuit="true">
      <param name="minInclusive">0</param>
      <param name="maxInclusive">300</param>
      <message key="heuresTravaillées.error"/>
    </field-validator>
  </field>
 
  <field name="joursTravailles" >
    <field-validator type="required" short-circuit="true">
      <message key="joursTravaillés.error"/>
    </field-validator>
 
    <field-validator type="conversion" short-circuit="true">
      <message key="joursTravaillés.error"/>
    </field-validator>
 
    <field-validator type="int" short-circuit="true">
      <param name="min">0</param>
      <param name="max">31</param>
      <message key="joursTravaillés.error"/>
    </field-validator>
  </field>
 
</validators>
  • As linhas 6–20 verificam se o campo hoursWorked é um número real no intervalo [0,300].
  • As linhas 22–36 verificam se o campo `joursTravaillés` é um número inteiro no intervalo [0,31].

Voltemos à configuração da ação [Form] no [struts.xml]:


    <!-- action Formulaire -->
    <action name="Formulaire" class="web.actions.Formulaire">
      ...
      <result name="input" type="tiles">saisie</result>
</action>

Sabemos que, em caso de erro de validação, o interceptor de validação devolve a chave «input». Por conseguinte, é devolvida a vista «Input» do Tiles.

Recorde-se que esta vista é composta pelos fragmentos [Header.jsp] e [Input.jsp], em que [Header.jsp] contém um título e uma lista de opções, e [Input.jsp] contém o formulário de entrada. Em caso de erros de entrada, o processo de validação garante que os campos errados sejam acompanhados por mensagens de erro e também exibam os seus valores incorretos. O fragmento [Entete.jsp] não desempenha qualquer papel no processo de validação. Vejamos o seu código:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
 
<table>
  <tr>
    <td><h1><s:text name="Pam.titre"/></h1></td>
    <td>
      <s:if test="menu.faireSimulation">
        |<a href="javascript:doSimulation()"><s:text name="Menu.FaireSimulation"/></a><br/>
      </s:if>
      <s:if test="menu.effacerSimulation">
        |<a href="<s:url action="Formulaire!input"/>"><s:text name="Menu.EffacerSimulation"/></a><br/>
      </s:if>
      <s:if test="menu.voirSimulations">
        |<a href="<s:url action="VoirSimulations"/>"><s:text name="Menu.VoirSimulations"/></a><br/>
      </s:if>
      <s:if test="menu.retourFormulaire">
        |<a href="<s:url action="RetourFormulaire"/>"><s:text name="Menu.RetourFormulaire"/></a><br/>
      </s:if>
      <s:if test="menu.enregistrerSimulation">
        |<a href="<s:url action="EnregistrerSimulation"/>"><s:text name="Menu.EnregistrerSimulation"/></a><br/>
      </s:if>
      <s:if test="menu.terminerSession">
        |<a href="<s:url action="TerminerSession"/>"><s:text name="Menu.TerminerSession"/></a><br/>
      </s:if>
    </td>
  </tr>
</table>

Os seis links são configurados pelo campo de menu do modelo (linhas 8, 11, 14, 17, 20, 23). Quando ocorre um erro, este modelo não é atualizado pela ação, resultando numa página sem menu. Para resolver este problema, a classe [Form] possui o seguinte método de validação:


package web.actions;
 
import com.opensymphony.xwork2.ActionSupport;
...
public class Formulaire extends ActionSupport implements Preparable, SessionAware {
 
  ...
  // menu
  private Menu menu;
 
  @Override
  public void prepare() throws Exception {
  ...
  }
 
  @Override
  public String input() {
  ...
  }
 
  // wage calculation
  public String calculSalaire() {
  ...
  }
 
  @Override
  public void validate() {
    // mistakes?
    if (!getFieldErrors().isEmpty()) {
      // menu
      menu = new Menu(true, false, false, true, false, true);
    }
  }
 
  // getters and setters
  ...
}
  • linha 27: sabemos que, quando isto está presente, o método validate é executado pelo processo de validação. Aproveitamos esta oportunidade para atualizar o menu na linha 4, que faz parte do modelo de fragmento [Entete.jsp].
  • linhas 29–32: se houver erros de validação, definimos o menu para voltar a apresentar a vista de entrada do Tiles. Se não houver erros de validação, não fazemos nada. O método `calculSalaire` é então responsável por criar o modelo de vista a ser apresentado.

19.8.2. Cálculo do salário

Voltemos ao código JSP no cabeçalho:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
 
<table>
  <tr>
    <td><h1><s:text name="Pam.titre"/></h1></td>
    <td>
      <s:if test="menu.faireSimulation">
        |<a href="javascript:doSimulation()"><s:text name="Menu.FaireSimulation"/></a><br/>
      </s:if>
      ...
    </td>
  </tr>
</table>
  • Linha 9: Quando o utilizador clica na ligação [Executar Simulação], a função JavaScript doSimulation é executada. Esta função está definida no fragmento [Saisie.jsp]:

<script language="javascript" type="text/javascript">
  function doSimulation(){
    // mail the form
    document.forms['Saisie'].elements['action'].name='action:Formulaire!calculSalaire'
    document.forms['Saisie'].submit();
  }
</script>
 
<!-- data entry -->
<s:form name="Saisie" id="Saisie">
  <s:select name="comboEmployesValue" list="employes" listKey="SS" listValue="prenom+' ' +nom" key="Pam.Saisie.employé"/>
  <s:textfield name="heuresTravaillees" key="Pam.Saisie.Heures.libellé" value="%{#parameters['heuresTravaillees']!=null ? #parameters['heuresTravaillees'] : heuresTravaillees==null ? '' : getText('double.format',{heuresTravaillees})}"/>
  <s:textfield name="joursTravailles" key="Pam.Saisie.Jours.libellé" value="%{#parameters['joursTravailles']!=null ? #parameters['joursTravailles'] : joursTravailles==null ? '' : joursTravailles}"/>
  <input type="hidden" name="action"/>
</s:form>
  • linha 14: um campo oculto denominado action será enviado para a ação [Form]. Este campo permite-nos especificar a ação e o método a executar quando o formulário for enviado. Como nos podemos lembrar dos primeiros exemplos, estes podem ser especificados num parâmetro denominado action:Action!method. O valor deste parâmetro não importa; basta que esteja presente.
  • Linhas 2–6: A função JavaScript que é executada quando o utilizador clica na ligação [Run Simulation] no fragmento [Header.jsp].
  • Linha 4: Alteramos o atributo name do campo de ação oculto. Asseguramos que este está no formato action:Action!method esperado pelo Struts.
  • Linha 5: O formulário denominado «Saisie de la ligne 5» é enviado. Como resultado, é enviada a seguinte cadeia de parâmetros:
comboEmployesValue=SS1&heuresTravaillees=xx&joursTravailles=yy&action:Formulaire!calculSalaire

SS1: número INSEE do funcionário selecionado na caixa combinada

heuresTravaillees: número de horas trabalhadas

diasTrabalhados: número de dias trabalhados

action:Form!calculateSalary: os elementos acima serão enviados para a ação [Form] e, em seguida, o método calculateSalary dessa ação será executado.

O método [Form].calculateSalary é o seguinte:


// wage calculation
  public String calculSalaire() {
    try {
      // salary calculation
      feuilleSalaire = config.getMetier().calculerFeuilleSalaire(comboEmployesValue, heuresTravaillees, joursTravailles);
      // put the simulation in the session
      session.put("simulation", new Simulation(0, "" + heuresTravaillees, "" + joursTravailles, feuilleSalaire));
      // menu
      menu = new Menu(true, true, true, true, false, true);
      // finish
      return "simulation";
    } catch (Throwable th) {
      ...
    }
  }
  • Linha 5: O cálculo da folha de pagamento é solicitado à camada [business]
  • linha 7: é adicionado um objeto Simulação à sessão do utilizador. Isto porque pode ser necessário para um pedido subsequente. A classe [Simulação] é a seguinte:

package web.entities;
 
import java.io.Serializable;
import metier.FeuilleSalaire;
 
public class Simulation implements Serializable{
 
  public Simulation() {
  }
 
  // simulation fields
  private Integer num;
  private FeuilleSalaire feuilleSalaire;
  private String heuresTravaillées;
  private String joursTravaillés;
 
  // manufacturer
  public Simulation(Integer num,String heuresTravaillées, String joursTravaillés, FeuilleSalaire feuilleSalaire){
    this.setNum(num);
    this.setFeuilleSalaire(feuilleSalaire);
    this.setHeuresTravaillées(heuresTravaillées);
    this.setJoursTravaillés(joursTravaillés);
  }
 
  public double getIndemnites(){
    return feuilleSalaire.getElementsSalaire().getIndemnitesEntretien()+ feuilleSalaire.getElementsSalaire().getIndemnitesRepas();
  }
 
  // getters and setters
...
}
  • linha 12: o número da simulação. É incrementado a cada nova simulação guardada.
  • linha 13: o recibo de vencimento do funcionário
  • linha 14: o número de horas trabalhadas
  • linha 15: o número de dias trabalhados
  • linha 25: o método getIndemnites devolve a remuneração total do funcionário

Veremos que a classe [Simulation] é o modelo para o fragmento [Simulations.jsp], que exibe todas as simulações realizadas.

De volta ao método [Form].calculateSalary:


// wage calculation
  public String calculSalaire() {
    try {
      // salary calculation
      feuilleSalaire = config.getMetier().calculerFeuilleSalaire(comboEmployesValue, heuresTravaillees, joursTravailles);
      // put the simulation in the session
      session.put("simulation", new Simulation(0, "" + heuresTravaillees, "" + joursTravailles, feuilleSalaire));
      // menu
      menu = new Menu(true, true, true, true, false, true);
      // finish
      return "simulation";
    } catch (Throwable th) {
 ...
  }
  • linha 9: atualizar o menu
  • linha 11: devolve a tecla de navegação «simulação».

Voltar à configuração da ação [Formulário]:


    <!-- action Formulaire -->
    <action name="Formulaire" class="web.actions.Formulaire">
         ...
      <result name="simulation" type="tiles">simulation</result>
</action>

A linha 4 mostra que a tecla de navegação «simulation» apresenta a vista Tiles denominada «simulation». Esta vista é composta pelos seguintes fragmentos JSP: [Header, Input, Simulation].

A vista renderizada é a seguinte:

  • em [1], o fragmento [Header.jsp]
  • em [2], o fragmento [Input.jsp]
  • em [3], o fragmento [Simulation.jsp]. Note-se que o recibo de vencimento apresentado é o recibo de vencimento fictício gerado pela camada [business].

Os dois primeiros fragmentos já foram apresentados. O fragmento [Simulation.jsp] é o seguinte:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
 
<hr/>
<!-- information Employees -->
<span class="titreInfos">
  <s:text  name="Simulation.Infos.employe"/>
</span>
<br/><br/>
<table>
  <!-- line 1 -->
  <tr>
    <th class="libelle">
      <s:text name="Simulation.Employe.nom"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Employe.prenom"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Employe.adresse"/>
    </th>
  </tr>
  <!-- line 2 -->
  <tr>
    <td class="info">   
      <s:property value="feuilleSalaire.employe.nom"/>      
    </td>
    <td class="info">
      <s:property value="feuilleSalaire.employe.prenom"/>
    </td>
    <td class="info">
      <s:property value="feuilleSalaire.employe.adresse"/>
    </td>
</table>
<table>
  <!-- line 1 -->
  <tr>
    <th class="libelle"><s:text name="Simulation.Employe.ville"/></th>
    <th class="libelle">
      <s:text name="Simulation.Employe.codePostal"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Employe.indice"/>
    </th>
  </tr>
  <!-- line 2 -->
  <tr>
    <td class="info">
      <s:property value="feuilleSalaire.employe.ville"/>
    </td>
    <td class="info">
      <s:property value="feuilleSalaire.employe.codePostal"/>
    </td>
    <td class="info">
      <s:property value="feuilleSalaire.employe.indemnite.indice"/>
    </td>
</table>
<!-- information Contributions -->
<br/>
<span class="titreInfos">
  <s:text name="Simulation.Infos.cotisations"/>
</span>
 
<br/><br/>
<table>
  <!-- line 1 -->
  <tr>
    <th class="libelle">
      <s:text name="Simulation.Cotisations.csgrds"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Cotisations.csgrds"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Cotisations.retraite"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Cotisations.secu"/>
    </th>
  </tr>
  <!-- line 2 -->
  <tr>
    <td class="info">
      <s:text name="Format.pourcent">
        <s:param value="feuilleSalaire.cotisation.csgrds"/>
      </s:text>
    </td>
    <td  class="info">
      <s:text name="Format.pourcent">
        <s:param value="feuilleSalaire.cotisation.csgd"/>
      </s:text>
    </td>
    <td class="info">
      <s:text name="Format.pourcent">
        <s:param value="feuilleSalaire.cotisation.retraite"/>
      </s:text>
    </td>
    <td class="info">
      <s:text name="Format.pourcent">
        <s:param value="feuilleSalaire.cotisation.secu"/>
      </s:text>
    </td>
</table>
<!-- information Indemnities -->
<br/>
<span class="titreInfos">
  <s:text name="Form.Infos.indemnites"/>
</span>
<br/><br/>
<table>
  <!-- line 1 -->
  <tr>
    <th class="libelle">
      <s:text name="Simulation.Indemnites.salaireHoraire"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Indemnites.entretienJour"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Indemnites.repasJour"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Indemnites.congésPayés"/>
    </th>
  </tr>
  <!-- line 2 -->
  <tr>
    <td class="info">
      <s:text name="Format.monnaie">
        <s:param value="feuilleSalaire.employe.indemnite.baseHeure"/>
      </s:text>
    </td>
    </td>
    <td class="info">
      <s:text name="Format.monnaie">
        <s:param value="feuilleSalaire.employe.indemnite.entretienJour"/>
      </s:text>
    </td>
    <td class="info">
      <s:text name="Format.monnaie">
        <s:param value="feuilleSalaire.employe.indemnite.repasJour"/>
      </s:text>
    </td>
    <td class="info">
      <s:text name="Format.monnaie">
        <s:param value="feuilleSalaire.employe.indemnite.indemnitesCP"/>
      </s:text>
    </td>
  </tr>
</table>
<!-- salary information -->
<br/>
<span class="titreInfos">
  <s:text name="Simulation.Infos.Salaire"/>
</span>
<br/><br/>
<table>
  <!-- line 1 -->
  <tr>
    <th class="libelle">
      <s:text name="Simulation.Salaire.salaireBase"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Salaire.cotisationsSociales"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Salaire.entretien"/>
    </th>
    <th class="libelle">
      <s:text name="Simulation.Salaire.repas"/>
    </th>
  </tr>
 
  <!-- line 2 -->
  <tr>
    <td class="info">
      <s:text name="Format.monnaie">
        <s:param value="feuilleSalaire.elementsSalaire.salaireBase"/>
      </s:text>
    </td>
    <td class="info">
      <s:text name="Format.monnaie">
        <s:param value="feuilleSalaire.elementsSalaire.cotisationsSociales"/>
      </s:text>
    </td>
    <td class="info">
      <s:text name="Format.monnaie">
        <s:param value="feuilleSalaire.elementsSalaire.indemnitesEntretien"/>
      </s:text>
    </td>
    <td class="info">
      <s:text name="Format.monnaie">
        <s:param value="feuilleSalaire.elementsSalaire.indemnitesRepas"/>
      </s:text>
    </td>
  </tr>
</table>
<!-- Net salary-->
<br/>
<table>
  <tr>
    <td class="libelle">
      <s:text name="Simulation.salaireNet"/>
    <td></td>
    <td class="info">
      <s:text name="Format.monnaie">
        <s:param value="feuilleSalaire.elementsSalaire.salaireNet"/>
      </s:text>
    </td>
  </tr>
</table>

É longo... mas funcionalmente simples. Este trecho de código apresenta as várias propriedades do campo [Form].paystub, que representa o recibo de vencimento do funcionário.

De volta ao método [Form].calculatePay:


// wage calculation
  public String calculSalaire() {
    try {
      ...
      return "simulation";
    } catch (Throwable th) {
      erreurs = new ArrayList<Erreur>();
      while (th != null) {
        erreurs.add(new Erreur(th.getClass().getName(), th.getMessage()));
        th = th.getCause();
      }
      // menu
      menu = new Menu(false, false, false, false, true, true);
      return "exception";
    }
  }

O cálculo do salário pode falhar. Seria o caso, por exemplo, se a ligação ao SGBD falhasse. Neste caso, tratamos a exceção que ocorre. Já nos deparámos com este cenário ao estudar o método [Form].input.

  • Linhas 7–11: Criamos uma lista de objetos Error a partir da pilha de exceções
  • linha 13: definimos o menu
  • linha 14: definimos a chave da exceção.

A chave de exceção irá apresentar a vista de exceções do Tiles:


    <!-- action Formulaire -->
    <action name="Formulaire" class="web.actions.Formulaire">
      <result name="exception" type="tiles">exception</result>
      ...
</action>

Esta vista Tiles já foi apresentada. Tem o seguinte aspeto:

Image

19.9. Guardar uma simulação

Após executar uma simulação, o utilizador poderá querer guardá-la na sessão.

  • Em [1], a simulação é guardada
  • Em [2], a resposta apresenta a lista de simulações já realizadas, à qual a nova simulação é adicionada

O link [Guardar Simulação] está localizado no fragmento [Entete.jsp]:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
 
<table>
  <tr>
    <td><h1><s:text name="Pam.titre"/></h1></td>
    <td>
      ...
      <s:if test="menu.enregistrerSimulation">
        |<a href="<s:url action="EnregistrerSimulation"/>"><s:text name="Menu.EnregistrerSimulation"/></a><br/>
      </s:if>
      ...
    </td>
  </tr>
</table>

Podemos ver que clicar no link aciona a ação [SaveSimulation]. Esta ação está configurada no ficheiro [struts.xml] da seguinte forma:


   <!-- action EnregistrerSimulation -->
    <action name="EnregistrerSimulation" class="web.actions.Enregistrer" method="execute">
      <result name="error" type="tiles">erreur</result>
      <result name="simulations" type="tiles">simulations</result>
</action>
  • Linha 1: A ação [SaveSimulation] está associada à classe [Save] e ao seu método execute.

A classe [Save] é a seguinte:


package web.actions;
 
...
public class Enregistrer extends ActionSupport implements SessionAware {
 
  // session
  private Map<String, Object> session;
  // menu
  private Menu menu;
 
  @Override
  public void setSession(Map<String, Object> session) {
    this.session = session;
  }
 
  // action execution
  public String execute() {
    // retrieve the last simulation in the session
    Simulation simulation = (Simulation) session.get("simulation");
    if (simulation == null) {
      return ERROR;
    }
    ...
  }
 
  // getters and setters
  ...
}
  • linha 4: como a ação precisa de aceder à sessão, ela implementa a interface SessionAware.
  • linha 7: a sessão
  • linha 9: o menu

Quando a ação [Save] é instanciada, o seu método execute é chamado. Recorde-se que a sua função é colocar a simulação mais recente na sessão. Esta simulação será adicionada à lista de simulações concluídas, que também é armazenada na sessão.

  • linha 19: recuperamos a última simulação colocada na sessão.
  • linhas 20–22: se não for encontrada, então a sessão provavelmente expirou. Na verdade, a sessão dura apenas um determinado período de tempo, que pode ser definido no ficheiro [web.xml] que configura a aplicação.
  • Linha 21: Devolvemos a chave de erro.

De volta à configuração da ação [SaveSimulation]:


   <!-- action EnregistrerSimulation -->
    <action name="EnregistrerSimulation" class="web.actions.Enregistrer" method="execute">
      <result name="error" type="tiles">erreur</result>
      <result name="simulations" type="tiles">simulations</result>
</action>

Podemos ver que a chave "error" (linha 3) aciona a exibição da vista Tiles denominada "error". Esta vista é composta pelos fragmentos [Entete.jsp] e [Erreur.jsp] e tem o seguinte aspeto:

Image

O fragmento [Erreur.jsp] é o seguinte:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
 
<h2><s:text name="Pam.Erreur.libelle"/></h2>
<h4><s:text name="Erreur.sessionexpiree"/></h4>

De volta ao método [Save].execute:


// action execution
  public String execute() {
    // retrieve the last simulation in the session
    Simulation simulation = (Simulation) session.get("simulation");
    if (simulation == null) {
      return ERROR;
    }
    // retrieve the number of the last simulation
    Integer numDerniereSimulation = (Integer) session.get("numDerniereSimulation");
    if (numDerniereSimulation == null) {
      numDerniereSimulation = 0;
    }
    // increment it
    numDerniereSimulation++;
    // we give it the new number in the session
    session.put("numDerniereSimulation", numDerniereSimulation);
    // retrieve the list of simulations
    List<Simulation> simulations = (List<Simulation>) session.get("simulations");
    if (simulations == null) {
      simulations = new ArrayList<Simulation>();
      session.put("simulations", simulations);
    }
    // we add the current simulation
    simulation.setNum(numDerniereSimulation);
    simulations.add(simulation);
    // the list of simulations is displayed
    menu = new Menu(false, false, false, false, true, true);
    return "simulations";
  }
  • linhas 9–16: As várias simulações são numeradas a partir de 1. O último número atribuído é armazenado na sessão sob a chave numDerniereSimulation. O código nas linhas 9–16 recupera esta chave e incrementa o valor a ela associado.
  • linhas 18–22: A lista de simulações é armazenada na sessão sob a chave simulations. As linhas 18–22 recuperam esta lista, caso exista, ou criam-na, caso não exista.
  • Linhas 24–25: Assim que a lista de simulações é obtida, a simulação atual é adicionada a ela (linha 25). Anteriormente, foi atribuído um número à simulação atual (linha 24).
  • Linha 27: O menu a ser exibido é definido
  • Linha 28: Devolvemos a chave de navegação «simulations».

Voltar à configuração da ação [EnregistrerSimulation] em [struts.xml]:


   <!-- action EnregistrerSimulation -->
    <action name="EnregistrerSimulation" class="web.actions.Enregistrer" method="execute">
      <result name="error" type="tiles">erreur</result>
      <result name="simulations" type="tiles">simulations</result>
</action>

Linha 4: A chave «simulations» aciona a exibição da vista Tiles denominada «simulations». Esta vista é composta pelos fragmentos [Entete.jsp] e [Simulations.jsp]. A vista exibida é a seguinte:

  • em [1], o fragmento [Entete.jsp], com o qual já estamos familiarizados.
  • em [2], o fragmento [Simulations.jsp]

O fragmento [Simulations.jsp] é o seguinte:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
 
<!-- empty simulation list -->
<s:if test="#session['simulations']==null || #session['simulations'].size()==0">
  <h2><s:text name="Pam.SimulationsVides.titre"/></h2>
</s:if>
<!-- non-empty simulation list -->
<s:if test="#session['simulations'].size()!=0">
  <h2><s:text name="Pam.Simulations.titre"/></h2>
  <table>
    <tr class="titreInfos">
      <th><s:text name="Pam.Simulations.num"/></th>
      <th><s:text name="Pam.Simulations.nom"/></th>
      <th><s:text name="Pam.Simulations.prenom"/></th>
      <th><s:text name="Pam.Simulations.heures"/></th>
      <th><s:text name="Pam.Simulations.jours"/></th>
      <th><s:text name="Pam.Simulations.salairebase"/></th>
      <th><s:text name="Pam.Simulations.indemnites"/></th>
      <th><s:text name="Pam.Simulations.cotisationsociales"/></th>
      <th><s:text name="Pam.Simulations.salairenet"/></th>
    </tr>
    <s:iterator value="#session['simulations']">
      <s:url action="SupprimerSimulation" var="url">
        <s:param name="id" value="num"/>
      </s:url>
      <tr>
        <td class="libelle"><s:property value="num"/></td>
        <td class="info"><s:property value="feuilleSalaire.employe.nom"/></td>
        <td class="info"><s:property value="feuilleSalaire.employe.prenom"/></td>
        <td class="info"><s:property value="heuresTravaillées"/></td>
        <td class="info"><s:property value="joursTravaillés"/></td>
        <td class="info">
          <s:text name="Format.monnaie">
            <s:param value="feuilleSalaire.elementsSalaire.salaireBase"/>
          </s:text>
        </td>
        <td class="info">
          <s:text name="Format.monnaie">
            <s:param value="indemnites"/>
          </s:text>
        </td>
        <td class="info">
          <s:text name="Format.monnaie">
            <s:param value="feuilleSalaire.elementsSalaire.cotisationsSociales"/>
          </s:text>
        </td>
        <td class="info">
          <s:text name="Format.monnaie">
            <s:param value="feuilleSalaire.elementsSalaire.salaireNet"/>
          </s:text>
        </td>
        <td class="info"><a href="<s:property value="#url"/>">Retirer</a></td>
      </tr>
    </s:iterator>
  </table>
</s:if>
  • linhas 5-7: se não houver simulações na sessão, é apresentada a seguinte vista:

Image

  • linhas 13-21: exibem os cabeçalhos das colunas da tabela

Image

  • linhas 23-55: percorre a lista de simulações encontradas na sessão
  • linhas 24-26: criação de um URL denominado url (atributo id). O link HTML gerado por este URL é o seguinte:
<a href="<a href="view-source:http://localhost:8084/pam/SupprimerSimulation.action?id=1">/pam/SupprimerSimulation.action?id=1</a>">Retirer</a>

Podemos ver que o link aponta para a ação [DeleteSimulation] com o parâmetro id, que representa o número da simulação a ser removida da lista.

  • Linhas 28–54: Para cada iteração pela lista de simulações, são apresentadas as propriedades da simulação atual.

Image

19.10. Remover uma simulação

O utilizador pode querer remover uma simulação da lista de simulações:

  • em [1], a simulação n.º 1 foi eliminada
  • em [2], a simulação n.º 1 foi removida

O link [Remover] encontra-se no fragmento [Simulations.jsp] que já analisámos:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
 
<!-- empty simulation list -->
<s:if test="#session['simulations']==null || #session['simulations'].size()==0">
  <h2><s:text name="Pam.SimulationsVides.titre"/></h2>
</s:if>
<!-- non-empty simulation list -->
<s:if test="#session['simulations'].size()!=0">
  <h2><s:text name="Pam.Simulations.titre"/></h2>
  <table>
    <tr class="titreInfos">
      ...
    </tr>
    <s:iterator value="#session['simulations']">
      <s:url action="SupprimerSimulation" var="url">
        <s:param name="id" value="num"/>
      </s:url>
      <tr>
        ...
        <td class="info">
          <s:text name="Format.monnaie">
            <s:param value="feuilleSalaire.elementsSalaire.salaireNet"/>
          </s:text>
        </td>
        <td class="info"><a href="<s:property value="#url"/>">Retirer</a></td>
      </tr>
    </s:iterator>
  </table>
</s:if>
  • Linhas 16-18: geram o link HTML
<a href="<a href="view-source:http://localhost:8084/pam-01/SupprimerSimulation.action?id=2">/pam-01/SupprimerSimulation.action?id=</a>num">Retirer</a>

onde num é o número da simulação a ser removida.

A ação [DeleteSimulation] é definida da seguinte forma no ficheiro [struts.xml]:


<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
    "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
    "http://struts.apache.org/dtds/struts-2.0.dtd">
 
<struts>
  <!-- internationalization -->
  <constant name="struts.custom.i18n.resources" value="messages" />
  <!-- spring integration -->
  <constant name="struts.objectFactory.spring.autoWire" value="name" />
 
 
  <!-- struts /Tiles actions -->
  <package name="default" namespace="/" extends="tiles-default">
    ...
   <!-- action RetirerSimulation -->
    <action name="SupprimerSimulation" class="web.actions.Supprimer">
      <result name="erreur" type="tiles">erreur</result>
      <result name="simulations" type="tiles">simulations</result>
    </action>
   ...
  </package>
 
    <!-- Add packages here -->
 
</struts>
  • Linha 16: A ação [SupprimerSimulation] está associada à classe [Supprimer]. Como não foi especificado nenhum método, será executado o método execute. A classe [Supprimer] é a seguinte:

package web.actions;
 
...
public class Supprimer extends ActionSupport implements SessionAware {
 
  // session
  private Map<String, Object> session;
  // id of simulation to be deleted
  private String id;
  // menu
  private Menu menu;
 
  @Override
  public void setSession(Map<String, Object> session) {
    this.session = session;
  }
 
  // action execution
  public String execute() {
    // simulations are retrieved from the
    List<Simulation> simulations = (List<Simulation>) session.get("simulations");
    if (simulations == null) {
      // abnormal case - session must have expired
      menu = new Menu(false, false, false, false, true, true);
      return "erreur";
    }
    // id test
    int num = 0;
    boolean erreur = false;
    try {
      num = Integer.parseInt(id);
      erreur = num <= 0;
    } catch (NumberFormatException ex) {
      // abnormal
      erreur = true;
    }
    // mistake?
    if (erreur) {
      menu = new Menu(false, false, false, false, true, true);
      return "erreur";
    }
    // search for the simulation to be deleted
    for (int i = 0; i < simulations.size(); i++) {
      if (num == simulations.get(i).getNum()) {
        simulations.remove(i);
        break;
      }
    }
    // the list of simulations is displayed
    menu = new Menu(false, false, false, false, true, true);
    return "simulations";
  }
 
  // getters and setters
...
}
  • Linha 4: A ação [Delete] implementa a interface [SessionAware] para aceder à sessão.
  • linha 7: a sessão
  • linha 9: o número da simulação a ser eliminada. Recorde-se que instanciamos a classe [Delete] através do URL HTML:
<a href="<a href="view-source:http://localhost:8084/pam-01/SupprimerSimulation.action?id=2">/pam-01/SupprimerSimulation.action?id=</a>num">Retirer</a>

onde num é o número da simulação a ser removida. Este número será armazenado no campo id na linha 9.

  • Linha 11: o menu da vista que será exibido em resposta ao pedido
  • Linha 19: O método `execute` que irá gerar a resposta ao pedido.
  • Linha 21: Recuperamos a lista de simulações já realizadas na sessão
  • linhas 22-26: a incapacidade de recuperar esta lista da sessão significa provavelmente que a sessão expirou. Já nos deparámos com este cenário anteriormente. Devolvemos a chave de erro, que apresenta a vista de erro do Tiles:

<!-- action RetirerSimulation -->
    <action name="SupprimerSimulation" class="web.actions.Supprimer">
      <result name="erreur" type="tiles">erreur</result>
      ...
</action>

A visualização de erros do Tiles foi apresentada na Secção 19.9.

  • linhas 28–36: verificamos se a cadeia de caracteres id na linha 9 é, de facto, um número inteiro maior que 0.
  • linhas 38-40: se não for esse o caso, a chave de erro é devolvida novamente, o que exibirá a vista de erro do Tiles
  • linhas 43–48: A simulação a ser removida é procurada na lista de simulações. Se encontrada, é eliminada.
  • Linha 50: Atualiza o menu da vista de simulações do Tiles.
  • Linha 51: A chave `simulations` é devolvida. Isto irá apresentar a vista de simulações Tiles:

<!-- action RetirerSimulation -->
    <action name="SupprimerSimulation" class="web.actions.Supprimer">
      ...
      <result name="simulations" type="tiles">simulations</result>
</action>

A vista «Simulações em mosaico» foi apresentada na Secção 19.9.

19.11. Voltar ao formulário

A partir da vista «Simulações em mosaico», o utilizador pode regressar ao formulário:

  • em [1], clique na ligação para voltar ao formulário
  • em [2], aparece um formulário vazio

O link [Voltar ao formulário de simulação] está definido no fragmento [Entete.jsp]:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
 
<table>
  <tr>
    <td><h1><s:text name="Pam.titre"/></h1></td>
    <td>
      ...
      <s:if test="menu.retourFormulaire">
        |<a href="<s:url action="RetourFormulaire"/>"><s:text name="Menu.RetourFormulaire"/></a><br/>
      </s:if>
      ...
    </td>
  </tr>
</table>
  • Linha 10: O link aponta para a ação [ReturnForm]. Esta é definida da seguinte forma no ficheiro [struts.xml]:

   <!-- action RetourFormulaire -->
    <action name="RetourFormulaire" >
      <result type="redirectAction">
        <param name="actionName">Formulaire!input</param>
        <param name="namespace">/</param>
      </result>
</action>

Podemos ver que esta ação não está associada a nenhuma classe. Simplesmente redireciona o navegador do cliente para a ação [/Form!input]. Estamos, portanto, na mesma situação que ao apresentar a vista inicial explicada na secção 19.7. Assim, regressamos a esta vista inicial [2].

19.12. Ver a lista de simulações

A partir das vistas «Simulation Tiles» ou «Input», o utilizador pode solicitar a visualização das simulações:

  • em [1], clique na ligação [Ver Simulações]
  • em [2], é apresentada a lista de simulações

O link [Ver simulações] está definido no fragmento [Entete.jsp]:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
 
<table>
  <tr>
    <td><h1><s:text name="Pam.titre"/></h1></td>
    <td>
      ...
      <s:if test="menu.voirSimulations">
        |<a href="<s:url action="VoirSimulations"/>"><s:text name="Menu.VoirSimulations"/></a><br/>
      </s:if>
      ...
    </td>
  </tr>
</table>
  • Linha 10: O link [Ver Simulações] chama a ação [ViewSimulations]. Esta está definida da seguinte forma no ficheiro [struts.xml]:

   <!-- action VoirSimulations -->
    <action name="VoirSimulations" class="web.actions.Voir">
      <result name="success" type="tiles">simulations</result>
</action>

A ação [ViewSimulations] está associada à classe [View] sem especificar um método. Por conseguinte, será executado o método [View].execute. A classe [View] é a seguinte:


package web.actions;
 
import com.opensymphony.xwork2.ActionSupport;
import web.entities.Menu;
 
public class Voir extends ActionSupport{
  // menu
  private Menu menu=new Menu(false,false,false,false,true,true);
  // getters and setters
 
  public Menu getMenu() {
    return menu;
  }
 
  public void setMenu(Menu menu) {
    this.menu = menu;
  }
 
}

A ação [View] faz apenas uma coisa: define o menu para a vista de simulações do Tiles (linha 8). Não existe um método execute. Por isso, será executado o da classe pai [ActionSupport]. Sabemos que este não faz nada, exceto devolver o sinalizador de sucesso.

De volta à ação em [struts.xml]:


   <!-- action VoirSimulations -->
    <action name="VoirSimulations" class="web.actions.Voir">
      <result name="success" type="tiles">simulations</result>
</action>

Na linha 3, vemos que a chave «success» aciona a exibição da vista de simulações do Tiles. Isto foi descrito na página 157.

19.13. Limpar a simulação atual

A partir da vista de simulação Tiles, o utilizador pode solicitar a limpeza da simulação atual:

  • em [1], a simulação atual é apagada
  • em [2], o formulário de entrada é apagado

O link [Limpar Simulação] está definido no fragmento [Entete.jsp]:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
 
<table>
  <tr>
    <td><h1><s:text name="Pam.titre"/></h1></td>
    <td>
      ...
      <s:if test="menu.effacerSimulation">
        |<a href="<s:url action="Formulaire!input"/>"><s:text name="Menu.EffacerSimulation"/></a><br/>
      </s:if>
      ...
    </td>
  </tr>
</table>

Na linha 10, vemos que o link [Limpar simulação] aciona a ação [Form!input]. Sabemos que esta ação leva à vista inicial [2].

19.14. Terminar a sessão atual

A partir de qualquer vista de mosaicos, o utilizador pode solicitar o encerramento da sessão:

  • Em [1], partimos da vista de simulações e encerramos a sessão
  • Em [2], o formulário de entrada está vazio. Solicitamos a visualização das simulações.
  • Em [3], a lista de simulações está agora vazia.

O link [Terminar sessão] está definido no fragmento [Entete.jsp]:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>
 
<table>
  <tr>
    <td><h1><s:text name="Pam.titre"/></h1></td>
    <td>
      ...
      <s:if test="menu.terminerSession">
        |<a href="<s:url action="TerminerSession"/>"><s:text name="Menu.TerminerSession"/></a><br/>
      </s:if>
    </td>
  </tr>
</table>

Na linha 10, vemos que o link [End Session] aciona a ação [EndSession]. Esta está definida da seguinte forma no ficheiro [struts.xml]:


    <action name="TerminerSession" class="web.actions.Terminer">
      <result name="success" type="redirectAction">
        <param name="actionName">Formulaire!input</param>
        <param name="namespace">/</param>
      </result>
</action>
  • Linha 1: Podemos ver que a classe [Terminer] será instanciada e o seu método execute será chamado.
  • Linhas 2–5: Após a execução do método [Terminer].execute, o utilizador será redirecionado para a vista de entrada inicial. Isto explica o Ecrã 2.

A classe [Terminer] é a seguinte:


package web.actions;
 
import com.opensymphony.xwork2.ActionSupport;
import java.util.Map;
import org.apache.struts2.interceptor.SessionAware;
 
public class Terminer extends ActionSupport implements SessionAware {
 
  // session
  private Map<String, Object> session;
 
  @Override
  public String execute() {
    // quit current session
    session.clear();
    return SUCCESS;
  }
 
  @Override
  public void setSession(Map<String, Object> session) {
    this.session = session;
  }
}

A função da ação [Finish] é limpar os atributos da sessão atual.

  • Linha 7: A ação [Terminate] implementa a interface [SessionAware] para aceder à sessão.
  • linha 10: o dicionário da sessão
  • linha 13: o método execute é chamado
  • linha 15: limpa o dicionário da sessão. Como resultado, a lista de simulações atualmente na sessão desaparecerá. Isto explica a tela n.º 3.
  • Linha 16: Retorna a chave «success», que, como vimos, exibirá a vista Tiles com o valor [2].

19.15. Conclusão

Comentámos exaustivamente a versão 1 do nosso estudo de caso, que funciona com uma camada [de negócios] simulada:

Resta apenas «ligar» a camada de negócios real à camada [web].