Skip to content

7. برنامه QuiEst

در اینجا ما یک برنامهٔ Struts را توصیف می‌کنیم که اندکی پیچیده‌تر از نمونه‌های قبلی است، که برای اهداف آموزشی باید ساده می‌بودند.

7.1. کلاس users

یک کلاس جاوا وجود دارد که اطلاعات مربوط به کاربران یک ماشین یونیکس را ذخیره می‌کند. این اطلاعات در سه فایل مشخص ذخیره می‌شوند:

  • /etc/passwd: فهرست کاربران
  • /etc/group: فهرست گروه‌ها
  • /etc/aliases: فهرست نام‌های مستعار ایمیل

محتویات این سه فایل به شرح زیر است:

- /etc/passwd

خطوط این فایل به شکل زیر قالب‌بندی شده‌اند:


    login:pwd:uid:gid:id:dir:shell

با

login
نام کاربری ورود
pwd
رمز عبور رمزگذاری‌شدهٔ آنها
uid
شماره کاربری آنها
gid
شماره گروه او
id
هویت او
dir
دایرکتوری ارتباط آن
shell
تفسیرگر فرمان آنها

بنابراین خط کاربر ممکن است به این شکل باشد:

dupond:xg675SDFEkl09:110:57:Guillaume Dupond:/home/iup2-auto/dupond:/bin/bash

کاربر قبلی شناسهٔ ۱۱۰ دارد و به گروه ۵۷ تعلق دارد. تعریف گروه ۵۷ را می‌توان در فایل /etc/group یافت.

- /etc/group

خطوط این فایل شکل زیر را دارند:


    nomGroupe:pwd:gid:membre1,membre2,....

با

nomGroupe
نام گروه
pwd
رمز عبور رمزگذاری‌شده آن – این فیلد معمولاً خالی گذاشته می‌شود
gid
شماره گروه
membrei
ورودی‌های کاربر – این فیلد ممکن است خالی باشد

بنابراین، خط مربوط به گروه ۵۷ در بالا ممکن است به این صورت باشد:

iup2-auto::57:

که نشان می‌دهد گروه ۵۷ با نام iup2-auto نامگذاری شده است.

- /etc/aliases

خطوط این فایل به شکل زیر هستند:

alias:[tab]login

با

alias
alias
[tab]
یک یا چند زبانه
login
ورودی کاربری که نام مستعار به او تعلق دارد

بنابراین، خط


    guillaume.dupond:    dupond

به این معنی است که نام مستعار guillaume.dupond متعلق به کاربری با نام کاربری dupond است. به یاد داشته باشید که نام‌های مستعار در آدرس‌های ایمیل استفاده می‌شوند. بنابراین، اگر در مثال قبلی نام ماشین یونیکس shiva.istia.univ-angers.fr باشد، ایمیلی با آدرس guillaume.dupond@ایمیل ارسال‌شده به آدرس shiva.istia.univ-angers.fr به صندوق پستی کاربری با نام کاربری dupond روی آن ماشین تحویل داده خواهد شد.

ما در اینجا به کل رابطه‌ی کلاس **users** نمی‌پردازیم، بلکه صرفاً به سازنده‌ی آن و چند متد:

import java.io.*;
import java.util.*;

public class users{


   // ویژگیها
  private Hashtable usersByLogin=new Hashtable();       // ورود --> نام کاربری، رمز عبور، ...، دایرکتوری
    private ArrayList erreurs=new ArrayList();             // فهرست پیامهای خطا

....

   // سازنده
  public users(String usersFileName, String groupsFileName, String aliasesFileName) throws Exception {
        // usersFileNam: نام فایل کاربری حاوی خطوطی به شکل
         // login:pwd:uid:gid:id:dir:shell
         //groupsFileName: نام فایل گروهها که شامل خطوطی به شکل زیر است
         // name:pwd:number:member1,member2,..
         // aliasesFileName: نام فایل مستعار حاوی خطوطی به شکل
         // alias:[tab]login
         // واژهنامه usersByLogin را ایجاد میکند
....
    }// سازنده

     // فهرست کاربران
  public Hashtable getUsersByLogin(){
    return usersByLogin;
  }

   // خطاها
  public ArrayList getErreurs(){
    return erreurs;
  }
usersByLogin
یک دیکشنری (جدول هش) که کلیدهای آن نام‌های کاربری موجود در فایل passwd هستند. مقدار مرتبط با هر کلید، یک آرایه از رشته‌ها (String [7]) است که عناصر آن شامل ۷ فیلدِ سطرِ مربوط به آن نام کاربری در فایل passwd می‌باشد. اگر سطر کمتر از ۷ فیلد داشته باشد، برخی فیلدها ممکن است خالی باشند.
erreurs
فهرست پیام‌های خطا – خالی اگر خطایی وجود نداشته باشد

7.2. برنامه وب مورد نظر

ما پیشنهاد می‌کنیم که اپلیکیشن وب زیر (صفحه فرم) را بسازیم:

خیر.
نام
نوع HTML
نقش
1
cmbLogins
<select ...>...</select>
فهرستی از تمام ورودها را نمایش می‌دهد که می‌توان اطلاعات آن‌ها را درخواست کرد
2
btnChercher
<input type="submit" ...>
برای شروع جستجو

وقتی کاربر روی دکمه [Chercher] (2) کلیک می‌کند، ورود (1) از یک شیء U از نوع 'users' درخواست می‌شود. اگر ورود وجود داشته باشد، پاسخ زیر بازگردانده می‌شود (صفحه اطلاعات):

همانطور که در URL مرورگر در بالا نشان داده شده است، پارامترهای فرم از طریق یک GET به سرور ارسال می‌شوند. بنابراین می‌توانیم این URL پیکربندی‌شده را مستقیماً به مرورگر ارسال کنیم. کاری که ما در اینجا انجام می‌دهیم همین است، تا یک نام کاربری را وارد کنیم که وجود ندارد. پاسخ زیر را دریافت می‌کنیم (صفحه خطا):

7.3. معماری برنامه

این معماری شامل اجزای زیر است:

