10. نسخه ۵ – برنامه وب PAM / JSF
10.1. معماری برنامه
معماری برنامه وب PAM به شرح زیر خواهد بود:
![]() |
در این نسخه، سرور GlassFish میزبان تمام لایههای برنامه خواهد بود:
- لایه [web] توسط کانتینر سرولت سرور (۱ در بالا) میزبانی میشود
- بقیه لایههای [metier, DAO, jpa] توسط کانtejینر EJB3 سرور (۲ در بالا) میزبانی میشوند
![]() |
کامپوننتهای [metier, DAO] برنامه که در کانتینر EJB3 در حال اجرا هستند، پیش از این در برنامهٔ کلاینت/سرور مورد بحث در بخش 7.1 توصیف شدهاند که معماری آن به شرح زیر بود:
![]() |
لایههای [metier, DAO] در داخل کانتینر EJB3 روی سرور GlassFish اجرا میشدند و لایه [ui] در یک برنامه کنسول یا Swing روی ماشین دیگری اجرا میشد:
![]() |
در معماری برنامه جدید:
![]() |
فقط لایه [web / jsf] نیاز به نوشتن دارد. لایههای دیگر [metier, DAO, jpa] از قبل موجود هستند.
سند [ref3] نشان میدهد که یک برنامه وب که لایه وب آن با استفاده از Java Server Faces پیادهسازی شده است، معماری مشابه زیر را دارد:
![]() |
این معماری الگوی طراحی MVC (مدل، نما، کنترلکننده) را پیادهسازی میکند. پردازش یک درخواست مشتری به شرح زیر انجام میشود:
اگر درخواست از طریق GET ارسال شود، دو مرحله زیر انجام میشود:
- درخواست – مرورگر مشتری یک درخواست را به کنترلکننده [Faces Servlet] ارسال میکند. این کنترلکننده تمام درخواستهای مشتری را مدیریت میکند. این نقطه ورود به برنامه است. این «C» در MVC است.
- پاسخ – کنترلکننده C دستور نمایش صفحهٔ انتخابشده، JSF، را صادر میکند. این نما (View) است، حرف «V» در MVC. صفحه JSF از یک قالب M استفاده میکند تا بخشهای پویا از پاسخی را که باید برای کلاینت ارسال کند، اولیه سازد. این قالب یک کلاس جاوا است که میتواند از لایههای [métier] و [4a] فراخوانی کند تا نمای V را با دادههای مورد نیازش فراهم آورد.
اگر درخواست با استفاده از POST انجام شود، دو مرحله اضافی بین درخواست و پاسخ درج میشود:
- درخواست – مرورگر کلاینت درخواستی به کنترلکننده [Faces Servlet] ارسال میکند.
- پردازش – کنترلکننده C این درخواست را پردازش میکند. این به این دلیل است که یک درخواست POST با دادههایی همراه است که نیاز به پردازش دارند. برای انجام این کار، کنترلکننده توسط دستگیرکنندههای رویداد (event handlers) خاص برنامه که به صورت [2a] نوشته شدهاند، یاری میشود. این رسیدگیکنندهها ممکن است به لایه کسبوکار [2b] نیاز داشته باشند. رسیدگیکننده رویداد ممکن است نیاز به بهروزرسانی برخی مدلهای M [2c] داشته باشد. پس از پردازش درخواست مشتری، ممکن است پاسخهای مختلفی را ایجاد کند. یک مثال معمول عبارت است از:
- یک صفحه خطا اگر درخواست نتوانست به درستی پردازش شود
- در غیر این صورت، یک صفحه تأیید
پردازشگر رویداد نتیجهای از نوع رشته را که به کلید ناوبری معروف است، به کنترلکننده [Faces Servlet] بازمیگرداند.
- navigation – کنترلکننده صفحه JSF (= نما) را برای ارسال به کلاینت انتخاب میکند. این انتخاب بر اساس کلید ناوبری بازگرداندهشده توسط پردازشگر رویداد است.
- response – صفحهٔ انتخابشده JSF پاسخ را برای کلاینت ارسال میکند. این صفحه از قالب M خود برای راهاندازی بخشهای پویا استفاده میکند. این قالب همچنین ممکن است از لایههای [métier] و [4a] فراخوانی کند تا دادههای مورد نیاز صفحه JSF را فراهم کنند.
در یک پروژه JSF:
- کنترلکننده C، سرولِت [javax.faces.webapp.FacesServlet] است. این در کتابخانه [jsf-api.jar] یافت میشود.
- ویوهای V توسط صفحات JSF پیادهسازی میشوند.
- مدلهای M و دستگیرکنندههای رویداد توسط کلاسهای جاوا که اغلب به آنها «بیینهای پشتیبان» گفته میشود، پیادهسازی شدهاند.
- در نسخههای JSF و 1.x، تعاریف بین (bean) و قواعد ناوبری از یک صفحه به صفحه دیگر در فایل [faces-config.xml] تعریف شدهاند. این فایل شامل فهرست نماها و قواعد انتقال از یکی به دیگری است. از نسخه JSF 2 به بعد، بیینها میتوانند با استفاده از anotationها تعریف شوند و انتقالها بین صفحات میتوانند مستقیماً در کد بیین درج شوند.
10.2. نحوه کار برنامه
هنگامی که برای اولین بار به برنامه دسترسی پیدا میشود، صفحه زیر نمایش داده میشود:
![]() |
سپس فرم را پر کرده و حقوق خود را درخواست میکنید:
![]() |
نتیجه زیر را مشاهده خواهید کرد:
![]() |
این نسخه یک حقوق فرضی را محاسبه میکند. شما نباید به محتوای صفحه توجه کنید، بلکه باید به چیدمان آن توجه کنید. وقتی روی دکمه [Raz] کلیک میکنید، به صفحه [A] بازگردانده میشوید.
ورودیهای نادرست، همانطور که در مثال زیر نشان داده شده است، علامتگذاری میشوند:
![]() |
10.3. پروژه NetBeans
ما قصد داریم یک نسخه اولیه از برنامه را بسازیم که در آن لایه [métier] شبیهسازی خواهد شد. معماری زیر را خواهیم داشت:
![]() |
هنگامی که دستگیرکنندههای رویداد یا مدلها از لایه [métier] [2b, 4a] درخواست داده میکنند، این لایه دادههای آزمایشی را بازمیگرداند. هدف ایجاد یک لایه وب است که به درستی به درخواستهای کاربر پاسخ دهد. پس از دستیابی به این هدف، تنها کاری که باقی میماند نصب لایه سرور است که در بخش 7.1 توسعه یافته است:
![]() |
این نسخهٔ ۲ وب اپلیکیشن ما PAM خواهد بود.
پروژه NetBeans برای نسخه ۱، پروژه Maven زیر است:
![]() |
- در [1]، فایلهای پیکربندی
- در [2]، صفحات در XHTML و فایل سبک
- در [3]، کلاسها در لایه [web]
- در [4]، اشیاء مبادلهشده بین لایه [web] و لایه [métier] و خود لایه [métier]
- به [5]، فایل پیام برای بینالمللیسازی برنامه
- در [6]، وابستگیهای برنامه
ما برخی از این عناصر را بررسی خواهیم کرد.
10.3.1. فایلهای پیکربندی
فایل [web.xml] فایلی است که بهطور پیشفرض توسط NetBeans تولید میشود و همچنین شامل پیکربندی برای یک صفحهٔ استثنا است:
- خط ۳۰: [index.html] صفحهٔ اصلی برنامه است
- خطوط ۳۲–۳۹: پیکربندی صفحهٔ استثنا
صفحه [exception.html] از [ref3] مشتق شده است. کد آن به شرح زیر است:
هر استثنایی که بهطور صریح توسط کد وباپلیکیشن مدیریت نشود، منجر به نمایش صفحهای مشابه صفحه زیر خواهد شد:
![]() |
فایل [faces-config.xml] به شرح زیر خواهد بود:
لطفاً به نکات زیر توجه کنید:
- خطوط ۹–۱۴: فایل [messages.properties] برای بینالمللیسازی صفحات استفاده خواهد شد. این فایل در صفحات XHTML از طریق کلید msg قابل دسترسی خواهد بود.
- خط 15: فایل [messages.properties] را بهعنوان اولین فایل برای بررسی پیامهای خطا که توسط تگهای <h:messages> و <h:message> نمایش داده میشوند، تعریف میکند. این امکان را میدهد که برخی از پیامهای خطای پیشفرض از JSF بازنویسی شوند. این گزینه در اینجا استفاده نمیشود.
10.3.2. فایل سبک
فایل [styles.css] به شرح زیر است:
.libelle{
background-color: #ccffff;
font-family: 'Times New Roman',Times,serif;
font-size: 14px;
font-weight: bold
}
body{
background-color: #ffccff
}
.error{
color: #ff3333
}
.info{
background-color: #99cc00
}
.titreInfos{
background-color: #ffcc00
}
در اینجا چند نمونه از کد JSF که از این سبکها استفاده میکند، آورده شده است:
| |
| |
| ![]() |
10.3.3. فایل پیام
فایل پیام [messages_fr.properties] به شرح زیر است:
form.titre=Feuille de salaire
form.comboEmployes.libell\u00e9=Employ\u00e9
form.heuresTravaill\u00e9es.libell\u00e9=Heures travaill\u00e9es
form.joursTravaill\u00e9s.libell\u00e9=Jours travaill\u00e9s
form.heuresTravaill\u00e9es.required=Indiquez le nombre d'heures travaill\u00e9es
form.heuresTravaill\u00e9es.validation=Donn\u00e9e incorrecte
form.joursTravaill\u00e9s.required=Indiquez le nombre de jours travaill\u00e9s
form.joursTravaill\u00e9s.validation=Donn\u00e9e incorrecte
form.btnSalaire.libell\u00e9=Salaire
form.btnRaz.libell\u00e9=Raz
exception.header=L'exception suivante s'est produite
exception.httpCode=Code HTTP de l'erreur
exception.message=Message de l'exception
exception.requestUri=Url demand\u00e9e lors de l'erreur
exception.servletName=Nom de la servlet demand\u00e9e lorsque l'erreur s'est produite
form.infos.employ\u00e9=Informations Employ\u00e9
form.employe.nom=Nom
form.employe.pr\u00e9nom=Pr\u00e9nom
form.employe.adresse=Adresse
form.employe.ville=Ville
form.employe.codePostal=Code postal
form.employe.indice=Indice
form.infos.cotisations=Informations Cotisations sociales
form.cotisations.csgrds=CSGRDS
form.cotisations.csgd=CSGD
form.cotisations.retraite=Retraite
form.cotisations.secu=S\u00e9curit\u00e9 sociale
form.infos.indemnites=Informations Indemnit\u00e9s
form.indemnites.salaireHoraire=Salaire horaire
form.indemnites.entretienJour=Entretien / Jour
form.indemnites.repasJour=Repas / Jour
form.indemnites.cong\u00e9sPay\u00e9s=Cong\u00e9s pay\u00e9s
form.infos.salaire=Informations Salaire
form.salaire.base=Salaire de base
form.salaire.cotisationsSociales=Cotisations sociales
form.salaire.entretien=Indemnit\u00e9s d'entretien
form.salaire.repas=Indemnit\u00e9s de repas
form.salaire.net=Salaire net
این پیامها همگی در صفحه [index.xhtml] استفاده میشوند، به استثنای پیامهای خطوط ۱۱ تا ۱۵ که در صفحه [exception.xhtml] استفاده میشوند.
10.3.4. دامنهٔ بیینها
بیان [web.forms.Form] دارای دامنه درخواست خواهد بود:
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.RequestScoped;
@ManagedBean
@RequestScoped
public class Form implements Serializable {
بیان [web.utils.ChangeLocale] دارای دامنهٔ برنامه خواهد بود:
package web.utils;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
@ManagedBean
@SessionScoped
public class ChangeLocale implements Serializable{
// محلی که صفحات در آن قرار دارند
private String locale="fr";
public ChangeLocale() {
}
public String setFrenchLocale(){
locale="fr";
return null;
}
public String setEnglishLocale(){
locale="en";
return null;
}
public String getLocale() {
return locale;
}
public void setLocale(String locale) {
this.locale = locale;
}
}
10.3.5. لایه [métier]
لایه [métier] رابط زیر IMetierLocal را پیادهسازی میکند:
package metier;
import java.util.List;
import javax.ejb.Local;
import jpa.Employe;
@Local
public interface IMetierLocal {
//دریافت فیش حقوقی
FeuilleSalaire calculerFeuilleSalaire(String SS, double nbHeuresTravaillées, int nbJoursTravaillés );
// فهرست کارمندان
List<Employe> findAllEmployes();
}
این رابط در بخش سرور از برنامهٔ کلاینت/سرور توصیفشده در بخش 7.1 استفاده میشود.
کلاس Metier، که برای آزمایش لایه [web] استفاده خواهیم کرد، این رابط را به شرح زیر پیادهسازی میکند:
package metier;
...
public class Metier implements IMetierLocal {
// فهرست کارکنان مرتبشده بر اساس شماره SS
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)));
//فرهنگ کارکنان نمایهشده بر اساس شماره SS
for(Employe e:listEmployes){
hashEmployes.put(e.getSS(),e);
}
}
// فهرست کارمندان را بازمیگرداند
return listEmployes;
}
}
تفسیر این کد را به خواننده واگذار میکنیم. به روش بهکاررفته توجه کنید: برای اجتناب از پیادهسازی بخش EJB برنامه، لایه [métier] را شبیهسازی میکنیم. پس از تأیید لایه [web]، میتوانیم آن را با لایه واقعی [métier] جایگزین کنیم.
10.4. فرم [index.xhtml] و قالب آن [Form.java]
ما اکنون در حال ساخت صفحه فرم XHTML و قالب آن هستیم.
مطالعهٔ پیشنهادی در [ref3]:
- مثال ۳ (mv-jsf2-03) برای فهرست برچسبهایی که میتوان در یک فرم استفاده کرد
- نمونهٔ ۴ (mv-jsf2-04) برای لیستهای کشویی که توسط قالب پر میشوند
- نمونه شماره ۶ (mv-jsf2-06) برای اعتبارسنجی ورودی کاربر
- مثال ۷ (mv-jsf2-07) برای مدیریت دکمه [Raz]
10.4.1. مرحله ۱
سؤال: فرم [index.xhtml] و مدل آن [Form.java] را ایجاد کنید تا صفحه زیر تولید شود:
![]() |
اجزای ورودی به شرح زیر هستند:
id | نوع JSF | قالب | نقش | |
comboEmployes | <h:selectOneMenu> | رشته comboEmployesValue لیست<Employee> getEmployes() | شامل لیست کارمندان به فرمت "نام کوچک نام خانوادگی". | |
heuresTravaillees | <h:inputText> | رشته heuresTravaillées | تعداد ساعات کاری – تعداد واقعی | |
joursTravailles | <h:inputText> | رشته joursTravaillés | تعداد روزهای کاری – عدد صحیح | |
btnSalaire | <h:commandButton> | شروع محاسبه حقوق | ||
btnRaz | <h:commandButton> | فرم را به وضعیت اولیه خود بازمیگرداند |
- متد getEmployes فهرستی از کارمندان را که از لایه [métier] بهدست آمده است، بازمیگرداند. ابجکتهای نمایشدادهشده توسط لیست کشویی دارای ویژگی itemValue، شناسه کارمند SS و ویژگی itemLabel خواهند بود که رشتهای متشکل از نام و نام خانوادگی کارمند است.
- دکمههای [Salaire] و [Raz] فعلاً به هیچ رویدادپردازندهای متصل نخواهند شد.
- اعتبار ورودیها بررسی خواهد شد.

