5. Chu trình sống của các Servlet

Mọi Servlet đều có chu trình sống như sau:

  • Server nạp và khởi tạo Servlet
  • Servlet xử lý các yêu cầu của các Client
  • Server huỷ bỏ Servlet khi không còn cần thiết.

Như trên đã phân tích, mọi Servlet đều có một chu trình sống nhất định. Nó được khởi tạo (nạp về), xử lý các yêu cầu của Client và sau khi hoàn tất các dịch vụ thì bị Server huỷ bỏ (Hình 6.4).

5.1 Khởi động Servlet

Việc khởi động một Servlet được thực hiện mặc định bởi HttpServlet. Để khởi động một Servlet riêng, ta phải viết đè phương thức init().

public class MyServlet …{

public void init() throws ServletException{

// Khởi tạo các giá trị ban đầu

}

. . .

}

Khi một Server nạp xong một Servlet thì nó gọi init() để bắt đầu Servlet đó.

Lưu ý: Server chỉ gọi init() một lần khi nạp Servlet và sau đó sẽ không gọi lại nữa, trừ khi phải nạp lại nó. Server không thể nạp lại nếu Servlet đó chưa bị huỷ bỏ bởi lời gọi destroy(). Phương thức init() có thể được nạp chồng theo mẫu

public void init(ServletConfig config) throws ServletException

như đã nêu ở trên.

5.2 Tương tác với các Client

Lớp HttpServlet xử lý các yêu cầu của Client thông qua các dịch vụ của nó. Các  phương thức trong HttpServlet xử lý yêu cầu của Client đều có hai đối số:

  • Một là đối tượng của HttpServletRequest, bao gồm cả dữ liệu từ Client. Nó cho phép nhận được các tham số mà Client gửi đến như một phần của các yêu cầu, thông qua các phương thức getParameterName(), getParameterValue() để xác định tên gọi và giá trị của các tham số.
  • Hai là đối tượng của HttpServletResponse, bao gồm cả dữ liệu hồi đáp cho Client. HttpServletResponse cung cấp hai phương thức để trả lại kết quả cho Client. Phương thức getWriter() ghi dữ liệu dưới dạng văn bản còn getOutputStream() cho lại dữ liệu dạng nhị phân.

Ngoài ra, để xử lý được các yêu cầu của HTTP gửi đến cho một Servlet thì phải mở rộng lớp HttpServlet và viết đè các phương thức xử lý các yêu cầu như doGet(), doPost().

5.3 Huỷ bỏ Servlet

Sau khi nạp các Servlet và xử lý chúng xong thì cần phải dọn dẹp hệ thống, loại bỏ những Servlet không còn được sử dụng tiếp. Phương thức destroy() của lớp HttpServlet được sử dụng để loại bỏ một đối tượng Servlet khi nó không còn cần thiết. Để loại bỏ một tài nguyên nào đó cụ thể trong một Servlet riêng thì phải viết đè phương thức destroy().

Server sẽ gọi destroy() để huỷ một Servlet,  sau khi nó kết thúc tất cả các dịch vụ theo yêu cầu. Ví dụ sau đây mô tả một Servlet mở một kết nối với CSDL thông qua phương thức init() và sau đó nó sẽ bị phá huỷ bởi phương thức destroy().

public class DBServlet extends HttpServlet{

Connection connection = null;

public void init() throws ServletException{

// Mở một kết nối với CSDL

try{

databaseUrl = getInitParameter(“databaseUrl”);

// Đọc tên người sử dụng và mật khẩu

connection = DriverManager.getConnection(

userName, password);

}catch(Exception e){

throw new UnavailableException(this,

“Không mở được CSDL”);

}

}

// …

public void destroy(){

// Đóng các kết nối để dọn dẹp bộ nhớ

connection.close();

connection = null;

}

}

Server sẽ gọi destroy() sau khi tất cả các dịch vụ đã được kết thúc.

6. Xử lý các yêu cầu

Nhiệm vụ của các Servlet là nhận các yêu cầu, xử lý chúng và trả lời cho các Client.

6.1 Truy tìm thông tin

Để xây dựng thành công một WebSite, ta cần có những thông tin từ Server, nơi thực hiện các Servlet. Servlet có những phương thức sau giúp cho việc nhận được những thông tin yêu cầu.

  • int port = req.getServerPort(): Xác định cổng trao đổi tin của máy chủ.
  • public String ServletConfig.getInitParameter(String name): Cho lại giá trị ban đầu của tham số có tên name.
  • public Enumeration ServletConfig.getInitParameterName(): Xác định dãy liệt kê tên gọi của tất cả các tham số khởi đầu của Servlet như là đối tượng của Enumeration. Lớp GenericServlet sử dụng phương thức này để truy cập đến các Servlet.

