Skip to content

3. مقدمه‌ای بر سرولت‌های جاوا و صفحات JSP

این فصل حاوی مثال‌های مختلفی از سرلت‌ها و صفحات JSP است. این موارد با سرور Tomcat که روی پورت 8080 اجرا می‌شود، آزمایش شده‌اند. با دنبال کردن لینک‌های موجود در صفحه اصلی، می‌توانید به مثال‌های سرلت‌ها و صفحات JSP دسترسی پیدا کنید. اکثر مثال‌های زیر از مثال‌های Tomcat گرفته شده‌اند. برای آزمایش آنها، کافی است Tomcat را راه‌اندازی کنید، صفحه URL http://localhost:8080 را با استفاده از یک مرورگر درخواست کنید و پیوندهای servlet را دنبال کنید.

3.1. سرولت‌های جاوا

3.1.1. ارسال محتوا HTML به یک کلاینت وب

بیایید به مثال «Hello World» بالا نگاهی بیندازیم. سرولت به شرح زیر است:


import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HelloWorld extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
    {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<head>");
        out.println("<title>Hello World!</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>Hello World!</h1>");
        out.println("</body>");
        out.println("</html>");
    }
}

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

Image

لطفاً به نکات زیر توجه کنید:

  • برای سرولت‌ها باید کلاس‌های ویژه وارد شوند:

import javax.servlet.*;
import javax.servlet.http.*;

کتابخانه javax.servlet همیشه به صورت استاندارد همراه با JDK گنجانده نمی‌شود. در این صورت، می‌توان آن را مستقیماً از وب‌سایت Sun دانلود کرد.

  • یک سرولت از کلاس HttpServlet مشتق می‌شود

public class HelloWorld extends HttpServlet {
  • یک درخواست GET به سرویس‌گیر ارسال شده و توسط متد doGet پردازش می‌شود

    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
  • به همین ترتیب، یک درخواست POST که به سرولت ارسال شده است، توسط متد doPost پردازش می‌شود.

    public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
  • ابژه درخواست HttpServletRequest ابجه‌ای است که دسترسی ما را به درخواستی که توسط کلاینت وب ارسال شده است فراهم می‌کند. پاسخ سرولت از طریق ابژه پاسخ HttpServletResponse بازگردانده خواهد شد.
  • ابژه response به ما امکان می‌دهد تا سربرگ‌های HTTP را که به کلاینت ارسال می‌شوند، تنظیم کنیم. برای مثال، سربرگ Content-Type: text/html در اینجا با استفاده از: تنظیم می‌شود.
        response.setContentType("text/html");
  • برای ارسال پاسخ به کلاینت، سرولت از یک استریم خروجی که توسط شی response فراهم شده است، استفاده می‌کند:
        PrintWriter out = response.getWriter();
  • پس از به‌دست‌آوردن این جریان خروجی، کد HTML در آن نوشته شده و بدین ترتیب به کلاینت ارسال می‌شود:
        out.println("<html>");
        out.println("<body>");
        out.println("<head>");
        out.println("<title>Hello World!</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h1>Hello World!</h1>");
        out.println("</body>");
        out.println("</html>");

3.1.2. بازیابی پارامترهای ارسال‌شده توسط کلاینت وب

مثال زیر نشان می‌دهد که چگونه یک سروِلت می‌تواند پارامترهای ارسال‌شده توسط کلاینت وب را بازیابی کند. یک فرم ورودی:

Image

پاسخ ارسال‌شده توسط سرولت:

Image

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


import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class myRequestParamExample extends HttpServlet {

    String title="Récupération des paramètres d'un formulaire";

    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<body>");
        out.println("<head>");
        out.println("<title>" + title + "</title>");
        out.println("</head>");
        out.println("<body bgcolor=\"white\">");
        out.println("<h3>" + title + "</h3>");
        String firstName = request.getParameter("firstname");
        String lastName = request.getParameter("lastname");
        if (firstName != null || lastName != null) {
            out.println("firstname= " + firstName + "<br>");
            out.println("lastname= " + lastName);
        } else {
            out.println("pas de paramètres");
        }
        out.println("<P>");
        out.print("<form action=\"RequestParamExample\" method=\"POST\">");
        out.println("firstname= <input type=text size=20 name=firstname>");
        out.println("<br>");
        out.println("lastname= <input type=text size=20 name=lastname>");
        out.println("<br>");
        out.println("<input type=submit>");
        out.println("</form>");
        out.println("</body>");
        out.println("</html>");
    }

    public void doPost(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        doGet(request, response);
    }

}

تغییرات زیر را در مقایسه با مثال قبلی توجه کنید:

  • پارامترهای ارسال‌شده توسط مرورگر به شرح زیر بازیابی می‌شوند:

        String firstName = request.getParameter("firstname");
        String lastName = request.getParameter("lastname");

متد request.getParameter("nomParamètre") اگر پارامتر nomParamètre در میان پارامترهای ارسال‌شده توسط کلاینت وب نباشد، نشانگر null را برمی‌گرداند.

  • این فرم مشخص می‌کند که مرورگر باید پارامترها را با استفاده از روش POST ارسال کند.
        out.print("<form action=\"RequestParamExample\" method=\"POST\">");
  • پارامترهای دریافتی توسط متد doPost سرویس‌لت پردازش خواهند شد. در اینجا این متد صرفاً متد doGet را فراخوانی می‌کند. بنابراین این سرولت مقادیر فرم را بدون توجه به اینکه توسط GET یا POST ارسال شده‌اند، پردازش می‌کند.

    public void doPost(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        doGet(request, response);
    }

3.1.3. بازیابی سربرگ‌های HTTP ارسال‌شده توسط کلاینت وب

سرولتی که در ادامه آمده است نشان می‌دهد چگونه سربرگ‌های HTTP ارسال‌شده توسط کلاینت وب را بازیابی کنیم:

Image

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


import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class RequestHeaderExample extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
    {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        Enumeration e = request.getHeaderNames();
        while (e.hasMoreElements()) {
            String name = (String)e.nextElement();
            String value = request.getHeader(name);
            out.println(name + " = " + value);
        }
    }
}

نکات قابل توجه:

  • این شیء request و متد آن getHeaderNames است که دسترسی ما را به سربرگ‌های HTTP ارسال‌شده توسط مرورگر در قالب یک شمارش‌گر فراهم می‌کند:
        Enumeration e = request.getHeaderNames();
  • متد request.getHeader("header") به شما امکان می‌دهد یک هدر HTTP خاص را بازیابی کنید. مثال بالا چند نمونه از آن‌ها را نشان می‌دهد. شایان ذکر است که هدرهای نمایش داده شده در اینجا توسط مرورگر ارسال می‌شوند. سرور نیز هدرهای HTTP خود را دارد که گاهی اوقات آینه‌ای از هدرهای مرورگر هستند. هدرهای HTTP ارسالی از سوی مرورگر با هدف اطلاع‌رسانی به سرور در مورد قابلیت‌های مرورگر ارسال می‌شوند.
سربرگ
معنی
User-Agent
هویت مرورگر
Accept
فرمت‌های MIME که توسط مرورگر پشتیبانی می‌شوند. بنابراین، image/gif به این معنی است که مرورگر می‌تواند تصاویر را در فرمت GIF پردازش کند.
Host
در قالب hote:port. نشان می‌دهد که مرورگر می‌خواهد با کدام ماشین و کدام پورت تماس برقرار کند.
Accept-Encoding
فرمت رمزگذاری پذیرفته‌شده توسط مرورگر برای اسناد ارسال‌شده توسط سرور. بنابراین، اگر یک سرور یک سند را به صورت استاندارد و فشرده‌نشده و دیگری را در فرمت فشرده‌شده gzip داشته باشد، و مرورگر نشان داده باشد که می‌تواند با فرمت gzip کار کند، آنگاه سرور می‌تواند سند را در فرمت gzip ارسال کند تا در پهنای باند صرفه‌جویی شود.
Accept-language
زبان‌های پشتیبانی‌شده توسط مرورگر. اگر یک سرور یک سند یکسان را به چندین زبان داشته باشد، نسخه آن را به زبانی ارسال می‌کند که مرورگر از آن پشتیبانی می‌کند.
Referer
درخواست‌شده توسط مرورگر URL
Connection
حالت اتصال مورد درخواست مرورگر. Keep-alive به این معنی است که سرور نباید پس از ارسال صفحهٔ درخواست‌شده به مرورگر، اتصال را قطع کند. اگر مرورگر متوجه شود که صفحهٔ دریافت‌شده حاوی لینک‌هایی به تصاویر است، برای مثال، می‌تواند درخواست‌های بیشتری را برای دریافت آن‌ها از سرور ارسال کند، بدون اینکه نیاز به برقراری اتصال جدید باشد. در این حالت، مرورگر است که پس از دریافت تمام اجزای صفحه، ابتکار عمل را برای بستن اتصال به دست می‌گیرد.

3.1.4. بازیابی اطلاعات محیط زمان اجرا

سرولتی که در ادامه آمده است، نشان می‌دهد چگونه به اطلاعات محیط زمان اجرای سرولت دسترسی پیدا کنیم. بخشی از این اطلاعات توسط مرورگر به صورت هدرهای HTTP ارسال می‌شود و بنابراین می‌توان با استفاده از روش توصیف‌شده در بالا آن را بازیابی کرد.

Image

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


import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class RequestInfo extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
    {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<body>");
        out.println("<head>");
        out.println("<title>Request Information Example</title>");
        out.println("</head>");
        out.println("<body>");
        out.println("<h3>Request Information Example</h3>");
        out.println("Method: " + request.getMethod());
        out.println("Request URI: " + request.getRequestURI());
        out.println("Protocol: " + request.getProtocol());
        out.println("PathInfo: " + request.getPathInfo());
        out.println("Remote Address: " + request.getRemoteAddr());
        out.println("</body>");
        out.println("</html>");
    }

  
    public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
    {
        doGet(request, response);
    }
}

اطلاعات اینجا با استفاده از روش‌های مختلف به‌دست آمده است:

        out.println("Method: " + request.getMethod());
        out.println("Request URI: " + request.getRequestURI());
        out.println("Protocol: " + request.getProtocol());
        out.println("PathInfo: " + request.getPathInfo());
        out.println("Remote Address: " + request.getRemoteAddr());

فهرستی از برخی روش‌های موجود و معانی آن‌ها به شرح زیر است:

روش
معنی
getServerName()
نام وب‌سرور
getServerPort()
پورت فعال وب‌سرور
getMethod()
روش (GET یا POST) مورد استفاده توسط مرورگر برای ارسال درخواست خود
getRemoteHost()
نام ماشین کلاینت که مرورگر از آن درخواست خود را ارسال کرده است
getRemoteAddr()
آدرس IP همان ماشین
getContentType()
نوع محتوایی که توسط مرورگر ارسال شده است (سربرگ HTTP Content-type)
getContentLength()
تعداد کاراکترهای ارسال‌شده توسط مرورگر (سربرگ HTTP Content-length)
getProtocol()
نسخه پروتکل HTTP درخواست‌شده توسط مرورگر
getRequestURI()
URI درخواست‌شده توسط مرورگر. این معادل بخشی از URL است که پس از شناسه‌ی hote:port در http://hote:port/URI می‌آید.

3.1.5. ایجاد یک سروِلت با JBuilder، استقرار آن با Tomcat

اکنون نحوهٔ ایجاد و اجرای یک سروِلِت جاوا را توضیح می‌دهیم. از دو ابزار استفاده خواهیم کرد: JBuilder برای کامپایل سروِلِت و Tomcat برای اجرای آن. ممکن است Tomcat به‌تنهایی کافی باشد؛ اما قابلیت‌های اشکال‌زدایی محدودی دارد. از مثالی که قبلاً توسعه داده شده است استفاده می‌کنیم که پارامترهای دریافتی سرور را نمایش می‌دهد. سروِلِت ابتدا فرم ورودی زیر را ارسال می‌کند:

Image

پاسخ ارسال‌شده توسط سرولت:

Image

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


import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class myRequestParamExample extends HttpServlet {

    String title="Récupération des paramètres d'un formulaire";

    public void doGet(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        response.setContentType("text/html");
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<body>");
        out.println("<head>");
        out.println("<title>" + title + "</title>");
        out.println("</head>");
        out.println("<body bgcolor=\"white\">");
        out.println("<h3>" + title + "</h3>");
        String firstName = request.getParameter("firstname");
        String lastName = request.getParameter("lastname");
        if (firstName != null || lastName != null) {
            out.println("firstname= " + firstName + "<br>");
            out.println("lastname= " + lastName);
        } else {
            out.println("pas de paramètres");
        }
        out.println("<P>");
        out.print("<form action=\"RequestParamExample\" method=\"POST\">");
        out.println("firstname= <input type=text size=20 name=firstname>");
        out.println("<br>");
        out.println("lastname= <input type=text size=20 name=lastname>");
        out.println("<br>");
        out.println("<input type=submit>");
        out.println("</form>");
        out.println("</body>");
        out.println("</html>");
    }

    public void doPost(HttpServletRequest request,
                      HttpServletResponse response)
        throws IOException, ServletException
    {
        doGet(request, response);
    }

}
  • یک پروژه به نام myRequestParamExample را با استفاده از JBuilder ایجاد کنید و برنامه قبلی myRequestParamExample.java را در آن وارد کنید.
  • در حین کامپایل ممکن است با مشکل زیر مواجه شوید: ممکن است JBuilder شما کتابخانه javax.servlet مورد نیاز برای کامپایل سرولت‌ها را نداشته باشد. در این صورت باید JBuilder را برای استفاده از کتابخانه‌های کلاس اضافی پیکربندی کنید. این رویه در ضمیمه‌های این سند برای JBuilder 7 توضیح داده شده است. ما آن را در اینجا خلاصه کرده‌ایم:
  • گزینه Tools/Configure JDKs یا (Options/Configure JDK) را فعال کنید

Image

در بخش تنظیمات JDK در بالا، فیلد Name معمولاً حاوی JDK 1.3.1 است. اگر نسخه جدیدتری از JDK دارید، از دکمه Change برای مشخص کردن مسیر نصب آن استفاده کنید. در بالا، ما مسیر E:\Program Files\jdk14 را مشخص کرده‌ایم، جایی که نسخه‌ای از JDK 1.4 نصب شده بود. از این پس، JBuilder از این JDK برای کامپایل‌ها و اجراهای خود استفاده خواهد کرد. در بخش (Class, Source, Documentation)، فهرستی از تمام کتابخانه‌های کلاسی که توسط JBuilder اسکن خواهند شد، وجود دارد؛ در این مورد، کلاس‌های مربوط به JDK 1.4. کلاس‌های موجود در آن برای توسعه وب جاوا کافی نیستند. برای افزودن کتابخانه‌های کلاس بیشتر، از دکمه Add استفاده کرده و فایل‌های اضافی .jar مورد نظر خود را مشخص کنید. فایل‌های .jar کتابخانه‌های کلاس هستند. Tomcat 4.x شامل تمام کتابخانه‌های کلاسی مورد نیاز برای توسعه وب است. این کتابخانه‌ها در <tomcat>\common\lib قرار دارند، که در آن <tomcat> مسیر نصب Tomcat است:

Image

با استفاده از دکمه Add، این کتابخانه‌ها را یکی یکی به لیست کتابخانه‌های اسکن‌شده توسط JBuilder اضافه خواهیم کرد:

Image

از این پس، می‌توانید برنامه‌های جاوایی را که با استاندارد J2EE مطابقت دارند، از جمله سرولیت‌های جاوا، کامپایل کنید. JBuilder صرفاً برای کامپایل کردن استفاده می‌شود؛ اجرای برنامه بعداً توسط Tomcat انجام می‌گیرد.

  • اکنون می‌توانید برنامه myRequestParamExample.java را کامپایل کرده و سرولتی با نام myRequestParamExample.class ایجاد کنید. این سرولت باید در کجا قرار گیرد؟ اگر پیکربندی اولیه Tomcat تغییر نکرده باشد، کلاس‌های سرولت باید در <tomcat>\webapps\examples\WEB-INF\classes (Tomcat 4.x) قرار گیرند.
  • اطمینان حاصل کنید که Tomcat در حال اجرا است و با استفاده از یک مرورگر، URL را در http://localhost:8080/examples/servlet/myRequestParamExample درخواست کنید:

Image

3.1.6. مثال‌ها

برای مثال‌های زیر، از روش توصیف‌شده در بالا استفاده کرده‌ایم:

  • کامپایل کردن کد منبع XX.java برای سرویس‌لت با استفاده از JBuilder
  • نصب servlet XX.class در <tomcat>\webapps\examples\WEB-INF\classes
  • پس از راه‌اندازی Tomcat، با استفاده از یک مرورگر به آدرس http://localhost:8080/examples/servlet/XX مراجعه کنید.

3.1.6.1. تولید پویا فرم – ۱

به‌عنوان مثال، تولید فرم شامل تنها یک کنترل: یک لیست را در نظر بگیرید. محتوای این لیست به‌صورت پویا با استفاده از مقادیری که از یک آرایه گرفته شده‌اند، ساخته می‌شود. در عمل، این مقادیر اغلب از یک پایگاه داده بازیابی می‌شوند. فرم به شرح زیر است:

Image

اگر در مثال بالا، Envoyer را اجرا کنیم، پاسخ زیر را دریافت می‌کنیم:

Image

توجه داشته باشید که URL که پاسخ را تولید می‌کند، همان چیزی است که فرم را نمایش می‌دهد. در اینجا، ما یک سرولت (servlet) داریم که خود پاسخ فرم ارسالی را پردازش می‌کند. این یک سناریوی رایج است. کد فرم، HTML، به شرح زیر است:

<html>
    <head><title>Génération de formulaire</title></head>
    <body>
    <h3>Choississez un nombre</h3><hr>
    <form method="POST">
      <select name="cmbValeurs" size="1">
        <option>zéro</option>
        <option>un</option>
        <option>deux</option>
        <option>trois</option>
        <option>quatre</option>
        <option>cinq</option>
        <option>six</option>
        <option>sept</option>
        <option>huit</option>
        <option>neuf</option>
      </select>
      <input type="submit" value="Envoyer">
    </form>
    </body>
</html>

شایان ذکر است که مقادیر ارسال‌شده توسط فرم با استفاده از روش POST ارسال می‌شوند. کد HTML در پاسخ:

<html>
    <head><title>Voici ma réponse</title></head>
  <body>
      Vous avez choisi le nombre<h2>neuf</h2>
  </body>
</html>

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

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class gener1 extends HttpServlet{
    // متغیرهای نمونه
    private String title="Génération d'un formulaire";
    private final String[] valeurs={"zéro","un","deux","trois","quatre","cinq","six",
      "sept","huit","neuf"};
    private final String HTML1=
                "<html>" +
                  "<head>" +
                    "<title>Génération de formulaire</title>"+
                    "</head>" +
                  "<body>" +
                     "<h3>Choississez un nombre</h3>"+
                     "<hr>" +
                     "<form method=\"POST\">";
        private final String HTML2="<input type=\"submit\" value=\"Envoyer\">";
        private final String HTML3="</form>\n</body>\n</html>";

    // GET
    public void doGet(HttpServletRequest request,HttpServletResponse response)
          throws IOException, ServletException{

      // به کلاینت نوع سند ارسالی اطلاع داده می‌شود
      response.setContentType("text/html");
       // فرم ارسال می‌شود
      PrintWriter out=response.getWriter();
            // شروع
      out.println(HTML1);
            // منوی کشویی
            out.println("<select name=\"cmbValeurs\" size=\"1\">");
            for (int i=0;i<valeurs.length;i++){
                out.println("<option>"+valeurs[i]+"</option>");
            }//برای
            out.println("</select>");
            //پایان فرم
            out.println(HTML2+HTML3);
    }//GET

         // POST
        public void doPost(HttpServletRequest request,HttpServletResponse response)
          throws IOException, ServletException{

             //بازیابی انتخاب کاربر
            String choix=request.getParameter("cmbValeurs");
            if(choix==null) doGet(request,response);

             // آماده‌سازی پاسخ
            String réponse="<html><head><title>Voici ma réponse</title></head>";
            réponse+="<body>Vous avez choisi le nombre <h2>"+choix+"</h2></body></html>";
             // اطلاع‌رسانی به کلاینت درباره نوع سند ارسال‌شده
            response.setContentType("text/html");
             //ارسال فرم
            PrintWriter out=response.getWriter();
            out.println(réponse);
    }//POST
    }//کلاس

روش doGet برای تولید فرم استفاده می‌شود. یک بخش پویا وجود دارد که محتوای لیست است؛ این محتوا از یک آرایه گرفته می‌شود. روش doPost برای تولید پاسخ استفاده می‌شود. در اینجا، تنها بخش پویا مقدار انتخاب کاربر از لیست فرم است. این مقدار از طریق request.getParameter("cmbValeurs") به دست می‌آید، که در آن cmbValeurs نام لیست است:

      <select name="cmbValeurs" size="1">

در نهایت، لطفاً نکات زیر را توجه فرمایید:

  • مرورگر مقادیر فرم را به سرولتی که فرم را ایجاد کرده است ارسال می‌کند، زیرا تگ <form> دارای ویژگی <action> نیست. در این حالت، مرورگر داده‌های وارد شده در فرم را به URL که آن را فراهم کرده است، ارسال می‌کند.
  • تگ <form> مشخص می‌کند که داده‌های فرم باید با استفاده از روش POST ارسال شوند. به همین دلیل این مقادیر توسط متد doPost سرولت بازیابی می‌شوند.

3.1.6.2. تولید پویا فرم – ۲

ما مثال قبلی را برداشته و آن را به شرح زیر تغییر می‌دهیم. فرم ارائه‌شده هنوز هم یکسان است:

Image

آیا پاسخ متفاوت است؟

Image

در پاسخ، فرم بازگردانده می‌شود و عدد انتخاب‌شده توسط کاربر در زیر آن نمایش داده می‌شود. علاوه بر این، این عدد همان عددی است که هنگام نمایش لیست به‌عنوان انتخاب‌شده ظاهر می‌شود. کاربر سپس می‌تواند عدد دیگری را انتخاب کند:

Image

و سپس ارسال Envoyer. آنها پاسخ زیر را دریافت می‌کنند:

Image

کد سرولتی به نام gener2.java به شرح زیر است:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class gener2 extends HttpServlet{
        // متغیرهای نمونه
        private String title="Génération d'un formulaire";
        private final String[] valeurs={"zéro","un","deux","trois","quatre","cinq","six",
            "sept","huit","neuf"};
        private final String HTML1=
                "<html>" +
                    "<head>" +
                        "<title>Génération de formulaire</title>"+
                    "</head>" +
                    "<body>" +
                         "<h3>Choisissez un nombre</h3>"+
                         "<hr>" +
                         "<form method=\"POST\">";
        private final String HTML2="<input type=\"submit\" value=\"Envoyer\"></form>\n";
        private final String HTML3="</body>\n</html>";

        // GET
        public void doGet(HttpServletRequest request,HttpServletResponse response)
                    throws IOException, ServletException{

            // انتخاب کاربر را بازیابی کنید، در صورت وجود
            String choix=request.getParameter("cmbValeurs");
            if(choix==null) choix="";

             // اطلاع‌رسانی به کلاینت در مورد نوع سند ارسال‌شده
            response.setContentType("text/html");
             //ارسال فرم
            PrintWriter out=response.getWriter();
            // شروع
            out.println(HTML1);
            // منوی کشویی
            out.println("<select name=\"cmbValeurs\" size=\"1\">");
            String selected="";
            for (int i=0;i<valeurs.length;i++){
                if(valeurs[i].equals(choix)) selected="selected"; else selected="";
                out.println("<option "+selected+">"+valeurs[i]+"</option>");
            }//برای
            out.println("</select>");
            // فرم ادامه دارد
            out.println(HTML2);
            if(! choix.equals("")){
                // نمایش انتخاب کاربر
                out.println("<hr>Vous avez choisi le nombre <h2>"+choix+"</h2>");
            }//اگر
             // پایان فرم
            out.println(HTML3);
        }//GET

        //POST
        public void doPost(HttpServletRequest request,HttpServletResponse response)
                    throws IOException, ServletException{

            //به GET هدایت می‌کند
            doGet(request,response);
        }//POST
    }//class

روش doGet همه کارها را انجام می‌دهد: فرم را تولید می‌کند، آن را به کلاینت ارسال می‌کند و مقادیر بازگشتی از کلاینت را پردازش می‌کند. نکات زیر باید مورد توجه قرار گیرند:

  • بررسی می‌کند که آیا پارامتر cmbValeurs مقداری دارد یا خیر.
  • اگر چنین باشد، هنگام تولید محتوای لیست، هر آیتم در لیست با انتخاب کاربر مقایسه می‌شود تا ویژگی selected روی آیتم انتخاب‌شده توسط کاربر تنظیم شود: <option selected>item</option>. علاوه بر این، مقدار انتخاب در زیر فرم نمایش داده می‌شود.

3.1.6.3. ایجاد پویا فرم – ۳

ما دوباره به همان مسئله قبلی می‌پردازیم، اما این بار مقادیر از یک پایگاه داده بازیابی می‌شوند. در مثال ما، این پایگاه داده MySQL است:

  • پایگاه داده dbValeurs نام دارد
  • مالک آن admDbValeurs است، با رمز عبور mdpDbValeurs
  • پایگاه داده تنها یک جدول به نام tvaleurs دارد
  • این جدول تنها یک فیلد عددی به نام «value» دارد
E:\Program Files\EasyPHP\mysql\bin>mysql --database=dbValeurs --user=admDbValeurs --password=mdpDbVa
leurs

mysql> show tables;
+---------------------+
| Tables_in_dbValeurs |
+---------------------+
| tvaleurs            |
+---------------------+
1 row in set (0.00 sec)

mysql> describe tvaleurs;
+--------+---------+------+-----+---------+-------+
| Field  | Type    | Null | Key | Default | Extra |
+--------+---------+------+-----+---------+-------+
| valeur | int(11) |      |     | 0       |       |
+--------+---------+------+-----+---------+-------+

mysql> select * from tvaleurs;
+--------+
| valeur |
+--------+
|      0 |
|      1 |
|      2 |
|      3 |
|      4 |
|      6 |
|      5 |
|      7 |
|      8 |
|      9 |
+--------+
10 rows in set (0.00 sec)

پایگاه داده MySQL dbValeurs توسط یک درایور ODBC برای MySQL قابل دسترسی شده است. نام آن، DSN (نام منبع داده)، odbc-valeurs است. کد سرولت به شرح زیر است:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;
import java.util.*;

public class gener3 extends HttpServlet{
        // عنوان صفحه
        private final String title="Génération d'un formulaire";
         // پایگاه دادهٔ مقادیر لیست
        private final String DSNValeurs="odbc-valeurs";
        private final String admDbValeurs="admDbValeurs";
        private final String mdpDbValeurs="mdpDbValeurs";
         // مقادیر لیست
        private String[] valeurs=null;
         // پیام خطا
        private String msgErreur=null;
        // کد HTML
        private final String HTML1=
                "<html>" +
                    "<head>" +
                        "<title>Génération de formulaire</title>"+
                    "</head>" +
                    "<body>" +
                         "<h3>Choisissez un nombre</h3>"+
                         "<hr>" +
                         "<form method=\"POST\">";
        private final String HTML2="<input type=\"submit\" value=\"Envoyer\"></form>\n";
        private final String HTML3="</body>\n</html>";

        // GET
        public void doGet(HttpServletRequest request,HttpServletResponse response)
                    throws IOException, ServletException{

            //مشتری از نوع سند ارسالی مطلع می‌شود
            response.setContentType("text/html");
             //جریان خروجی
            PrintWriter out=response.getWriter();

            // آیا inicialization سرویسلت با موفقیت انجام شد؟
            if (msgErreur!=null){
                 // یک خطا رخ داده است – یک صفحهٔ خطا تولید می‌شود
                out.println("<html><head><title>"+title+"</title></head>");
                out.println("<body><h3>Application indisponible ("+msgErreur+
                                        ")</h3></body></html>");
                return;
            }//اگر

             // انتخاب کاربر را بازیابی کنید، در صورت وجود
            String choix=request.getParameter("cmbValeurs");
            if(choix==null) choix="";

             // فرم ارسال شده است
             // شروع
            out.println(HTML1);
            // لیست کشویی
            out.println("<select name=\"cmbValeurs\" size=\"1\">");
            String selected="";
            for (int i=0;i<valeurs.length;i++){
                if(valeurs[i].equals(choix)) selected="selected"; else selected="";
                out.println("<option "+selected+">"+valeurs[i]+"</option>");
            }//برای
            out.println("</select>");
            // فرم ادامه دارد
            out.println(HTML2);
            if(! choix.equals("")){
                // نمایش انتخاب کاربر
                out.println("<hr>Vous avez choisi le nombre <h2>"+choix+"</h2>");
            }//اگر
             // پایان فرم
            out.println(HTML3);
        }//GET

        //POST
        public void doPost(HttpServletRequest request,HttpServletResponse response)
                    throws IOException, ServletException{

            //به GET هدایت می‌کند
            doGet(request,response);
        }//POST

        // ابتدای سرویس‌لت
        public void init(){
             // آرایه مقادیر را از پایگاه داده ODBC پر می‌کند
             // نامیده شده DSN: DSNvaleurs
            Connection connexion=null;
            Statement st=null;
            ResultSet rs=null;
            try{
                 //اتصال به پایگاه داده ODBC
                Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
                connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
                 // شیء عبارت
                st=connexion.createStatement();
                 //اجرای یک پرس‌وجوی SELECT برای بازیابی مقادیر
                rs=st.executeQuery("select valeur from Tvaleurs");
                // مقادیر بازیابی شده و در یک آرایه پویا قرار می‌گیرند
                ArrayList lstValeurs=new ArrayList();
                while(rs.next()){
                    //مقدار در لیست ثبت می‌شود
                    lstValeurs.add(rs.getString("valeur"));
                }//در حالی که
                 //تبدیل لیست به آرایه
                valeurs=new String[lstValeurs.size()];
                for (int i=0;i<lstValeurs.size();i++){
                    valeurs[i]=(String)lstValeurs.get(i);
                }
            }catch(Exception ex){
                 // مشکل
                msgErreur=ex.getMessage();
            }finally{
                try{rs.close();}catch(Exception ex){}
                try{st.close();}catch(Exception ex){}
                try{connexion.close();}catch(Exception ex){}
            }//تلاش
        }//init
    }//کلاس

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

  1. یک سروِلت می‌تواند توسط متدی با امضای `public void init()` راه‌اندازی شود. این متد تنها زمانی اجرا می‌شود که سروِلت برای اولین بار بارگذاری شود.
  2. پس از بارگذاری، یک سرولت همواره در حافظه باقی می‌ماند. این بدان معناست که پس از ارائه خدمات به یک کلاینت، از حافظه تخلیه نمی‌شود. بنابراین به درخواست‌های کلاینت سریع‌تر پاسخ می‌دهد.
  3. در سرولتی که ما ساخته‌ایم، باید یک لیست از مقادیر از یک پایگاه داده بازیابی شود. از آنجایی که این لیست در طول زمان تغییر نمی‌کند، متد `init` بهترین زمان برای بازیابی آن است. بنابراین، پایگاه داده تنها یک بار توسط سرولت، در زمان بارگذاری اولیه آن، و نه در هر درخواست مشتری، مورد دسترسی قرار می‌گیرد.
  4. دسترسی به پایگاه داده ممکن است با شکست مواجه شود. متد init در سرولت ما در صورت وقوع شکست، یک پیام خطا msgErreur را تنظیم می‌کند. این پیام در متد doGet بررسی می‌شود و در صورت وقوع خطا، متد doGet صفحه‌ای را که این موضوع را نشان می‌دهد، تولید می‌کند.
  5. پیاده‌سازی متد `init` از دسترسی استاندارد به پایگاه داده از طریق درایورهای ODBC-JDBC استفاده می‌کند. در صورت لزوم، به خواننده توصیه می‌شود روش‌های دسترسی به پایگاه داده `JDBC` را بررسی کند.

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

Image

اگر اکنون سرور MySQL را راه‌اندازی کنید، صفحه زیر را مشاهده خواهید کرد:

Image

اگر عدد ۶ را انتخاب کرده و روی «ارسال» کلیک کنید:

Image

3.1.6.4. بازیابی مقادیر فرم

بیایید مثالی را که قبلاً دیده‌ایم دوباره مرور کنیم، یعنی فرم وب زیر:

Image

کد HTML برای فرم balises2.htm به شرح زیر است:

<html>

  <head>
      <title>balises</title>
    <script language="JavaScript">
        function effacer(){
          alert("Vous avez cliqué sur le bouton Effacer");
      }//حذف
        </script>
  </head>

  <body background="/images/standard.jpg">
...

    <form method="POST" action="http://localhost:8080/examples/servlet/parameters">

      <table border="0">
        <tr>
          <td>Etes-vous marié(e)</td>
          <td>
              <input type="radio" value="Oui" name="R1">Oui
              <input type="radio" name="R1" value="non" checked>Non
          </td>
        </tr>
        <tr>
          <td>Cases à cocher</td>
          <td>
              <input type="checkbox" name="C1" value="un">1
              <input type="checkbox" name="C2" value="deux" checked>2
              <input type="checkbox" name="C3" value="trois">3
          </td>
        </tr>
        <tr>
          <td>Champ de saisie</td>
          <td>
              <input type="text" name="txtSaisie" size="20" value="qqs mots">
          </td>
        </tr>
        <tr>
          <td>Mot de passe</td>
          <td>
              <input type="password" name="txtMdp" size="20" value="unMotDePasse">
          </td>
        </tr>
        <tr>
          <td>Boîte de saisie</td>
          <td>
               <textarea rows="2" name="areaSaisie" cols="20">
ligne1
ligne2
ligne3
</textarea>
          </td>
        </tr>
        <tr>
          <td>combo</td>
          <td>
              <select size="1" name="cmbValeurs">
                <option>choix1</option>
                <option selected>choix2</option>
                <option>choix3</option>
              </select>
          </td>
        </tr>
        <tr>
          <td>liste à choix simple</td>
          <td>
              <select size="3" name="lst1">
                <option selected>liste1</option>
                <option>liste2</option>
                <option>liste3</option>
                <option>liste4</option>
                <option>liste5</option>
              </select>
          </td>
        </tr>
        <tr>
          <td>liste à choix multiple</td>
          <td>
              <select size="3" name="lst2" multiple>
                <option selected>liste1</option>
                <option>liste2</option>
                <option selected>liste3</option>
                <option>liste4</option>
                <option>liste5</option>
              </select>
          </td>
        </tr>
        <tr>
          <td>bouton</td>
          <td>
              <input type="button" value="Effacer" name="cmdEffacer" onclick="effacer()">
          </td>
        </tr>
        <tr>
          <td>envoyer</td>
          <td>
              <input type="submit" value="Envoyer" name="cmdRenvoyer">
          </td>
        </tr>
        <tr>
          <td>rétablir</td>
          <td>
              <input type="reset" value="Rétablir" name="cmdRétablir">
          </td>
        </tr>
      </table>
      <input type="hidden" name="secret" value="uneValeur">
    </form>
  </body>