  • نماها:
    • logins.jsp، برای نمایش لیست ورودها (نما ۱)
    • infos.jsp، برای نمایش اطلاعات مربوط به یک ورود (نما ۲)
    • erreurs.jsp، برای نمایش فهرست خطاها (نمایش ۳)
  • فرم‌های از نوع ActionForm که توسط اقدامات استفاده می‌شوند:
    • formLogins برای جمع‌آوری داده‌ها از فرم logins.jsp استفاده می‌شود
  • اقدامات:
    • SetupLoginAction، که محتوای formulaire.jsp را آماده کرده و سپس این نما را نمایش می‌دهد
    • InfosLoginAction، که محتوای logins.jsp را پس از ارسال به سرور پردازش می‌کند
    • ForwardAction، که لینک [Retour vers le formulaire] را از ویوها infos.jsp و erreurs.jsp پردازش می‌کند
  • کلاس کسب‌وکار «users» که توسط اکشن‌ها برای بازیابی داده‌هایشان استفاده می‌شود
  • مدلی که توسط سه فایل تخت `passwd`group` و `aliases` ارائه می‌شود

7.4. فایل‌های پیکربندی وب‌اپلیکیشن

7.4.1. فایل server.xml

زمینهٔ برنامه /strutsquiest2 نام‌گذاری خواهد شد. بنابراین خط زیر را به فایل Tomcat به نام server.xml اضافه خواهیم کرد:

    <Context path="/strutsquiest2" docBase="..." />

پس از انجام این کار، ممکن است لازم باشد Tomcat را مجدداً راه‌اندازی کنیم تا زمینه (context) جدید را تشخیص دهد. می‌توانیم با درخواست http://localhost:8080/strutsquiest2 در فایل URL، اعتبار آن را بررسی کنیم.

7.4.2. فایل web.xml

فایل پیکربندی web.xml برنامه به شرح زیر خواهد بود:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
<web-app>
    <servlet>
        <servlet-name>strutsquiest2</servlet-name>
        <servlet-class>istia.st.struts.quiest.Quiest2ActionServlet</servlet-class>
        <init-param>
            <param-name>config</param-name>
            <param-value>/WEB-INF/struts-config.xml</param-value>            
        </init-param>
        <init-param>
            <param-name>passwdFileName</param-name>
            <param-value>data/passwd</param-value>            
        </init-param>
        <init-param>
            <param-name>groupFileName</param-name>
            <param-value>data/group</param-value>            
        </init-param>        
    </servlet>

    <servlet-mapping>
        <servlet-name>strutsquiest2</servlet-name>
        <url-pattern>*.do</url-pattern>        
    </servlet-mapping>
</web-app>

این فایل، web.xml، یک ویژگی جدید را معرفی می‌کند. کنترلر Struts دیگر org.apache.struts.action.ActionServlet نیست، بلکه یک کلاس مشتق است که ما در اینجا آن را istia.st.struts.quiest.Quiest2ActionServlet نامیده‌ایم. این امکان را به ما می‌دهد تا دو پارامتر راه‌اندازی را بازیابی کنیم: passwdFileName (مکان فایل passwd) و groupFileName (مکان فایل group). فایل aliases در این برنامه مورد نیاز نیست.

7.4.3. فایل struts-config.xml

فایل struts-config.xml به شرح زیر خواهد بود:

<?xml version="1.0" encoding="ISO-8859-1" ?>

<!DOCTYPE struts-config PUBLIC
          "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">

<struts-config>
    <form-beans>
        <form-bean name="formLogins" type="org.apache.struts.action.DynaActionForm">
            <form-property name="cmbLogins" type="java.lang.String" initial=""/>
            <form-property name="tLogins" type="java.lang.String[]"/>            
        </form-bean>            
    </form-beans>

    <action-mappings>

      <action
          path="/init"
            name="formLogins"
            validate="false" 
            scope="session"
          type="istia.st.struts.quiest.SetupLoginsAction"
      >
            <forward name="afficherLogins" path="/vues/logins.jsp"/>
            <forward name="afficherErreurs" path="/vues/erreurs.jsp"/>            
        </action>

      <action
          path="/infosLogin"
            name="formLogins"
            validate="false" 
            scope="session"
          type="istia.st.struts.quiest.InfosLoginAction"
      >
            <forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
            <forward name="afficherInfos" path="/vues/infos.jsp"/>            
        </action>

      <action
          path="/retourLogins"
            parameter="/vues/logins.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />

    </action-mappings>

        <message-resources 
          parameter="istia.st.struts.quiest.ApplicationResources"
    null="false"
  />    

</struts-config>

این شامل سه بخش اصلی است:

  • اعلامیه‌های فرم در بخش <form-beans>
  • اعلام اکشن‌ها در بخش <action-mappings>
  • اعلام فایل منبعی در <message-resources>

7.4.4. ابجکت‌های فرم برنامه (بیان‌ها)

    <form-beans>
        <form-bean name="formLogins" type="org.apache.struts.action.DynaActionForm">
            <form-property name="cmbLogins" type="java.lang.String" initial=""/>
            <form-property name="tLogins" type="java.lang.String[]"/>            
        </form-bean>            
    </form-beans>    

در برنامه ما تنها یک فرم‌بین به نام formLogins وجود دارد که از نوع مشتق‌شده از DynaActionForm است. این فرم‌بین در شرایط زیر استفاده خواهد شد:

  • برای نگهداری داده‌های مورد نیاز برای نمایش نما شماره ۱
  • بازیابی مقادیر از فرم در نما شمارهٔ ۱ هنگام ارسال آن توسط کاربر

ساختار بیون formLogins به فرم در نمای شماره ۱ متصل است. بیایید آن را بررسی کنیم:

خیر.
نام
نوع HTML
نقش
1
cmbLogins
<select ...>...</select>
فهرستی از تمام ورودها را نمایش می‌دهد که می‌توان اطلاعات آن‌ها را درخواست کرد
2
btnChercher
<input type="submit" ...>
برای شروع جستجو

بیایید بین چندین مورد تمایز قائل شویم:

  • از سمت کلاینت به سمت سرور، شیء formLogins برای نگهداری مقادیر فرم HTML بالا استفاده می‌شود که از طریق دکمه [Envoyer] ارسال خواهد شد. بنابراین به یک فیلد cmbLogins نیاز است تا مقدار فیلدهای HTML، cmbLogins، c.a.d – یعنی نام کاربری انتخاب‌شده توسط کاربر – را دریافت کند.
  • از سمت سرور به کلاینت، شیء formLogins برای ارائه محتوای اولیه برای نما شماره ۱ استفاده می‌شود. فیلد آن tLogins به‌عنوان محتوای فهرست ۱ عمل خواهد کرد. میدان آن cmbLogins برای مشخص کردن آیتم انتخابی در فهرست ۱ استفاده خواهد شد.

7.4.5. اقدامات برنامه

اقدامات توسط اشیاء از نوع Action یا انواع مشتق‌شده از آن مدیریت می‌شوند. اقدامات در داخل تگ‌های <action-mappings> پیکربندی می‌شوند:

    <action-mappings>

      <action
          path="/init"
            name="formLogins"
            validate="false" 
            scope="session"
          type="istia.st.struts.quiest.SetupLoginsAction"
      >
            <forward name="afficherLogins" path="/vues/logins.jsp"/>
            <forward name="afficherErreurs" path="/vues/erreurs.jsp"/>            
        </action>

      <action
          path="/infosLogin"
            name="formLogins"
            validate="false" 
            scope="session"
          type="istia.st.struts.quiest.InfosLoginAction"
      >
            <forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
            <forward name="afficherInfos" path="/vues/infos.jsp"/>            
        </action>

      <action
          path="/retourLogins"
            parameter="/vues/logins.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />

    </action-mappings>

دستور /init

      <action
          path="/init"
            name="formLogins"
            validate="false" 
            scope="session"
          type="istia.st.struts.quiest.SetupLoginsAction"
      >
            <forward name="afficherLogins" path="/vues/logins.jsp"/>
            <forward name="afficherErreurs" path="/vues/erreurs.jsp"/>            
        </action>

بیایید نحوه عملکرد اکشن /init را توضیح دهیم:

  • عمل /init معمولاً تنها یک‌بار در اولین چرخه درخواست-پاسخ انجام می‌شود، زمانی که کاربر URL http://localhost:8080/strutsquiest2/init.do را درخواست می‌کند
  • شیء formsLogins ایجاد یا بازیافت می‌شود. این شیء در جلسه (session) بر اساس مقدار ویژگی scope بازیابی (recycling) یا قرار داده (creation) می‌شود.
  • متد reset آن فراخوانی می‌شود. توجه داشته باشید که این متد به‌طور پیش‌فرض در کلاس ActionForm و مشتقات آن هیچ کاری انجام نمی‌دهد. این متد درست قبل از کپی شدن داده‌های درخواست مشتری در شی ActionForm فراخوانی می‌شود و برای پاکسازی شی قبل از این عملیات کپی به کار می‌رود. در اینجا درخواست کلاینت چیست؟ اکشن /init زمانی که URL درخواست‌شده http://localhost:8080/strutsquiest2/init.do باشد، اجرا می‌شود. این URL می‌تواند توسط یک GET یا یک POST فراخوانی شود. تنها کافی است پارامترهایی را در این درخواست با نام‌های فیلد مشابه formLogins وارد کنید تا همان‌طور که در مثال زیر نشان داده شده، این‌ها مقداردهی اولیه شوند:

Image

  • درخواست شامل پارامتر cmbLogins (afterpak) است. بنابراین کنترل‌کننده Struts مقدار این پارامتر را در فیلد cmbLogins از formLogins کپی کرده است. سپس اکشن SetupLoginsAction اجرا شد و با نمایش ویوی logins.jsp به پایان رسید. این ویو شامل یک فرم است که در آن برخی فیلدها مقادیر خود را از formLogins دریافت می‌کنند. بدین ترتیب، فیلد select با نام cmbLogins در HTML مقدار خود را از فیلد cmbLogins (=afterpak) در formLogins دریافت کرد. به همین دلیل است که فهرست ورودها با ورود afterpak انتخاب‌شده نمایش داده می‌شود.
  • جالب خواهد بود که پارامتر tLogins را نیز به صورت زیر ارسال کنیم:
http://localhost:8080/strutsquiest2/init.do?cmbLogins=afterpak&tLogins=login1&tLogins=login2

این کار فیلد tLogins را از formLogins با آرایه {"login1","login2"} مقداردهی اولیه می‌کند. با این حال، همانطور که بعداً خواهیم دید، اکشن SetupLoginsAction مقداری را به فیلد tLogins اختصاص می‌دهد و آرایه ایجاد شده را با یک آرایه جدید جایگزین می‌کند. این آرایه دوم است که در نمای logins.jsp ظاهر می‌شود.

  • بحث قبلی، اگرچه تا حدی پیچیده است، نشان می‌دهد که نمی‌توانیم فرض کنیم که اکشن /init بدون پارامتر از سمت کلاینت اجرا خواهد شد. بنابراین، ممکن است استفاده از متد reset برای پاک کردن formLogins مفید باشد. در این مورد، ما باید کلاس DynaActionForm را مشتق کنیم. ما در اینجا این کار را انجام نداده‌ایم.
    • پس از فراخوانی متد reset در formLogins، کنترلر داده‌ها را از درخواست کلاینت به فیلدهای هم‌نام در formLogins کپی می‌کند. به طور معمول، اکشن /init بدون هیچ پارامتر کلاینتی فراخوانی می‌شود، اما ما قبلاً نشان داده‌ایم که هیچ چیزی مانع از این نمی‌شود که کلاینت اکشن /init را با پارامترهای دلخواه فراخوانی کند. بنابراین، در پایان این فاز، ممکن است فیلدهای cmbLogins و tLogins دارای مقدار باشند. ما دیده‌ایم که فیلد cmbLogins این مقدار را حفظ می‌کند، اما فیلد tLogins این کار را انجام نمی‌دهد.
    • سپس کنترل‌کننده، ویژگی `validate` اکشن را بررسی می‌کند. در اینجا، مقدار آن «false» است. متد `validate` از `formLogins` فراخوانی نخواهد شد. بنابراین، آن را نخواهیم نوشت.
    • شیء SetupLoginsAction ایجاد می‌شود، یا در صورت وجود قبلی بازیافت می‌شود، و متد `execute` آن فراخوانی می‌شود. هدف اصلی آن اختصاص دادن مقداری به فیلد tLogins از formLogins است. این مقدار آرایه‌ای از نام‌های کاربری است که از کلاس کسب‌وکار «users» درخواست می‌شود. این عملیات ممکن است با شکست مواجه شود. به همین دلیل ممکن است پس از اقدام /init دو نما دنبال شود:
      • ویوی erreurs.jsp اگر کلاس «کاربران» نتوانست آرایه ورود را فراهم کند
      • در غیر این صورت، نمای logins.jsp نمایش داده می‌شود
    • کنترلر یکی از این دو نما را نمایش خواهد داد
    • چرخه درخواست-پاسخ برای اکشن /init کامل می‌شود.

عمل /infosLogin

      <action
          path="/infosLogin"
            name="formLogins"
            validate="false" 
            scope="session"
          type="istia.st.struts.quiest.InfosLoginAction"
      >
            <forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
            <forward name="afficherInfos" path="/vues/infos.jsp"/>            
        </action>

بیایید نحوه عملکرد اقدام / infosLogin را توضیح دهیم:

  • عمل /infosLogin معمولاً زمانی رخ می‌دهد که کاربر دکمه [Chercher] را در نمای logins.jsp کلیک کند. سپس درخواستی به سرور ارسال می‌شود که توسط تگ <form> HTML در نما تعریف شده است:
<html:form name="formLogins" method="get" action="/infosLogin" type="org.apache.struts.action.DynaActionForm">
  • می‌توانیم ببینیم که درخواست با استفاده از روش GET به سرور ارسال می‌شود. بنابراین کاربر می‌تواند آن را به‌صورت دستی وارد کند:

Image

  • شیء formsLogins ایجاد یا بازیافت می‌شود. این شیء (بازیافتی) بازیابی یا (در حالت ایجاد) در جلسه (session) قرار داده می‌شود، همان‌طور که در ویژگی scope مشخص شده است.
  • متد `reset` آن درست قبل از کپی شدن داده‌های درخواست کلاینت در شی `ActionForm` فراخوانی می‌شود. این معمولاً به شکل http://localhost:8080/strutsquiest2/infosLogin.do?cmbLogins=xx است، که در آن xx نام کاربری‌ای است که از فهرست نام‌های کاربری انتخاب شده است. با این حال، اگر کاربر از URL قبلی با ارسال پارامترهای دلخواه استفاده کرده باشد، می‌تواند هر چیزی باشد. توالی صفحات زیر را در نظر بگیرید:

Image

  • اقدام /infosLogin با رشته پارامتر cmbLogins=xx&tLogins=login1&tLogins=login2 فراخوانی شد. بنابراین، فیلدهای cmbLogins و tLogins از formLogins به ترتیب مقادیر «xx» و {«login1»، «login2»} را دریافت خواهند کرد. اقدام /infosLogin اطلاعات مربوط به ورود «xx» را از کلاس کسب‌وکار 'users' درخواست می‌کند. کلاس 'users' پاسخ می‌دهد که این ورود وجود ندارد. بنابراین نمای بالا نمایش داده می‌شود. اکنون بیایید از لینک [Retour au formulaire] بالا استفاده کنیم:

Image

  • این اکشن /retourLogins است که توسط لینک [Retour au formulaire] فراخوانی می‌شود. این اکشن به سادگی نما logins.jsp را بدون هیچ مرحله میانی نمایش می‌دهد. به یاد داشته باشید که فیلد tLogins برای پر کردن لیست ورودها در نمای logins.jsp استفاده می‌شود. از آنجایی که کاربر این مقدار را به {"login1","login2"} تغییر داده است، این دو ورود اکنون در لیست ظاهر می‌شوند. بار دیگر، نمی‌توانیم به اندازه کافی بر ضرورت مطلق در نظر گرفتن مورد پارامترهای دلخواه تعیین‌شده توسط کاربر یا یک برنامه در عملکرد یک برنامه تأکید کنیم. راه‌حل مشکل مطرح‌شده در اینجا این است که لینک [Retour au formulaire] به اقدام /init اشاره کند. این کار تضمین می‌کند که لیست صحیح نام‌های کاربری به دست آید.
  • بیایید به یک درخواست استاندارد به اقدام /infosLogin بازگردیم، مانند:

http://localhost:8080/strutsquiest2/infosLogin.do?cmbLogins=afterpak

  • کنترل‌کننده Struts یک مقدار را به فیلد cmbLogins از شیء ActionForm اختصاص خواهد داد. با این حال، فیلد tLogins مقداری اختصاص نخواهد یافت (زیرا در درخواست ارسال‌شده فیلد متناظری وجود ندارد). این رفتار برای ما قابل قبول است. بنابراین نیازی به نوشتن روش ریست سفارشی برای formLogins نداریم.
    • به محض فراخوانی متد ریست برای formLogins، کنترلر داده‌ها را از درخواست کلاینت در فیلدهای همنام در formLogins کپی می‌کند. میدان در cmbLogins مقداری را دریافت خواهد کرد: ورودنامه‌ای که کاربر انتخاب کرده است (afterpak).
    • سپس کنترلر ویژگی «validate» اکشن را بررسی می‌کند. در اینجا مقدار آن «false» است. متد «validate» از formLogins فراخوانی نخواهد شد.
    • شیء InfosLoginAction ایجاد یا در صورت وجود قبلی، بازیافت می‌شود و متد `execute` آن فراخوانی می‌گردد. وظیفه آن بازیابی اطلاعات مرتبط با نام کاربری cmbLogins است. این اطلاعات از کلاس تجاری 'users' درخواست خواهد شد. این عملیات ممکن است با خطا مواجه شود (برای مثال، اگر نام کاربری وجود نداشته باشد). به همین دلیل است که ممکن است پس از اقدام /infosLogin دو نما بیاید:
      • ویوی erreurs.jsp اگر کلاس «کاربران» نتوانست اطلاعات درخواستی را فراهم کند
      • در غیر این صورت، نمای infos.jsp نمایش داده می‌شود
    • کنترلر یکی از این دو نما را نمایش خواهد داد
    • چرخه درخواست-پاسخ برای اقدام /infosLogin کامل می‌شود.

عمل /retourLogins

      <action
          path="/retourLogins"
            parameter="/vues/logins.jsp"
          type="org.apache.struts.actions.ForwardAction"
      />
  • اقدام /retourLogins با کلیک بر روی لینک [Retour au formulaire] در نماهای erreurs.jsp و infos.jsp اجرا می‌شود.
  • در این مورد، هیچ فرم مرتبطی با این اقدام وجود ندارد. بنابراین، ما بلافاصله به اجرای متد `execute` یک شیء `ForwardAction` می‌رویم، که یک شیء `ActionForward` را بازمی‌گرداند که به نمای `/vues/logins.jsp` اشاره می‌کند.

7.4.6. فایل پیام برنامه

بخش سوم فایل struts-config.xml، فایل پیام‌ها است:

        <message-resources 
      parameter="istia.st.struts.quiest.ApplicationResources"
    null="false"
  />        

فایل ApplicationResources.properties در WEB-INF/classes/istia/st/struts/quiest قرار دارد. محتویات آن به شرح زیر است:

errors.header=<ul>
errors.footer=</ul>
parametreManquant=<li>Le paramètre [{0}] n'a pas été initialisé</li>
usersException=<li>Erreur d'initialisation de l'application : {0}</li>
loginInconnu=<li>Le login [{0}] n'existe pas</li>

7.5. کد ویو

به خوانندگان توصیه می‌شود اگر کد مربوط به نماهای نشان‌داده‌شده در زیر را درک نمی‌کنند، درس مربوط به مدیریت فرم را مجدداً مرور کنند.

7.5.1. ویو logins.jsp

به یاد داشته باشید که این نما در دو حالت نمایش داده می‌شود:

  • وقتی اکشن /init در اولین چرخه درخواست-پاسخ فراخوانی می‌شود
  • وقتی اکشن /retourLogins در چرخه‌های بعدی فراخوانی می‌شود

کد مربوط به نما logins.jsp به شرح زیر است:

<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>

<html>
    <head>
      <title>Quiest - formulaire</title>
  </head>
  <body background="<html:rewrite page="/images/standard.jpg"/>">
      <center>
        <h2>Application QuiEst</h2>
      <hr>
      <html:form name="formLogins" method="get" action="/infosLogin" type="org.apache.struts.action.DynaActionForm">
          <table>
            <tr>
              <td>Login cherché</td>
            <td>
                <html:select name="formLogins" property="cmbLogins">
                      <html:options name="formLogins" property="tLogins"/>
                  </html:select>
            </td>
            <td>
                <html:submit value="Chercher"/>
            </td>
          </tr>
        </table>
      </html:form>
    </center>
  </body>
</html>

7.5.2. نما infos.jsp

این نما پس از موفقیت‌آمیز فراخوانی اکشن /infosLogin نمایش داده می‌شود. کد آن به شرح زیر است:

<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>

<html>
    <head>
      <title><bean:write name="infosLoginBean" scope="request" property="titre"/></title>
  </head>
  <body background="<html:rewrite page="/images/standard.jpg"/>">
      <h2><bean:write name="infosLoginBean" scope="request" property="titre"/></h2>
    <hr>
    <table border="1">
            <tr>
                <th>login</th><th>pwd</th><th>uid</th><th>gid</th><th>id</th><th>dir</th><th>shell</th>
            </tr>
            <tr>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[0]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[1]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[2]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[3]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[4]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[5]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[6]"/></td>            
            </tr>                                                                                                                                                                                                                
    </table>                                      
    <br>
    <html:link page="/retourLogins.do">
            Retour au formulaire
        </html:link>        
  </body>
</html>

این نما از ابجکتی به نام infosLoginBean استفاده می‌کند که توسط اکشن /infosLogin در پرس‌وجو درج می‌شود. این ابجکت دو فیلد دارد:

String titre;                        // عنوانی که باید در نما نمایش داده شود
String[] infosLogin;        // جدول اطلاعات برای نمایش در نما

ما هنگام بحث در مورد کد کلاس InfosLoginAction، به جزئیات بیشتری در مورد این کلاس خواهیم پرداخت.

7.5.3. نما erreurs.jsp

این نما زمانی نمایش داده می‌شود که عملیات /init یا /infosLogin با خطا پایان یابد. کد آن به شرح زیر است:

<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>

<html>
    <head>
      <title>Application QuiEst - erreurs</title>
  </head>
  <body background="<html:rewrite page="/images/standard.jpg"/>">
        <h2 align="center">Application QuiEst - Erreurs</h2>
        <hr>
      <h2>Les erreurs suivantes se sont produites</h2>
        <html:errors/>
    <html:link page="/retourLogins.do">
            Retour au formulaire
        </html:link>    
  </body>
</html>

7.6. کلاس‌های جاوا

فایل web.xml به یک کلاس جاوا ارجاع می‌دهد:

<web-app>
    <servlet>
      <servlet-name>strutsquiest2</servlet-name>
    <servlet-class>istia.st.struts.quiest.Quiest2ActionServlet</servlet-class>
....
  </servlet>

...
</web-app>

فایل پیکربندی struts-config.xml به دو کلاس جاوا ارجاع می‌دهد:

      <action
          path="/infosLogin"
            name="formLogins"
            validate="false" 
            scope="session"
          type="istia.st.struts.quiest.InfosLoginAction"
      >
            <forward name="afficherErreurs" path="/vues/erreurs.jsp"/>
            <forward name="afficherInfos" path="/vues/infos.jsp"/>            
        </action>

      <action
          path="/init"
            name="formLogins"
            validate="false" 
            scope="session"
          type="istia.st.struts.quiest.SetupLoginsAction"
      >
            <forward name="afficherLogins" path="/vues/logins.jsp"/>
            <forward name="afficherErreurs" path="/vues/erreurs.jsp"/>            
        </action>

7.6.1. کلاس Quiest2ActionServlet

کلاس Quiest2ActionServlet از کلاس ActionServlet، کلاس کنترل‌کننده Struts، مشتق شده است. ما کلاس ActionServlet را مشتق می‌کنیم تا متد init آن را سفارشی‌سازی کنیم. این متد که تنها یک بار هنگام بارگذاری اولیه servlet اجرا می‌شود، به ما اجازه می‌دهد یک شیء تجاری از نوع 'users' بسازیم. این شیء تنها یک بار نیاز به ساخت دارد و متد init مکان مناسبی برای انجام این ساخت است. ابژه users برای ساخت نیاز به دو فایل دارد: فایل‌های **passwd و group. مکان این دو فایل به عنوان پارامتر در فایل **web.xml برنامه به سرولت ارسال می‌شوند:

    <servlet>
      <servlet-name>strutsquiest2</servlet-name>
    <servlet-class>istia.st.struts.quiest.Quiest2ActionServlet</servlet-class>
    <init-param>
        <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    <init-param>
        <param-name>passwdFileName</param-name>
      <param-value>data/passwd</param-value>
    </init-param>
    <init-param>
        <param-name>groupFileName</param-name>
      <param-value>data/group</param-value>
    </init-param>        
  </servlet>

کد سرولت به شرح زیر است:

package istia.st.struts.quiest;

import java.util.*;
import javax.servlet.*;
import org.apache.struts.action.*;
import istia.st.users.*;

public class Quiest2ActionServlet
  extends ActionServlet {

   //ویژگی‌های سرولت
  private users u = null;
  private ActionErrors erreurs = new ActionErrors();
  private String[] tLogins;

   //init
  public void init() throws ServletException {

    //فراموش نکنید کلاس والد را مقداردهی اولیه کنید
    super.init();

     // متغیرهای محلی
    final String[] initParams = {"passwdFileName", "groupFileName"};
    Properties params = new Properties();

    // بازیابی پارامترهای راه‌اندازی سرولت
    ServletConfig config = getServletConfig();
    String servletPath = config.getServletContext().getRealPath("/");
    for (int i = 0; i < initParams.length; i++) {
      String valeur = config.getInitParameter(initParams[i]);
      if (valeur == null) {
        erreurs.add(ActionErrors.GLOBAL_ERROR, new ActionError("parametreManquant", initParams[i]));
        valeur = "";
      }
       //پارامتر را ذخیره کنید
      params.setProperty(initParams[i], valeur);
    } //برای
     // در صورت وجود هرگونه خطای инициализация بازگشت می‌دهد
    if (erreurs.size() != 0) {
      return;
    }
     // یک شیء 'users' ایجاد می‌کند
    try {
      u = new users(servletPath + "/" + params.getProperty("passwdFileName"),
                    servletPath + "/" + params.getProperty("groupFileName"), null);
    }
    catch (Exception ex) {
      erreurs.add(ActionErrors.GLOBAL_ERROR, new ActionError("usersException", ex.getMessage()));
      return;
    } //catch
     // بازیابی فهرست ورودها
    tLogins = new String[u.getUsersByLogin().size()];
    Enumeration eLogins = u.getUsersByLogin().keys();
    for (int i = 0; i < tLogins.length; i++) {
      tLogins[i] = (String) eLogins.nextElement();
    }
     //مرتب‌سازی ورودها
    Arrays.sort(tLogins);
  } //init

   //متد برای دسترسی به اطلاعات خصوصی سرولت
  public Object[] getInfos() {
    return new Object[] {erreurs, u, tLogins};
  }
}

به‌طور خلاصه، متد **init** به شرح زیر عمل می‌کند:

  • ابتدا، متد `init` کلاس والد (ActionServlet) فراخوانی می‌شود تا به درستی مقداردهی اولیه انجام شود
  • سپس پارامترهای инициализация خوانده می‌شوند. اگر هر کدام مفقود باشند، ویژگی خصوصی ActionErrors errors مقداردهی می‌شود.
  • اگر پارامترهای инициализация موجود باشند، یک شی `users` ساخته می‌شود. این ساخت ممکن است یک استثنا پرتاب کند. در این صورت، ویژگی `errors` با مقدار `ActionErrors` پر می‌شود.
  • اگر ساخت موفقیت‌آمیز باشد، لیست تمام ورودها از شی ایجاد شده بازیابی شده و در یک آرایه مرتب می‌شود، که سپس در ویژگی خصوصی `String[] tLogins` ذخیره می‌گردد.
  • شیء ساخته‌شده `users` در ویژگی خصوصی `users u` ذخیره می‌شود.
  • متد عمومی getInfos سه ویژگی خصوصی (u، errors، tLogins) را در یک آرایه از اشیاء بازیابی می‌کند.

7.6.2. کلاس SetupLoginsAction

هدف این اقدام، مقداردهی اولیه به شیء DynaActionForm formLogins است. پس از قرار گرفتن این شیء در جلسه (session)، دیگر نیازی به مقداردهی مجدد آن در ادامه نخواهد بود. بنابراین، اقدام SetupLoginsAction تنها یک بار انجام می‌شود. کد آن به شرح زیر است:

package istia.st.struts.quiest;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;

public class SetupLoginsAction
  extends Action {

  public ActionForward execute(ActionMapping mapping, ActionForm form,
                               HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException {

     // فرم را برای نمایش آماده می‌کند
     //بازیابی اطلاعات از سرولت کنترل‌کننده
     // info=(ActionErrors errors, users u, String[] tLogins)
    Object[] infos = ( (Quiest2ActionServlet)this.getServlet()).getInfos();

     //آیا خطاهای инициализатсия وجود داشت؟
    ActionErrors erreurs = (ActionErrors) infos[0];
    if (!erreurs.isEmpty()) {
      this.saveErrors(request, erreurs);
      return mapping.findForward("afficherErreurs");
    }

     //ما اطلاعات ورود را در فرم وارد می‌کنیم
    DynaActionForm formLogins=(DynaActionForm) form;
    formLogins.set("tLogins",infos[2]);
    return mapping.findForward("afficherLogins");
  }
}

مانند تمام اکشن‌های Struts، کد در متد execute قرار دارد. این متد:

  • اطلاعاتی را که کنترل‌کننده Struts با استفاده از متد `init` خود ذخیره کرده است، بازیابی می‌کند. این کار توسط متد `getServlet()` کلاس `Action` انجام می‌شود.
  • در میان این‌ها ویژگی ActionErrors (خطاهای کنترل‌کننده) قرار دارد. اگر این فهرست خطاها خالی نباشد، در پرس‌وجو گنجانده می‌شود و نما erreurs.jsp نمایش داده می‌شود.
  • اگر فهرست خطاها خالی باشد، آنگاه فهرست ورودهایی که در ابتدا توسط کنترلر ایجاد شده است، به فیلد tLogins از بین formLogins اختصاص داده می‌شود. سپس نما logins.jsp درخواست می‌شود که فهرست ورودها را نمایش خواهد داد.

7.6.3. کلاس‌های InfosLoginBean و InfosLoginAction

هدف از اقدام InfosLoginAction بازیابی اطلاعات مرتبط با ورود انتخاب‌شده توسط کاربر و ارائه آن به او است. اطلاعات در یک شیء از نوع InfosLoginBean جمع‌آوری خواهد شد:

package istia.st.struts.quiest;

public class InfosLoginBean implements java.io.Serializable{

   // بیانی حاوی اطلاعات مورد نیاز برای صفحه اطلاعات
  private String titre;
  private String[] infosLogin;

  // سازنده
  public InfosLoginBean(String titre, String[] infosLogin){
    this.titre=titre;
    this.infosLogin=infosLogin;
  }

   // گیرنده‌ها
  public String getTitre(){
    return this.titre;
  }
  public String[] getInfosLogin(){
    return this.infosLogin;
  }
  public String getInfosLogin(int i){
    return this.infosLogin[i];
  }
}

کلاس قبلی یک بیِن است، c.a.d. یک کلاس جاوا که در آن یک ویژگی خصوصی T unAttribut به‌طور خودکار با دو متد خصوصی مرتبط می‌شود:

  • void setUnAttribut(T value) { unAttribut = value; }
  • T getUnAttribut() { return unAttribut; }

به نحو ویژهٔ متدهای get و set توجه کنید. اگر ویژگی یک آرایه باشد T[] unAttribut، می‌توان متدهای get و set را برای عناصر آرایه ایجاد کرد:

  • void setUnAttribut(T value, int i){unAttribut[i]=value;}
  • T getUnAttribut(int i) { return unAttribut[i]; }

برای درک بهتر، بیایید نگاهی به کد ویو infos.jsp بیندازیم که باید پس از اکشن InfosLoginAction ارسال شود:

<%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %>
<%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %>

<html>
    <head>
      <title><bean:write name="infosLoginBean" scope="request" property="titre"/></title>
  </head>
  <body background="<html:rewrite page="/images/standard.jpg"/>">
      <h2><bean:write name="infosLoginBean" scope="request" property="titre"/></h2>
    <hr>
    <table border="1">
            <tr>
                <th>login</th><th>pwd</th><th>uid</th><th>gid</th><th>id</th><th>dir</th><th>shell</th>
            </tr>
            <tr>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[0]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[1]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[2]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[3]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[4]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[5]"/></td>
                <td><bean:write name="infosLoginBean" scope="request" property="infosLogin[6]"/></td>            
            </tr>                                                                                                                                                                                                                
    </table>                                      
    <br>
    <html:link page="/retourLogins.do">
            Retour au formulaire
        </html:link>        
  </body>
</html>

بیایید تگ زیر را در نظر بگیریم:

<bean:write name="infosLoginBean" scope="request" property="titre"/>

درخواست می‌شود که مقدار فیلد (خواصیت) «title» از شیء infosLoginBean (نام) که در پرس‌وجو (دامنه) گنجانده شده است، نوشته شود. مقداری که باید نوشته شود از طریق request.getAttribute("infosLoginBean") به‌دست می‌آید.getTitre(). بنابراین متد getTitre باید در کلاس InfosLoginBean وجود داشته باشد. این امر برقرار است. تگ

<bean:write name="infosLoginBean" scope="request" property="infosLogin[0]"/>

درخواست می‌کند که مقدار عنصر infosLogin[0] از شیء infosLoginBean که در پرس‌وجو گنجانده شده است، نوشته شود. مقدار قابل نوشتن از طریق request.getAttribute("infosLoginBean").getInfosLogin(0). به دست خواهد آمد. بنابراین متد getInfosLogin(int i) باید در کلاس InfosLoginBean وجود داشته باشد. این امر برقرار است.

هدف کلاس InfosLoginAction ساختن شی قبلی InfosLoginBean از روی لاگینی است که کاربر انتخاب کرده است. کد آن به شرح زیر است:

package istia.st.struts.quiest;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;
import istia.st.users.*;

public class InfosLoginAction
  extends Action {

  public ActionForward execute(ActionMapping mapping, ActionForm form,
                               HttpServletRequest request, HttpServletResponse response) throws IOException,ServletException {

     // باید اطلاعات مرتبط با ورود را نمایش دهد

     // اطلاعات را از سرویس‌لت کنترل‌کننده بازیابی می‌کند
     // info=(ActionErrors خطاها، کاربران u، LoginBean[] tLogins)
    Object[] infos = ( (Quiest2ActionServlet)this.getServlet()).getInfos();

     //آیا خطای инициализатсия وجود داشت؟
    ActionErrors erreurs = (ActionErrors) infos[0];
    if (!erreurs.isEmpty()) {
      this.saveErrors(request, erreurs);
      return mapping.findForward("afficherErreurs");
    }

     //ابتدا این ورود را بازیابی کنید
    String login = (String) ( (DynaActionForm) form).get("cmbLogins");

    //آیا اینجا چیزی هست؟
    if (login == null) {
      // این درست نیست – ما فرم ورود را دوباره می‌فرستیم
            DynaActionForm formLogins=(DynaActionForm) form;
            formLogins.set("tLogins",infos[2]);
            return mapping.findForward("afficherLogins");
    }

     //ما یک فرم ورود داریم – در حال جستجوی آن هستیم
    String[] infosLogin = (String[]) ( (users) infos[1]).getUsersByLogin().get(login);

     //آیا آن را پیدا کرده‌ایم؟
    if (infosLogin == null) {
      // ورود پیدا نشده است – صفحهٔ خطا نمایش داده می‌شود
      ActionErrors erreurs2=new ActionErrors();
      erreurs2.add(ActionErrors.GLOBAL_ERROR, new ActionError("loginInconnu", login));
      this.saveErrors(request, erreurs2);
      return mapping.findForward("afficherErreurs");
    }

     //ورود پیدا شد – درخواست را با جزئیات یافت‌شده پر کنید
    String titre="Application QuiEst - login["+login+"]";
    InfosLoginBean infosLoginBean= new InfosLoginBean(titre,infosLogin);
    request.setAttribute("infosLoginBean",infosLoginBean);
    return mapping.findForward("afficherInfos");
  }

}

متد **execute** به شرح زیر عمل می‌کند:

  • اطلاعات جمع‌آوری‌شده توسط کنترلر Struts در هنگام راه‌اندازی بازیابی می‌شود. اگر کنترلر خطایی را تشخیص دهد، اجرای کد در این نقطه متوقف شده و پیامی برای نمایش این خطاها صادر می‌شود.
  • ما بررسی می‌کنیم که آیا ورود وجود دارد یا خیر. اگر کاربر از فرم انتخاب ورود استفاده کرده باشد، ورود موجود خواهد بود. با این حال، کاربر ممکن است URL اقدام را مستقیماً در مرورگر خود بدون ارسال هیچ پارامترهایی وارد کند. اگر ورود موجود نباشد، لیست ورودها را مجدداً نمایش می‌دهیم.
  • اگر ورود وجود داشته باشد، اطلاعات مرتبط با کلاس تجاری «users» را بازیابی می‌کنیم. اگر این کلاس نتواند ورود مورد نظر را پیدا کند، صفحه خطا نمایش داده می‌شود. در غیر این صورت، یک شیء InfosLoginBean برای نگهداری اطلاعات مورد نیاز نمای infos.jsp ایجاد می‌شود. این شیء در درخواست گنجانده شده و صفحه infos.jsp نمایش داده می‌شود.

7.7. استقرار

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

 

7.8. نتیجه‌گیری

ما از Struts در یک برنامه کاربردی واقعی با استفاده از یک کلاس کسب‌وکار بهره برده‌ایم. همچنین نشان داده‌ایم که باید توجه ویژه‌ای به درخواستی که از سوی کلاینت ارسال می‌شود داشت و نباید هیچ فرضی در مورد ماهیت آن کرد. یک درخواست می‌تواند هر شکلی داشته باشد و هر برنامه‌ای باید کار خود را با تأیید اعتبار آن آغاز کند.