Ví dụ Servlet sau in ra tên và giá trị ban đầu của tất cả các tham số.

import java.io.*;

import javax.servlet.*;

import java.util.*;

public class ReadServlet extends GenericServlet{

public void service(ServletRequest re, ServletResponse resp)

throws IOException{

resp.setContentType(“text/plain”);

PrintWriter out = resp.getWriter();

out.println(“Tham so khoi dau la:”);

Enumeration eno = getInitParameterNames();

while(eno.hasMoreElements()){

String nm = (String)eno.nextElement();

out.println(nm + ” : ” + getInitParameter(nm));

}

}

}

6.2 Gửi thông tin

Các Servlet cần phải trả lời cho Client về các thông tin dưới dạng HTML bao gồm:

  • Các mã hiện trạng. Mã hiện trạng là một số nguyên, nó mô tả tình trạng của việc hồi đáp thành công hay thất bại. Phần lớn các mã này đã được định nghĩa trong lớp HttpServletResponse như ở bảng 6.1.
  • Số các phần đầu (tiêu đề) của HTTP. Phương thức setHeader() của lớp HttpServletResponse  cho phép đặt lại các giá trị cho các tiêu đề.
  • Nội dung hồi đáp. Nội dung hồi đáp dưới dạng một trang HTML.

Bảng 6.1

Tên gợi nhớ Mã số Thông báo
SC_OK 200 Ok (tốt)
SC_NO_CONTENT 204 Không có nội dung
SC_MOVED_PERMANENTLY 301 Đã chuyển đi vĩnh viễn
SC_MOVED_TEMPORARLY 302 Đã chuyển đi tạm thời
SC_NOT_FOUND 404 Không tìm thấy
SC_UNAUTHORIZED 401 Không được phép

Ví dụ Servlet gửi cho Client một trang ngẫu nhiên trong danh sách các trang Web của nó.

// RandomSend.java

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

import java.util.*;