</html>

تگ <form> در فرم به صورت زیر تعریف شده است:

    <form method="POST" action="http://localhost:8080/examples/servlet/parameters">

مرورگر مقادیر فرم را به URL http://localhost:8080/examples/servlet/parameters ارسال می‌کند، که URL یک سروِلت است که توسط Tomcat مدیریت می‌شود و مقادیر فرم قبلی را نمایش می‌دهد. اگر سرویس‌دهنده parameters مستقیماً فراخوانی شود، نتایج زیر به دست می‌آیند:

Image

اگر فرم واردشده balises2.htm این باشد:

Image

و زمانی که دکمه ارسال (از نوع submit) را کلیک می‌کنید، سروِلت parameters این بار با پارامترها فراخوانی می‌شود. سپس پاسخ زیر را برمی‌گرداند:

Image

این پاسخ به وضوح حاوی مقادیر وارد شده در فرم است. کد سرولت به شرح زیر است:

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class parameters extends HttpServlet{
    //متغیرهای نمونه
    String title="Récupération des paramètres d'un formulaire";

    private String getParameter(HttpServletRequest request, String contrôle){
      //مقدار request.getParameter (check) یا "" را برمی‌گرداند اگر وجود نداشته باشد
      String valeur=request.getParameter(contrôle);
      if(valeur==null) return ""; else return valeur;
    }//getParameter

    //GET
    public void doGet(HttpServletRequest request,HttpServletResponse response)
        throws IOException, ServletException
    {
      // ما با بازیابی پارامترهای فرم شروع می‌کنیم
      String R1=getParameter(request,"R1");
      String C1=getParameter(request,"C1");
      String C2=getParameter(request,"C2");
      String C3=getParameter(request,"C3");
      String txtSaisie=getParameter(request,"txtSaisie");
      String txtMdp=getParameter(request,"txtMdp");
      String areaSaisie=getParameter(request,"areaSaisie");
      String[] lignes=areaSaisie.split("\\r\\n");
      String cmbValeurs=getParameter(request,"cmbValeurs");
      String lst1=getParameter(request,"lst1");
      String[] lst2=request.getParameterValues("lst2");
      String secret=getParameter(request,"secret");

      // ما محتوای سند را مشخص می‌کنیم
        response.setContentType("text/html");
       // ما سند را ارسال می‌کنیم
        PrintWriter out = response.getWriter();
        out.println("<html>");
        out.println("<body>");
        out.println("<head>");
        out.println("<title>" + title + "</title>");
        out.println("</head>");
        out.println("<body bgcolor=\"white\">");
        out.println("<h3>" + title + "</h3>");
        out.println("<hr>");
        out.println("<table border=\"1\">");
        out.println("<tr><td>R1</td><td>"+R1+"</td></tr>");
        out.println("<tr><td>C1</td><td>"+C1+"</td></tr>");
        out.println("<tr><td>C2</td><td>"+C2+"</td></tr>");
        out.println("<tr><td>C3</td><td>"+C3+"</td></tr>");
        out.println("<tr><td>txtSaisie</td><td>"+txtSaisie+"</td></tr>");
        out.println("<tr><td>txtMdp</td><td>"+txtMdp+"</td></tr>");
        for(int i=0;i<lignes.length;i++)
          out.println("<tr><td>areaSaisie["+i+"]</td><td>"+lignes[i]+"</td></tr>");
        out.println("<tr><td>cmbValeurs</td><td>"+cmbValeurs+"</td></tr>");
        out.println("<tr><td>lst1</td><td>"+lst1+"</td></tr>");
        if(lst2==null)
          out.println("<tr><td>lst2</td><td></td></tr>");
        else
          for(int i=0;i<lst2.length;i++)
            out.println("<tr><td>lst2</td><td>"+lst2[i]+"</td></tr>");
        out.println("<tr><td>secret</td><td>"+secret+"</td></tr>");
        out.println("</body>");
        out.println("</html>");
    }

     //POST
    public void doPost(HttpServletRequest request,HttpServletResponse response)
        throws IOException, ServletException
    {
       //به GET هدایت می‌کند
      doGet(request,response);
    }
}

این کد از تکنیک‌های ارائه‌شده در مثال دیگری در بالا استفاده می‌کند. دو نکته قابل توجه است:

  1. کنترل lst2 یک لیست چندگزینه‌ای است، به این معنی که می‌توان چندین مورد را انتخاب کرد. این مورد در مثال ما صادق است، جایی که موارد liste1 و liste3 انتخاب شده‌اند. مقادیر lst2 توسط مرورگر در قالب lst2=liste1&lst2=liste3 به سرور ارسال شده‌اند. سرولت جاوا می‌تواند این مقادیر را با استفاده از متد getParameterValues در یک آرایه بازیابی کند: در اینجا، request.getParameterValues("lst2") یک آرایه از دو رشته، ["liste1","liste3"] را برمی‌گرداند.
  2. کنترل areaSaisie یک فیلد ورودی چندخطی است. request.getParameter("areaSaisie") محتوای این فیلد را به صورت یک رشته واحد برمی‌گرداند. اگر بخواهید خطوط مجزای تشکیل‌دهنده این رشته را استخراج کنید، می‌توانید از تابع split در کلاس String استفاده کنید. کد زیر
      String areaSaisie=getParameter(request,"areaSaisie");
      String[] lignes=areaSaisie.split("\\r\\n");

خط‌ها را از فیلد ورودی بازیابی می‌کند. این خط‌ها با کاراکترهای \r\n (0D0A) پایان می‌یابند.

برای انجام آزمایش‌ها، داریم:

  • سرولت parameters را با JBuilder، همان‌طور که قبلاً توضیح داده شد، ساخت و کامپایل کرده‌ایم
  • کلاس تولید شده را در <tomcat>\webapps\examples\WEB-INF\classes قرار دادیم، که در آن <tomcat> مسیر نصب Tomcat است.
  • به صفحه URL http://localhost:81/html/balises2.htm دسترسی پیدا کرد، که کُد آن در بالا نشان داده شده بود
  • فرم را پر کرد و روی دکمه Envoyer کلیک کرد.

3.1.6.5. بازیابی سربرگ‌ها از یک کلاینت وب

ما از همان مثال قبلی استفاده می‌کنیم، اما در پاسخ به کلاینت وبی که مقادیر فرم را ارسال کرده است، سربرگ‌های HTTP را که در همان زمان ارسال کرده بود، بازمی‌گردانیم. ما فقط یک تغییر در فرم خود ایجاد می‌کنیم:

    <form method="GET" action="http://localhost:8080/examples/servlet/headers">

مقادیر فرم از طریق متد GET به یک سرولت جاوا به نام headers که در <tomcat>\webapps\examples\WEB-INF\classes قرار دارد، ارسال خواهند شد. سرولت headers با JBuilder ساخته و کامپایل شده است:

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class headers extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
    {
       // نوع سند تنظیم شده است
        response.setContentType("text/html");
       //یک جریان نوشتاری به دست میآید
        PrintWriter out = response.getWriter();
      // نمایش فهرست سربرگها HTTP
        Enumeration e = request.getHeaderNames();
        while (e.hasMoreElements()) {
            String name = (String)e.nextElement();
            String value = request.getHeader(name);
            out.println("<b>"+name + "</b> = " + value + "<br>");
        }
    }//GET

    public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws IOException, ServletException
    {
      //GET
      doGet(request,response);
    }//POST
}

ما URL http://localhost:81/html/balises2.htm را درخواست می‌کنیم و بدون تغییر فرم، Envoyer را تولید می‌کنیم. پاسخ زیر را دریافت می‌کنیم:

Image

توجه داشته باشید که URL پیکربندی‌شده در فیلد Address مرورگر موجود است، که روش (GET) مورد استفاده برای ارسال پارامترها را نشان می‌دهد. همان مثال را در نظر بگیرید اما روش ارسال پارامترها (POST) را تغییر دهید:

    <form method="POST" action="http://localhost:8080/examples/servlet/headers">

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

Image

به سربرگ‌های HTTP، content-type و content-length توجه کنید که مشخصه ارسال از طریق POST هستند. علاوه بر این، باید توجه داشت که در فیلد Address مرورگر، مقادیر فرم دیگر نمایش داده نمی‌شوند.

3.2. صفحات JSP

صفحات JSP (Java Server Pages) روش دیگری برای نوشتن برنامه‌های سرور وب هستند. در واقع، این صفحات JSP قبل از اجرا به سرولت‌ها تبدیل می‌شوند و بنابراین از فناوری سرولت استفاده می‌شود. صفحات JSP به برجسته‌سازی بهتر ساختار صفحات تولیدشده HTML کمک می‌کنند. در زیر چند مثال آورده شده است که برخی از آن‌ها را می‌توان با دنبال کردن لینک JSP در صفحه اصلی Tomcat دسترسی پیدا کرد:

3.2.1. بازیابی اطلاعات محیط

در اینجا مثالی را که قبلاً با استفاده از یک سرولت پوشش داده شده بود، مجدداً بررسی می‌کنیم: نمایش متغیرهای محیطی یک سرولت. این مثال snoop از میان مثال‌های موجود در JSP است:

Image

کد منبع صفحه JSP را می‌توان در <tomcat>\jakarta-tomcat\examples\jsp\snp\snoop.jsp یافت. (Tomcat 3.x) یا <tomcat>\examples\jsp\snp\snoop.jsp (Tomcat 4.x)


<html>
<!--
  Copyright (c) 1999 The Apache Software Foundation.  All rights 
  reserved.
-->

  <body bgcolor="white">
    <h1> Request Information </h1>
    <font size="4">
      JSP Request Method: <%= request.getMethod() %>
      <br>
      Request URI: <%= request.getRequestURI() %>
      <br>
      Request Protocol: <%= request.getProtocol() %>
      <br>
      Servlet path: <%= request.getServletPath() %>
      <br>
      Path info: <%= request.getPathInfo() %>
      <br>
      Path translated: <%= request.getPathTranslated() %>
      <br>
      Query string: <%= request.getQueryString() %>
      <br>
      Content length: <%= request.getContentLength() %>
      <br>
      Content type: <%= request.getContentType() %>
      <br>
      Server name: <%= request.getServerName() %>
      <br>
      Server port: <%= request.getServerPort() %>
      <br>
      Remote user: <%= request.getRemoteUser() %>
      <br>
      Remote address: <%= request.getRemoteAddr() %>
      <br>
      Remote host: <%= request.getRemoteHost() %>
      <br>
      Authorization scheme: <%= request.getAuthType() %> 
      <hr>
      The browser you are using is <%= request.getHeader("User-Agent") %>
      <hr>
    </font>
  </body>
</html>

نکات زیر باید مورد توجه قرار گیرند:

  • کد اینجا شباهت زیادی به HTML دارد. با این حال، حاوی تگ‌های <%= expression %> است که مختص زبان JSP هستند. کامپایلر JSP کل تگ در متن HTML را با مقدار expression جایگزین می‌کند.
  • این مثال از متدهای شیء جاوا request استفاده می‌کند که همان شیء request است که پیش از این در مطالعه سرولت‌ها با آن مواجه شده‌ایم. بنابراین، این یک شیء HttpServletRequest است. بدین ترتیب، تگ <%= request.getRemoteHost() %> در کد HTML با نام میزبان (hostname) کلاینت وبی که درخواست را ارسال کرده است، جایگزین خواهد شد.
  • می‌توان به همان نتیجه با استفاده از یک سرولت نیز رسید، اما در اینجا ساختار صفحه وب واضح‌تر است.

3.2.2. بازیابی پارامترهای ارسال‌شده توسط کلاینت وب

در اینجا مثال قبلی را که قبلاً با استفاده از یک سرولت بررسی کرده بودیم، مجدداً مرور می‌کنیم. یک فرم در مرورگر نمایش داده می‌شود:

Image

در پاسخ به درخواست فوق، مرورگر صفحه زیر را دریافت می‌کند:

Image

کد صفحه JSP به شرح زیر است:


<%
   // متغیرهای محلی در رویه اصلی
  String title="Récupération des paramètres d'un formulaire";
  String firstName = request.getParameter("firstname");
  String lastName = request.getParameter("lastname");
%>

<!-- کد HTML -->
<html>
  <head>
    <title><%= title %></title>
  </head>
  <body bgcolor="white">
    <h3><%= title %></h3>
    <%
      if (firstName != null || lastName != null) {
        out.println("firstname= " + firstName + "<br>");
        out.println("lastname= " + lastName);
      } else {
        out.println("pas de paramètres");
      }
    %>
    <P>
    <form method="POST">
      firstname= <input type="text" size="20" name="firstname">
      <br>
      lastname= <input type="text" size="20" name="lastname">
      <br>
      <input type="submit">
    </form>
  </body>
</html>
  • در حالی که تگ <%= expression %> که قبلاً در مثال قبلی دیده شد، موجود است، یک تگ جدید ظاهر می‌شود: <% Java instructions; %>. تگ <%> کد جاوا را معرفی می‌کند. این کد زمانی پایان می‌یابد که به تگ پایانی کد %> برخورد شود.
  • کل کد فوق (HTML + JSP) به یک سرولت جاوا تبدیل خواهد شد. این کد در یک متد واحد به نام متد اصلی صفحه (JSP) قرار خواهد گرفت. به همین دلیل است که متغیرهای جاوایی که در ابتدای صفحه JSP اعلام شده‌اند، در بخش‌های دیگر کد JSP که در سراسر کد HTML پراکنده شده‌اند، قابل دسترسی هستند: این متغیرها و بخش‌های کد بخشی از یک متد جاوایی واحد را تشکیل خواهند داد. با این حال، اگر کد ما JSP شامل متدهایی بود، متغیرهای title، firstname و lastname به دلیل جداسازی متد، در داخل آن‌ها قابل دسترسی نخواهند بود. ما باید آن‌ها را به متغیرهای سراسری تبدیل کنیم یا آن‌ها را به‌عنوان پارامتر به متدها ارسال کنیم. بعداً به این موضوع باز خواهیم گشت.
  • برای درج عناصر پویا در کد HTML، دو روش ممکن وجود دارد: <%= expression %> یا out.println(expression). شیء out یک جریان خروجی است که مشابه جریانی با همین نام در مثال‌های سرولت است، اما از همان نوع نیست: این یک شیء JspWriter است و نه یک شیء PrintWriter. این امکان را به شما می‌دهد که با استفاده از متدهای print و println در استریم HTML بنویسید.
  • صفحه JSP ساختار صفحه تولیدشده HTML را بهتر از سرولتی معادل آن منعکس می‌کند.

3.2.3. برچسب‌های JSP

در اینجا فهرستی از برچسب‌هایی که ممکن است در یک صفحه JSP یافت شوند و معانی آن‌ها آمده است.

برچسب
معنی
<!-- توضیح -->
توضیح HTML. برای مشتری ارسال شد.
<%-- توضیح --%>
توضیح: JSP. برای مشتری ارسال نشده است.
<%! déclarations, méthodes %>
متغیرها و متدهای سراسری را اعلام می‌کند. متغیرها در تمام متدها قابل دسترسی خواهند بود.
<%= expression %>
مقدار عبارت در صفحه HTML به جای تگ درج خواهد شد
<% code Java %>
حاوی کد جاوا است که بخشی از متد اصلی صفحه JSP را تشکیل خواهد داد
<%@ page attribut1=valeur1
attribut2=valeur2 … %>
ویژگی‌ها را برای صفحه JSP تنظیم می‌کند. برای مثال:
import="java.util.*,java.sql.*" برای مشخص کردن کتابخانه‌های مورد نیاز صفحه JSP
extends="aParentClass" برای ارث‌بری صفحه JSP از یک کلاس دیگر

3.2.4. ابجکت‌های ضمنی JSP

در مثال‌های قبلی با دو شیء اعلام‌نشده مواجه شدیم: request و out. این دو از شیءهایی هستند که به‌طور خودکار در سرولتی که صفحه JSP به آن تبدیل می‌شود، تعریف شده‌اند. این‌ها به عنوان اشیاء ضمنی یا از پیش تعریف‌شده شناخته می‌شوند. اشیاء دیگری نیز وجود دارند، اما این‌ها به همراه شیء response، رایج‌ترین موارد استفاده هستند:

ابژه
به معنای
HttpServletRequest request
ابجئتی که از طریق آن به درخواست مشتری وب دسترسی پیدا می‌شود (getParameter, getParameterNames, getParameterValues)
HttpServletResponse response
ابجئتی که برای ساخت پاسخ سرور وب به کلاینت آن استفاده می‌شود. به شما امکان می‌دهد سربرگ‌های HTTP را که باید به کلاینت وب ارسال شوند، تنظیم کنید.
JspWriter out
جریان خروجی که به ما امکان می‌دهد کد HTML را به کلاینت ارسال کنیم (print, println)

3.2.5. تبدیل یک صفحه JSP به یک سرولت

بیایید کد JSP را از myRequestParamExample.jsp مجدداً بررسی کنیم:


<%
   // متغیرهای محلی در رویهٔ اصلی
  String title="Récupération des paramètres d'un formulaire";
  String firstName = request.getParameter("firstname");
  String lastName = request.getParameter("lastname");
%>

<!-- کد HTML -->
<html>
  <head>
    <title><%= title %></title>
  </head>
  <body bgcolor="white">
    <h3><%= title %></h3>
    <%
      if (firstName != null || lastName != null) {
        out.println("firstname= " + firstName + "<br>");
        out.println("lastname= " + lastName);
      } else {
        out.println("pas de paramètres");
      }
    %>
    <P>
    <form method="POST">
      firstname= <input type="text" size="20" name="firstname">
      <br>
      lastname= <input type="text" size="20" name="lastname">
      <br>
      <input type="submit">
    </form>
  </body>
