Skip to content

19. مطالعه موردی – نسخه ۱

19.1. لایه شبیه‌سازی‌شده [metier]

بیایید بار دیگر نگاهی به معماری برنامه‌ای که در حال ساخت آن هستیم بیندازیم:

ما آرشیو لایه‌های [metier, dao, jpa] را داریم و عناصری از این لایه‌ها را که لایه [web] باید از آن‌ها آگاه باشد، مشخص کرده‌ایم. اکنون آماده نوشتن این لایه با استفاده از فریم‌ورک Struts هستیم.

برای ساده‌سازی تست اپلیکیشن در حین توسعه، ما یک لایه کسب‌وکار شبیه‌سازی‌شده ایجاد خواهیم کرد که رابط لایه [metier] را پیاده‌سازی می‌کند. معماری به شرح زیر خواهد بود:

ما لایه [web] را در کنار لایه شبیه‌سازی‌شده [métier] توسعه خواهیم داد. تست‌کردن ساده‌تر خواهد بود زیرا دیگر در معماری پایگاه‌داده وجود ندارد. به لطف Spring و استفاده از رابط‌ها، جایگزینی لایه شبیه‌سازی‌شده [metier] با معماری واقعی [metier, dao, jpa] در مرحله بعد، هیچ تأثیری بر کد لایه [web / struts2] نخواهد داشت. لایه [web / struts2] که اکنون قصد توسعه آن را داریم، می‌تواند به همین شکل مورد استفاده قرار گیرد.

لایه شبیه‌سازی‌شده [metier] که قصد داریم استفاده کنیم به شرح زیر است:


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 {

  // فهرست کارمندان
  private Map<String, Employe> hashEmployes = new HashMap<String, Employe>();
  private List<Employe> listEmployes;

  //بازیابی فیش حقوقی
  public FeuilleSalaire calculerFeuilleSalaire(String SS,
          double nbHeuresTravaillées, int nbJoursTravaillés) {
    //بازیابی کارمند با شناسه SS
    Employe e = hashEmployes.get(SS);
    // تولید فیش حقوقی ساختگی
    return new FeuilleSalaire(e, new Cotisation(3.49, 6.15, 9.39, 7.88), new ElementsSalaire(100, 100, 100, 100, 100));
  }

  // فهرست کارمندان
  public List<Employe> findAllEmployes() {
    if (listEmployes == null) {
      //ایجاد فهرستی از دو کارمند
      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)));
      //فرهنگ کارمند
      for (Employe e : listEmployes) {
        hashEmployes.put(e.getSS(), e);
      }
    }
    //فهرست کارمندان را بازمی‌گرداند
    return listEmployes;
  }
}
  • خط ۱۱: کلاس [MetierSimule] رابط [IMetier] را پیاده‌سازی می‌کند، رابطی که لایه واقعی [metier] آن را پیاده‌سازی می‌کند.
  • خط ۱۴: یک فرهنگ لغت از کارمندان که بر اساس شماره INSEE آن‌ها نمایه شده است
  • خط ۱۵: فهرست کارمندان
  • خطوط ۲۷–۳۹: پیاده‌سازی روش findAllEmployes از رابط [IMetier].
  • خطوط ۳۰–۳۳: ایجاد فهرستی از دو کارمند
  • خطوط ۳۴–۳۶: ایجاد فرهنگ لغت کارمند با نمایه شماره INSEE
  • خطوط ۱۸–۲۴: پیاده‌سازی متد calculerSalaire از رابط [IMetier]. در اینجا یک فیش حقوقی ساختگی بازگردانده می‌شود.

19.2. پروژه NetBeans

پروژه NetBeans به شرح زیر است:

  • در [1]:
    • [applicationContext.xml] فایل پیکربندی Spring است
    • [tiles.xml] فایل پیکربندی فریم‌ورکی به نام Tiles است.
    • [web.xml] فایل پیکربندی برنامهٔ وب است
  • در [2]: نماهای مختلف برنامه
  • در [3]:
    • [messages.properties]: فایل پیام‌ها
    • [struts.xml]: فایل پیکربندی Struts
  • در [4]: کد منبع برنامه. اکشن‌های Struts در پکیج [web.actions] قرار دارند.
  • در [5]: لایه شبیه‌سازی‌شده [metier]
  • در [6]: آرشیوهای مورد استفاده. این‌ها حاوی آرشیوهای ابزارهای مختلف مورد استفاده هستند: Spring، Tiles، Struts 2، افزونه یکپارچه‌سازی Struts 2/Spring و افزونه یکپارچه‌سازی Struts 2/Tiles.
  • در [7]: آرشیو لایه واقعی [metier, dao, jpa]. این به ما دسترسی به انتیت‌های JPA، رابط [IMetier] و کلاس‌های [FeuilleSalaire] و [ElementsSalaire] را می‌دهد. در واقع، تمام این عناصر توسط کلاس [MetierSimule] ما استفاده می‌شوند.

19.3. پیکربندی پروژه

پروژه توسط فایل‌های مختلف پیکربندی شده است:

  • [web.xml]، که برنامهٔ وب را پیکربندی می‌کند
  • [struts.xml]، که چارچوب Struts را پیکربندی می‌کند
  • [applicationContext.xml]، که چارچوب Spring را پیکربندی می‌کند
  • [tiles.xml]، که فریم‌ورک Tiles را پیکربندی می‌کند

19.3.1. پیکربندی برنامه وب

فایل [web.xml] به شرح زیر است:


<?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>
    <!-- استروتس ۲ -->
  <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>
     <!-- اسپرینگ -->
  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
</web-app>
  • خطوط ۱۳–۲۰: پیکربندی فیلتر Struts 2 – همان‌طور که قبلاً دیده شد
  • خطوط 22–24: پیکربندی listener اسپرینگ – همان‌طور که قبلاً دیده شد
  • خطوط ۹–۱۱: پیکربندی listener Tiles. کلاس [org.apache.struts2.tiles.StrutsTilesListener] هنگام راه‌اندازی برنامه وب ایجاد می‌شود. سپس از فایل پیکربندی خود استفاده می‌کند. این فایل در خطوط ۵–۸ تعریف شده است. بنابراین فایل پیکربندی Tiles، فایل [WEB-INF/tiles.xml] است.

در نهایت، وقتی برنامه Struts راه‌اندازی می‌شود، سه کلاس ایجاد می‌شوند:

  • یکی برای فیلتر Struts 2. این کلاسی است که حرف «C» در MVC را فراهم می‌کند.
  • یکی دیگر برای لیسنر (listener) اسپرینگ. اسپرینگ از فایل [applicationContext.xml] برای نمونه سازی لایه‌های [métier, dao, jpa] برنامه استفاده خواهد کرد. اسپرینگ همچنین، همانند مثال قبلی، کلاسی به نام [Config] را نمونه سازی خواهد کرد که حاوی داده‌های با دامنه Application خواهد بود. در نهایت، اسپرینگ یک مرجع به این نمونه یکتای [Config] را به هر اکشن Struts که به آن نیاز داشته باشد تزریق خواهد کرد.
  • یک نمونه دیگر برای لیسنر Tiles مورد نیاز است. این فریم‌ورک نماها را مدیریت خواهد کرد. به زودی به این موضوع باز خواهیم گشت.

19.3.2. پیکربندی چارچوب Struts