public class RandomSend extends HttpServlet{

Vector st = new Vector();

Random rd = new Random();

public void init(ServletConfig config) throws ServletException{

super.init(config);

st.addElement(“http://www.yahoo.com”);

st.addElement(“http://www.hotmail.com”);

st.addElement(“http://www.zednet.com”);

st.addElement(“http://www.java.sun.com”);

}

public void doGet(HttpServletRequest re, HttpServletResponse res)

throws IOExceptio{

res.setContentType(“text/html”);

PrintWriter out = resp.getWrite();

int siteIndex = Math.abs(rd.nextInt()) % st.size();

String st = (String) st.elementAt(siteIndex);

res.setStatus(res.SC_MOVED_TEMPORARLY);

res.setHeadr(“Location”, st);

}

}

Khi một Client dùng HTML để gửi đi một yêu cầu thì nó có thể sẽ gửi đi một số các tiêu đề (Header).

Việc đọc các tiêu đề là tương đối đơn giản, có thể sử dụng hàm getHaeder() của HttpServletRequest để nhận được các tiêu đề (header) yêu cầu. Sau đây là các tiêu đề phổ dụng trong các yêu cầu.

  • Accept: Các kiểu tệp được trình duyệt chấp nhận, như các tệp image/gif, image/jpeg, hay application/msword, v.v.
  • Accept-Charset: Tập các ký tự mà trình duyệt mong đợi.
  • Accept-Encoding: Các kiểu mã hoá dữ liệu (ví dụ gzip) mà trình duyệt biết cách giải mã.
  • Accept-Language: Ngôn ngữ mà trình duyệt mong đợi.
  • Authorization: Thông tin về giấy phép, uỷ quyền.
  • Connection: Khẳng định có kết nối thường xuyên hay không? Nếu một Servlet đặt là Keep-Alive thì nó có thể tận dụng được những ưu thế của cơ chế kết nối thường xuyên.
  • Content-Length: Số dữ liệu được đưa vào yêu cầu.
  • Cookie: Một trong các tiêu đề quan trọng nhất. Một Cookie là một xâu (dãy ký tự) được gửi tới cho một Client để bắt đầu một phiên làm việc.
  • Host: Máy chủ và cổng được chỉ ra trong URL.
  • User-Agent: Loại trình duyệt.

Các tiêu đề trên là tuỳ chọn, trừ Content-Length là được yêu cầu chỉ đối với yêu cầu POST.

Để tìm một tiêu đề cụ thể, trước tiên ta cần sử dụng getHaederNames() để có được một dãy (đối tượng của Enumeration) tất cả các tiêu đề nhận được từ một yêu cầu cụ thể, sau đó tìm lần lượt tiêu đề đó.

Một vấn đề nữa cần biết khi phải tìm các tiêu đề của một yêu cầu là phải biết thông tin về phương thức chính được sử dụng.  Phương thức getMethod() sẽ cho ta biết về phương thức chính của mỗi yêu cầu (thường là GET, POST, hoặc cũng có thể là HEAD, PUT và DELETE). Phương thức getRequestURI() sẽ cho lại URI (một phần của URL, phần đứng sau tên của địa chỉ máy chủ với cổng kết nối và trước phần dữ liệu mẫu).

Ví dụ Xây dựng một Servlet để đọc và hiển thị một bảng các tiêu đề và nội dung của một yêu cầu.

// ShowRequestHeaders.java

package myservlet;

import java.io.*;

import javax.servlet.*;

import javax.servlet.http.*;

import java.util.*;

/* Chỉ ra tất cả các tiêu đề của một yêu cầu cụ thể dưới dạng một bảng bao gồm tiêu đề và nội dung

*/

public class ShowRequestHeaders extends HttpServlet {

public void doGet(HttpServletRequest request,

HttpServletResponse response)

throws ServletException, IOException {

response.setContentType(“text/html”);

PrintWriter out = response.getWriter();

String title = “Servlet Example: Showing Request Headers”;

out.println(“<!DOCTYPE HTML PUBLIC \”-//W3C//DTD HTML 4.0 Transitional//EN\”>”

+  “<HTML>\n” + “<HEAD><TITLE>” + title +

“</TITLE></HEAD>\n” + “<BODY BGCOLOR=\”#FDF5E6\”>\n” +

“<H1 ALIGN=CENTER>” + title + “</H1>\n” +

“<B>Request Method: </B>” +

request.getMethod() + “<BR>\n” +

“<B>Request URI: </B>” +

request.getRequestURI() + “<BR>\n” +

“<B>Request Protocol: </B>” +

request.getProtocol() + “<BR><BR>\n” +

“<TABLE BORDER=1 ALIGN=CENTER>\n” +

“<TR BGCOLOR=\”#FFAD00\”>\n” +

“<TH>Header Name<TH>Header Value”);

Enumeration headerNames = request.getHeaderNames();

while(headerNames.hasMoreElements()) {

String headerName = (String)headerNames.nextElement();

out.println(“<TR><TD>” + headerName);

out.println(”    <TD>” + request.getHeader(headerName));

}

out.println(“</TABLE>\n</BODY></HTML>”);

}

public void doPost(HttpServletRequest request,

HttpServletResponse response)

throws ServletException, IOException {

doGet(request, response);

}

}

Sau đây là hai kết quả hiển thị đối với hai loại trình duyệt Netscape và Internet Explore.

Hiển thị các tiêu đề yêu cầu với Netscape

Hiển thị các tiêu đề yêu cầu với IE

6.3 Xử lý các dữ liệu mẫu

Khi sử dụng máy tìm kiếm Web Search Engine,  bạn có thể truy cập dạng

http://host/path?user=Marty+Hall&orgin=bwi&dest=lax

Phần tin sau dấu ‘?’ (user=Marty+Hall&orgin=bwi&dest=lax) được xem như là dữ liệu mẫu, đây là cách chung thường được sử dụng để chương trình Server nhận dữ liệu vào từ trang Web.

Việc tách những thông tin cần thiết từ dữ liệu dạng trên là một phần việc mà các chương trình CGI thường làm.

  • Trước tiên, đọc dữ liệu vào cho các tham số yêu cầu của GET hoặc POST
  • Sau đó, chặt ra từng cặp ở dấu ‘&’ rồi lại tách tiếp để xác định tên của các tham biến (phần bên trái dấu ‘=’) và giá trị của những tham biến đó (phần bên phải của dấu ‘=’).
  • Thực hiện giải mã URL theo những giá trị đó.

Lưu ý: Tất cả các chữ cái, chữ số được gửi đi và không thay đổi, nhưng dấu cách ‘ ‘ được chuyển thành dấu ‘+’. Những ký tự khác được chuyển thành %XX, trong đó XX là giá trị (hex) của ký tự ASCII (khác với chữ cái, chữ số). Ví dụ, nếu bạn muốn nhập vào dữ liệu mẫu “~hall, ~gates” vào trường văn bản có tên “user” ở dạng HTML thì dữ liệu gửi đi phải là “user=%7Ehall%2C+%7Egates”.

Những công việc nhàm chán trên khi lập trình với CGI đã được Java hỗ trợ để xử lý các dữ liệu mẫu một cách tự động. Đơn giản là bạn gọi getParameter() của HttpServletRequest để nhận được tên gọi của tham số như là một đối số. Dữ liệu mẫu được gửi đi trong GET và POST là giống hệt nhau. Khi một tham số có nhiều hơn một giá trị thì gọi getParameterValues() thay vì gọi getParameter(), kết quả trả lại là mảng các xâu. Tương tự, sử dụng getParameterName() để xác định tập các tên gọi của các tham số, kết quả của nó là danh sách các tên gọi thuộc lớp Enumeration.

Ví dụ Chương trình đọc và hiển thị các tham số được gửi đến cho Servlet qua GET hay POST.

// ShowParameters.java

package hall;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
/** Hiển thị tất cả các tham số được gửi đến cho Servlet qua GET hoặc POST.   Các tham số đặc biệt có thể  *  không có giá trị hoặc có nhiều giá trị.

*/

public class ShowParameters extends HttpServlet {

public void doGet(HttpServletRequest request,

HttpServletResponse response)

throws ServletException, IOException {

response.setContentType(“text/html”);

PrintWriter out = response.getWriter();

String title = “Reading All Request Parameters”;

out.println(“<!DOCTYPE HTML PUBLIC \”-//W3C//DTD HTML 4.0

Transitional//EN\”>” +

“<BODY BGCOLOR=\”#FDF5E6\”>\n” +

“<H1 ALIGN=CENTER>” + title + “</H1>\n” +

“<TABLE BORDER=1 ALIGN=CENTER>\n” +

“<TR BGCOLOR=\”#FFAD00\”>\n” +

“<TH>Parameter Name<TH>Parameter Value(s)”);

Enumeration paramNames = request.getParameterNames();

while(paramNames.hasMoreElements()) {

String paramName = (String)paramNames.nextElement();

out.println(“<TR><TD>” + paramName + “\n<TD>”);

String[] paramValues = request.getParameterValues(paramName);

if (paramValues.length == 1) {

String paramValue = paramValues[0];

if (paramValue.length() == 0)

out.print(“<I>No Value</I>”);

else

out.print(paramValue);

} else {

out.println(“<UL>”);

for(int i=0; i<paramValues.length; i++) {

out.println(“<LI>” + paramValues[i]);

}

out.println(“</UL>”);

}

}

out.println(“</TABLE>\n</BODY></HTML>”);

}

public void doPost(HttpServletRequest request,

HttpServletResponse response)

throws ServletException, IOException {

doGet(request, response);

}

}

Để chương trình Servlet trên nhận được các tham số thì cần phải có một trang HTML gửi chúng. Trang HTML (PostForm.html) sau đây sử dụng POST để gửi dữ liệu (theo các mẫu forms có các mục đầu vào), thể hiện các giá trị mà Servlet nhận được theo cả hai phương thức doGet()doPost().

Tệp PostForm.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
  <TITLE>A Sample FORM using POST</TITLE>
</HEAD>
 
<BODY BGCOLOR="#FDF5E6">
<H1 ALIGN="CENTER">A Sample FORM using POST</H1>
 
<FORM ACTION="/servlet/hall.ShowParameters"
      METHOD="POST">
  Item Number:
  <INPUT><BR>
  Quantity:
  <INPUT><BR>
  Price Each:
  <INPUT VALUE="$"><BR>
  <HR>
  First Name:
  <INPUT><BR>
  Last Name:
  <INPUT><BR>
  Middle Initial:
  <INPUT><BR>
  Shipping Address:
  <TEXTAREA ROWS=3 COLS=40></TEXTAREA><BR>
  Credit Card:<BR>
    <INPUT
                     VALUE="Visa">Visa<BR>
    <INPUT
                     VALUE="Master Card">Master Card<BR>
    <INPUT
                     VALUE="Amex">American Express<BR>
    <INPUT
                     VALUE="Discover">Discover<BR>
    <INPUT
                     VALUE="Java SmartCard">Java SmartCard<BR>
  Credit Card Number:
  <INPUT><BR>
  Repeat Credit Card Number:
  <INPUT><BR><BR>
  <CENTER>
    <INPUT VALUE="Submit Order">
  </CENTER>
</FORM>
</BODY>
</HTML>



Các bài liên quan:
Tìm hiểu Servlet - Phần 1

ình 6.5 Hiển thị các tiêu đề yêu cầu với Netscape