این نسخه را آزمایش کنید. بهویژه بررسی کنید که خطاهای ورودی بهدرستی علامتگذاری شدهاند.
توجه: مهم است که ویژگیهای «id» اجزای صفحه حاوی حروف با علامت نگارشی نباشند. با GlassFish 3.1.2، این امر باعث کرش برنامه میشود.
10.4.2. مرحله ۲
سؤال: فرم [index.xhtml] و قالب آن [Form.java] را تکمیل کنید تا پس از کلیک روی دکمه [Salaire] صفحه زیر نمایش داده شود:
![]() |
دکمه [Salaire] در قالب به رویدادپرداز calculerSalaire متصل خواهد شد. این متد از متد calculerFeuilleSalaire در لایه [métier] استفاده خواهد کرد. این فیش حقوقی برای کارمند انتخابشده در [1] تولید خواهد شد.
در قالب، برگه پرداخت با فیلد خصوصی زیر نمایش داده میشود:
private FeuilleSalaire feuilleSalaire;
با متدهای get و set.
برای بازیابی اطلاعات موجود در این شیء، میتوان عبارتهایی مانند عبارت زیر را در صفحه JSF نوشت:
<h:outputText value="#{form.feuilleSalaire.employe.nom}"/>
بیان در ویژگی «value» به شرح زیر ارزیابی میشود:
[form].getFeuilleSalaire().getEmploye().getNom() که در آن [form] نمایانگر یک نمونه از کلاس [Form.java] است. خواننده میتواند تأیید کند که متدهای get که در اینجا استفاده شدهاند، واقعاً در کلاسهای [Form]، [FeuilleSalaire] و [Employe] به ترتیب وجود دارند. اگر اینطور نبود، هنگام ارزیابی عبارت یک استثنا پرتاب میشد.
این نسخه جدید را آزمایش کنید.
10.4.3. مرحله ۳
سؤال: فرم [index.xhtml] و قالب آن [Form.java] را تکمیل کنید تا اطلاعات اضافی زیر را بهدست آورید:
![]() |
ما همان رویهٔ قبلی را دنبال خواهیم کرد. برای مثال، در [1] مسئلهای با نماد ارز یورو وجود دارد. در زمینه یک برنامه بینالمللیسازیشده، ترجیح داده میشود که فرمت نمایش و نماد ارز locale مورد استفاده (انگلیسی، آلمانی، فرانسوی و غیره) مشخص باشد. این اطلاعات را میتوان به شرح زیر به دست آورد:
<h:outputFormat value="{0,number,currency}">
<f:param value="#{form.feuilleSalaire.employe.indemnite.entretienJour}"/>
</h:outputFormat>
میتوانست نوشت:
<h:outputText value="#{form.feuilleSalaire.employe.indemnite.entretienJour} є">
اما با لوکال en_GB (انگلیسی GB)، نمایش همچنان به یورو خواهد بود، در حالی که باید از پوند (£) استفاده شود. تگ <h:outputFormat> امکان نمایش اطلاعات را بر اساس locale صفحه در حال نمایش JSF فراهم میکند:
- خط ۱: پارامتر {0} را نمایش میدهد که عددی است و نمایانگر مجموع مبلغی است
- خط ۲: تگ <f:param> مقداری را به پارامتر {0} اختصاص میدهد. یک تگ <f:param> دوم مقداری را به پارامتر {1} اختصاص میدهد، و به همین ترتیب.
10.4.4. مرحله ۴
مطالعه پیشنهادی: مثال شماره ۷ (mv-jsf2-07) در [ref3].
سؤال: فرم [index.xhtml] و قالب آن [Form.java] را برای مدیریت دکمه [Raz] تکمیل کنید.
دکمه [Raz] فرم را به حالتی که هنگام اولین درخواست از طریق GET داشت، بازمیگرداند. در اینجا چندین مشکل وجود دارد. برخی از این موارد در [ref3] توضیح داده شدهاند.
فرم رندر شده توسط دکمه [Raz] کل فرم نیست، بلکه فقط بخش saisie آن است:

این نتیجه را میتوان با استفاده از تگ <f:subview> به صورت زیر به دست آورد:
<f:subview id="viewInfos" rendered="#{form.viewInfosIsRendered}">
... la partie du formulaire qu'on veut pouvoir ne pas afficher
</f:subview>
تگ <f:subview> کل بخشی از فرم را که میتواند نمایش داده شود یا پنهان گردد، در بر میگیرد. هر کامپوننت را میتوان با استفاده از ویژگی rendered نمایش داد یا پنهان کرد. اگر rendered="true" باشد، کامپوننت نمایش داده میشود؛ اگر rendered="false" باشد، نمایش داده نمیشود. اگر ویژگی rendered در قالب روی مقداری تنظیم شود، نمایش کامپوننت میتواند به صورت برنامهای کنترل شود.
در مثال بالا، نمایش ویو viewInfos با استفاده از فیلد زیر کنترل خواهد شد:
private boolean viewInfosIsRendered;
به همراه متدهای آن get و set. متدهای مسئول کلیک روی دکمههای [Salaire] و [Raz] این مقدار بولین را بسته به اینکه آیا نما viewInfos باید نمایش داده شود یا خیر، بهروزرسانی خواهند کرد.

