چارچوب Struts از طریق فایل زیر پیکربندی می‌شود: [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>
  <!--بین‌المللی‌سازی -->
  <constant name="struts.custom.i18n.resources" value="messages" />
  <!-- ادغام Spring -->
  <constant name="struts.objectFactory.spring.autoWire" value="name" />

  
  <!-- اقدامات Struts/Tiles -->
  <package name="default" namespace="/" extends="tiles-default">
    <!-- عملکرد پیش‌فرض -->
    <default-action-ref name="index" />
    <action name="index">
      <result type="redirectAction">
        <param name="actionName">Formulaire</param>
        <param name="namespace">/</param>
      </result>
    </action>
    <!-- عمل فرم -->
    <action name="Formulaire" class="web.actions.Formulaire" method="input">
      <result name="success" type="tiles">saisie</result>
      <result name="exception" type="tiles">exception</result>
    </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>
    <!-- EnregistrerSimulation action -->
    <action name="EnregistrerSimulation" class="web.actions.Enregistrer" method="execute">
      <result name="error" type="tiles">erreur</result>
      <result name="simulations" type="tiles">simulations</result>
    </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>

ما در حین بررسی هر یک از اکشن‌های Struts، در مورد آن‌ها توضیح خواهیم داد. فعلاً، به نکات زیر توجه کنید:

  • خط ۸: فایل پیام را تعریف می‌کند
  • خط ۱۰: تعریف می‌کند که چگونه بیین‌های Spring به اکشن‌های Struts تزریق می‌شوند. تزریق بر اساس نام بیین انجام می‌شود. فیلدی در اکشن Struts که باید توسط Spring مقداردهی اولیه شود، باید نامی یکسان با بیین مورد نظر برای تزریق داشته باشد.
  • خط ۲۵: نمای نمایش داده شده برای کلید ناوبری success از اقدام [Formulaire] را تعریف می‌کند. می‌توانیم ببینیم که نتیجه <result> دارای یک ویژگی type='tiles' است که با آن آشنا نیستیم. ما با نوع redirect که برای هدایت مشتری به یک نما استفاده می‌شود، آشنا بودیم. در اینجا، نمای از نوع tiles توسط فریم‌ورک Tiles مدیریت می‌شود. نوع tiles در فایل [struts-plugin.xml] درون آرشیو [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>
  • خطوط ۳–۵: تعریف نوع نتیجه tiles.
  • خط ۲: این نوع در بسته [tiles-default] تعریف شده است که بسته [struts-default] را گسترش می‌دهد.
  • خط ۱۴: بسته [default] را تعریف می‌کند که شامل تمام اکشن‌های برنامه خواهد بود. برای استفاده از تعریف نوع نما tiles، این بسته از [tiles-default] ارث‌بری می‌کند.

19.3.3. پیکربندی چارچوب Spring

چارچوب Spring توسط فایل زیر پیکربندی می‌شود: [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">
  
  <!-- لایه‌های برنامه -->
 
<!-- وب -->
  <bean id="config" class="web.Config" init-method="init">
    <property name="metier" ref="metier"/>
  </bean>
  <!-- کسب‌وکار -->
  <bean id="metier" class="metier.MetierSimule"/>

</beans>
  • خط ۱۳: لایه شبیه‌سازی‌شده [metier] که توسط کلاس [metier.MetierSimule] نمونه‌سازی شده است
  • خطوط ۹–۱۱: یک بیون به نام config را پیکربندی کنید. همان‌طور که در مثال قبلی بحث شد، این بیون برای جاسازی اطلاعات دامنه Application استفاده خواهد شد. کلاس مرتبط با این بیون کلاس زیر [Config] است:

package web;

import java.util.List;
import jpa.Employe;
import metier.IMetier;

public class Config {

  // لایه کسب‌وکار که توسط Spring راه‌اندازی شده است
  private IMetier metier;
  // فهرست کارمندان
  private List<Employe> employes;
  // خطاها
  private Exception initException;

  // سازنده
  public Config() {
  }

  // متد Spring برای инициалиزه‌سازی شیء
  public void init() {
    //درخواست فهرست کارمندان
    try {
      employes = metier.findAllEmployes();
    } catch (Exception ex) {
      initException = ex;
    }
  }

  // گیرنده‌ها و تنظیم‌کننده‌ها
  ... 
}

بیایید به پیکربندی بیون config بازگردیم:


  <bean id="config" class="web.Config" init-method="init">
    <property name="metier" ref="metier"/>
  </bean>
  <!-- منطق کسب‌وکار -->
  <bean id="metier" class="metier.Metier">
    ...  
</bean>

در خط ۲، می‌توانیم ببینیم که بین metier در خط ۵ به فیلدی به نام metier از شیء [Config] تزریق (ref) شده است. بیان metier یک مرجع بر روی لایه [metier] است:

برای تعامل با لایه [metier]، تمام اکشن‌های Struts در لایه [web] به یک مرجع به آن نیاز خواهند داشت. می‌توان گفت که ارجاع به لایه [metier] یک متغیر دامنه برای Application است. تمام درخواست‌ها از تمام کاربران به آن نیاز خواهند داشت. به همین دلیل است که این ارجاع را در شیء [Config] قرار می‌دهیم. علاوه بر این، در خط ۱، پیکربندی bean config دارای یک ویژگی init-method است. این ویژگی متد bean را مشخص می‌کند که پس از instantiate شدن bean باید اجرا شود. در اینجا مشخص می‌کنیم که پس از نمونه‌سازی کلاس [web.Config]، متد init آن باید اجرا شود. این متد به شرح زیر است:


// لایه کسب‌وکار راه‌اندازی‌شده توسط اسپرینگ
  private IMetier metier;
  // فهرست کارمندان
  private List<Employe> employes;
  // خطاها
  private Exception initException;

  // سازنده
  public Config() {
  }

  // متد اسپرینگ برای инициалиزه‌سازی شیء
  public void init() {
    //درخواست فهرست کارمندان
    try {
      employes = metier.findAllEmployes();
    } catch (Exception ex) {
      initException = ex;
    }
  }

هنگامی که متد init اجرا می‌شود، فیلد metier از کلاس توسط Spring نمونه برداری شده است. بنابراین متد init به لایه رابط کاربری کسب‌وکار [IMetier] دسترسی دارد (خط ۲):


package metier;

import java.util.List;
import jpa.Employe;

public interface IMetier {
  //بازیابی فیش حقوقی
  FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés );
  // فهرست کارمندان
  List<Employe> findAllEmployes();
}
  • خط ۸: این متد حقوق یک کارمند را محاسبه می‌کند
  • خط ۱۰: این متد فهرست کارمندان را بازیابی می‌کند

می‌توانیم ببینیم که متد init لیست کارمندان را از لایه [métier] درخواست می‌کند. این لیست در فیلد در خط ۴ ذخیره می‌شود. اگر استثنایی رخ دهد، در فیلد در خط ۶ ذخیره می‌شود.

در نتیجه، شیء واحد [Config] شامل موارد زیر است:

  • یک مرجع به لایه [métier]
  • فهرست کارمندان

19.4. تولید نماهای کاشی

همانطور که در فایل پیکربندی Struts دیدیم، ویوها توسط فریم‌ورک Tiles تولید خواهند شد. ما فقط آنچه را که برای نوشتن برنامه خود به طور دقیق ضروری است، توضیح خواهیم داد.

Tiles امکان تولید ویوها از یک صفحهٔ اصلی را فراهم می‌کند. این صفحهٔ اصلی، که در اینجا با نام [MasterPage.JSP] اشاره می‌شود، ترکیبی از قطعات زیر از JSP خواهد بود:

Entete.JSP
Saisie.JSP
Simulation.JSP
Simulations.JSP
Exception.JSP
Erreur.JSP

این قطعات JSP در پروژه NetBeans تعریف شده‌اند:

  

چارچوب Tiles به ما امکان می‌دهد مشخص کنیم کدام قطعات در صفحهٔ اصلی درج شوند.

صفحهٔ اصلی [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>

صفحهٔ اصلی یک محفظه برای قطعات JSP است. در اینجا، شامل شش قطعه است، قطعاتی که در خطوط 17 تا 23 قرار دارند. در طول تولید، ممکن است بین 0 تا 6 قطعه در صفحه اصلی مونتاژ شوند. این تولید توسط فایل [WEB-INF/tiles.xml] کنترل می‌شود که نماهای 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>
 
<!-- صفحه اصلی -->
  <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>
 
<!-- نمایندگی ورود داده -->
  <definition name="saisie" extends="masterPage">
    <put-attribute name="saisie"   value="/Saisie.JSP"/>     
  </definition>

  <!--نمای شبیه‌سازی -->
 <definition name="simulation" extends="saisie">
    <put-attribute name="simulation"   value="/Simulation.JSP"/>     
  </definition>

<!--نمای شبیه‌سازی‌ها -->
  <definition name="simulations" extends="masterPage">
    <put-attribute name="simulations"   value="/Simulations.JSP"/>     
  </definition>
  
<!--نمای استثنا -->
  <definition name="exception" extends="masterPage">
    <put-attribute name="exception"   value="/Exception.JSP"/>     
  </definition>
 
<!--نمای خطا -->
 <definition name="erreur" extends="masterPage">
    <put-attribute name="erreur"   value="/Erreur.JSP"/>     
  </definition>
</tiles-definitions>
  • فایل فوق شش نمای Tiles را با نام‌های زیر تعریف می‌کند: masterPage (خط ۹)، entry (خط ۲۰)، simulation (خط ۲۵)، simulations (خط ۳۰)، exception (خط ۳۵) و error (خط ۴۰).
  • رده‌های ۹–۱۷: یک نما به نام masterPage (name) را تعریف کنید که با صفحهٔ اصلی [MasterPage.JSP] (template) مرتبط است. ما دیده‌ایم که این صفحه، JSP، شش زیرنمای را تعریف می‌کند. یک نما با نام Tiles که با صفحهٔ اصلی مرتبط است، باید قطعهٔ JSP را که با هر یک از شش زیرنمای مرتبط است، مشخص کند. می‌توانیم ببینیم که به برخی از زیرنمایه‌ها رشتهٔ خالی (value) اختصاص داده شده است. این زیرنمایه‌ها در صفحهٔ اصلی [MasterPage.JSP] گنجانده نخواهند شد. بنابراین، نمای Tiles که با نام masterPage شناخته می‌شود، صرفاً از زیر-قطعه [Entete.JSP] تشکیل شده است.
  • خطوط ۲۰–۲۲: یک نما به نام saisie (name) را تعریف می‌کند که نمای masterPage را که قبلاً مشاهده شد، گسترش می‌دهد (extends). این بدان معناست که تمام تعاریفِ نمای masterPage را در خود جای می‌دهد. تعریف آن معادل موارد زیر است:

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

می‌توان دید که این نما با صفحه JSP [MasterPage.JSP] مرتبط است و بنابراین، باید شش زیرنما (sub-view) این صفحه را تعریف کند. می‌توانیم ببینیم که تعریف در خط ۹ بر تعریف در خط ۴ اولویت دارد. بنابراین نما Tiles که با نام saisie شناخته می‌شود، از قطعات JSP و [Entete.JSP, Saisie.JSP] تشکیل شده است.

اگر این خط استدلال را ادامه دهیم، جدول زیر به دست می‌آید:

نما کاشی‌ها
صفحات 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. فایل‌های پیام

این برنامه بین‌المللی‌سازی شده است. پیام‌ها را می‌توان در فایل‌های [messages.properties] و [Formulaire.properties] یافت.

فایل [messages.properties] به شرح زیر است:


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
# قالب‌ها
Format.heure = {0,time}
Format.nombre = {0,number,#0.0##}
Format.pourcent = {0,number,##0.00' %'}
Format.monnaie={0,number,##0.00' \u20ac'}
# فهرست شبیه‌سازی‌ها
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.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
# پیام خطا
Erreur.sessionexpiree=La session a expir\u00e9
Erreur.numSimulation=N\u00b0 de simulation incorrect
# خطای تبدیل
xwork.default.invalid.fieldvalue=Valeur invalide pour le champ "{0}".

فایل [Formulaire.properties] به شرح زیر است:


# برای اطمینان از اینکه اعداد اعشاری در قالب محلی هستند
double.format={0,number,#0.00##}
# پیام خطا
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. برگهٔ سبک

نماهای Tiles از شیت سبک زیر استفاده می‌کنند: [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. نمای اولیه

برای تحلیل برنامه، آن را بر اساس اقدامات مختلف کاربر بررسی خواهیم کرد. در هر مورد، به اقدام Struts که این عمل را اجرا می‌کند، و نمای Tiles که در پاسخ بازگردانده می‌شود، نگاه خواهیم کرد.

در [struts.xml]، ما اقدامات زیر را داریم:


  <!-- عمل پیش‌فرض -->
    <default-action-ref name="index" />
    <action name="index">
      <result type="redirectAction">
        <param name="actionName">Formulaire!input</param>
        <param name="namespace">/</param>
      </result>
    </action>
    <!-- عمل فرم -->
    <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>

  • خطوط ۲–۸: اقدام پیش‌فرض برنامه [Formulaire!input] است.

کلاس [Formulaire] به شرح زیر است:


package web.actions;

...
public class Formulaire extends ActionSupport implements Preparable, SessionAware {

  //پیکربندی توسط Spring راه‌اندازی شده است
  private Config config;
  // فهرست کارمندان
  private List<Employe> employes;
  // فهرست خطاها
  private List<Erreur> erreurs;
  // برگه پرداخت
  private FeuilleSalaire feuilleSalaire;
  // ورودی‌ها
  private String comboEmployesValue;
  private Double heuresTravaillees;
  private Integer joursTravailles;
  // جلسه
  private Map<String, Object> session;
  // منو
  private Menu menu;

  @Override
  public void prepare() throws Exception {
    ...
  }

  @Override
  public String input() {
  ....
  }

  // محاسبه حقوق
  public String calculSalaire() {
 ...
    }
  }

  @Override
  public void validate() {
  ...
  }

  @Override
  public void setSession(Map<String, Object> map) {
    session = map;
  }

  // گیرنده و تنظیم‌کننده
  ...
}
  • خط ۴: اکشن [Formulaire] رابط Preparable را پیاده‌سازی می‌کند. این رابط تنها یک متد دارد، متد prepare در خط ۲۴. این متد یک بار قبل از هر متد دیگری در اکشن اجرا می‌شود. این متد معمولاً برای راه‌اندازی مدل اکشن استفاده می‌شود.

مدل اقدام شامل خطوط ۶ تا ۲۱ است:

  • خط ۷: فیلد config همان‌طور که توضیح داده شد توسط Spring مقداردهی اولیه می‌شود. این فیلد دسترسی به داده‌های در مقیاس برنامه را فراهم می‌کند:
    • یک مرجع به لایه [métier]
    • یک مرجع به فهرست کارمندان.
    • یک مرجع به استثنایی که ممکن است در حین نمونه‌سازی شیء [Config] رخ داده باشد
  • خط ۹: فهرستی از کارمندان. این فهرست، لیست کشویی کارمندان را در قطعه [Saisie.JSP] پر می‌کند.
  • خط ۱۱: فهرستی از خطاها. این مورد، قطعه [Erreur.JSP] را پر می‌کند.
  • خط ۲۱: فهرست گزینه‌های منو برای قطعه [Entete.JSP]

در [1]، لینک‌های منوی نمایش‌داده‌شده توسط فیلد menu از اقدام [Formulaire] کنترل می‌شوند.

روش prepare قبل از روش input اجرا می‌شود. به شرح زیر است:


  @Override
  public void prepare() throws Exception {
    // خطای پیکربندی؟
    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();
    }
}
  • خط ۴: استثناء از شی [Config] که توسط Spring ایجاد شده است بازیابی می‌شود.
  • خط ۵: اگر هنگام نمونه‌سازی شیء [Config] خطایی رخ دهد، لیست خطاها در خط ۱۱ مقداردهی اولیه می‌شود. کلاس [Erreur] به شرح زیر است:

package web.entities;

import java.io.Serializable;

public class Erreur implements Serializable{
  
  public Erreur() {
  }
  
  // fields
  private String classe;
  private String message;

  // سازنده
  public Erreur(String classe, String message){
    this.setClasse(classe);
    this.message=message;
  }
  
  // گیرنده‌ها و تنظیم‌کننده‌ها
... 
}

این کلاس برای ذخیرهٔ استک استثنا استفاده می‌شود:

  • خط ۱۱: کلاس استثنا
  • خط ۱۲: پیام استثنا

بیایید به متد prepare بازگردیم:

  • خط ۱۳: لیست کارمندان از شیء [Config] در فیلد employes از اکشن ذخیره می‌شود.

پس از اجرای متد prepare، متد input به ترتیب اجرا خواهد شد. به شرح زیر است:


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

متد input صرفاً لیست گزینه‌های منو را برای نمایش تنظیم می‌کند. کلاس [Menu] به شرح زیر است:


package web.entities;

import java.io.Serializable;

public class Menu implements Serializable {
  // موارد منو

  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;
  }
  
  // گیرنده‌ها و تنظیم‌کننده‌ها
...  
}
  • خطوط ۸–۱۳: ۶ لینک ممکن در منو وجود دارد
  • خطوط ۱۸–۲۵: سازنده کلاس به شما امکان می‌دهد مشخص کنید کدام لینک‌ها باید نمایش داده شوند و کدام نباید.

لینک‌های منو در [Entete.JSP] نمایش داده می‌شوند، در حالی که قطعه JSP در تمام نماهای Tiles موجود است. هر اقدام دارای یک فیلد menu برای کنترل نمایش منوی [Entete.JSP] خواهد بود.

بیایید به متد input بازگردیم:


  @Override
  public String input() {
    if (erreurs == null) {
      // منو
      menu = new Menu(true, false, false, true, false, true);
      return SUCCESS;
    } else {
      // منو
      menu = new Menu(false, false, false, false, false, false);
      return "exception";
    }
}
  • خطوط ۳–۶: اگر لیست خطاها خالی باشد، منوی [Faire la simulation, Voir les simulations, Terminer la session] نمایش داده می‌شود و کلید input بازگردانده می‌شود.
  • خطوط ۹–۱۰: اگر فهرست خطاها خالی نباشد، منو خالی نمایش داده می‌شود و کلید exception بازگردانده می‌شود.

بیایید به پیکربندی عمل [Formulaire] در داخل [struts.xml] بازگردیم:


    <!-- عمل فرم -->
    <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>
  • خط ۵: کلید input نمای کاشی‌ها با نام saisie را نمایش می‌دهد
  • خط ۴: کلید exception نمای کاشی‌ها با نام exception را نمایش می‌دهد

بیایید با نمای Tiles با نام saisie شروع کنیم. این نما شامل قطعات JSP، [Entete.JSP] و [Saisie.JSP] است.

قطعه [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.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>
  • خطوط ۸–۲۵: نمایش شش پیوند منو [اجرای شبیه‌سازی (خطوط ۸–۱۰)، پاک کردن شبیه‌سازی (خطوط ۱۱–۱۳)، مشاهده شبیه‌سازی‌ها (خطوط 14–16)، بازگشت به فرم (خطوط 17–19)، ذخیره شبیه‌سازی (خطوط 20–22)، پایان جلسه (خطوط 23–25).
  • خطوط ۸، ۱۱، ۱۴، ۱۷، ۲۰ و ۲۳: نمایش پیوندها توسط فیلد menu از اقدام جاری کنترل می‌شود.

توجه داشته باشید که قطعه [Entete.JSP] یک جدول HTML (خطوط 4–28) را نمایش می‌دهد اما یک صفحه HTML کامل نیست. در اینجا مهم است که به خاطر داشته باشید تمام نماهای برنامه در صفحه اصلی زیرین، [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>

قطعه [Entete.JSP] در خط 17، درون یک صفحه HTML استاندارد، درج شده است.

قطعه [Saisie.JSP] در خط ۱۹ درج می‌شود. این نمای زیر است:

 

کد قطعه [Saisie.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">


<script language="javascript" type="text/javascript">
  function doSimulation(){
    //ارسال فرم
    document.forms['Saisie'].elements['action'].name='action:Formulaire!calculSalaire'
    document.forms['Saisie'].submit();
  }  
</script>

<!-- وارد کردن اطلاعات -->
<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>
  • خط ۱۷: فرم فاقد ویژگی action است. به طور پیش‌فرض، مقدار آن روی action='Formulaire' تنظیم شده است.
  • خط ۱۸: نمایش لیست کشویی کارمندان. محتوای لیست کشویی (ویژگی list) توسط فیلد employes از اقدام جاری فراهم می‌شود. ویژگی value برای گزینه‌ها، شماره کارمند SS (ویژگی listKey) خواهد بود. برچسب نمایش‌داده‌شده برای هر گزینه، نام و نام خانوادگی کارمند خواهد بود (ویژگی listValue). شماره SS کارمند انتخاب‌شده از لیست کشویی در فیلد [Formulaire].comboEmployesvalue (ویژگی name) ارسال خواهد شد.
  • خط ۱۹: فیلد برای وارد کردن ساعات کاری. مقدار نمایش‌داده‌شده (ویژگی value) مربوط به فیلد heuresTravaillees در اقدام [Formulaire] است، به شکل زیر (Formulaire.properties):

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

این مقدار به فیلد [Formulaire].heuresTravaillees (ویژگی name) ارسال خواهد شد.

  • خط ۲۰: فیلدی برای وارد کردن روزهای کاری. مقدار نمایش داده شده (ویژگی value) مربوط به فیلد joursTravailles در اقدام [Formulaire] است.

مقدار به فیلد [Formulaire].joursTravailles (ویژگی name) ارسال خواهد شد.

در نهایت، نمای کاشی‌ها saisie که هنگام راه‌اندازی و در صورت عدم وجود خطا نمایش داده می‌شود، به شرح زیر است:

 

بیایید به پیکربندی اقدام [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>

ما دیدیم که عمل [Formulaire].input همچنین می‌تواند کلید exception را از خط ۴ بازگرداند. در این حالت، نمای کاشی‌ها با نام exception نمایش داده می‌شود. این نما شامل قطعات [Entete.JSP] و [Exception.JSP] است. ما قبلاً قطعه [Entete.JSP] را ارائه کرده‌ایم. قطعه [Exception.JSP] به شرح زیر است:

 

این صفحهٔ شروع نسخهٔ ۲ برنامه است وقتی که سامانهٔ مدیریت پایگاه داده راه‌اندازی نشده باشد. کد JSP از قطعهٔ [Erreur.JSP] به شرح زیر است:


<%@ 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>
  • خطوط ۱۰–۱۴: یک ایتراتور بر روی مجموعه خطاها `List<Error>` از اقدام `[Formulaire]`. به یاد داشته باشید که در صورت وقوع خطا، یک پشته استثناها در آنجا ذخیره شده بود.

19.8. شبیه‌سازی را اجرا کنید

پس از نمایش صفحهٔ شروع، می‌توان محاسبهٔ حقوق را از طریق لینک [Faire une simulation] انجام داد.

19.8.1. اعتبارسنجی ورودی‌ها

بیایید این دنباله را در نظر بگیریم:

  • به [1]، یک ورودی نادرست
  • به [2]، پاسخ ارسال می‌شود.

بیایید پیکربندی اقدام [Formulaire] در [struts.xml] را در نظر بگیریم:


    <!-- عملکرد فرم -->
    <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>

ما می‌دانیم که در صورت بروز خطای اعتبارسنجی، اینترسپتور اعتبارسنجی، کلید input را بازمی‌گرداند. بنابراین، نمای Tiles با شناسه saisie است که بازگردانده می‌شود. فرآیند اعتبارسنجی تضمین می‌کند که فیلدهای نادرست با پیام‌های خطا همراه باشند.

اعتبارسنجی اقدام [Formulaire] توسط فایل زیر انجام می‌شود: [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>
  • خطوط ۶–۲۰ بررسی می‌کنند که فیلد heuresTravaillees یک عدد حقیقی در بازه [0,300] باشد.
  • خطوط ۲۲–۳۶ بررسی می‌کنند که فیلد joursTravailles یک عدد صحیح در بازه [0,31] باشد.

بیایید به پیکربندی اقدام [Formulaire] در [struts.xml] بازگردیم:


    <!-- عملکرد فرم -->
    <action name="Formulaire" class="web.actions.Formulaire">
      ...
      <result name="input" type="tiles">saisie</result>
</action>

می‌دانیم که در صورت بروز خطای اعتبارسنجی، interceptor اعتبارسنجی کلید input را بازمی‌گرداند. بنابراین نمای Tiles با کلید saisie بازگردانده می‌شود.

شایان ذکر است که این نما از قطعات [Entete.JSP] و [Saisie.JSP] تشکیل شده است، که در آن [Entete.JSP] شامل یک عنوان و مجموعه‌ای از گزینه‌ها است و [Saisie.JSP] شامل فرم ورودی می‌باشد. در صورت وجود خطا در ورودی، فرایند اعتبارسنجی تضمین می‌کند که فیلدهای نادرست همراه با پیام‌های خطا باشند و همچنین مقادیر نادرست آن‌ها نمایش داده شود. قطعه [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.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>

شش لینک توسط فیلد menu در قالب (خطوط ۸، ۱۱، ۱۴، ۱۷، ۲۰، ۲۳) پیکربندی شده‌اند. هنگامی که خطایی رخ می‌دهد، این قالب توسط اقدام به‌روزرسانی نمی‌شود و در نتیجه صفحه‌ای بدون منو ایجاد می‌شود. برای حل این مشکل، کلاس [Formulaire] دارای متد زیر است، validate:


package web.actions;

import com.opensymphony.xwork2.ActionSupport;
...
public class Formulaire extends ActionSupport implements Preparable, SessionAware {

  ...
  // منو
  private Menu menu;

  @Override
  public void prepare() throws Exception {
  ...
  }

  @Override
  public String input() {
  ...
  }

  // محاسبه حقوق
  public String calculSalaire() {
  ...
  }

  @Override
  public void validate() {
    // آیا خطایی وجود دارد؟
    if (!getFieldErrors().isEmpty()) {
      // منو
      menu = new Menu(true, false, false, true, false, true);
    }
  }

  // گیرنده‌ها و تنظیم‌کننده‌ها
  ...
}
  • خط ۲۷: می‌دانیم که وقتی این موجود باشد، متد validate توسط فرآیند اعتبارسنجی اجرا می‌شود. از این فرصت استفاده می‌کنیم تا منو را در خط ۴ که بخشی از قالب قطعه [Entete.JSP] است، به‌روزرسانی کنیم.
  • خطوط ۲۹–۳۲: اگر خطاهای اعتبارسنجی وجود داشته باشد، منو برای نمایش مجدد نمای کاشی‌ها saisie تنظیم می‌شود. اگر خطاهای اعتبارسنجی وجود نداشته باشد، هیچ اقدامی انجام نمی‌شود. سپس متد calculSalaire مسئول ایجاد قالب نمای نمایشی است.

19.8.2. محاسبه حقوق

بیایید به کد 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.faireSimulation">
        |<a href="javascript:doSimulation()"><s:text name="Menu.FaireSimulation"/></a><br/>
      </s:if>
      ...
    </td>
  </tr>
</table>
  • خط ۹: وقتی کاربر روی لینک [Faire la simulation] کلیک می‌کند، تابع جاوااسکریپت doSimulation اجرا می‌شود. این در قطعه کد [Saisie.JSP] تعریف شده است:

<script language="javascript" type="text/javascript">
  function doSimulation(){
    //ارسال فرم
    document.forms['Saisie'].elements['action'].name='action:Formulaire!calculSalaire'
    document.forms['Saisie'].submit();
  } 
</script>

<!-- وارد کردن اطلاعات -->
<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>
  • خط ۱۴: یک فیلد مخفی با نام action به اکشن [Formulaire] ارسال خواهد شد. این فیلد به ما امکان می‌دهد تا اکشن و متدی را که باید در POST در فرم اجرا شود، مشخص کنیم. ممکن است به خاطر داشته باشید که در مثال‌های قبلی می‌توان این موارد را در پارامتری به نام action:Action!method مشخص کرد. مقدار این پارامتر اهمیتی ندارد؛ فقط باید وجود داشته باشد.
  • خطوط ۲–۶: تابع جاوااسکریپتی که هنگام کلیک کاربر روی لینک [Faire la simulation] در قطعه [Entete.JSP] اجرا می‌شود.
  • خط ۴: ویژگی name از فیلد مخفی action تغییر می‌کند. اطمینان حاصل می‌شود که این ویژگی در قالب action:Action!method مورد انتظار Struts قرار دارد.
  • خط ۵: فرم با نام Saisie در خط ۵ ارسال می‌شود. در نتیجه، رشته پارامتر زیر ارسال می‌گردد:
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.

روش [Formulaire].calculSalaire به شرح زیر است:


// محاسبه حقوق
  public String calculSalaire() {
    try {
      //محاسبه حقوق
      feuilleSalaire = config.getMetier().calculerFeuilleSalaire(comboEmployesValue, heuresTravaillees, joursTravailles);
      // شبیه‌سازی به جلسه اضافه می‌شود
      session.put("simulation", new Simulation(0, "" + heuresTravaillees, "" + joursTravailles, feuilleSalaire));
      // منو
      menu = new Menu(true, true, true, true, false, true);
      //انجام شد
      return "simulation";
    } catch (Throwable th) {
      ...
    }
  }
  • خط ۵: محاسبه فیش حقوقی از لایه [métier] درخواست می‌شود
  • خط ۷: یک شیء از نوع Simulation در جلسه کاربر قرار داده می‌شود. این ممکن است برای پرس‌وجوی بعدی مورد نیاز باشد. کلاس [Simulation] به شرح زیر است:

package web.entities;

import java.io.Serializable;
import metier.FeuilleSalaire;

public class Simulation implements Serializable{
  
  public Simulation() {
  }

  // fields in a simulation
  private Integer num;
  private FeuilleSalaire feuilleSalaire;
  private String heuresTravaillées;
  private String joursTravaillés;
  
  // سازنده
  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();
  }

  // گیرنده‌ها و تنظیم‌کننده‌ها
...
}
  • خط ۱۲: شماره شبیه‌سازی. این مقدار هر بار که یک شبیه‌سازی جدید ثبت می‌شود، افزایش می‌یابد.
  • خط ۱۳: فیش حقوقی کارمند
  • خط ۱۴: تعداد ساعات کاری
  • خط ۱۵: تعداد روزهای کاری
  • خط ۲۵: متد getIndemnites مجموع مزایای کارمند را برمی‌گرداند

مشاهده خواهیم کرد که کلاس [Simulation] قالب برای قطعه [Simulations.JSP] است که تمام شبیه‌سازی‌های انجام‌شده را نمایش می‌دهد.

بازگردیم به متد [Formulaire].calculSalaire:


//محاسبه حقوق
  public String calculSalaire() {
    try {
      //محاسبه حقوق
      feuilleSalaire = config.getMetier().calculerFeuilleSalaire(comboEmployesValue, heuresTravaillees, joursTravailles);
      // ما شبیه‌سازی را به جلسه اضافه می‌کنیم
      session.put("simulation", new Simulation(0, "" + heuresTravaillees, "" + joursTravailles, feuilleSalaire));
      // منو
      menu = new Menu(true, true, true, true, false, true);
      //انجام شد
      return "simulation";
    } catch (Throwable th) {
 ...
  }
  • خط ۹: به‌روزرسانی منو
  • خط ۱۱: بازگشت کلید ناوبری simulation.

بازگشت به پیکربندی اقدام [Formulaire]:


    <!-- عمل فرم -->
    <action name="Formulaire" class="web.actions.Formulaire">
         ...
      <result name="simulation" type="tiles">simulation</result>
</action>

خط ۴ نشان می‌دهد که کلید ناوبری simulation نمای کاشی‌ها با نام simulation را نمایش می‌دهد. این نما شامل قطعات زیر است: JSP و [Entete, Saisie, Simulation].

نمای رندر شده به شرح زیر است:

  • در [1]، قطعه [Entete.JSP]
  • در [2]، قطعه [Saisie.JSP]
  • به [3]، و قطعه [Simulation.JSP]. شایان ذکر است که فیش حقوقی نمایش داده شده، فیش حقوقی فرضی است که توسط لایه [metier] رندر شده است.

دو قطعه اول قبلاً ارائه شده‌اند. قطعه [Simulation.JSP] به شرح زیر است:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>

<hr/>
<!-- اطلاعات کارمند -->
<span class="titreInfos">
  <s:text  name="Simulation.Infos.employe"/>
</span>
<br/><br/>
<table>
  <!-- ردیف ۱ -->
  <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>
  <!-- خط ۲ -->
  <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>
  <!-- خط ۱ -->
  <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>
  <!-- خط ۲ -->
  <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>
<!-- اطلاعات مشارکت‌ها -->
<br/>
<span class="titreInfos">
  <s:text name="Simulation.Infos.cotisations"/>
</span>

<br/><br/>
<table>
  <!-- خط ۱ -->
  <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>
  <!-- خط ۲ -->
  <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>
<!-- اطلاعات مزایا -->
<br/>
<span class="titreInfos">
  <s:text name="Form.Infos.indemnites"/>
</span>
<br/><br/>
<table>
  <!-- خط ۱ -->
  <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>
  <!-- خط ۲ -->
  <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>
<!-- اطلاعات حقوق -->
<br/>
<span class="titreInfos">
  <s:text name="Simulation.Infos.Salaire"/>
</span>
<br/><br/>
<table>
  <!-- خط ۱ -->
  <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>

  <!-- خط ۲ -->
  <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>
<!-- حقوق خالص-->
<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>

طولانی است... اما از نظر عملکردی ساده است. این قطعه ویژگی‌های مختلف فیلد [Formulaire].feuilleSalaire را نمایش می‌دهد که نشان‌دهنده فیش حقوقی کارمند است.

بازگردیم به متد [Formulaire].calculSalaire:


// محاسبه حقوق
  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 = new Menu(false, false, false, false, true, true);
      return "exception";
    }
  }

محاسبه حقوق ممکن است با شکست مواجه شود. برای مثال، اگر اتصال به سامانه مدیریت پایگاه داده (DBMS) قطع شود، این وضعیت پیش می‌آید. در این حالت، ما استثناء رخ‌داده را مدیریت می‌کنیم. ما قبلاً هنگام بررسی متد [Formulaire].input با این سناریو مواجه شده‌ایم.

  • خطوط ۷–۱۱: یک فهرست از اشیاء Erreur از روی پشته استثنا ایجاد می‌شود
  • خط ۱۳: منو را تنظیم می‌کنیم
  • خط ۱۴: کلید exception بازگردانده می‌شود.

کلید exception نمای کاشی‌ها exception را نمایش می‌دهد:


    <!-- عملکرد فرم -->
    <action name="Formulaire" class="web.actions.Formulaire">
      <result name="exception" type="tiles">exception</result>
      ...
</action>

این نمای کاشی‌ها قبلاً نمایش داده شده است. به این صورت است:

 

19.9. ذخیره یک شبیه‌سازی

پس از اجرای یک شبیه‌سازی، کاربر ممکن است بخواهد آن را در جلسه ذخیره کند.

  • در [1]، شبیه‌سازی ذخیره می‌شود
  • در [2]، پاسخی که شامل فهرست شبیه‌سازی‌های انجام‌شده قبلی است و شبیه‌سازی جدید به آن اضافه می‌شود

لینک [Enregistrer la simulation] در قطعه [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>

می‌توانیم ببینیم که کلیک روی لینک باعث اجرای اقدام [EnregistrerSimulation] می‌شود. این در فایل [struts.xml] به شرح زیر پیکربندی شده است:


   <!-- عمل 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>
  • خط ۱: اکشن [EnregistrerSimulation] با کلاس [Enregistrer] و متد آن execute مرتبط است.

کلاس [Enregistrer] به شرح زیر است:


package web.actions;

...
public class Enregistrer extends ActionSupport implements SessionAware {

  // جلسه
  private Map<String, Object> session;
  // منو
  private Menu menu;

  @Override
  public void setSession(Map<String, Object> session) {
    this.session = session;
  }

  // اجرای اقدام
  public String execute() {
    // بازیابی آخرین شبیه‌سازی از جلسه
    Simulation simulation = (Simulation) session.get("simulation");
    if (simulation == null) {
      return ERROR;
    }
    ...
  }

  // گیرنده‌ها و تنظیم‌کننده‌ها
  ...
}
  • خط ۴: از آنجا که این اکشن نیاز به دسترسی به جلسه دارد، رابط SessionAware را پیاده‌سازی می‌کند.
  • خط ۷: جلسه
  • خط ۹: منو

هنگامی که اکشن [Enregistrer] نمونه برداری می‌شود، متد آن execute اجرا می‌گردد. به یاد داشته باشید که وظیفه آن قرار دادن جدیدترین شبیه‌سازی در سشن است. این مورد به لیست شبیه‌سازی‌های انجام شده قبلی که آن نیز در سشن ذخیره شده است، اضافه خواهد شد.

  • خط ۱۹: آخرین شبیه‌سازی قرار داده شده در جلسه بازیابی می‌شود.
  • خطوط ۲۰–۲۲: اگر آن را پیدا نکند، احتمالاً جلسه منقضی شده است. این به این دلیل است که جلسه فقط برای یک دوره زمانی مشخص دوام می‌آورد که می‌توان آن را در فایل [web.xml] که برنامه را پیکربندی می‌کند، تنظیم کرد.
  • خط ۲۱: کلید error بازگردانده می‌شود.

بازگردیم به پیکربندی اقدام [EnregistrerSimulation]:


   <!-- اقدام 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>

می‌توانیم ببینیم که کلید error (خط ۳) نمای کاشی‌های « » با نام erreur را فعال می‌کند. این شامل قطعات [Entete.JSP] و [Erreur.JSP] است و به این صورت به نظر می‌رسد:

 

قطعه [Erreur.JSP] به شرح زیر است:


<%@ 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>

بازگردیم به روش [Enregistrer].execute:


// اجرای اقدام
  public String execute() {
    // بازیابی آخرین شبیه‌سازی در جلسه
    Simulation simulation = (Simulation) session.get("simulation");
    if (simulation == null) {
      return ERROR;
    }
    // بازیابی شماره آخرین شبیه‌سازی
    Integer numDerniereSimulation = (Integer) session.get("numDerniereSimulation");
    if (numDerniereSimulation == null) {
      numDerniereSimulation = 0;
    }
    // آن را افزایش می‌دهد
    numDerniereSimulation++;
    // شماره جدید را در جلسه تنظیم می‌کند
    session.put("numDerniereSimulation", numDerniereSimulation);
    // فهرست شبیه‌سازی‌ها را بازیابی می‌کند
    List<Simulation> simulations = (List<Simulation>) session.get("simulations");
    if (simulations == null) {
      simulations = new ArrayList<Simulation>();
      session.put("simulations", simulations);
    }
    // افزودن شبیه‌سازی فعلی به فهرست
    simulation.setNum(numDerniereSimulation);
    simulations.add(simulation);
    // فهرست شبیه‌سازی‌ها نمایش داده می‌شود
    menu = new Menu(false, false, false, false, true, true);
    return "simulations";
  }
  • خطوط ۹–۱۶: شبیه‌سازی‌های مختلف از عدد ۱ شماره‌گذاری می‌شوند. آخرین عدد اختصاص‌یافته در جلسه (session) تحت کلید numDerniereSimulation ذخیره می‌شود. کد در خطوط ۹–۱۶ این کلید را بازیابی کرده و مقدار مرتبط با آن را افزایش می‌دهد.
  • خطوط ۱۸–۲۲: لیست شبیه‌سازی‌ها در جلسه مرتبط با کلید simulations ذخیره می‌شود. خطوط ۱۸–۲۲ این لیست را در صورت وجود بازیابی کرده یا در صورت عدم وجود آن را ایجاد می‌کنند.
  • خطوط ۲۴–۲۵: پس از به‌دست‌آوردن فهرست شبیه‌سازی‌ها، شبیه‌سازی جاری به آن اضافه می‌شود (خط ۲۵). پیش از این، به شبیه‌سازی جاری عددی اختصاص داده شده بود (خط ۲۴).
  • خط ۲۷: منوی نمایش داده شده تنظیم می‌شود
  • خط ۲۸: کلید ناوبری روی simulations تنظیم می‌شود.

بازگشت به پیکربندی اقدام [EnregistrerSimulation] در [struts.xml]:


   <!-- اقدام 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>

خط ۴: کلید simulations باعث نمایش نمای کاشی‌های « » با نام simulations می‌شود. این نما از قطعات [Entete.JSP] و [Simulations.JSP] تشکیل شده است. نما نمایش داده شده به شرح زیر است:

  • در [1]، قطعه [Entete.JSP] که اکنون با آن آشنا هستیم.
  • در [2]، قطعه [Simulations.JSP]

قطعه [Simulations.JSP] به شرح زیر است:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>

<!-- فهرست خالی شبیه‌سازی‌ها -->
<s:if test="#session['simulations']==null || #session['simulations'].size()==0">
  <h2><s:text name="Pam.SimulationsVides.titre"/></h2>
</s:if>
<!-- فهرست غیرخالی شبیه‌سازی‌ها -->
<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"/>">حذف
      </tr>
    </s:iterator>
  </table>
</s:if>
  • خطوط ۵–۷: اگر در جلسه هیچ شبیه‌سازی وجود نداشته باشد، نمای زیر نمایش داده می‌شود:
 
  • خطوط ۱۳–۲۱: نمایش سربرگ‌های ستون جدول
 
  • خطوط 23–55: لیست شبیه‌سازی‌های یافت‌شده در جلسه را به‌صورت حلقه‌ای پردازش می‌کند
  • خطوط ۲۴–۲۶: ایجاد یک URL به نام URL (ویژگی id). لینک HTML تولیدشده توسط این URL به شرح زیر است:
<a href="<a href="view-source:http://localhost:8084/pam/SupprimerSimulation.action?id=1">/pam/SupprimerSimulation.action?id=1</a>">Retirer</a>

می‌توانیم ببینیم که لینک، اکشن [SupprimerSimulation] را با پارامتر id هدف‌گیری می‌کند که نمایانگر شماره شبیه‌سازی‌ای است که باید از لیست حذف شود.

  • خطوط ۲۸–۵۴: در هر تکرار لیست شبیه‌سازی‌ها، ویژگی‌های شبیه‌سازی جاری نمایش داده می‌شود.
 

19.10. حذف یک شبیه‌سازی

کاربر ممکن است بخواهد یک شبیه‌سازی را از فهرست شبیه‌سازی‌ها حذف کند:

  • در [1]، شبیه‌سازی شماره ۱ حذف شده است
  • در [2]، شبیه‌سازی شمارهٔ ۱ حذف شده است

لینک [Retirer] در قطعه [Simulations.JSP] یافت می‌شود که قبلاً بررسی کرده‌ایم:


<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="s" uri="/struts-tags" %>

<!-- فهرست شبیه‌سازی خالی -->
<s:if test="#session['simulations']==null || #session['simulations'].size()==0">
  <h2><s:text name="Pam.SimulationsVides.titre"/></h2>
</s:if>
<!-- فهرست غیرخالی شبیه‌سازی‌ها -->
<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"/>">حذف کردن
      </tr>
    </s:iterator>
  </table>
</s:if>
  • خطوط ۱۶–۱۸: ایجاد لینک 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>

که در آن num شماره شبیه‌سازی‌ای است که باید حذف شود.

عمل [SupprimerSimulation] در فایل [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>
  <!--بین‌المللی‌سازی -->
  <constant name="struts.custom.i18n.resources" value="messages" />
  <!-- یکپارچه‌سازی Spring -->
  <constant name="struts.objectFactory.spring.autoWire" value="name" />

  
  <!-- اقدامات Struts/Tiles -->
  <package name="default" namespace="/" extends="tiles-default">
    ...
   <!-- اقدام RetirerSimulation -->
    <action name="SupprimerSimulation" class="web.actions.Supprimer">
      <result name="erreur" type="tiles">erreur</result>
      <result name="simulations" type="tiles">simulations</result>
    </action>
   ...
  </package>

    <!-- پکیج‌ها را اینجا اضافه کنید -->

</struts>
  • خط ۱۶: عمل [SupprimerSimulation] با کلاس [Supprimer] مرتبط است. از آنجا که هیچ متدی مشخص نشده است، متد آن execute اجرا خواهد شد. کلاس [Supprimer] به شرح زیر است:

package web.actions;

...
public class Supprimer extends ActionSupport implements SessionAware {

  // جلسه
  private Map<String, Object> session;
  //شناسه شبیه‌سازی که باید حذف شود
  private String id;
  // منو
  private Menu menu;

  @Override
  public void setSession(Map<String, Object> session) {
    this.session = session;
  }

  // اجرای اقدام
  public String execute() {
    //بازیابی شبیه‌سازی‌ها از جلسه
    List<Simulation> simulations = (List<Simulation>) session.get("simulations");
    if (simulations == null) {
      // وضعیت غیرعادی – جلسه باید منقضی شده باشد
      menu = new Menu(false, false, false, false, true, true);
      return "erreur";
    }
    // بررسی شناسه
    int num = 0;
    boolean erreur = false;
    try {
      num = Integer.parseInt(id);
      erreur = num <= 0;
    } catch (NumberFormatException ex) {
      // غیرعادی
      erreur = true;
    }
    // خطا؟
    if (erreur) {
      menu = new Menu(false, false, false, false, true, true);
      return "erreur";
    }
    // در حال جستجو برای شبیه‌سازی برای حذف
    for (int i = 0; i < simulations.size(); i++) {
      if (num == simulations.get(i).getNum()) {
        simulations.remove(i);
        break;
      }
    }
    // نمایش لیست شبیه‌سازی‌ها
    menu = new Menu(false, false, false, false, true, true);
    return "simulations";
  }

  // گیرنده‌ها و تنظیم‌کننده‌ها
...
}
  • خط ۴: متد [Supprimer] رابط [SessionAware] را برای دسترسی به جلسه پیاده‌سازی می‌کند.
  • خط ۷: جلسه
  • خط ۹: شماره شبیه‌سازی که باید حذف شود. به یاد داشته باشید که کلاس [Supprimer] از طریق HTML URL نمونه برداری می‌شود:
<a href="<a href="view-source:http://localhost:8084/pam-01/SupprimerSimulation.action?id=2">/pam-01/SupprimerSimulation.action?id=</a>num">Retirer</a>

که در آن num شماره شبیه‌سازی برای حذف است. این شماره در فیلد id در خط ۹ ذخیره خواهد شد.

  • خط ۱۱: منوی نمایی که در پاسخ به درخواست نمایش داده خواهد شد
  • خط ۱۹: متد execute که پاسخ به درخواست را تولید خواهد کرد.
  • خط ۲۱: فهرست شبیه‌سازی‌های انجام‌شده در جلسه بازیابی می‌شود
  • خطوط ۲۲–۲۶: عدم بازیابی این لیست از جلسه احتمالاً به معنای منقضی شدن جلسه است. ما قبلاً با این سناریو مواجه شده‌ایم. ما کلید erreur را بازمی‌گردانیم که نمای کاشی‌ها erreur را نمایش می‌دهد:

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

نما erreur در بخش ۱۹.۹ ارائه شد.

  • خطوط ۲۸–۳۶: بررسی می‌کنیم که رشته id در خط ۹ واقعاً یک عدد صحیح مثبت است.
  • خطوط ۳۸–۴۰: اگر اینطور نباشد، کلید erreur دوباره بازگردانده می‌شود که نمای Tiles erreur را نمایش خواهد داد.
  • خطوط ۴۳–۴۸: شبیه‌سازی مورد نظر برای حذف در فهرست شبیه‌سازی‌ها جستجو می‌شود. اگر پیدا شود، حذف می‌گردد.
  • خط ۵۰: منو برای نمای کاشی‌ها simulations به‌روزرسانی می‌شود.
  • خط ۵۱: کلید simulations بازگردانده می‌شود. این نمای کاشی‌ها simulations را نمایش می‌دهد:

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

نما Tiles simulations در بخش 19.9 ارائه شد.

19.11. بازگشت به فرم

از نمای کاشی‌ها simulations، کاربر می‌تواند به فرم بازگردد:

  • در [1]، برای بازگشت به فرم روی لینک کلیک کنید
  • در [2]، یک فرم خالی نمایش داده می‌شود

لینک [Retour au formulaire de simulation] در قطعه [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>
  • خط ۱۰: لینک به اقدام [RetourFormulaire] اشاره می‌کند. این در فایل [struts.xml] به شرح زیر تعریف شده است:

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

می‌توانیم ببینیم که این اکشن با هیچ کلاسی مرتبط نیست. این اکشن صرفاً مرورگر کلاینت را به اکشن [/Formulaire!input] هدایت می‌کند. بنابراین ما در همان وضعیتی هستیم که هنگام نمایش نمای اولیه توضیح‌داده‌شده در بند 19.7 بود. بنابراین ما این نمای اولیه، [2] را پیدا می‌کنیم.

19.12. مشاهده فهرست شبیه‌سازی‌ها

از نماهای کاشی simulation یا saisie، کاربر می‌تواند درخواست مشاهده شبیه‌سازی‌ها را بدهد:

  • در [1]، روی پیوند [Voir les simulations] کلیک کنید
  • در [2]، فهرست شبیه‌سازی‌ها نمایش داده می‌شود

لینک [Voir les simulations] در قطعه [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>
  • خط ۱۰: لینک [Voir les simulations] اقدام [VoirSimulations] را فراخوانی می‌کند. این در فایل [struts.xml] به صورت زیر تعریف شده است:

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

اقدام [VoirSimulations] با کلاس [Voir] بدون مشخص کردن متد مرتبط است. بنابراین، متد [Voir].execute اجرا خواهد شد. کلاس [Voir] به شرح زیر است:


package web.actions;

import com.opensymphony.xwork2.ActionSupport;
import web.entities.Menu;

public class Voir extends ActionSupport{
  // منو
  private Menu menu=new Menu(false,false,false,false,true,true);
  // گیرنده‌ها و تنظیم‌کننده‌ها

  public Menu getMenu() {
    return menu;
  }

  public void setMenu(Menu menu) {
    this.menu = menu;
  }
  
}

اقدام [Voir] تنها یک کار انجام می‌دهد: تنظیم منو برای نمای «شبیه‌سازی‌های کاشی» (خط ۸). متد execute وجود ندارد. بنابراین، متد از کلاس والد [ActionSupport] اجرا خواهد شد. می‌دانیم که این متد هیچ کاری انجام نمی‌دهد و تنها کلید success را برمی‌گرداند.

بازگردیم به عمل در [struts.xml]:


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

در خط ۳، می‌بینیم که کلید success باعث نمایش نمای کاشی‌ها simulations می‌شود. این موضوع در صفحهٔ ۱۶۱ توضیح داده شده است.

19.13. پاک کردن شبیه‌سازی فعلی

از نمای کاشی‌ها simulation، کاربر می‌تواند درخواست دهد که شبیه‌سازی فعلی پاک شود:

  • در [1]، شبیه‌سازی جاری پاک می‌شود
  • در [2]، فرم ورودی خالی نمایش داده می‌شود

لینک [Effacer la simulation] در قطعه [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>

در خط ۱۰ می‌توان دید که لینک [Effacer la simulation] اقدام [Formulaire!input] را فعال می‌کند. می‌دانیم که این اقدام به نمای اولیه [2] منتهی می‌شود.

19.14. پایان دادن به جلسهٔ جاری

از هر یک از نماهای کاشی، کاربر می‌تواند درخواست پایان جلسه را بدهد:

  • در [1]، با شروع از نمای شبیه‌سازی‌ها، جلسه پایان می‌یابد
  • در [2]، فرم ورودی خالی است. ما درخواست مشاهده شبیه‌سازی‌ها را داریم.
  • در [3]، فهرست شبیه‌سازی‌ها اکنون خالی است.

لینک [Terminer la session] در قطعه [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>

در خط ۱۰ می‌توان دید که لینک [Terminer la session] اقدام [TerminerSession] را فعال می‌کند. این در فایل [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>
  • خط ۱: می‌بینیم که کلاس [Terminer] نمونه برداری شده و متد آن execute اجرا می‌شود.
  • خطوط ۲–۵: پس از اجرای متد [Terminer].execute، کاربر به نمای اولیه ورود داده هدایت می‌شود. این توضیح مربوط به صفحه شماره ۲ است.

کلاس [Terminer] به شرح زیر است:


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 {

  // جلسه
  private Map<String, Object> session;
  
  @Override
  public String execute() {
    // خروج از جلسهٔ جاری
    session.clear();
    return SUCCESS;
  }

  @Override
  public void setSession(Map<String, Object> session) {
    this.session = session;
  }
}

نقش اقدام [Terminer] پاک کردن ویژگی‌های جلسهٔ جاری است.

  • خط ۷: اکشن [Terminer] رابط [SessionAware] را برای دسترسی به جلسه پیاده‌سازی می‌کند.
  • خط ۱۰: دیکشنری جلسه
  • خط ۱۳: متد execute اجرا می‌شود
  • خط ۱۵: این دیکشنری جلسه را پاک می‌کند. در نتیجه، فهرست شبیه‌سازی‌های فعلی در جلسه ناپدید خواهد شد. این توضیح‌دهندهٔ صفحهٔ شمارهٔ ۳ است.
  • خط 16: این کلید success را بازمی‌گرداند که همان‌طور که دیدیم، نمای کاشی‌ها saisie [2] را نمایش خواهد داد.

19.15. Conclusion

ما برای نسخهٔ ۱ مطالعهٔ موردی خود، که با لایهٔ شبیه‌سازی‌شده [metier] کار می‌کند، توضیحات کامل ارائه کرده‌ایم:

تنها کاری که باقی مانده این است که لایه کسب‌وکار واقعی را به لایه [web] متصل کنیم.