</html>

وقتی مرورگر این صفحه، JSP، را از سرور Tomcat درخواست می‌کند، سرور آن را به یک servlet تبدیل می‌کند. اگر URL درخواستی باشد

http://localhost:8080/examples/jsp/perso/intro/myRequestParamExample.jsp، Tomcat 4.x سرولت تولیدشده را در دایرکتوری <tomcat>\work\localhost\examples\jsp\perso\intro قرار خواهد داد:

Image

این نام شامل URL http://localhost:8080/examples/jsp/perso/intro/myRequestParamExample.jsp برای صفحه JSP است. همان‌طور که در بالا نشان داده شده است، ما به کد جاوا سرولتی که برای صفحه JSP تولید شده است دسترسی داریم. در مثال ما، به شرح زیر است:

package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;


public class myRequestParamExample$jsp extends HttpJspBase {


    static {
    }
    public myRequestParamExample$jsp( ) {
    }

    private static boolean _jspx_inited = false;

    public final void _jspx_init() throws org.apache.jasper.runtime.JspException {
    }

    public void _jspService(HttpServletRequest request, HttpServletResponse  response)
        throws java.io.IOException, ServletException {

        JspFactory _jspxFactory = null;
        PageContext pageContext = null;
        HttpSession session = null;
        ServletContext application = null;
        ServletConfig config = null;
        JspWriter out = null;
        Object page = this;
        String  _value = null;
        try {

            if (_jspx_inited == false) {
                synchronized (this) {
                    if (_jspx_inited == false) {
                        _jspx_init();
                        _jspx_inited = true;
                    }
                }
            }
            _jspxFactory = JspFactory.getDefaultFactory();
            response.setContentType("text/html;charset=ISO-8859-1");
            pageContext = _jspxFactory.getPageContext(this, request, response,
                        "", true, 8192, true);

            application = pageContext.getServletContext();
            config = pageContext.getServletConfig();
            session = pageContext.getSession();
            out = pageContext.getOut();


                   // متغیرهای محلی در رویه اصلی
                  String title="Récupération des paramètres d'un formulaire";
                  String firstName = request.getParameter("firstname");
                  String lastName = request.getParameter("lastname");

                out.write("\r\n\r\n<!-- code HTML -->\r\n<html>\r\n  <head>\r\n    <title>");
                out.print( title );
                out.write("</title>\r\n  </head>\r\n  <body bgcolor=\"white\">\r\n    <h3>");
                out.print( title );
                out.write("</h3>\r\n    ");

                      if (firstName != null || lastName != null) {
                        out.println("firstname= " + firstName + "<br>");
                        out.println("lastname= " + lastName);
                      } else {
                        out.println("pas de paramètres");
                      }

                out.write("\r\n    <P>\r\n    <form method=\"POST\">\r\n      firstname= <input type=\"text\" size=\"20\" name=\"firstname\">\r\n      <br>\r\n      lastname= <input type=\"text\" size=\"20\" name=\"lastname\">\r\n      <br>\r\n      <input type=\"submit\">\r\n    </form>\r\n  </body>\r\n</html>\r\n");

        } catch (Throwable t) {
            if (out != null && out.getBufferSize() != 0)
                out.clearBuffer();
            if (pageContext != null) pageContext.handlePageException(t);
        } finally {
            if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext);
        }
    }
}

کد تولیدشده بسیار پیچیده است. ما تنها بر نکات زیر تمرکز خواهیم کرد:

  • متد اصلی سرولت به شرح زیر است:
    public void _jspService(HttpServletRequest request, HttpServletResponse  response)
        throws java.io.IOException, ServletException {

این متد هنگام راه‌اندازی سرولت فراخوانی می‌شود. می‌بینیم که دو پارامتر می‌گیرد: درخواست کلاینت request و یک شی response برای تولید پاسخ آن به کلاینت وب.

  • در متد اصلی، یک شیء JspWriter به نام «out» تعریف و سپس مقداردهی اولیه می‌شود. این شیء برای ارسال کد HTML به کلاینت از طریق دستور out.print("codeHTML") استفاده می‌شود.
        JspWriter out = null;
...
            out = pageContext.getOut();
  • کد جاوا

<%
   // متغیرهای محلی در رویهٔ اصلی
  String title="Récupération des paramètres d'un formulaire";
  String firstName = request.getParameter("firstname");
  String lastName = request.getParameter("lastname");
%>

به‌طور کامل در متد اصلی servlet با شناسه _jspService گنجانده شده است. همین امر در مورد هر کدی که درون تگ‌های <%… %> قرار دارد نیز صدق می‌کند.

  • کد HTML در صفحه JSP موضوع دستورهای out.print("codeHTML") یا out.write است(...). برای مثال

                out.write("</title>\r\n  </head>\r\n  <body bgcolor=\"white\">\r\n    <h3>");
  • در این مثال، هیچ متدی به جز متد اصلی _jspService وجود ندارد.

3.2.6. متدها و متغیرهای سراسری یک صفحه JSP

صفحه زیر را در نظر بگیرید JSP:


<%!
   // برچسب قبلی شروع بخش مربوط به متغیرها و متدهای سراسری را نشان می‌دهد
   // این بخش بدون تغییر در سرولت گنجانده خواهد شد
  
   //یک متغیر سراسری
  String prenom="inconnu";

   //یک متد  
  private String sonChien(){
    return "milou";
  }//sonChien

   // یک متد دیگر
  private void afficheAmi(JspWriter out) throws Exception{
    out.println("<p>Son ami s'appelle Haddock</p>");
  }//afficheAmi

   //پایان بخش جهانی سرولت
%>  

<%
   // برچسب قبلی نشان می‌دهد که کد بعدی ذخیره خواهد شد
   // در متد اصلی سرولت
  
   // متغیر محلی در متد اصلی
  String nom="tintin";
%>


<%--کد HTML --%>
<html>
  <head>
    <title>Page JSP</title>
  </head>
  <body>
    <center>
      <h2>Page JSP</h2>
      <p>Son nom est <%= nom %></p>
      <p>Son prénom est <%= prenom %></p>
      <p>Son chien s'appelle <%= sonChien() %></p>
      <%
         // نام دوستش
        afficheAmi(out);
      %>
    </center>
  </body>
</html>

این صفحه، JSP، صفحه وب زیر را تولید می‌کند:

Image

بیایید نگاهی بیندازیم که چگونه چهار خط بالا تولید می‌شوند:


      <p>Son nom est <%= nom %></p>
      <p>Son prénom est <%= prenom %></p>
      <p>Son chien s'appelle <%= sonChien() %></p>
      <%
         // نام دوستش
        afficheAmi(out);
      %>

خطوط بالا در داخل تگ <%..%> قرار دارند و بنابراین بخشی از متد اصلی _jspService سرولتی که تولید خواهد شد را تشکیل می‌دهند. آنها چگونه به متغیرهای nom، prenom و متدهای sonChien و afficheAmi دسترسی پیدا می‌کنند؟

nom (tintin)
یک متغیر محلی در متد اصلی صفحه JSP است و بنابراین در داخل آن قابل دسترسی است
prenom (inconnu)
یک متغیر سراسری در صفحه JSP است و بنابراین در روش اصلی شناخته می‌شود
sonChien (milou)
یک متد عمومی صفحه JSP است و بنابراین از متد اصلی قابل دسترسی است
afficheAmi (Haddock)
یک متد عمومی صفحه JSP است و بنابراین از متد اصلی قابل دسترسی است. توجه داشته باشید که شیء out به عنوان پارامتر به متد ارسال می‌شود. این کار در اینجا الزامی است. دلیل آن این است که شیء out در متد اصلی سرولت تعریف و مقداردهی اولیه شده است و یک متغیر سراسری (global variable) نیست.

اکنون بیایید نگاهی به کد سرویس‌گیر جاوا (Java servlet) که از این صفحه، JSP، تولید شده است، بیندازیم، پس از حذف کدهای غیرضروری:

package org.apache.jsp;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;


public class tintin$jsp extends HttpJspBase {

          // برچسب قبلی شروع بخش مربوط به متغیرها و متدهای سراسری را نشان می‌دهد
           // این بخش بدون تغییر در سرولت گنجانده خواهد شد

           //یک متغیر سراسری
          String prenom="inconnu";

           //یک متد  
          private String sonChien(){
            return "milou";
          }//sonChien

           // یک متد دیگر
          private void afficheAmi(JspWriter out) throws Exception{
            out.println("<p>Son ami s'appelle Haddock</p>");
          }//afficheAmi

           // پایان بخش جهانی سرولت

    static {
    }
    public tintin$jsp( ) {
    }

    private static boolean _jspx_inited = false;

    public final void _jspx_init() throws org.apache.jasper.runtime.JspException {
    }

    public void _jspService(HttpServletRequest request, HttpServletResponse  response)
        throws java.io.IOException, ServletException {

        JspFactory _jspxFactory = null;
        PageContext pageContext = null;
        HttpSession session = null;
        ServletContext application = null;
        ServletConfig config = null;
        JspWriter out = null;
        Object page = this;
        String  _value = null;
        try {

            if (_jspx_inited == false) {
                synchronized (this) {
                    if (_jspx_inited == false) {
                        _jspx_init();
                        _jspx_inited = true;
                    }
                }
            }
            _jspxFactory = JspFactory.getDefaultFactory();
            response.setContentType("text/html;charset=ISO-8859-1");
            pageContext = _jspxFactory.getPageContext(this, request, response,
                        "", true, 8192, true);

            application = pageContext.getServletContext();
            config = pageContext.getServletConfig();
            session = pageContext.getSession();
            out = pageContext.getOut();

                out.write("  \r\n\r\n");
                  // برچسب قبلی نشان می‌دهد که کد بعدی ذخیره خواهد شد
                   // در متد اصلی سرولت

                   // یک متغیر محلی در متد اصلی
                  String nom="tintin";

                out.write("\r\n\r\n\r\n");
                out.write("\r\n<html>\r\n  <head>\r\n    <title>Page JSP</title>\r\n  </head>\r\n  <body>\r\n    <center>\r\n      <h2>Page JSP</h2>\r\n      <p>Son nom est ");
                out.print( nom );
                out.write("</p>\r\n      <p>Son prénom est ");
                out.print( prenom );
                out.write("</p>\r\n      <p>Son chien s'appelle ");
                out.print( sonChien() );
                out.write("</p>\r\n      ");

                        // نام دوستش
                        afficheAmi(out);

                out.write("\r\n    </center>\r\n  </body>\r\n</html>\r\n");
        } catch (Throwable t) {
            if (out != null && out.getBufferSize() != 0)
                out.clearBuffer();
            if (pageContext != null) pageContext.handlePageException(t);
        } finally {
            if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext);
        }
    }
}

همانطور که در بالا مشاهده می‌شود، کد جاوا که بین تگ‌های JSP <%! .. %> قرار داشت، به طور کامل گنجانده شده است و بخشی از متد اصلی سرولت، _jspService، نیست. متغیرهای تعریف‌شده در این بخش، متغیرهای نمونه (instance variables) هستند و بنابراین برای متدها سراسری (global) محسوب می‌شوند؛ همچنین در همین قسمت می‌توان متدهایی غیر از _jspService را تعریف کرد.


  // این بخش بدون تغییر در سرویس‌لت گنجانده خواهد شد

   // یک متغیر سراسری
  String prenom="inconnu";

   // یک متد
  private String sonChien(){
    return "milou";
  }//sonChien

   // یک متد دیگر
  private void afficheAmi(JspWriter out) throws Exception{
    out.println("<p>Son ami s'appelle Haddock</p>");
  }//afficheAmi

   //پایان بخش سراسری سرولت

3.2.7. استقرار و اشکال‌زدایی صفحات JSP روی سرور Tomcat

وقتی می‌خواهید یک صفحه JSP ایجاد کنید و از آن با سرور Tomcat استفاده کنید، این سؤال پیش می‌آید که صفحه را در کجای ساختار دایرکتوری سرور قرار دهید. راه‌های مختلفی برای انجام این کار وجود دارد که بعداً به آن‌ها باز خواهیم گشت. فعلاً، ساده‌ترین روش این است که صفحه JSP را در یک پوشه درون ساختار دایرکتوری <tomcat>\webapps\examples\jsp (Tomcat 4.x) قرار دهید، جایی که <tomcat> دایرکتوری نصب Tomcat است. بنابراین، URL از مثال قبلی http://localhost:8080/examples/jsp/perso/tintin/tintin.jsp بود. این بدان معناست که صفحه tintin.jsp در پوشه <tomcat>\webapps\examples\jsp\perso\tintin قرار داشت.

صفحه‌ای با نام JSP به یک فایل منبع جاوا تبدیل می‌شود که سپس توسط Tomcat کامپایل می‌شود، زمانی که فایل URL برای صفحه JSP توسط یک مرورگر درخواست شود. ممکن است خطاهای کامپایل رخ دهند. تامکت این خطاها را در پاسخ خود به مرورگر گزارش می‌کند. به‌ویژه، خطوطی از فایل .java را که حاوی خطا هستند مشخص می‌کند. خطاها می‌توانند علل مختلفی داشته باشند:

  1. کد JSP در صفحه نادرست است (مثلاً خطاهایی در تگ‌های JSP استفاده شده)
  2. کد جاوا درج‌شده در صفحه JSP نادرست است

علت اول را می‌توان با بررسی کد JSP در صفحه رد کرد. دومین مورد را می‌توان با بررسی کد جاوا رد کرد. این کار را می‌توان با کامپایل مستقیم فایل .java تولیدشده برای صفحه JSP با استفاده از ابزاری مانند JBuilder انجام داد که قابلیت‌های دیباگ پیشرفته‌تری نسبت به Tomcat ارائه می‌دهد.

3.2.8. مثال‌ها

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

Image

برای ساخت این صفحه، کد سرولت را بازیابی کرده و آن را به شرح زیر اصلاح کردیم:

  • کد جاوا را که کد HTML را تولید نمی‌کرد، بدون تغییر باقی گذاشتیم
  • کد جاوا که کد HTML را تولید می‌کرد، به ترکیبی از کد HTML و کد JSP تبدیل شد.

این امر منجر به صفحه زیر JSP می‌شود:



<%@ page import="java.sql.*, java.util.*" %>

<%!

         // متغیرهای در سطح برنامه
         // عنوان صفحه
        private final String title="Génération d'un formulaire";
         // پایگاه داده مقادیر لیست
        private final String DSNValeurs="odbc-valeurs";
        private final String admDbValeurs="admDbValeurs";
        private final String mdpDbValeurs="mdpDbValeurs";
         // مقادیر لیست
        private String[] valeurs=null;
         // پیام خطا
        private String msgErreur=null;
    
         // ابتدای صفحه JSP – فقط یک بار اجرا می‌شود
        public void jspInit(){
             //جدول مقادیر را از پایگاه داده ODBC پر می‌کند
             // با نام DSN: DSNvaleurs
            Connection connexion=null;
            Statement st=null;
            ResultSet rs=null;
            try{
                 //اتصال به پایگاه داده ODBC
                Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
                connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
                 //شیء عبارت
                st=connexion.createStatement();
                 //اجرای پرس‌وجوی SELECT برای بازیابی مقادیر
                rs=st.executeQuery("select valeur from Tvaleurs");
                //مقادیر بازیابی شده و در یک آرایه پویا قرار می‌گیرند
                ArrayList lstValeurs=new ArrayList();
                while(rs.next()){
                    //مقدار به لیست اضافه می‌شود
                    lstValeurs.add(rs.getString("valeur"));
                }//در حالی که
                 //تبدیل لیست به آرایه
                valeurs=new String[lstValeurs.size()];
                for (int i=0;i<lstValeurs.size();i++){
                    valeurs[i]=(String)lstValeurs.get(i);
                }
            }catch(Exception ex){
                 //مشکل
                msgErreur=ex.getMessage();
            }finally{
                try{rs.close();}catch(Exception ex){}
                try{st.close();}catch(Exception ex){}
                try{connexion.close();}catch(Exception ex){}
            }//تلاش
        }//مقدار اولیه را تنظیم کنید
   
%>

<%
     // کد از _jspService در هر درخواست مشتری اجرا می‌شود
   //آیا هنگام راه‌اندازی صفحه JSP خطایی رخ داد؟
  if(msgErreur!=null){
%>
       <!-- کد HTML -->
    <html>
        <head>
          <title>Erreur</title>
      </head>
      <body>
          <h3>Application indisponible (<%= msgErreur %></h3>
      </body>
    </html>
<%
       //پایان jspService
    return;
  }//اگر
  
     // انتخاب کاربر را بازیابی کنید، در صورت وجود
    String choix=request.getParameter("cmbValeurs");
    if(choix==null) choix="";
%>
  
   <%-- بدون خطا – کد HTML برای صفحهٔ عادی --%>
      <html>
        <head>
          <title><%= title %></title>
      </head>
      <body>
          <h3>Choisissez une valeur</h3>
          <form method="POST">
            <select name="cmbValeurs">
              <%
                 //نمایش پویا مقادیر
                          String selected="";
                          for (int i=0;i<valeurs.length;i++){
                              if(valeurs[i].equals(choix)) selected="selected"; else selected="";
                              out.println("<option "+selected+">"+valeurs[i]+"</option>");
                          }//برای
            %>
          </select>
          <input type="submit" value="Envoyer">
        </form>
<%
         //آیا مقداری انتخاب شده بود؟
                if(! choix.equals("")){
        //نمایش انتخاب کاربر
%>        
                <hr>Vous avez choisi le nombre<h2><%= choix %></h2>
<%        
                }//اگر
%>        
      </body>
    </html>

لطفاً به نکات زیر توجه کنید:

  • دستورهای import در سرولت در داخل یک دستور <% page import="..." %> قرار گرفته‌اند
  • برچسب <%! ... %> متغیرهای سراسری برنامه و متدهای جاوا را در بر می‌گیرد
  • متد init سرولِت، که تنها یک بار هنگام بارگذاری سرولِت اجرا می‌شود، برای یک صفحه JSP فراخوانی می‌شود: jspInit. این دو متد یک هدف را دنبال می‌کنند. بنابراین، کد متد init سرویس‌لِت را در اینجا به‌طور کامل بازتولید کرده‌ایم.
  • متغیرهای نمونه سرولت — یعنی آنهایی که باید در چندین متد قابل دسترسی باشند — دقیقاً به همان صورت که هستند کپی شده‌اند. این متغیرها اساساً `title`valeurs` و `msgErreur` هستند که متعاقباً در کد `JSP` استفاده می‌شوند.
  • برچسب‌های <% ... %> کد جاوا را در بر می‌گیرند که در متد _jspService گنجانده می‌شود، متدی که هنگام ارسال درخواست توسط کلاینت اجرا می‌گردد.
  • مانند سرولت، متد _jspService با بررسی مقدار متغیر msgErreur شروع می‌شود تا مشخص کند که آیا نیاز به تولید صفحه خطا دارد یا خیر. اگر خطایی وجود داشته باشد، صفحه خطا را تولید کرده و متوقف می‌شود (return).
  • اگر خطایی وجود نداشته باشد، فرم را با لیست مقادیر تولید می‌کند
  • پس از انجام این کار، بررسی می‌کند که آیا کاربر عددی را انتخاب کرده است، که در این صورت آن عدد را در صفحه تولید شده نمایش می‌دهد

مزایای آن در مقایسه با سرولت چیست؟ بدون شک دید واضح‌تری از کد تولیدشده HTML. با این حال، هنوز حجم زیادی از کد جاوا این نما را «شلوغ» می‌کند. بعداً به روش دیگری به نام تفویض (delegation) خواهیم پرداخت که در آن می‌توانیم بخش عمده کد جاوا را در یک سرولت قرار دهیم و صفحه JSP تنها کدهای HTML و JSP را حفظ می‌کند. این به وضوح منطق کسب‌وکار را از لایهٔ ارائه جدا می‌کند.

3.3. راه‌اندازی یک برنامه وب روی سرور Tomcat

اکنون توضیح می‌دهیم که چگونه برنامه‌های وب جاوا را با استفاده از سرور تامکت مستقر کنیم. اگرچه اطلاعات ارائه‌شده در اینجا مختص این سرور است، استقرار یک برنامه وب جاوا در یک کانتینر دیگر J2EE شامل رویه‌هایی مشابه با موارد توصیف‌شده در زیر خواهد بود.

3.3.1. فایل‌های پیکربندی server.xml و web.xml

تا به حال، برای آزمایش سرولت‌ها و صفحات JSP، ما قرار داده‌ایم

  • سرولت‌ها را در پوشه <tomcat>\webapps\examples\WEB-INF\classes قرار دادیم. سپس از طریق URL http://localhost:8080/examples/servlet/nomServlet قابل دسترسی بودند.
  • صفحات JSP در ساختار دایرکتوری <tomcat>\webapps\examples\jsp. سپس از طریق URL http://localhost:8080/examples/jsp/nomPageJSP قابل دسترسی بودند.

ما هرگز توضیح نداده‌ایم که چرا این‌گونه بوده است. پیکربندی سرور Tomcat در یک فایل متنی به نام server.xml که در پوشه <tomcat>\conf قرار دارد، مشخص شده است:

Image

این فایل متنی در واقع یک فایل XML (زبان نشانه‌گذاری eXtended) است. یک سند XML یک سند متنی حاوی تگ‌ها است، درست مانند یک سند HTML. با این حال، در حالی که تگ‌ها در زبان HTML به‌خوبی تعریف شده‌اند، تگ‌ها در زبان XML تعریف نشده‌اند. بنابراین، سند زیر یک سند XML است:

<personne>
    <prenom>Pierre</prenom>
  <nom>Lucas</nom>
  <age>28</age>
</personne>

یک سند XML به سادگی یک سند «علامت‌گذاری‌شده» است که از قوانین نشانه‌گذاری خاصی پیروی می‌کند:

  • متنی که به شکل <xx att1="val1" att2="val2" ....>text</xx> نشانه‌گذاری شده است
  • یک تگ ممکن است به تنهایی وجود داشته باشد و شکل <xx att1="val1" att2="val2" ..../> را به خود بگیرد.

میدان‌های 'atti' ویژگی‌های تگ xx نامیده می‌شوند و میدان‌های 'vali' مقادیر مرتبط با این ویژگی‌ها هستند. برخی از اسناد HTML سندهای معتبر XML نیستند. برای مثال، تگ <br> یک تگ معتبر XML نیست. برای معتبر بودن باید به صورت <br/> نوشته شود تا با این قاعده که تمام تگ‌های XML باید بسته شوند، مطابقت داشته باشد. یک گونه‌ی HTML به نام XHTML ایجاد شده است تا اطمینان حاصل شود که هر سند XHTML یک سند معتبر XML است. برخی از جدیدترین مرورگرهای وب قادر به نمایش فایل‌های XML هستند. بنابراین، اگر سند XML را که در مثال بالا نشان داده شده است، personne.xml بنامیم و آن را با استفاده از IE6 مشاهده کنیم، نمایش زیر را خواهیم داشت:

Image

IE6 تگ‌ها را شناسایی کرده و آن‌ها را رنگ‌آمیزی می‌کند. همچنین ساختار سند را با استفاده از تگ‌ها تشخیص می‌دهد. بنابراین، اگر سند زیر را personne2.xml بنامیم:

<personne><prenom>Pierre</prenom><nom>Lucas</nom><age>28</age></personne>

و آن را با استفاده از IE6 مشاهده کنیم، همان نمایش را خواهیم داشت:

Image

IE6 ساختار و محتوای سند را به درستی تشخیص داده است. تمام نکته سند XML در همین ویژگی نهفته است: بازیابی ساختار و محتوای یک سند XML آسان است. این کار با استفاده از برنامه‌ای به نام پارسر XML انجام می‌شود. اسناد XML در حال تبدیل شدن به استاندارد تبادل اسناد در وب هستند. بیایید ماشینی به نام A را در نظر بگیریم که نیاز دارد یک سند DOC را به ماشینی به نام B ارسال کند. سند DOC از اطلاعاتی که در یک پایگاه داده DB-A موجود است، ساخته می‌شود. ماشین B، از سوی خود، باید سند DOC را در یک پایگاه داده DB-B ذخیره کند. تبادل می‌تواند به شرح زیر انجام شود:

  • ماشین A داده‌ها را از پایگاه داده DB-A بازیابی می‌کند و آن را در یک سند متنی XML جاسازی می‌کند.
  • سند XML از طریق شبکه به ماشین B ارسال می‌شود
  • ماشین B سند دریافتی را با استفاده از یک تجزیه‌گر XML تحلیل می‌کند و هم ساختار و هم داده‌ها را استخراج می‌کند (همان‌طور که در مثال ما IE6 انجام داد). سپس می‌تواند داده‌های دریافتی را در پایگاه داده DB-B ذخیره کند

ما دیگر در مورد زبان XML صحبت نخواهیم کرد، زبانی که شایسته یک کتاب مستقل است.

در اینجا، Tomcat توسط فایل XML server.xml پیکربندی شده است. اگر این را با استفاده از IE6 مشاهده کنیم، یک سند پیچیده به دست می‌آوریم. ما صرفاً بر خطوط زیر تمرکز خواهیم کرد:

Image

آنچه در اینجا برای ما جالب است، تگ <Context ...> است. این تگ برای تعریف برنامه‌های وب استفاده می‌شود. دو ویژگی آن شایان ذکر است:

  • path: این نام برنامه وب است
  • docBase: این پوشه‌ای است که در آن قرار دارد. در اینجا، یک مسیر نسبی است: examples. نسبت به کدام پوشه؟ پاسخ را می‌توان در فایل server.xml در خط زیر نیز یافت:

Image

خط بالا وب سرور را تعریف می‌کند:

  • name: نام وب‌سرور
  • appBase: ریشهٔ درخت دایرکتوری اسناد سروی‌شده. بار دیگر، ما یک نام نسبی داریم: webapps. این نام نسبت به دایرکتوری نصب سرور Tomcat، <tomcat>، نسبی است. بنابراین، این به پوشه <tomcat>\webapps اشاره دارد.

برنامه وب «examples» اسناد خود را در پوشه examples دارد (به docBase در بالا مراجعه کنید). این نام نسبت به ریشه درخت دایرکتوری وب سرور، c.a.d. <tomcat>\webapps، نسبی است. بنابراین، این پوشه <tomcat>\webapps\examples است. بیایید نگاهی دقیق‌تر به این پوشه بیندازیم:

Image

در اینجا پوشه WEB-INF\classes را می‌یابیم که در آن سرولت‌های خود را برای آزمایش ذخیره کرده‌ایم. پوشه WEB-INF حاوی فایلی به نام web.xml است:

Image

این فایل برای پیکربندی برنامه وب examples استفاده می‌شود. در حال حاضر، ما وارد جزئیات این فایل که بسیار پیچیده است، نمی‌شویم. ما فقط روی چند خط زیر تمرکز می‌کنیم:

    <servlet>
      <servlet-name>
          servletToJsp
      </servlet-name>
      <servlet-class>
          servletToJsp
      </servlet-class>
    </servlet>

برچسب <servlet> برای تعریف یک سرولت در یک برنامه وب استفاده می‌شود. باید توجه داشت که برنامه وب مورد نظر examples است. برچسب servlet شامل دو برچسب دیگر است:

  • <servlet-name>servletToJsp</servlet-name>: نام سرولت را تعریف می‌کند
  • <servlet-name>servletToJsp</servlet-name>: نام کلاسی را که باید هنگام درخواست servlet اجرا شود، تعریف می‌کند. در این مثال، servlet و کلاس آن نام یکسانی دارند. این امر اجباری نیست.

چگونه سروِلت servletToJsp توسط یک مرورگر از سرور Tomcat درخواست می‌شود؟

  • مرورگر servlet URL را درخواست می‌کند http://localhost:8080/examples/servlet/servletToJsp
  • تام‌کت مسیر servlet /examples/servlet/servletToJsp را تحلیل می‌کند. بخش اول مسیر، /examples، را به عنوان نام یک برنامه وب تفسیر کرده و فایل پیکربندی آن، server.xml، را برای یافتن محل ذخیره‌سازی اسناد آن برنامه جستجو می‌کند. همان‌طور که قبلاً دیدیم، این در پوشه <tomcat>\webapps\examples قرار دارد.
  • تام‌کت از باقی‌مانده مسیر سرولت برای یافتن آن در داخل برنامه وب examples استفاده می‌کند. این مسیر /servlet/servletToJsp نشان می‌دهد که باید سرولتی به نام servletToJsp را اجرا کند. سپس تامکت فایل پیکربندی web.xml را برای برنامه examples می‌خواند که آن را در <tomcat>\webapps\examples\WEB-INF پیدا می‌کند. در این فایل متوجه می‌شود که سرولِت servletToJsp به کلاس جاوا servletToJsp پیوند داده شده است (به فایل web.xml در بالا مراجعه کنید). سپس این کلاس را در پوشه WEB-INF\classes از برنامه وب examples، c.a.d جستجو خواهد کرد. در <tomcat>\webapps\examples\WEB-INF\classes و آن را اجرا خواهد کرد.

Image

3.3.2. مثال: استقرار وب‌اپلیکیشن «list»

ما دوباره به سراغ سرولتی می‌رویم که قبلاً بررسی کرده‌ایم، که فهرستی از اعداد را به کاربر نمایش می‌داد تا یکی را انتخاب کند. سپس سرولت عدد انتخاب‌شده را تأیید می‌کرد:

Image

همان‌طور که در فیلد Address مرورگر بالا نشان داده شده است، فایل کلاس servlet با نام gener3 نام‌گذاری شده بود. بر اساس توضیحات ارائه‌شده قبلاً:

  • فایل‌های URL و /examples/servlet/gener3 نشان می‌دهند که این یک سرولت به نام gener3 از برنامه وب examples است.
  • در فایل web.xml از برنامه examples، هیچ چیزی یافت نمی‌شود که به سرولتی به نام gener3 اشاره کند. پس تام‌کت چگونه آن را پیدا کرد؟ با بررسی کامل فایل web.xml، نمی‌توانم با اطمینان بگویم… سؤال همچنان باقی است…

ما تصمیم گرفتیم سرولت gener3.class را با نام lstValeurs در یک برنامه وب به نام «liste» که در پوشه E:\data\serge\Servlets\lstValeurs قرار دارد، مستقر کنیم:

Image

ما فایل gener3.class را در پوشه WEB-INF\classes قرار می‌دهیم:

Image

ما با افزودن خطوط زیر به فایل server.xml، در بالا از خطوط تعریف‌کنندهٔ برنامهٔ وب manager، برنامهٔ وب 'liste' را پیکربندی می‌کنیم:

                 <!-- کاربر: lstValeurs -->
                <Context path="/liste" docBase="e:/data/serge/servlets/lstValeurs" />

                <!-- زمینهٔ مدیر تامکت -->
                <Context path="/manager" docBase="manager" debug="0" privileged="true" />
                <!-- زمینهٔ مثال‌های Tomcat -->
                <Context path="/examples" docBase="examples" debug="0" reloadable="true" crossContext="true">
........

خطی که برنامه را تعریف می‌کند، نشان می‌دهد که این برنامه در پوشه e:/data/serge/servlets/lstValeurs قرار دارد. اکنون باید فایل web.xml را برای این برنامه تعریف کنیم. این فایل تنها سروِلِتِ برنامه را تعریف خواهد کرد:

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

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <servlet>
      <servlet-name>lstValeurs</servlet-name>
    <servlet-class>gener3</servlet-class>
  </servlet>
</web-app>

فایل بالا نشان می‌دهد که سرولتی به نام lstValeurs با فایل کلاسی gener3.class مرتبط است. این فایل، web.xml، باید در پوشه WEB-INF اپلیکیشن 'liste' ایجاد و ذخیره شود:

Image

اسکرین‌شات بالا پوشه‌ای به نام src را نشان می‌دهد که فایل منبعی gener3.java در آن قرار گرفته است. این پوشه ممکن است وجود نداشته باشد. این پوشه در این نمایش هیچ کاربردی ندارد. اکنون برای اجرای تست‌ها آماده هستیم:

  • تامکت را متوقف کرده و دوباره راه‌اندازی کنید تا فایل پیکربندی خود server.xml را مجدداً بخواند. در اینجا از ویندوز استفاده می‌کنیم. در یونیکس می‌توانید بدون متوقف کردن تامکت، آن را مجبور به بارگیری مجدد فایل پیکربندی‌اش کنید.
  • با استفاده از یک مرورگر وب، فایل URL را در آدرس http://localhost:8080/liste/servlet/lstValeurs درخواست کنید.

Image

می‌توانیم ببینیم که URL قبلی حاوی کلمه کلیدی servlet است، درست مانند تمام servletهای URL که تاکنون استفاده شده‌اند. ما می‌توانیم با مرتبط کردن سرولت lstValeurs با یک قالب URL (url-pattern) در فایل web.xml برنامه، از آن صرف‌نظر کنیم:

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

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <servlet>
      <servlet-name>lstValeurs</servlet-name>
    <servlet-class>gener3</servlet-class>
  </servlet>
  <servlet-mapping>
      <servlet-name>lstValeurs</servlet-name>
    <url-pattern>/valeurs</url-pattern>
  </servlet-mapping>
</web-app>

در داخل تگ <servlet-mapping>، ما مسیر /values را به سرولتی به نام lstValeurs که در خطوط قبلی تعریف شده است، نگاشت می‌کنیم. ما فایل جدید web.xml و درخواست‌های URL و http://localhost:8080/liste/valeurs را ذخیره می‌کنیم:

Image

3.3.3. راه‌اندازی صفحات عمومی یک برنامه وب

ما همین‌اکنون به استقرار یک برنامهٔ وب متشکل از یک سروِلِت واحد نگاهی انداختیم. یک برنامهٔ وب می‌تواند اجزای زیادی داشته باشد: سروِلِت‌ها، صفحات JSP، فایل‌های HTML، اپلِت‌های جاوا و غیره. این اجزای برنامه در کجا قرار می‌گیرند؟ اگر <application> دایرکتوری برنامه وب باشد که توسط ویژگی docBase برنامه در فایل پیکربندی Tomcat server.xml تعریف شده است، دیدیم که سرولت‌ها در <application>\WEB-INF\classes قرار می‌گیرند. سایر اجزای برنامه می‌توانند در هر نقطه‌ای از ساختار پوشه <application> قرار گیرند، به جز در پوشه WEB-INF. بیایید برنامه‌های JSP و listvaleurs.jsp را که قبلاً بررسی کرده‌ایم، در نظر بگیریم:

Image

این صفحه، JSP، در پوشه <tomcat>\webapps\examples\jsp\perso\listvaleurs ذخیره شده بود. این صفحه می‌تواند جزئی از برنامه «liste» باشد که قبلاً مستقر شده است. بیایید فایل listvaleurs.jsp را مستقیماً در پوشه این برنامه قرار دهیم:

Image

بیایید پیکربندی برنامه liste را در فایل server.xml به یاد بیاوریم:

                <Context path="/liste" docBase="e:/data/serge/servlets/lstValeurs" />

هر فایل URL که با مسیر /liste شروع شود، به‌عنوان بخشی از برنامه liste در نظر گرفته می‌شود و در پوشه مشخص‌شده جستجو خواهد شد. بیایید با استفاده از یک مرورگر وب به فایل URL در http://localhost:8080/liste/listvaleurs.jsp دسترسی پیدا کنیم:

Image

ما در واقع صفحهٔ مورد انتظار JSP را دریافت کرده‌ایم.

3.3.4. پارامترهای راه‌اندازی سرولت

ما دیدیم که یک سرولت از طریق فایل <application>\WEB-INF\web.xml پیکربندی می‌شود، که در آن <application> پوشه وب‌اپلیکیشن مربوطه است. امکان درج پارامترهای راه‌اندازی سرولت در این فایل وجود دارد. بیایید به سرولت lstValeurs از برنامه وب liste بازگردیم که فایل پیکربندی آن به شرح زیر بود:

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

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <servlet>
      <servlet-name>lstValeurs</servlet-name>
    <servlet-class>gener3</servlet-class>
  </servlet>
  <servlet-mapping>
      <servlet-name>lstValeurs</servlet-name>
    <url-pattern>/valeurs</url-pattern>
  </servlet-mapping>
</web-app>

کلاس مرتبط با سرولت، کلاس gener3 است. کد منبع این کلاس شامل تعریف چندین مقدار ثابت است:

public class gener3 extends HttpServlet{
        // عنوان صفحه
        private final String title="Génération d'un formulaire";
         // پایگاه داده مقادیر لیست
        private final String DSNValeurs="odbc-valeurs";
        private final String admDbValeurs="admDbValeurs";
        private final String mdpDbValeurs="mdpDbValeurs";

بیایید معنای چهار ثابت تعریف‌شده در بالا را به یاد بیاوریم:

title
عنوان سند HTML تولید شده توسط سرولت
DSNValeurs
نام DSN پایگاه داده ODBC که سرولت از آن داده‌ها را بازیابی می‌کند
admDbValeurs
نام کاربری یک کاربر با دسترسی خواندن به پایگاه داده مذکور
mdpDbvaleurs
رمز عبور آنها

اگر مدیر پایگاه داده DSNValeurs رمز عبور کاربر admDbValeurs را تغییر دهد، باید کد منبع سرولت اصلاح و مجدداً کامپایل شود. این کار چندان عملی نیست. فایل پیکربندی servlet web.xml با اجازه دادن به تعریف پارامترهای инициализация servlet با استفاده از تگ <init-param>، یک جایگزین ارائه می‌دهد:

    <init-param>
        <param-name>...</param-name>
        <param-value>...</param-value>
    </init-param>
<param-name>
به شما امکان می‌دهد نام پارامتر را تعریف کنید
<param-value>
مقدار مرتبط با پارامتر قبلی را تعریف می‌کند

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

[Servlet].getServletConfig()
یک متد از کلاس Servlet که کلاس HttpServlet—مورد استفاده در برنامه‌نویسی وب—از آن مشتق شده است. یک شیء ServletConfig را بازمی‌گرداند که دسترسی به پارامترهای پیکربندی سرولت را فراهم می‌کند.
[ServletConfig].getInitParameter("paramètre")
یک متد از کلاس ServletConfig که مقدار پارامتر راه‌اندازی «parameter» را برمی‌گرداند

ما برنامه liste را با فایل جدید web.xml زیر پیکربندی می‌کنیم:

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

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <servlet>
      <servlet-name>lstValeurs</servlet-name>
    <servlet-class>gener3</servlet-class>
  </servlet>
    <servlet>
      <servlet-name>lstValeurs2</servlet-name>
    <servlet-class>gener5</servlet-class>
    <init-param>
        <param-name>title</param-name>
      <param-value>Génération d'un formulaire</param-value>
    </init-param>
    <init-param>
        <param-name>DSNValeurs</param-name>
      <param-value>odbc-valeurs</param-value>
    </init-param>
    <init-param>
        <param-name>admDbValeurs</param-name>
      <param-value>admDbValeurs</param-value>
    </init-param>
    <init-param>
        <param-name>mdpDbValeurs</param-name>
      <param-value>mdpDbValeurs</param-value>
    </init-param>   
  </servlet>
  <servlet-mapping>
      <servlet-name>lstValeurs</servlet-name>
    <url-pattern>/valeurs</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
      <servlet-name>lstValeurs2</servlet-name>
    <url-pattern>/valeurs2</url-pattern>
  </servlet-mapping>
</web-app>

در برنامه liste، ما یک سروِلت دوم به نام lstValeurs2 را تعریف می‌کنیم که به فایل کلاسی gener5 متصل است. فایل اخیر در <application>\WEB-INF\classes قرار داده شده است:

Image

سرولِت lstValeurs2 دارای چهار پارامتر راه‌اندازی است: title, DSNValeurs, admDbValeurs, mdpDbValeurs. علاوه بر این، نام مستعار /valeurs2 برای سرویس‌لت با استفاده از تگ <servlet-mapping> تعریف شده است. در نتیجه، سرولِت lstValeurs2 در اپلیکیشن liste از طریق URL و http://localhost:8080/liste/valeurs2 قابل دسترسی خواهد بود.

کد منبع سرولت به منظور بازیابی پارامترهای راه‌اندازی آن به شرح زیر تغییر یافته است:

public class gener5 extends HttpServlet{
    // عنوان صفحه
    private String title=null;
    // پایگاه داده‌های مقادیر لیست
    private String DSNValeurs=null;
    private String admDbValeurs=null;
    private String mdpDbValeurs=null;
...............

         // ابتدای‌سازی سرویس‌لت
        public void init(){
             //بازیابی پارامترهای راه‌اندازی سرولت
            ServletConfig config=getServletConfig();
            title=config.getInitParameter("title");
            DSNValeurs=config.getInitParameter("DSNValeurs");
            admDbValeurs=config.getInitParameter("admDbValeurs");
            mdpDbValeurs=config.getInitParameter("mdpDbValeurs");

             //آیا همه پارامترها بازیابی شده‌اند؟
            if(title==null || DSNValeurs==null || admDbValeurs==null
                 || mdpDbValeurs==null){
                msgErreur="Configuration incorrecte";
                return;
            }

             //پر کردن آرایه مقادیر از پایگاه داده ODBC
             //با نام DSN: DSNvaleurs
...............

برای آزمایش servlet، باید Tomcat را مجدداً راه‌اندازی کنید تا فایل پیکربندی جدید web.xml را برای برنامه liste تشخیص دهد. با استفاده از یک مرورگر وب، URL را از servlet http://localhost:8080/liste/valeurs2 درخواست کنید:

Image

اگر هر یک از پارامترهای راه‌اندازی مورد نیاز سرولت در فایل web.xml وجود نداشته باشد، صفحه زیر نمایش داده می‌شود:

Image

3.3.5. پارامترهای راه‌اندازی برای یک برنامه وب

در مثال قبلی، تنها سروِلت lstValeurs2 به پارامترهای title، DSNValeurs، admDbValeurs و mdpDbValeurs دسترسی دارد. قابل تصور است که یک سرولت دیگر در همان برنامه، liste، ممکن است به داده‌هایی از همان پایگاه داده که توسط سرولت lstValeurs2 استفاده می‌شود، نیاز داشته باشد. در این صورت، پارامترهای DSNValeurs، admDbValeurs و mdpDbValeurs باید در بخش پیکربندی فایل web.xml برای سرولتی که به تازگی ایجاد شده است، مجدداً تعریف شوند. راه حل دیگر این است که پارامترهای مشترک چند سرولت را به جای سطح سرولت، در سطح برنامه کاربردی تعریف کنید. فایل جدید web.xml برای برنامه کاربردی به شکل زیر در می‌آید:

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

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>

  <context-param>
      <param-name>DSNValeurs</param-name>
    <param-value>odbc-valeurs</param-value>
  </context-param>
  <context-param>
      <param-name>admDbValeurs</param-name>
    <param-value>admDbValeurs</param-value>
  </context-param>
  <context-param>
      <param-name>mdpDbValeurs</param-name>
    <param-value>mdpDbValeurs</param-value>
  </context-param>   

    <servlet>
      <servlet-name>lstValeurs</servlet-name>
    <servlet-class>gener3</servlet-class>
  </servlet>
    <servlet>
      <servlet-name>lstValeurs3</servlet-name>
    <servlet-class>gener6</servlet-class>
    <init-param>
        <param-name>title</param-name>
      <param-value>Génération d'un formulaire</param-value>
    </init-param>
  </servlet>
  <servlet-mapping>
      <servlet-name>lstValeurs</servlet-name>
    <url-pattern>/valeurs</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
      <servlet-name>lstValeurs3</servlet-name>
    <url-pattern>/valeurs3</url-pattern>
  </servlet-mapping>
</web-app>

سرولیت جدید lstValeurs3 نامیده می‌شود؛ این سرولیت به فایل کلاسی gener6 متصل است و با نام مستعار /valeurs3 (نقشه‌برداری سرولیت) مرتبط شده است. پارامتر title تنها پارامتری است که در تعریف سرولت حفظ شده است. سایر پارامترها در پیکربندی برنامه در تگ‌های <context-param> قرار داده شده‌اند. این تگ برای تعریف اطلاعات خاص برنامه به کار می‌رود، نه برای یک سروِلت یا صفحهٔ خاص JSP. سروِلت جاوا چگونه به این پارامترها، که اغلب پارامترهای زمینه (context parameters) نامیده می‌شوند، دسترسی پیدا می‌کند؟ روش‌های موجود برای بازیابی اطلاعات زمینه بسیار شبیه روش‌های بازیابی پارامترهای راه‌اندازی اختصاصی سروِلت هستند:

[Servlet].getServletContext()
یک متد از کلاس Servlet که کلاس HttpServlet—مورد استفاده در برنامه‌نویسی وب—از آن مشتق شده است. یک شیء ServletContext را بازمی‌گرداند که دسترسی به پارامترهای پیکربندی برنامه را فراهم می‌کند
[ServletContext].getInitParameter("paramètre")
یک متد از کلاس ServletContext که مقدار پارامتر راه‌اندازی «parameter» را برمی‌گرداند

کلاس gener6.java تنها تغییرات زیر را در کد جاوا کلاس gener5.java که قبلاً استفاده می‌شد، معرفی می‌کند:

             //پارامترهای راه‌اندازی servlet بازیابی می‌شوند
            ServletConfig config=getServletConfig();
            title=config.getInitParameter("title");

            ServletContext context=getServletContext();
            DSNValeurs=context.getInitParameter("DSNValeurs");
            admDbValeurs=context.getInitParameter("admDbValeurs");
            mdpDbValeurs=context.getInitParameter("mdpDbValeurs");

             //آیا همه پارامترها بازیابی شده‌اند؟
            if(title==null || DSNValeurs==null || admDbValeurs==null
                 || mdpDbValeurs==null){
                msgErreur="Configuration incorrecte";
                return;
            }

             //آرایه مقادیر را از پایگاه داده پر می‌کند ODBC
             // با نام DSN: DSNvaleurs
...............

پارامتر مخصوص سرولت title از طریق یک شیء ServletConfig به دست می‌آید. سه پارامتر دیگر که در سطح برنامه کاربردی تعریف شده‌اند، از طریق یک شیء ServletContext به دست می‌آیند. ما این کلاس را کامپایل کرده و مانند سایر کلاس‌ها آن را در <application>\WEB-INF\classes قرار می‌دهیم:

Image

ما تام‌کت را مجدداً راه‌اندازی می‌کنیم تا فایل جدید web.xml برنامه را شناسایی کرده و URL و http://localhost:8080/liste/valeurs3 را درخواست کند:

Image

3.3.6. پارامترهای راه‌اندازی برای یک صفحه JSP

ما دیدیم که چگونه پارامترهای راه‌اندازی را برای یک سرولت یا یک برنامه وب تعریف کنیم. آیا می‌توانیم همین کار را برای یک صفحه JSP انجام دهیم؟ بیایید به ابتدای کد صفحه listvaleurs.jsp که قبلاً بررسی کرده‌ایم بازگردیم:

<%@ page import="java.sql.*, java.util.*" %>

<%!
         // متغیرهای جهانی برنامه
         // عنوان صفحه
        private final String title="Génération d'un formulaire";
         // پایگاه داده مقادیر لیست
        private final String DSNValeurs="odbc-valeurs";
        private final String admDbValeurs="admDbValeurs";
        private final String mdpDbValeurs="mdpDbValeurs";
.........

ثوابت title، DSNValeurs، admDbValeurs و mdpDbValeurs در فایل web.xml برنامه تعریف شده‌اند. ثوابت DSNValeurs، admDbValeurs و mdpDbValeurs اکنون در سطح برنامه تعریف شده‌اند، بنابراین می‌توانیم فرض کنیم که صفحه‌ای با شناسه JSP که بخشی از این برنامه است، به آن‌ها دسترسی خواهد داشت. در واقع همین‌طور است. ما می‌دانیم که صفحه JSP به یک سرولت تبدیل خواهد شد. این سرولت از طریق متد getServletContext() به زمینه دسترسی خواهد داشت. مورد ثابت title پیچیده‌تر است. ما آن را به جای سطح برنامه، در سطح سرولت تعریف کرده‌ایم، به شرح زیر:

    <servlet>
      <servlet-name>lstValeurs3</servlet-name>
    <servlet-class>gener6</servlet-class>
    <init-param>
        <param-name>title</param-name>
      <param-value>Génération d'un formulaire</param-value>
    </init-param>
  </servlet>

برای صفحه JSP، سینتکس قبلی دیگر مناسب نیست زیرا مفهوم فایل کلاس دیگر کاربرد ندارد. با این حال، سینتکس پیکربندی برای یک صفحه JSP بسیار شبیه به سینتکس یک سرولت است. به شرح زیر است:

    <servlet>
      <servlet-name>JSPlstValeurs</servlet-name>
    <jsp-file>/listvaleurs2.jsp</jsp-file>
...
  </servlet>

در واقع، صفحه‌ای با نام JSP به‌عنوان یک سروِلت در نظر گرفته می‌شود که یک نام (servlet-name) به آن اختصاص داده شده است. به جای مرتبط کردن یک فایل کلاس با این سروِلت، فایل منبعی از صفحه JSP که باید اجرا شود (jsp-file) با آن مرتبط می‌گردد. بدین ترتیب، چند خط قبل یک سرولت به نام JSPlstvaleurs را تعریف می‌کنند که با صفحه JSP /listvaleurs2.jsp مرتبط است. مسیر /listvaleurs2.jsp نسبت به ریشهٔ برنامه کاربردی است. بنابراین، در مورد برنامه کاربردی ما liste، فایل listvaleurs2.jsp در پوشه docBase (به server.xml مراجعه کنید) در داخل برنامه liste قرار خواهد داشت:

Image

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

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

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
  <context-param>
      <param-name>DSNValeurs</param-name>
    <param-value>odbc-valeurs</param-value>
  </context-param>
  <context-param>
      <param-name>admDbValeurs</param-name>
    <param-value>admDbValeurs</param-value>
  </context-param>
  <context-param>
      <param-name>mdpDbValeurs</param-name>
    <param-value>mdpDbValeurs</param-value>
  </context-param>   
.......      
    <servlet>
      <servlet-name>JSPlstValeurs</servlet-name>
    <jsp-file>/listvaleurs2.jsp</jsp-file>
    <init-param>
        <param-name>JSPtitle</param-name>
      <param-value>Génération d'un formulaire</param-value>
    </init-param>
  </servlet>
..........
  <servlet-mapping>
      <servlet-name>JSPlstValeurs</servlet-name>
    <url-pattern>/jspvaleurs</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
........
</web-app>

صفحه JSP listvaleurs2.jsp در ریشهٔ برنامهٔ liste قرار دارد و با نام سرولت JSPlstValeurs مرتبط است (نام-سرولِت)، که خود با نام مستعار /jspvaleurs (نقشه‌برداری-سرولِت) مرتبط است. بنابراین، صفحه ما JSP از طریق URL و http://localhost:8080/liste/jspvaleurs قابل دسترسی خواهد بود.

صفحه اولیه JSP (listvaleurs.jsp) به listvaleurs2.jsp تغییر یافته و چهار پارامتر راه‌اندازی خود را از متد jspInit() دریافت می‌کند:

<%!
         // متغیرهای جهانی برنامه
         // عنوان صفحه
        private String title=null;
        // پایگاه داده مقادیر لیست
        private String DSNValeurs=null;
        private String admDbValeurs=null;
        private String mdpDbValeurs=null;
         // مقادیر لیست
        private String[] valeurs=null;
         // پیام خطا
        private String msgErreur=null;

         // ابتدای صفحه JSP – فقط یک بار اجرا می‌شود
        public void jspInit(){

             //– بازیابی پارامترهای راه‌اندازی سرولت
      ServletConfig config=getServletConfig();
            title=config.getInitParameter("JSPtitle");
      ServletContext context=getServletContext();
            DSNValeurs=context.getInitParameter("DSNValeurs");
            admDbValeurs=context.getInitParameter("admDbValeurs");
            mdpDbValeurs=context.getInitParameter("mdpDbValeurs");

             //آیا همه پارامترها بازیابی شده‌اند؟
            if(title==null || DSNValeurs==null || admDbValeurs==null
                 || mdpDbValeurs==null){
                msgErreur="Configuration incorrecte";
                return;
            }

             // آرایه مقادیر را از پایگاه داده‌ای به نام ODBC پر می‌کند
             //با نام DSN: DSNvaleurs
..............

صفحه JSP پارامترهای راه‌اندازی خود را به همان شیوه‌ای که سرویس‌لت‌ها دریافت می‌کنند، بازیابی می‌کند. فایل قبلی در دایرکتوری ریشهٔ برنامهٔ وب liste ذخیره می‌شود:

Image

سرور Tomcat مجدداً راه‌اندازی می‌شود تا مجبور شود فایل پیکربندی جدید برنامه web.xml را دوباره بخواند. سپس می‌توانیم صفحات URL و http://localhost:8080/liste/jspvaleurs را درخواست کنیم:

Image

3.3.7. همکاری بین سرولت‌ها و صفحات در یک برنامه وب

وقتی یک کلاینت درخواستی را به یک وب‌سرور ارسال می‌کند، پاسخ ممکن است توسط چندین سرولت و صفحه JSP تولید شود. تا کنون، پاسخ توسط یک سرولت یا صفحه واحد JSP تولید می‌شد. ما دیده‌ایم که صفحه JSP خوانایی ساختار سند تولید شده HTML را بهبود می‌بخشد. با این حال، این صفحه عموماً حاوی مقدار زیادی کد جاوا است. این امر را می‌توان با قرار دادن بهبود بخشید

  • کد جاوا را که کد پاسخ HTML را تولید نمی‌کند، به یک یا چند سرولت منتقل کنیم
  • به صفحات JSP، کد تولیدکنندهٔ اسناد مختلف HTML که در پاسخ به کلاینت ارسال می‌شوند

این باید به بهبود جداسازی بین کد جاوا و کد HTML کمک کند. ما این ساختار جدید را بر روی برنامه liste خود اعمال خواهیم کرد: یک سروِلِت جاوا lstValeurs4 مسئول خواندن مقادیر از پایگاه داده در هنگام راه‌اندازی و سپس تحلیل درخواست‌های مشتری خواهد بود. بسته به نتیجه این تحلیل، درخواست مشتری یا به یک صفحه خطا (erreur.jsp) هدایت می‌شود یا به صفحه‌ای که لیست اعداد را نمایش می‌دهد (liste.jsp). بنابراین، برنامه کاربردی liste شامل یک سرولت و دو صفحه، JSP خواهد بود.

یک سرولت چگونه می‌تواند درخواستی را که از یک کلاینت دریافت کرده است به سرولت دیگر یا به یک صفحه JSP ارسال کند؟ ما از روش‌های زیر استفاده خواهیم کرد:

[ServletContext].getRequestDispatcher(
String url)
یک متد از کلاس ServletContext که یک شیء RequestDispatcher را برمی‌گرداند. پارامتر url نام servlet-ی است که می‌خواهیم درخواست کلاینت را به آن ارسال کنیم. این درخواست تنها می‌تواند در همان برنامه ارسال شود. بنابراین، پارامتر url مسیری است که نسبت به ساختار دایرکتوری وب آن برنامه قرار دارد.
[RequestDispatcher].forward
(ServletRequest request,
 ServletResponse response)
یک متد از رابط RequestDispatcher که درخواست کلاینت request و شیء response – که باید برای تولید پاسخ استفاده شود – را به URL قبلی ارسال می‌کند.
[ServletRequest].setAttribute(String nom, Object obj)
هنگامی که یک سرولت یا صفحه JSP یک درخواست را به سرولت یا صفحه دیگری JSP ارسال می‌کند، عموماً باید اطلاعاتی فراتر از صرفاً درخواست کلاینت را به مورد دوم منتقل کند—اطلاعاتی که از پردازش خودِ درخواست به دست آمده است. متد setAttribute از کلاس ServletRequest اجازه می‌دهد تا ویژگی‌ها به شیء request سمت مشتری به شکلی شبیه به یک فرهنگ لغت از (ویژگی، مقدار)، که در آن attribut نام ویژگی و valeur هر شیئی است که نماینده مقدار آن می‌باشد.
[ServletRequest].getAttribute(
String attribut)
برای بازیابی مقادیر ویژگی‌های یک درخواست استفاده می‌شود. این متد توسط سرولت یا صفحه‌ی JSP که درخواست به آن فوروارد شده است، برای دریافت اطلاعاتی که به آن اضافه شده است، استفاده خواهد شد.

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

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

<!DOCTYPE web-app
    PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
    "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
  <context-param>
      <param-name>DSNValeurs</param-name>
    <param-value>odbc-valeurs</param-value>
  </context-param>
  <context-param>
      <param-name>admDbValeurs</param-name>
    <param-value>admDbValeurs</param-value>
  </context-param>
  <context-param>
      <param-name>mdpDbValeurs</param-name>
    <param-value>mdpDbValeurs</param-value>
  </context-param>   

............
    <servlet>
      <servlet-name>lstValeurs4</servlet-name>
    <servlet-class>gener7</servlet-class>
    <init-param>
        <param-name>title</param-name>
      <param-value>Génération d'un formulaire</param-value>
    </init-param>
    <init-param>    
        <param-name>JSPerreur</param-name>
      <param-value>/erreur.jsp</param-value>
    </init-param>      
    <init-param>      
        <param-name>JSPliste</param-name>
      <param-value>/liste.jsp</param-value>
    </init-param>      
    <init-param>      
        <param-name>URLservlet</param-name>
      <param-value>/liste/valeurs4</param-value>
    </init-param>      
  </servlet>
...........
  <servlet-mapping>
      <servlet-name>lstValeurs4</servlet-name>
    <url-pattern>/valeurs4</url-pattern>
  </servlet-mapping>
.......
</web-app>

سرولت lstValeurs4 دارای چهار پارامتر راه‌اندازی اختصاصی خواهد بود:

title
عنوان سند HTML که باید تولید شود
JSPerreur
URL مربوط به صفحه خطا JSP
JSPliste
URL از صفحه JSP که فهرست اعداد را نمایش می‌دهد
URLservlet
URL مرتبط با ویژگی «action» فرم نمایش‌داده‌شده در صفحه JSPliste. این URL متعلق به سرولتی خواهد بود که lstValeurs4 است.

سرولت دارای نام مستعار /valeurs4 (servlet-mapping) خواهد بود و بنابراین از طریق URL و http://localhost:8080/liste/valeurs4 قابل دسترسی است. این به فایل کلاسی gener7.java پیوند خورده است، که کد منبع کامل آن به شرح زیر است:

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.sql.*;
import java.util.*;

public class gener7 extends HttpServlet{
        // عنوان صفحه
        private String title=null;
        // پایگاه دادههای مقادیر لیست
        private String DSNValeurs=null;
        private String admDbValeurs=null;
        private String mdpDbValeurs=null;
         //صفحات نمایش JSP
        private String JSPerreur=null;
        private String JSPliste=null;
        // سرولت URL
        private String URLservlet=null;
        // مقادیر لیست
        private String[] valeurs=null;
         // پیام خطا
        private String msgErreur=null;

        // -----------------------------------------------------------------
        // GET
        public void doGet(HttpServletRequest request,HttpServletResponse response)
                    throws IOException, ServletException{

            // 'msgErreur,title' در ویژگیهای درخواست تنظیم شده است
            request.setAttribute("msgErreur",msgErreur);
            request.setAttribute("title",title);
            request.setAttribute("URLservlet",URLservlet);

             //آیا هنگام بارگذاری سرویسلت خطایی رخ داد؟
            if(msgErreur!=null){
                 // ارسال به صفحه خطا JSP
                getServletContext().getRequestDispatcher(JSPerreur).forward(request,response);
                 //پایان
                return;
            }

             // هیچ خطایی رخ نداده است
             // فهرست مقادیر در ویژگیهای درخواست قرار میگیرد
            request.setAttribute("valeurs",valeurs);

             // انتخاب کاربر را در صورت وجود بازیابی کنید
            String choix=request.getParameter("cmbValeurs");
            if(choix==null) choix="";
            request.setAttribute("choix",choix);

             //کنترل به صفحه JSP که فهرست را نمایش میدهد، منتقل میشود
            getServletContext().getRequestDispatcher(JSPliste).forward(request,response);
            //پایان
            return;
        }//GET

        // -----------------------------------------------------------------
        //POST
        public void doPost(HttpServletRequest request,HttpServletResponse response)
                    throws IOException, ServletException{

             //به GET هدایت میکند
            doGet(request,response);
        }//POST

        // -----------------------------------------------------------------
         // ابتدای سرویسلت
        public void init(){

             // بازیابی پارامترهای راهاندازی سرولت
            ServletConfig config=getServletConfig();
            title=config.getInitParameter("title");
            JSPerreur=config.getInitParameter("JSPerreur");
            JSPliste=config.getInitParameter("JSPliste");
            URLservlet=config.getInitParameter("URLservlet");

            ServletContext context=getServletContext();
            DSNValeurs=context.getInitParameter("DSNValeurs");
            admDbValeurs=context.getInitParameter("admDbValeurs");
            mdpDbValeurs=context.getInitParameter("mdpDbValeurs");


             //آیا همه پارامترها بازیابی شدهاند؟
            if(title==null || DSNValeurs==null || admDbValeurs==null
                 || mdpDbValeurs==null || JSPerreur==null || JSPliste==null || URLservlet==null){
                msgErreur="Configuration incorrecte";
                return;
            }

             //پر کردن آرایه مقادیر از یک پایگاه داده ODBC
             //با نام DSN: DSNvaleurs
            Connection connexion=null;
            Statement st=null;
            ResultSet rs=null;
            try{
                 //به پایگاه داده ODBC متصل میشود
                Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
                connexion=DriverManager.getConnection("jdbc:odbc:"+DSNValeurs,admDbValeurs,mdpDbValeurs);
                 //شیء عبارت
                st=connexion.createStatement();
                 //اجرای یک پرسوجوی SELECT برای بازیابی مقادیر
                rs=st.executeQuery("select valeur from Tvaleurs");
                // مقادیر بازیابی شده و در یک آرایه پویا قرار میگیرند
                ArrayList lstValeurs=new ArrayList();
                while(rs.next()){
                    //مقدار به لیست اضافه میشود
                    lstValeurs.add(rs.getString("valeur"));
                }//در حالی که
                 //تبدیل لیست به آرایه
                valeurs=new String[lstValeurs.size()];
                for (int i=0;i<lstValeurs.size();i++){
                    valeurs[i]=(String)lstValeurs.get(i);
                }
            }catch(Exception ex){
                 //مشکل
                msgErreur=ex.getMessage();
            }finally{
                try{rs.close();}catch(Exception ex){}
                try{st.close();}catch(Exception ex){}
                try{connexion.close();}catch(Exception ex){}
            }//تلاش
        }//مقداردهی اولیه
    }//کلاس

ویژگی جدید این کلاس این است که در صورت بروز خطا، درخواست مشتری به صفحه JSPerreur ارسال می‌شود؛ در غیر این صورت، به صفحه JSPliste ارسال می‌شود. این کلاس خود پاسخ را تولید نمی‌کند. این کار توسط صفحات JSP، JSPerreur و JSPliste انجام می‌شود. قبلاً، سرولت ویژگی‌هایی (setAttribute) به درخواست کلاینت اضافه می‌کرد:

  • پیام خطا msgErreur در صورت بروز خطا در صفحه JSPerreur
  • مقادیر (valeurs)) برای نمایش، مقدار انتخاب‌شده (choix) توسط کاربر، عنوان (title) فرم، URL (URLservlet) ویژگی action فرم برای صفحه JSPliste

این کلاس کامپایل شده و در کلاس‌های برنامه قرار می‌گیرد:

Image

صفحه JSP که پیغام خطا را نمایش می‌دهد، به شرح زیر پیکربندی شده است:

    <servlet>
      <servlet-name>JSPerreur</servlet-name>
    <jsp-file>/erreur.jsp</jsp-file>
    <init-param>
        <param-name>mainServlet</param-name>
      <param-value>/valeurs4</param-value>
    </init-param>
  </servlet>
.........
<servlet-mapping>
      <servlet-name>JSPerreur</servlet-name>
    <url-pattern>/JSPerreur</url-pattern>
  </servlet-mapping>

فایل JSP که با صفحه خطا مرتبط است، erreur.jsp نامیده می‌شود و در ریشهٔ برنامه قرار دارد:

Image

این فایل دارای نام مستعار /JSPerreur است که از طریق URL قابل دسترسی است. http://localhost:8080/liste/JSPerreur این پارامتر راه‌اندازی mainServlet را دارد که مقدار آن نام مستعار سرولت اصلی توصیف‌شده در بالا است. توجه داشته باشید که این نام مستعار نسبت به ریشهٔ برنامهٔ liste, است؛ در غیر این صورت، /liste/valeurs4. خواهد بود. کد صفحهٔ erreur.jsp به شرح زیر است:

<%
     // کد از _jspService
   //پارامتر راه‌اندازی mainServlet را بازیابی می‌کند
  String servletListValeurs=config.getInitParameter("mainServlet");
   // ویژگی msgErreur را بازیابی کنید
  String msgErreur=(String)request.getAttribute("msgErreur");
   //آیا این ویژگی معتبر است؟
  if(msgErreur!=null){
%>
       <!-- کد HTML -->
    <html>
        <head>
          <title>Erreur</title>
      </head>
      <body>
          <h3>Application indisponible (<%= msgErreur %>)</h3>
      </body>
    </html>
<%
    } else { // ویژگی نامعتبر msgErreur – بازگشت به سرولت اصلی
%>
    <jsp:forward page="<%= servletListValeurs %>" />  
<%    
  }
%>  

این صفحه معمولاً باید توسط سرولتی که قبلاً اجرا شده است فراخوانی شود، که باید ویژگی msgErreur را به آن منتقل کند. با این حال، هیچ چیز مانع از فراخوانی مستقیم آن نمی‌شود اگر ویژگی URL آن مشخص باشد. همچنین، اگر مشخص شود که ویژگی msgErreur وجود ندارد، درخواست به سرولت اصلی ارسال می‌شود. در اینجا، یک تگ مخصوص صفحات JSP با سینتکس زیر استفاده می‌شود:

<jsp:forward page="URL" />

که در آن URL نمایانگر URL سرولتی است که درخواست مشتری به آن ارسال می‌شود. اگر ویژگی msgErreur موجود باشد، صفحه خطا نمایش داده می‌شود.

صفحه JSP که فهرست اعداد را نمایش می‌دهد، به شرح زیر پیکربندی شده است:

    <servlet>
      <servlet-name>JSPliste</servlet-name>
    <jsp-file>/liste.jsp</jsp-file>
    <init-param>
        <param-name>mainServlet</param-name>
      <param-value>/valeurs4</param-value>
    </init-param>
.........
  <servlet-mapping>
      <servlet-name>JSPliste</servlet-name>
    <url-pattern>/JSPliste</url-pattern>
  </servlet-mapping>

فایل JSP که با صفحه خطا مرتبط است، liste.jsp نامیده می‌شود و در ریشهٔ برنامه قرار دارد:

Image

سرولت دارای نام مستعار /JSPliste است که آن را از طریق http://localhost:8080/liste/JSPliste URL قابل دسترسی می‌سازد. این سرولت دارای یک پارامتر راه‌اندازی به نام mainServlet است که مقدار آن نام مستعار سرولت اصلی است. کد صفحه liste.jsp به شرح زیر است:

   <%-- صفحه نمایش‌دهندهٔ فهرست مقادیر --%>
  <%
       // کد از jspService
       // بازیابی پارامتر راه‌اندازی
      String servletListValeurs=config.getInitParameter("mainServlet");

     // بازیابی ویژگی‌های درخواست از سرولت اصلی
    String title=(String) request.getAttribute("title");
    String[] valeurs=(String[]) request.getAttribute("valeurs");
    String choix=(String) request.getAttribute("choix");
    String URLservlet=(String) request.getAttribute("URLservlet");

     //ویژگی‌های معتبر؟
    if(title==null || valeurs==null || choix==null){
         //یک ویژگی نامعتبر وجود دارد – کنترل به سرولت منتقل می‌شود
   %>
   <jsp:forward page="<%= servletListValeurs %>" />
    <%
    }//اگر
  %>

  <%--کد HTML --%>  
      <html>
        <head>
          <title><%= title %></title>
      </head>
      <body>
          <h3>Choisissez une valeur</h3>
          <form method="POST" action="<%= URLservlet %>">
            <select name="cmbValeurs">
              <%
                 //نمایش پویا مقادیر
                          String selected="";
                          for (int i=0;i<valeurs.length;i++){
                              if(valeurs[i].equals(choix)) selected="selected"; else selected="";
                              out.println("<option "+selected+">"+valeurs[i]+"</option>");
                          }//برای
            %>
          </select>
          <input type="submit" value="Envoyer">
        </form>
                <%
             //آیا مقداری انتخاب شده بود؟
                    if(! choix.equals("")){
                //انتخاب کاربر نمایش داده می‌شود
                %>        
                <hr>Vous avez choisi le nombre<h2><%= choix %></h2>
                <%        
                    }//اگر
                %>        
      </body>
    </html>

این صفحه به همان شیوه صفحه erreur.jsp عمل می‌کند. این صفحه معمولاً باید توسط سرویس‌لِت /liste/valeurs4 فراخوانی شود و ویژگی‌های title، valeurs و choix را دریافت کند. اگر هر یک از این پارامترها وجود نداشته باشد، کنترل به servlet URLservlet (/liste/valeurs4) منتقل می‌شود. اگر همه پارامترها موجود باشند، لیست اعداد به همراه عددی که کاربر انتخاب کرده است، در صورت انتخاب، نمایش داده می‌شود.

اگر متد URL سرویس‌لِت اصلی فراخوانی شود، نتیجه زیر به دست می‌آید:

Image

با کد منبع زیر (View/Source):

<html>
    <head>
      <title>Génération d'un formulaire</title>
  </head>
  <body>
      <h3>Choisissez une valeur</h3>
      <form method="POST" action="/liste/valeurs4">
        <select name="cmbValeurs">
          <option >0</option>
        <option >1</option>
        <option >2</option>
        <option >3</option>
        <option >4</option>
        <option >6</option>
        <option >5</option>
        <option >7</option>
        <option >8</option>
        <option >9</option>
      </select>
      <input type="submit" value="Envoyer">
    </form>

  </body>
</html>

این سند HTMl توسط صفحه JSP liste.jsp تولید شده است. می‌توانیم ببینیم که ویژگی‌های title، valeurs و URLservlet در واقع بازیابی شده‌اند.

برای نتیجه‌گیری در مورد همکاری servlet/page JSP، مشاهده می‌کنیم که صفحات JSP در اینجا بسیار کوتاه هستند و هیچ کد Java‌ای ندارند که مستقیماً در ایجاد پاسخ HTML مشارکت نکند. بنابراین ساختار اسناد تولیدشده واضح‌تر دیده می‌شود.

3.4. چرخه عمر سرولت‌ها و صفحات JSP

3.4.1. چرخه عمر

در اینجا ما بر چرخه عمر سرولت‌ها تمرکز داریم. چرخه عمر صفحات JSP نیز از همین امر پیروی می‌کند. بیایید در نظر بگیریم که یک سرولت برای اولین بار فراخوانی می‌شود. سپس یک نمونه از این کلاس توسط وب سرور ایجاد و در حافظه بارگذاری می‌شود. سپس این نمونه به درخواست رسیدگی می‌کند. پس از انجام این کار، سرولت از حافظه تخلیه نمی‌شود. این سرولت در حافظه باقی می‌ماند تا به درخواست‌های بعدی رسیدگی کند و در نتیجه زمان پاسخ‌دهی سرور را بهینه‌سازی نماید. زمانی که یک دوره زمانی نسبتاً طولانی بدون رسیدگی به هیچ‌یک از درخواست‌های جدید سپری شود، این سرولت از حافظه تخلیه خواهد شد. این بازه زمانی معمولاً در داخل سرور وب قابل پیکربندی است.

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

تمام رشته‌های فوق‌الذکر متغیرهای نمونه سرولت را به اشتراک می‌گذارند. ممکن است برای جلوگیری از خرابی داده‌های سرولت، همگام‌سازی رشته‌ها ضروری باشد. بعداً به این موضوع باز خواهیم گشت.

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

public void init() throws ServletException{
}

برای صفحه‌ای با شناسه JSP، این متد است


  public void jspInit(){
  }

که اجرا می‌شود. در اینجا مثالی از صفحه‌ای با شناسه JSP که از روش jspInit استفاده می‌کند، آورده شده است:

<html>
  <head>
    <title>Compteur synchronisé</title>
  </head>
  <body>
    Compteur= <%= getCompteur() %>
  </body>
</html>

<%!
   // متغیرها و متدهای سراسری صفحه JSP

   // متغیر نمونه
  int compteur;

   // متد برای افزایش شمارنده  
  public int getCompteur(){
     // شمارنده افزایش می‌یابد
    int myCompteur=compteur;
    myCompteur++;
    compteur=myCompteur;
     //آن را بازمی‌گرداند
    return compteur;
  }

   // متدی که هنگام بارگذاری اولیه صفحه اجرا می‌شود
  public void jspInit(){
     // ابتدایی‌سازی شمارنده
    compteur=100;
  }
%>

صفحهٔ قبلی، JSP، یک شمارنده را در jspInit روی ۱۰۰ مقداردهی اولیه می‌کند. هر درخواست بعدی به سرولت، مقدار شمارنده را افزایش داده و سپس آن را نمایش می‌دهد:

اولین بار:

Image

بار دوم:

Image

از موارد فوق مشخص است که بین دو درخواست، سرولت بارگذاری نشده است؛ در غیر این صورت، شمارنده در درخواست دوم روی 101 تنظیم می‌شد. هنگامی که سرولت بارگذاری می‌شود، متد

public void destroy(){
}

اجرا می‌شود، در صورتی که وجود داشته باشد. برای صفحات JSP، این متد است


  public void jspDestroy(){
  }

در این متدها، می‌توانید برای مثال، اتصالات پایگاه داده را که در متدهای متناظر init باز شده‌اند، ببندید.

3.4.2. همگام‌سازی درخواست‌ها با یک سرولت

بیایید به صفحهٔ قبلی، JSP، بازگردیم که یک شمارنده را افزایش داده و آن را به کلاینت وب بازمی‌گرداند. فرض کنید دو درخواست همزمان وجود دارد. سپس دو نخ برای اجرای آن‌ها ایجاد می‌شوند؛ این نخ‌ها از همان نمونهٔ سرولت استفاده می‌کنند و در نتیجه از همان شمارنده بهره می‌برند. بیایید کد افزایش‌دهندهٔ شمارنده را به یاد بیاوریم:


  public int getCompteur(){
     // افزایش شمارنده
    int myCompteur=compteur;
    myCompteur++;
    compteur=myCompteur;
     //آن را بازگردانید
    return compteur;
  }

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

 
  1. در زمان T1، نخ TH1 اجرا می‌شود. این نخ شمارنده را (=145) از myCompteur می‌خواند، سپس متوقف شده و پردازنده را از دست می‌دهد. بنابراین فرصت نکرد تا شمارنده myCompteur را افزایش دهد و مقدار جدید را به compteur کپی کند.
  2. در زمان T2، نخ TH2 اجرا می‌شود. این نخ شمارنده را (=145) از myCompteur می‌خواند، سپس متوقف شده و پردازنده را از دست می‌دهد. شایان ذکر است که این دو نخ متغیرهای myCompteur متفاوتی دارند. آنها تنها متغیرهای نمونه را به اشتراک می‌گذارند، یعنی متغیرهایی که برای متدها سراسری هستند.
  3. در زمان T3، نخ TH1 کنترل را بازپس می‌گیرد و به پایان می‌رسد. بنابراین مقدار 146 را به فراخوان خود بازمی‌گرداند.
  4. در زمان T4، نخ TH2 کنترل را بازپس می‌گیرد و متوقف می‌شود. این نخ نیز عدد 146 را به مشتری خود بازمی‌گرداند، در حالی که باید عدد 147 را بازگردانده بود.

این یک مسئله همگام‌سازی نخ است. هنگامی که TH1 می‌خواهد شمارنده را افزایش دهد، هر نخ دیگری که بخواهد همین کار را انجام دهد باید از انجام این کار منع شود. برای برجسته کردن این مشکل، صفحه JSP را به شرح زیر بازنویسی می‌کنیم:

<html>
  <head>
    <title>Compteur synchronisé</title>
  </head>
  <body>
    Compteur= <%= getCompteur() %>
  </body>
</html>

<%!
   // متغیرها و متدهای سراسری صفحه JSP

   // متغیر نمونه
  int compteur;

   // متد برای افزایش شمارنده  
  public int getCompteur(){
     // خواندن شمارنده
    int myCompteur=compteur;
     // وقفه به مدت ۱۰ ثانیه
    try{
      Thread.sleep(10000);
    }catch (Exception ignored){}
     // افزایش شمارنده
    compteur=myCompteur+1;
     // آن را بازمی‌گرداند
    return compteur;
  }

   // متدی که هنگام بارگذاری اولیه صفحه اجرا می‌شود
  public void jspInit(){
     // ابتدایی‌سازی شمارنده
    compteur=100;
  }
%>

در اینجا، ما نخ را ۱۰ ثانیه پس از خواندن شمارنده متوقف کرده‌ایم. بنابراین باید پردازنده را واگذار کند و به نخ دیگری اجازه دهد شمارنده‌ای را بخواند که هنوز افزایش نیافته است. وقتی با استفاده از مرورگر درخواست‌هایی ارسال می‌کنیم، به جز انتظار ۱۰ ثانیه‌ای قبل از دریافت نتیجه، هیچ تفاوتی را متوجه نمی‌شویم.

Image

اکنون، اگر دو پنجره مرورگر باز کنیم و دو درخواست را با فاصله زمانی کافی از هم ارسال کنیم:

Image

Image

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

برنامه URL N

که

URL، URLِ servlet شمارشگر است

N تعداد درخواست‌هایی است که باید به این سرولت ارسال شود.

در اینجا نتایج حاصل از ۵ درخواست آورده شده است که به وضوح مشکل همگام‌سازی ضعیف رشته‌ها را نشان می‌دهد: همه آنها همان مقدار شمارنده را برمی‌گردانند.


DOS>java clientCompteurJSP http://localhost:8080/examples/jsp/perso/compteur/compteur2.jsp 5
Compteur=121
Compteur=121
Compteur=121
Compteur=121
Compteur=121

کد کلاینت جاوا به شرح زیر است.

import java.net.*;
import java.util.regex.*;
import java.io.*;

public class clientCompteurJSP {

    public static void main(String[] params){

         // داده‌ها
        String syntaxe="Syntaxe : pg URL nbAppels";

         //بررسی پارامتر
        if(params.length!=2){
            System.err.println(syntaxe);
            System.exit(1);
        }//اگر
         // URL
        URL urlCompteur=null;
        try{
            urlCompteur=new URL(params[0]);
            String query=urlCompteur.getQuery();
            if(query!=null) throw new Exception();
        }catch (Exception ex){
            System.err.println(syntaxe);
            System.err.println("URL ["+params[0]+" incorrecte");
            System.exit(2);
        }//try-catch
         //تعداد فراخوانی‌ها
        int nbAppels=0;
        try{
            nbAppels=Integer.parseInt(params[1]);
            if(nbAppels<=0) throw new Exception();
        }catch(Exception ex){
            System.err.println(syntaxe);
            System.err.println("Nombre d'appels ["+params[1]+" incorrect");
            System.exit(3);
        }//try-catch

         //پارامترها صحیح هستند – می‌توان به URL متصل شد
        try{
            getCompteurs(urlCompteur,nbAppels);
        }catch(Exception ex){
            System.err.println(syntaxe);
            System.err.println("L'erreur suivante s'est produite : "+ex.getMessage());
            System.exit(4);
        }//try-catch
    }//main

    private static void getCompteurs (URL urlCompteur, int nbAppels)
            throws Exception {
         //فراخوانی nbAppels را از URL و urlCompteur انجام می‌دهد
         //هر بار مقدار شمارنده بازگردانده‌شده توسط سرور وب را نمایش می‌دهد


         //اطلاعات مورد نیاز برای اتصال به سرور مالیاتی را از urlCompteur بازیابی می‌کند
        String path=urlCompteur.getPath();
        if(path.equals("")) path="/";
        String host=urlCompteur.getHost();
        int port=urlCompteur.getPort();
        if(port==-1) port=urlCompteur.getDefaultPort();

         //به URL انجام می‌شوند
        Socket[] clients=new Socket[nbAppels];
        for(int i=0;i<nbAppels;i++){
             // ما به سرور متصل می‌شویم
            clients[i]=new Socket(host,port);
             //ایجاد یک جریان نوشتن به سمت سرور
            PrintWriter OUT=new PrintWriter(clients[i].getOutputStream(),true);
             //درخواست URL – ارسال سربرگ‌ها HTTP
            OUT.println("GET " + path + " HTTP/1.1");
            OUT.println("Host: " + host + ":" + port);
            OUT.println("Connection: close");
            OUT.println("");
        }//برای

         // داده‌های محلی
        String réponse=null;                        // پاسخ سرور
         //الگوی مورد نظر در پاسخ سرور HTML
        Pattern modèleCompteur=Pattern.compile("^\\s*Compteur= (\\d+)");
         //الگوی یک پاسخ صحیح
        Pattern réponseOK=Pattern.compile("^.*? 200 OK");
         // نتیجهٔ مقایسه با الگو
        Matcher résultat=null;

        for(int i=0;i<nbAppels;i++){
             //هر کلاینت پاسخ ارسال‌شده توسط سرور را می‌خواند

             //جریان‌های ورودی-خروجی کلاینت ایجاد می‌شوند TCP
            BufferedReader IN=new BufferedReader(new InputStreamReader(clients[i].getInputStream()));

             // اولین خط پاسخ خوانده می‌شود
            réponse=IN.readLine();
             //خط HTTP با الگوی پاسخ صحیح مقایسه می‌شود
            résultat=réponseOK.matcher(réponse);
            if(! résultat.find()){
                 // مشکلی در URL وجود دارد
                throw new Exception("Client n° " + i + " - Le serveur a répondu : URL ["+ urlCompteur + "] inconnue");
            }//اگر

             // ما پاسخ را تا انتهای سربرگ‌ها می‌خوانیم
            while((réponse=IN.readLine())!=null && ! réponse.equals("")){
            }//در حالی که

             //وقتی سربرگ‌ها تمام شدند HTTP – به کد HTML می‌رویم
             //برای بازیابی قرائت کنتور
            boolean compteurTrouvé=false;
            while((réponse=IN.readLine())!=null){
                 // ما خط را با قالب شمارنده مقایسه می‌کنیم
                if(! compteurTrouvé){
                    résultat=modèleCompteur.matcher(réponse);
                    if(résultat.find()){
                         // متر پیدا شد
                        System.out.println("Compteur="+résultat.group(1));
                        compteurTrouvé=true;
                    }//اگر
                }//اگر
            }//در حالی که

             // تمام شد
            clients[i].close();
        }//برای
    }//getCompteurs

}//کلاس

بیایید کد بالا را توضیح دهیم:

  • برنامه دو پارامتر را می‌پذیرد:
    • URL برای صفحه JSP کنتور
    • تعداد مشتریانی که باید برای این URL ایجاد شوند
  • بنابراین برنامه با بررسی اعتبار پارامترها آغاز می‌شود: اینکه واقعاً دو پارامتر وجود دارد، اینکه اولی از نظر دستوری مشابه یک URL است و دومی یک عدد صحیح بزرگتر از 0 است. برای بررسی اینکه آیا URL از نظر دستوری صحیح است، ما از کلاس URL و سازنده آن URL (String) استفاده می‌کنیم، که یک شیء URL را از یک رشته مانند http://istia.univ-angers.fr ایجاد می‌کند. اگر رشته از نظر نحوی یک URL معتبر نباشد، یک استثنا پرتاب می‌شود. این امکان را به ما می‌دهد تا اعتبار پارامتر اول را بررسی کنیم.
  • پس از بررسی پارامترها، کنترل به رویه getCompteurs واگذار می‌شود. این رویه مشتریان nbAppels را ایجاد می‌کند که همگی به‌طور همزمان (یا تقریباً همزمان) به URL urlCompteur متصل می‌شوند.
  • پورت و ماشینی که کلاینت‌ها باید به آن متصل شوند از URL و urlCompteur: [URL].getHost() نام ماشین را بازیابی می‌کند، در حالی که [URL].getPort() پورت را بازیابی می‌کند.
  • یک حلقهٔ اولیه به هر کلاینت اجازه می‌دهد:
    • اتصال به سرور وب
    • درخواست URL urlCompteur

در این حلقه، کلاینت منتظر پاسخ سرور نمی‌ماند. این به آن دلیل است که می‌خواهیم سرور درخواست‌ها را تقریباً همزمان دریافت کند.

  • یک حلقه دوم به هر کلاینت اجازه می‌دهد پاسخ ارسال‌شده توسط سرور را دریافت و پردازش کند. این پردازش شامل یافتن خطی در پاسخ است که حاوی مقدار شمارنده است و نمایش آن.

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


<html>
  <head>
    <title>Compteur synchronisé</title>
  </head>
  <body>
    Compteur= <%= getCompteur() %>
  </body>
</html>

<%!
   // متغیرهای سراسری و متدهای صفحه JSP
  
   // متغیرهای نمونه
  int compteur;
  Object verrou=new Object();
  
   // متد برای افزایش شمارنده  
  public int getCompteur(){
  
     //بخش بحرانی همگام‌سازی شده است
    synchronized(verrou){
       // خواندن شمارنده
      int myCompteur=compteur;
       // وقف برای ۱۰ ثانیه
      try{
        Thread.sleep(10000);
      }catch (Exception ignored){}
       // افزایش شمارنده
      compteur=myCompteur+1;
    }//همگام‌سازی شد
     // بازنشانی
    return compteur;
  }//getCompteur

   // متدی که هنگام بارگذاری اولیه صفحه اجرا می‌شود
  public void jspInit(){
     // شروع شمارنده
    compteur=100;
  }
%>

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

dos>c:\perl\bin\perl.exe client2.pl http://localhost:8080/examples/jsp/perso/compteur/compteur3.jsp 5
    Compteur= 104
    Compteur= 106
    Compteur= 105
    Compteur= 107
    Compteur= 108

مستندات بیان می‌کند که وب‌سرور ممکن است گاهی اوقات چندین نمونه از یک سرولت یکسان ایجاد کند. در این صورت، همگام‌سازی قبلی دیگر کار نمی‌کند زیرا متغیر verrou محلی به یک نمونه است و بنابراین برای نمونه‌های دیگر قابل دسترس نیست. همین موضوع در مورد متغیر compteur نیز صدق می‌کند. برای اینکه آن‌ها را در همه نمونه‌ها جهانی کنید، بنویسید:


//متغیر کلاس
  static int compteur;
  static Object verrou=new Object();

بقیه کد بدون تغییر باقی می‌ماند.