<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 120,  comments - 19,  trackbacks - 0
    一、Servlet和JSP概述
    ? 作 者 : 仙人掌工作室
    ??
    ??
    ?    1.1 Java Servlet及其特點
    ??
    ?    Servlet是Java技術對CGI編程的回答。Servlet程序在服務器端運行,動態(tài)地生成Web頁面。與傳統(tǒng)的CGI和許多其他類似CGI的技術相比,Java Servlet具有更高的效率,更容易使用,功能更強大,具有更好的可移植性,更節(jié)省投資(更重要的是, Servlet程序員收入要比Perl程序員高:-):
    ??
    ? 高效。
    ??
    ? 在傳統(tǒng)的CGI中,每個請求都要啟動一個新的進程,如果CGI程序本身的執(zhí)行時間較短,啟動進程所需要的開銷很可能反而超過實際執(zhí)行時間。而在Servlet中,每個請求由一個輕量級的Java線程處理(而不是重量級的操作系統(tǒng)進程)。
    ? 在傳統(tǒng)CGI中,如果有N個并發(fā)的對同一CGI程序的請求,則該CGI程序的代碼在內存中重復裝載了N次;而對于Servlet,處理請求的是N個線程,只需要一份Servlet類代碼。在性能優(yōu)化方面,Servlet也比CGI有著更多的選擇,比如緩沖以前的計算結果,保持數(shù)據(jù)庫連接的活動,等等。
    ??
    ??
    ? 方便。
    ??
    ? Servlet提供了大量的實用工具例程,例如自動地解析和解碼HTML表單數(shù)據(jù)、讀取和設置HTTP頭、處理Cookie、跟蹤會話狀態(tài)等。
    ??
    ??
    ? 功能強大。
    ??
    ? 在Servlet中,許多使用傳統(tǒng)CGI程序很難完成的任務都可以輕松地完成。例如,Servlet能夠直接和Web服務器交互,而普通的CGI程序不能。Servlet還能夠在各個程序之間共享數(shù)據(jù),使得數(shù)據(jù)庫連接池之類的功能很容易實現(xiàn)。
    ??
    ??
    ? 可移植性好。
    ??
    ? Servlet用Java編寫,Servlet API具有完善的標準。因此,為I-Planet Enterprise Server寫的Servlet無需任何實質上的改動即可移植到Apache、Microsoft IIS或者WebStar。幾乎所有的主流服務器都直接或通過插件支持Servlet。
    ??
    ??
    ? 節(jié)省投資。
    ??
    ? 不僅有許多廉價甚至免費的Web服務器可供個人或小規(guī)模網(wǎng)站使用,而且對于現(xiàn)有的服務器,如果它不支持Servlet的話,要加上這部分功能也往往是免費的(或只需要極少的投資)。
    ?    1.2 JSP及其特點
    ??
    ?    JavaServer Pages(JSP)是一種實現(xiàn)普通靜態(tài)HTML和動態(tài)HTML混合編碼的技術,有關JSP基礎概念的說明請參見《JSP技術簡介 》。
    ??
    ?    許多由CGI程序生成的頁面大部分仍舊是靜態(tài)HTML,動態(tài)內容只在頁面中有限的幾個部分出現(xiàn)。但是包括Servlet在內的大多數(shù)CGI技術及其變種,總是通過程序生成整個頁面。JSP使得我們可以分別創(chuàng)建這兩個部分。例如,下面就是一個簡單的JSP頁面:
    ? <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
    ? <HTML>
    ? <HEAD><TITLE>歡迎訪問網(wǎng)上商店</TITLE></HEAD>
    ? <BODY>
    ? <H1>歡迎</H1>
    ? <SMALL>歡迎,
    ? <!-- 首次訪問的用戶名字為"New User" -->
    ? <% out.println(Utils.getUserNameFromCookie(request)); %>
    ? 要設置帳號信息,請點擊
    ? <A HREF="Account-Settings.html">這里</A></SMALL>
    ? <P>
    ? 頁面的其余內容。.
    ? </BODY></HTML>
    ??
    ??
    ??
    ?    下面是JSP和其他類似或相關技術的一個簡單比較:
    ??
    ? JSP和Active Server Pages(ASP)相比
    ??
    ? Microsoft的ASP是一種和JSP類似的技術。JSP和ASP相比具有兩方面的優(yōu)點。首先,動態(tài)部分用Java編寫,而不是VB Script或其他Microsoft語言,不僅功能更強大而且更易于使用。第二,JSP應用可以移植到其他操作系統(tǒng)和非Microsoft的Web服務器上。
    ??
    ??
    ? JSP和純Servlet相比
    ??
    ? JSP并沒有增加任何本質上不能用Servlet實現(xiàn)的功能。但是,在JSP中編寫靜態(tài)HTML更加方便,不必再用 println語句來輸出每一行HTML代碼。更重要的是,借助內容和外觀的分離,頁面制作中不同性質的任務可以方便地分開:比如,由頁面設計專家進行HTML設計,同時留出供Servlet程序員插入動態(tài)內容的空間。
    ??
    ??
    ? JSP和服務器端包含(Server-Side Include,SSI)相比
    ??
    ? SSI是一種受到廣泛支持的在靜態(tài)HTML中引入外部代碼的技術。JSP在這方面的支持更為完善,因為它可以用Servlet而不是獨立的程序來生成動態(tài)內容。另外,SSI實際上只用于簡單的包含,而不是面向那些能夠處理表單數(shù)據(jù)、訪問數(shù)據(jù)庫的“真正的”程序。
    ??
    ??
    ? JSP和JavaScript相比
    ??
    ? JavaScript能夠在客戶端動態(tài)地生成HTML。雖然JavaScript很有用,但它只能處理以客戶端環(huán)境為基礎的動態(tài)信息。除了Cookie之外,HTTP狀態(tài)和表單提交數(shù)據(jù)對JavaScript來說都是不可用的。另外,由于是在客戶端運行,JavaScript不能訪問服務器端資源,比如數(shù)據(jù)庫、目錄信息等等。
    ?2.1 安裝Servlet和JSP開發(fā)工具
    ??
    ?    要學習Servlet和JSP開發(fā),首先你必須準備一個符合Java Servlet 2.1/2.2和JavaServer Pages1.0/1.1規(guī)范的開發(fā)環(huán)境。Sun提供免費的JavaServer Web Development Kit(JSWDK),可以從http://java.sun.com/products/servlet/ 下載。
    ??
    ?    安裝好JSWDK之后,你還要告訴javac,在編譯文件的時候到哪里去尋找Servlet和JSP類。JSWDK安裝指南對此有詳細說明,但主要就是把servlet.jar和jsp.jar加入CLASSPATH。CLASSPATH是一個指示Java如何尋找類文件的環(huán)境變量,如果不設置CLASSPATH,Java在當前目錄和標準系統(tǒng)庫中尋找類;如果你自己設置了CLASSPATH,不要忘記包含當前目錄(即在CLASSPATH中包含“.”)。
    ??
    ?    另外,為了避免和其他開發(fā)者安裝到同一Web服務器上的Servlet產(chǎn)生命名沖突,最好把自己的Servlet放入包里面。此時,把包層次結構中的頂級目錄也加入CLASSPATH會帶來不少方便。請參見下文具體說明。
    ??
    ?    2.2 安裝支持Servlet的Web服務器
    ??
    ?    除了開發(fā)工具之外,你還要安裝一個支持Java Servlet的Web服務器,或者在現(xiàn)有的Web服務器上安裝Servlet軟件包。如果你使用的是最新的Web服務器或應用服務器,很可能它已經(jīng)有了所有必需的軟件。請查看Web服務器的文檔,或訪問http://java.sun.com/products/servlet/industry.html 查看支持Servlet的服務器軟件清單。
    ??
    ?    雖然最終運行Servlet的往往是商業(yè)級的服務器,但是開始學習的時候,用一個能夠在臺式機上運行的免費系統(tǒng)進行開發(fā)和測試也足夠了。下面是幾種當前最受歡迎的產(chǎn)品。
    ??
    ? Apache Tomcat.
    ??
    ? Tomcat是Servlet 2.2和JSP 1.1規(guī)范的官方參考實現(xiàn)。Tomcat既可以單獨作為小型Servlet、JSP測試服務器,也可以集成到Apache Web服務器。直到2000年早期,Tomcat還是唯一的支持Servlet 2.2和JSP 1.1規(guī)范的服務器,但已經(jīng)有許多其它服務器宣布提供這方面的支持。
    ??
    ? Tomcat和Apache一樣是免費的。不過,快速、穩(wěn)定的Apache服務器安裝和配置起來有點麻煩,Tomcat也有同樣的缺點。和其他商業(yè)級Servlet引擎相比,配置Tomcat的工作量顯然要多一點。具體請參見http://jakarta.apache.org/
    ??
    ??
    ? JavaServer Web Development Kit (JSWDK).
    ??
    ? JSWDK是Servlet 2.1和JSP 1.0的官方參考實現(xiàn)。把Servlet和JSP應用部署到正式運行它們的服務器之前,JSWDK可以單獨作為小型的Servlet、JSP測試服務器。JSWDK也是免費的,而且具有很好的穩(wěn)定性,但它的安裝和配置也較為復雜。具體請參見http://java.sun.com/products/servlet/download.html
    ??
    ??
    ? Allaire JRun.
    ??
    ? JRun是一個Servlet和JSP引擎,它可以集成到Netscape Enterprise或FastTrack Server、IIS、Microsoft Personal Web Server、版本較低的Apache、O'eilly的WebSite或者StarNine Web STAR。最多支持5個并發(fā)連接的限制版本是免費的,商業(yè)版本中不存在這個限制,而且增加了遠程管理控制臺之類的功能。具體請參見http://www.allaire.com/products/jrun/
    ??
    ??
    ? New Atlanta 的ServletExec
    ??
    ? ServletExec是一個快速的Servlet和JSP引擎,它可以集成到大多數(shù)流行的Web服務器,支持平臺包括Solaris、Windows、MacOS、HP-UX和Linux。ServletExec可以免費下載和使用,但許多高級功能和管理工具只有在購買了許可之后才可以使用。New Atlanta還提供一個免費的Servlet調試器,該調試器可以在許多流行的Java IDE下工作。具體請參見http://newatlanta.com/
    ??
    ??
    ? Gefion的LiteWebServer (LWS)
    ??
    ? LWS是一個支持Servlet 2.2和JSP 1.1的免費小型Web服務器。 Gefion還有一個免費的WAICoolRunner插件,利用該插件可以為Netscape FastTrack和Enterprise Server增加Servlet 2.2和JSP 1.1支持。具體請參見http://www.gefionsoftware.com/
    ??
    ??
    ? Sun的Java Web Server.
    ??
    ? 該服務器全部用Java寫成,而且是首先提供Servlet 2.1和JSP 1.0規(guī)范完整支持的Web服務器之一。雖然Sun現(xiàn)在已轉向Netscape/I-Planet Server,不再發(fā)展Java Web Server,但它仍舊是一個廣受歡迎的Servlet、JSP學習平臺。要得到免費試用版本,請訪問http://www.sun.com/software/jwebserver/try/
    ?
    3.1 Servlet基本結構
    ??
    ?    下面的代碼顯示了一個簡單Servlet的基本結構。該Servlet處理的是GET請求,所謂的GET請求,如果你不熟悉HTTP,可以把它看成是當用戶在瀏覽器地址欄輸入URL、點擊Web頁面中的鏈接、提交沒有指定METHOD的表單時瀏覽器所發(fā)出的請求。Servlet也可以很方便地處理POST請求。POST請求是提交那些指定了METHOD=“POST”的表單時所發(fā)出的請求,具體請參見稍后幾節(jié)的討論。
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ??
    ? public class SomeServlet extends HttpServlet {
    ?? public void doGet(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ??
    ?? // 使用“request”讀取和請求有關的信息(比如Cookies)
    ?? // 和表單數(shù)據(jù)
    ??
    ?? // 使用“response”指定HTTP應答狀態(tài)代碼和應答頭
    ?? // (比如指定內容類型,設置Cookie)
    ??
    ?? PrintWriter out = response.getWriter();
    ?? // 使用 "out"把應答內容發(fā)送到瀏覽器
    ?? }
    ? }
    ??
    ??
    ??
    ??
    ?    如果某個類要成為Servlet,則它應該從HttpServlet 繼承,根據(jù)數(shù)據(jù)是通過GET還是POST發(fā)送,覆蓋doGet、doPost方法之一或全部。doGet和doPost方法都有兩個參數(shù),分別為HttpServletRequest 類型和HttpServletResponse 類型。HttpServletRequest提供訪問有關請求的信息的方法,例如表單數(shù)據(jù)、HTTP請求頭等等。HttpServletResponse除了提供用于指定HTTP應答狀態(tài)(200,404等)、應答頭(Content-Type,Set-Cookie等)的方法之外,最重要的是它提供了一個用于向客戶端發(fā)送數(shù)據(jù)的PrintWriter 。對于簡單的Servlet來說,它的大部分工作是通過println語句生成向客戶端發(fā)送的頁面。
    ??
    ?    注意doGet和doPost拋出兩個異常,因此你必須在聲明中包含它們。另外,你還必須導入java.io包(要用到PrintWriter等類)、javax.servlet包(要用到HttpServlet等類)以及javax.servlet.http包(要用到HttpServletRequest類和HttpServletResponse類)。
    ??
    ?    最后,doGet和doPost這兩個方法是由service方法調用的,有時你可能需要直接覆蓋service方法,比如Servlet要處理GET和POST兩種請求時。
    ??
    ?    3.2 輸出純文本的簡單Servlet
    ??
    ?    下面是一個輸出純文本的簡單Servlet。
    ??
    ?    3.2.1 HelloWorld.java
    ? package hall;
    ??
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ??
    ? public class HelloWorld extends HttpServlet {
    ?? public void doGet(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? PrintWriter out = response.getWriter();
    ?? out.println("Hello World");
    ?? }
    ? }
    ??
    ??
    ??
    ??
    ?    3.2.2 Servlet的編譯和安裝
    ??
    ?    不同的Web服務器上安裝Servlet的具體細節(jié)可能不同,請參考Web服務器文檔了解更權威的說明。假定使用Java Web Server(JWS)2.0,則Servlet應該安裝到JWS安裝目錄的servlets子目錄下。在本文中,為了避免同一服務器上不同用戶的Servlet命名沖突,我們把所有Servlet都放入一個獨立的包hall中;如果你和其他人共用一個服務器,而且該服務器沒有“虛擬服務器”機制來避免這種命名沖突,那么最好也使用包。把Servlet放入了包hall之后,HelloWorld.java實際上是放在servlets目錄的hall子目錄下。
    ??
    ?    大多數(shù)其他服務器的配置方法也相似,除了JWS之外,本文的Servlet和JSP示例已經(jīng)在BEA WebLogic和IBM WebSphere 3.0下經(jīng)過測試。WebSphere具有優(yōu)秀的虛擬服務器機制,因此,如果只是為了避免命名沖突的話并非一定要用包。
    ??
    ?    對于沒有使用過包的初學者,下面我們介紹編譯包里面的類的兩種方法。
    ??
    ?    一種方法是設置CLASSPATH,使其指向實際存放Servlet的目錄的上一級目錄(Servlet主目錄),然后在該目錄中按正常的方式編譯。例如,如果Servlet的主目錄是C:\JavaWebServer\servlets,包的名字(即主目錄下的子目錄名字)是hall,在Windows下,編譯過程如下:
    ?? DOS> set CLASSPATH=C:\JavaWebServer\servlets;%CLASSPATH%
    ?? DOS> cd C:\JavaWebServer\servlets\hall
    ?? DOS> javac YourServlet.java
    ??
    ??
    ??
    ?    第二種編譯包里面的Servlet的方法是進入Servlet主目錄,執(zhí)行“javac directory\YourServlet.java”(Windows)或者“javac directory/YourServlet.java”(Unix)。例如,再次假定Servlet主目錄是C:\JavaWebServer\servlets,包的名字是hall,在Windows中編譯過程如下:
    ?? DOS> cd C:\JavaWebServer\servlets
    ?? DOS> javac hall\YourServlet.java
    ??
    ??
    ??
    ?    注意在Windows下,大多數(shù)JDK 1.1版本的javac要求目錄名字后面加反斜杠(\)。JDK1.2已經(jīng)改正這個問題,然而由于許多Web服務器仍舊使用JDK 1.1,因此大量的Servlet開發(fā)者仍舊在使用JDK 1.1。
    ??
    ?    最后,Javac還有一個高級選項用于支持源代碼和.class文件的分開放置,即你可以用javac的“-d”選項把.class文件安裝到Web服務器所要求的目錄。
    ??
    ?    3.2.3 運行Servlet
    ??
    ?    在Java Web Server下,Servlet應該放到JWS安裝目錄的servlets子目錄下,而調用Servlet的URL是http://host/servlet/ServletName。注意子目錄的名字是servlets(帶“s”),而URL使用的是“servlet”。由于HelloWorld Servlet放入包hall,因此調用它的URL應該是http://host/servlet/hall.HelloWorld。在其他的服務器上,安裝和調用Servlet的方法可能略有不同。
    ??
    ?    大多數(shù)Web服務器還允許定義Servlet的別名,因此Servlet也可能用http://host/any-path/any-file.html形式的URL調用。具體如何進行配置完全依賴于服務器類型,請參考服務器文檔了解細節(jié)。
    ??
    ?    3.3 輸出HTML的Servlet
    ??
    ?    大多數(shù)Servlet都輸出HTML,而不象上例一樣輸出純文本。要輸出HTML還有兩個額外的步驟要做:告訴瀏覽器接下來發(fā)送的是HTML;修改println語句構造出合法的HTML頁面。
    ??
    ?    第一步通過設置Content-Type(內容類型)應答頭完成。一般地,應答頭可以通過HttpServletResponse的setHeader方法設置,但由于設置內容類型是一個很頻繁的操作,因此Servlet API提供了一個專用的方法setContentType。注意設置應答頭應該在通過PrintWriter發(fā)送內容之前進行。下面是一個實例:
    ??
    ?    HelloWWW.java
    ? package hall;
    ??
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ??
    ? public class HelloWWW extends HttpServlet {
    ?? public void doGet(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? response.setContentType("text/html");
    ?? PrintWriter out = response.getWriter();
    ?? out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 " +
    ?? "Transitional//EN\">\n" +
    ?? "<HTML>\n" +
    ?? "<HEAD><TITLE>Hello WWW</TITLE></HEAD>\n" +
    ?? "<BODY>\n" +
    ?? "<H1>Hello WWW</H1>\n" +
    ?? "</BODY></HTML>");
    ?? }
    ? }
    ??
    ??
    ??
    ??
    ?    3.4 幾個HTML工具函數(shù)
    ??
    ?    通過println語句輸出HTML并不方便,根本的解決方法是使用JavaServer Pages(JSP)。然而,對于標準的Servlet來說,由于Web頁面中有兩個部分(DOCTYPE和HEAD)一般不會改變,因此可以用工具函數(shù)來封裝生成這些內容的代碼。
    ??
    ?    雖然大多數(shù)主流瀏覽器都會忽略DOCTYPE行,但嚴格地說HTML規(guī)范是要求有DOCTYPE行的,它有助于HTML語法檢查器根據(jù)所聲明的HTML版本檢查HTML文檔合法性。在許多Web頁面中,HEAD部分只包含<TITLE>。雖然許多有經(jīng)驗的編寫者都會在HEAD中包含許多META標記和樣式聲明,但這里只考慮最簡單的情況。
    ??
    ?    下面的Java方法只接受頁面標題為參數(shù),然后輸出頁面的DOCTYPE、HEAD、TITLE部分。清單如下:
    ??
    ?    ServletUtilities.java
    ? package hall;
    ??
    ? public class ServletUtilities {
    ?? public static final String DOCTYPE =
    ?? "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">";
    ??
    ?? public static String headWithTitle(String title) {
    ?? return(DOCTYPE + "\n" +
    ?? "<HTML>\n" +
    ?? "<HEAD><TITLE>" + title + "</TITLE></HEAD>\n");
    ?? }
    ??
    ?? // 其他工具函數(shù)的代碼在本文后面介紹
    ? }
    ??
    ??
    ??
    ??
    ?    HelloWWW2.java
    ??
    ?    下面是應用了ServletUtilities之后重寫HelloWWW類得到的HelloWWW2:
    ? package hall;
    ??
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ??
    ? public class HelloWWW2 extends HttpServlet {
    ?? public void doGet(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? response.setContentType("text/html");
    ?? PrintWriter out = response.getWriter();
    ?? out.println(ServletUtilities.headWithTitle("Hello WWW") +
    ?? "<BODY>\n" +
    ?? "<H1>Hello WWW</H1>\n" +
    ?? "</BODY></HTML>");
    ?? }
    ? }
    4.1 表單數(shù)據(jù)概述
    ??
    ?    如果你曾經(jīng)使用過Web搜索引擎,或者瀏覽過在線書店、股票價格、機票信息,或許會留意到一些古怪的URL,比如“http://host/path?user=Marty+Hall&origin=bwi&dest=lax”。這個URL中位于問號后面的部分,即“user=Marty+Hall&origin=bwi&dest=lax”,就是表單數(shù)據(jù),這是將Web頁面數(shù)據(jù)發(fā)送給服務器程序的最常用方法。對于GET請求,表單數(shù)據(jù)附加到URL的問號后面(如上例所示);對于POST請求,表單數(shù)據(jù)用一個單獨的行發(fā)送給服務器。
    ??
    ?    以前,從這種形式的數(shù)據(jù)提取出所需要的表單變量是CGI編程中最麻煩的事情之一。首先,GET請求和POST請求的數(shù)據(jù)提取方法不同:對于GET請求,通常要通過QUERY_STRING環(huán)境變量提取數(shù)據(jù);對于POST請求,則一般通過標準輸入提取數(shù)據(jù)。第二,程序員必須負責在“&”符號處截斷變量名字-變量值對,再分離出變量名字(等號左邊)和變量值(等號右邊)。第三,必須對變量值進行URL反編碼操作。因為發(fā)送數(shù)據(jù)的時候,字母和數(shù)字以原來的形式發(fā)送,但空格被轉換成加號,其他字符被轉換成“%XX”形式,其中XX是十六進制表示的字符ASCII(或者ISO Latin-1)編碼值。例如,如果HTML表單中名為“users”的域值為“~hall, ~gates, and ~mcnealy”,則實際向服務器發(fā)送的數(shù)據(jù)為“users=%7Ehall%2C+%7Egates%2C+and+%7Emcnealy”。最后,即第四個導致解析表單數(shù)據(jù)非常困難的原因在于,變量值既可能被省略(如“param1=val1&param2=&param3=val3”),也有可能一個變量擁有一個以上的值,即同一個變量可能出現(xiàn)一次以上(如“param1=val1&param2=val2&param1=val3”)。
    ??
    ?    Java Servlet的好處之一就在于所有上述解析操作都能夠自動完成。只需要簡單地調用一下HttpServletRequest的getParameter方法、在調用參數(shù)中提供表單變量的名字(大小寫敏感)即可,而且GET請求和POST請求的處理方法完全相同。
    ??
    ?    getParameter方法的返回值是一個字符串,它是參數(shù)中指定的變量名字第一次出現(xiàn)所對應的值經(jīng)反編碼得到得字符串(可以直接使用)。如果指定的表單變量存在,但沒有值,getParameter返回空字符串;如果指定的表單變量不存在,則返回null。如果表單變量可能對應多個值,可以用getParameterValues來取代getParameter。getParameterValues能夠返回一個字符串數(shù)組。
    ??
    ?    最后,雖然在實際應用中Servlet很可能只會用到那些已知名字的表單變量,但在調試環(huán)境中,獲得完整的表單變量名字列表往往是很有用的,利用getParamerterNames方法可以方便地實現(xiàn)這一點。getParamerterNames返回的是一個Enumeration,其中的每一項都可以轉換為調用getParameter的字符串。
    ??
    ?    4.2 實例:讀取三個表單變量
    ??
    ?    下面是一個簡單的例子,它讀取三個表單變量param1、param2和param3,并以HTML列表的形式列出它們的值。請注意,雖然在發(fā)送應答內容之前必須指定應答類型(包括內容類型、狀態(tài)以及其他HTTP頭信息),但Servlet對何時讀取請求內容卻沒有什么要求。
    ??
    ?    另外,我們也可以很容易地把Servlet做成既能處理GET請求,也能夠處理POST請求,這只需要在doPost方法中調用doGet方法,或者覆蓋service方法(service方法調用doGet、doPost、doHead等方法)。在實際編程中這是一種標準的方法,因為它只需要很少的額外工作,卻能夠增加客戶端編碼的靈活性。
    ??
    ?    如果你習慣用傳統(tǒng)的CGI方法,通過標準輸入讀取POST數(shù)據(jù),那么在Servlet中也有類似的方法,即在HttpServletRequest上調用getReader或者getInputStream,但這種方法對普通的表單變量來說太麻煩。然而,如果是要上載文件,或者POST數(shù)據(jù)是通過專門的客戶程序而不是HTML表單發(fā)送,那么就要用到這種方法。
    ??
    ?    注意用第二種方法讀取POST數(shù)據(jù)時,不能再用getParameter來讀取這些數(shù)據(jù)。
    ??
    ?    ThreeParams.java
    ? package hall;
    ??
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ? import java.util.*;
    ??
    ? public class ThreeParams extends HttpServlet {
    ?? public void doGet(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? response.setContentType("text/html");
    ?? PrintWriter out = response.getWriter();
    ?? String title = "讀取三個請求參數(shù)";
    ?? out.println(ServletUtilities.headWithTitle(title) +
    ?? "<BODY>\n" +
    ?? "<H1 ALIGN=CENTER>" + title + "</H1>\n" +
    ?? "<UL>\n" +
    ?? " <LI>param1: "
    ?? + request.getParameter("param1") + "\n" +
    ?? " <LI>param2: "
    ?? + request.getParameter("param2") + "\n" +
    ?? " <LI>param3: "
    ?? + request.getParameter("param3") + "\n" +
    ?? "</UL>\n" +
    ?? "</BODY></HTML>");
    ?? }
    ??
    ?? public void doPost(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? doGet(request, response);
    ?? }
    ? }
    ??
    ??
    ??
    ??
    ?    4.3 實例:輸出所有的表單數(shù)據(jù)
    ??
    ?    下面這個例子尋找表單所發(fā)送的所有變量名字,并把它們放入表格中,沒有值或者有多個值的變量都突出顯示。
    ??
    ?    首先,程序通過HttpServletRequest的getParameterNames方法得到所有的變量名字,getParameterNames返回的是一個Enumeration。接下來,程序用循環(huán)遍歷這個Enumeration,通過hasMoreElements確定何時結束循環(huán),利用nextElement得到Enumeration中的各個項。由于nextElement返回的是一個Object,程序把它轉換成字符串后再用這個字符串來調用getParameterValues。
    ??
    ?    getParameterValues返回一個字符串數(shù)組,如果這個數(shù)組只有一個元素且等于空字符串,說明這個表單變量沒有值,Servlet以斜體形式輸出“No Value”;如果數(shù)組元素個數(shù)大于1,說明這個表單變量有多個值,Servlet以HTML列表形式輸出這些值;其他情況下Servlet直接把變量值放入表格。
    ??
    ?    ShowParameters.java
    ??
    ?    注意,ShowParameters.java用到了前面介紹過的ServletUtilities.java。
    ? package hall;
    ??
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ? import java.util.*;
    ??
    ? 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 = "讀取所有請求參數(shù)";
    ?? out.println(ServletUtilities.headWithTitle(title) +
    ?? "<BODY BGCOLOR=\"#FDF5E6\">\n" +
    ?? "<H1 ALIGN=CENTER>" + title + "</H1>\n" +
    ?? "<TABLE BORDER=1 ALIGN=CENTER>\n" +
    ?? "<TR BGCOLOR=\"#FFAD00\">\n" +
    ?? "<TH>參數(shù)名字<TH>參數(shù)值");
    ?? 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);
    ?? }
    ? }
    ??
    ??
    ??
    ??
    ?    測試表單
    ??
    ?    下面是向上述Servlet發(fā)送數(shù)據(jù)的表單PostForm.html。就像所有包含密碼輸入域的表單一樣,該表單用POST方法發(fā)送數(shù)據(jù)。我們可以看到,在Servlet中同時實現(xiàn)doGet和doPost這兩種方法為表單制作帶來了方便。
    ? <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
    ? <HTML>
    ? <HEAD>
    ?? <TITLE>示例表單</TITLE>
    ? </HEAD>
    ??
    ? <BODY BGCOLOR="#FDF5E6">
    ? <H1 ALIGN="CENTER">用POST方法發(fā)送數(shù)據(jù)的表單</H1>
    ??
    ? <FORM ACTION="/servlet/hall.ShowParameters"
    ?? METHOD="POST">
    ?? Item Number:
    ?? <INPUT TYPE="TEXT" NAME="itemNum"><BR>
    ?? Quantity:
    ?? <INPUT TYPE="TEXT" NAME="quantity"><BR>
    ?? Price Each:
    ?? <INPUT TYPE="TEXT" NAME="price" VALUE="$"><BR>
    ?? <HR>
    ?? First Name:
    ?? <INPUT TYPE="TEXT" NAME="firstName"><BR>
    ?? Last Name:
    ?? <INPUT TYPE="TEXT" NAME="lastName"><BR>
    ?? Middle Initial:
    ?? <INPUT TYPE="TEXT" NAME="initial"><BR>
    ?? Shipping Address:
    ?? <TEXTAREA NAME="address" ROWS=3 COLS=40></TEXTAREA><BR>
    ?? Credit Card:<BR>
    ?? <INPUT TYPE="RADIO" NAME="cardType"
    ?? VALUE="Visa">Visa<BR>
    ?? <INPUT TYPE="RADIO" NAME="cardType"
    ?? VALUE="Master Card">Master Card<BR>
    ?? <INPUT TYPE="RADIO" NAME="cardType"
    ?? VALUE="Amex">American Express<BR>
    ?? <INPUT TYPE="RADIO" NAME="cardType"
    ?? VALUE="Discover">Discover<BR>
    ?? <INPUT TYPE="RADIO" NAME="cardType"
    ?? VALUE="Java SmartCard">Java SmartCard<BR>
    ?? Credit Card Number:
    ?? <INPUT TYPE="PASSWORD" NAME="cardNum"><BR>
    ?? Repeat Credit Card Number:
    ?? <INPUT TYPE="PASSWORD" NAME="cardNum"><BR><BR>
    ?? <CENTER>
    ?? <INPUT TYPE="SUBMIT" VALUE="Submit Order">
    ?? </CENTER>
    ? </FORM>
    ??
    ? </BODY>
    ? </HTML>
    5.1 HTTP請求頭概述
    ??
    ?    HTTP客戶程序(例如瀏覽器),向服務器發(fā)送請求的時候必須指明請求類型(一般是GET或者POST)。如有必要,客戶程序還可以選擇發(fā)送其他的請求頭。大多數(shù)請求頭并不是必需的,但Content-Length除外。對于POST請求來說Content-Length必須出現(xiàn)。
    ??
    ?    下面是一些最常見的請求頭:
    ??
    ? Accept:瀏覽器可接受的MIME類型。
    ? Accept-Charset:瀏覽器可接受的字符集。
    ? Accept-Encoding:瀏覽器能夠進行解碼的數(shù)據(jù)編碼方式,比如gzip。Servlet能夠向支持gzip的瀏覽器返回經(jīng)gzip編碼的HTML頁面。許多情形下這可以減少5到10倍的下載時間。
    ? Accept-Language:瀏覽器所希望的語言種類,當服務器能夠提供一種以上的語言版本時要用到。
    ? Authorization:授權信息,通常出現(xiàn)在對服務器發(fā)送的WWW-Authenticate頭的應答中。
    ? Connection:表示是否需要持久連接。如果Servlet看到這里的值為“Keep-Alive”,或者看到請求使用的是HTTP 1.1(HTTP 1.1默認進行持久連接),它就可以利用持久連接的優(yōu)點,當頁面包含多個元素時(例如Applet,圖片),顯著地減少下載所需要的時間。要實現(xiàn)這一點,Servlet需要在應答中發(fā)送一個Content-Length頭,最簡單的實現(xiàn)方法是:先把內容寫入ByteArrayOutputStream,然后在正式寫出內容之前計算它的大小。
    ? Content-Length:表示請求消息正文的長度。
    ? Cookie:這是最重要的請求頭信息之一,參見后面《Cookie處理》一章中的討論。
    ? From:請求發(fā)送者的email地址,由一些特殊的Web客戶程序使用,瀏覽器不會用到它。
    ? Host:初始URL中的主機和端口。
    ? If-Modified-Since:只有當所請求的內容在指定的日期之后又經(jīng)過修改才返回它,否則返回304“Not Modified”應答。
    ? Pragma:指定“no-cache”值表示服務器必須返回一個刷新后的文檔,即使它是代理服務器而且已經(jīng)有了頁面的本地拷貝。
    ? Referer:包含一個URL,用戶從該URL代表的頁面出發(fā)訪問當前請求的頁面。
    ? User-Agent:瀏覽器類型,如果Servlet返回的內容與瀏覽器類型有關則該值非常有用。
    ? UA-Pixels,UA-Color,UA-OS,UA-CPU:由某些版本的IE瀏覽器所發(fā)送的非標準的請求頭,表示屏幕大小、顏色深度、操作系統(tǒng)和CPU類型。
    ?    有關HTTP頭完整、詳細的說明,請參見http://www.w3.org/Protocols/ 的HTTP規(guī)范。
    ??
    ?    5.2 在Servlet中讀取請求頭
    ??
    ?    在Servlet中讀取HTTP頭是非常方便的,只需要調用一下HttpServletRequest的getHeader方法即可。如果客戶請求中提供了指定的頭信息,getHeader返回對應的字符串;否則,返回null。部分頭信息經(jīng)常要用到,它們有專用的訪問方法:getCookies方法返回Cookie頭的內容,經(jīng)解析后存放在Cookie對象的數(shù)組中,請參見后面有關Cookie章節(jié)的討論;getAuthType和getRemoteUser方法分別讀取Authorization頭中的一部分內容;getDateHeader和getIntHeader方法讀取指定的頭,然后返回日期值或整數(shù)值。
    ??
    ?    除了讀取指定的頭之外,利用getHeaderNames還可以得到請求中所有頭名字的一個Enumeration對象。
    ??
    ?    最后,除了查看請求頭信息之外,我們還可以從請求主命令行獲得一些信息。getMethod方法返回請求方法,請求方法通常是GET或者POST,但也有可能是HEAD、PUT或者DELETE。getRequestURI方法返回URI(URI是URL的從主機和端口之后到表單數(shù)據(jù)之前的那一部分)。getRequestProtocol返回請求命令的第三部分,一般是“HTTP/1.0”或者“HTTP/1.1”。
    ??
    ?    5.3 實例:輸出所有的請求頭
    ??
    ?    下面的Servlet實例把所有接收到的請求頭和它的值以表格的形式輸出。另外,該Servlet還會輸出主請求命令的三個部分:請求方法,URI,協(xié)議/版本。
    ??
    ?    ShowRequestHeaders.java
    ? package hall;
    ??
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ? import java.util.*;
    ??
    ? 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 = "顯示所有請求頭";
    ?? out.println(ServletUtilities.headWithTitle(title) +
    ?? "<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);
    ?? }
    ? }
    6.1 CGI變量概述
    ??
    ?    如果你是從傳統(tǒng)的CGI編程轉而學習Java Servlet,或許已經(jīng)習慣了“CGI變量”這一概念。CGI變量匯集了各種有關請求的信息:
    ??
    ? 部分來自HTTP請求命令和請求頭,例如Content-Length頭;
    ? 部分來自Socket本身,例如主機的名字和IP地址;
    ? 也有部分與服務器安裝配置有關,例如URL到實際路徑的映射。
    ?    6.2 標準CGI變量的Servlet等價表示
    ??
    ?    下表假定request對象是提供給doGet和doPost方法的HttpServletRequest類型對象。 CGI變量 含義 從doGet或doPost訪問
    ? AUTH_TYPE 如果提供了Authorization頭,這里指定了具體的模式(basic或者digest)。 request.getAuthType()
    ? CONTENT_LENGTH 只用于POST請求,表示所發(fā)送數(shù)據(jù)的字節(jié)數(shù)。 嚴格地講,等價的表達方式應該是String.valueOf(request.getContentLength())(返回一個字符串)。但更常見的是用request.getContentLength()返回含義相同的整數(shù)。
    ? CONTENT_TYPE 如果指定的話,表示后面所跟數(shù)據(jù)的類型。 request.getContentType()
    ? DOCUMENT_ROOT 與http://host/對應的路徑。 getServletContext().getRealPath("/")
    ? 注意低版本Servlet規(guī)范中的等價表達方式是request.getRealPath("/")。
    ??
    ? HTTP_XXX_YYY 訪問任意HTTP頭。 request.getHeader("Xxx-Yyy")
    ? PATH_INFO URL中的附加路徑信息,即URL中Servlet路徑之后、查詢字符串之前的那部分。 request.getPathInfo()
    ? PATH_TRANSLATED 映射到服務器實際路徑之后的路徑信息。 request.getPathTranslated()
    ? QUERY_STRING 這是字符串形式的附加到URL后面的查詢字符串,數(shù)據(jù)仍舊是URL編碼的。在Servlet中很少需要用到未經(jīng)解碼的數(shù)據(jù),一般使用getParameter訪問各個參數(shù)。 request.getQueryString()
    ? REMOTE_ADDR 發(fā)出請求的客戶機的IP地址。 request.getRemoteAddr()
    ? REMOTE_HOST 發(fā)出請求的客戶機的完整的域名,如java.sun.com。如果不能確定該域名,則返回IP地址。 request.getRemoteHost()
    ? REMOTE_USER 如果提供了Authorization頭,則代表其用戶部分。它代表發(fā)出請求的用戶的名字。 request.getRemoteUser()
    ? REQUEST_METHOD 請求類型。通常是GET或者POST。但偶爾也會出現(xiàn)HEAD,PUT, DELETE,OPTIONS,或者 TRACE. request.getMethod()
    ? SCRIPT_NAME URL中調用Servlet的那一部分,不包含附加路徑信息和查詢字符串。 request.getServletPath()
    ? SERVER_NAME Web服務器名字。 request.getServerName()
    ? SERVER_PORT 服務器監(jiān)聽的端口。 嚴格地說,等價表達應該是返回字符串的String.valueOf(request.getServerPort())。但經(jīng)常使用返回整數(shù)值的request.getServerPort()。
    ? SERVER_PROTOCOL 請求命令中的協(xié)議名字和版本(即HTTP/1.0或HTTP/1.1)。 request.getProtocol()
    ? SERVER_SOFTWARE Servlet引擎的名字和版本。 getServletContext().getServerInfo()
    ??
    ??
    ?    6.3 實例:讀取CGI變量
    ??
    ?    下面這個Servlet創(chuàng)建一個表格,顯示除了HTTP_XXX_YYY之外的所有CGI變量。HTTP_XXX_YYY是HTTP請求頭信息,請參見上一節(jié)介紹。
    ??
    ?    ShowCGIVariables.java
    ? package hall;
    ??
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ? import java.util.*;
    ??
    ? public class ShowCGIVariables extends HttpServlet {
    ?? public void doGet(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? response.setContentType("text/html");
    ?? PrintWriter out = response.getWriter();
    ?? String[][] variables =
    ?? { { "AUTH_TYPE", request.getAuthType() },
    ?? { "CONTENT_LENGTH", String.valueOf(request.getContentLength()) },
    ?? { "CONTENT_TYPE", request.getContentType() },
    ?? { "DOCUMENT_ROOT", getServletContext().getRealPath("/") },
    ?? { "PATH_INFO", request.getPathInfo() },
    ?? { "PATH_TRANSLATED", request.getPathTranslated() },
    ?? { "QUERY_STRING", request.getQueryString() },
    ?? { "REMOTE_ADDR", request.getRemoteAddr() },
    ?? { "REMOTE_HOST", request.getRemoteHost() },
    ?? { "REMOTE_USER", request.getRemoteUser() },
    ?? { "REQUEST_METHOD", request.getMethod() },
    ?? { "SCRIPT_NAME", request.getServletPath() },
    ?? { "SERVER_NAME", request.getServerName() },
    ?? { "SERVER_PORT", String.valueOf(request.getServerPort()) },
    ?? { "SERVER_PROTOCOL", request.getProtocol() },
    ?? { "SERVER_SOFTWARE", getServletContext().getServerInfo() }
    ?? };
    ?? String title = "顯示CGI變量";
    ?? out.println(ServletUtilities.headWithTitle(title) +
    ?? "<BODY BGCOLOR=\"#FDF5E6\">\n" +
    ?? "<H1 ALIGN=CENTER>" + title + "</H1>\n" +
    ?? "<TABLE BORDER=1 ALIGN=CENTER>\n" +
    ?? "<TR BGCOLOR=\"#FFAD00\">\n" +
    ?? "<TH>CGI Variable Name<TH>Value");
    ?? for(int i=0; i<variables.length; i++) {
    ?? String varName = variables[i][0];
    ?? String varValue = variables[i][1];
    ?? if (varValue == null)
    ?? varValue = "<I>Not specified</I>";
    ?? out.println("<TR><TD>" + varName + "<TD>" + varValue);
    ?? }
    ?? out.println("</TABLE></BODY></HTML>");
    ?? }
    ??
    ?? public void doPost(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? doGet(request, response);
    ?? }
    ? }
    7.1 狀態(tài)代碼概述
    ??
    ?    Web服務器響應瀏覽器或其他客戶程序的請求時,其應答一般由以下幾個部分組成:一個狀態(tài)行,幾個應答頭,一個空行,內容文檔。下面是一個最簡單的應答:
    ? HTTP/1.1 200 OK
    ? Content-Type: text/plain
    ??
    ? Hello World
    ??
    ??
    ??
    ??
    ?    狀態(tài)行包含HTTP版本、狀態(tài)代碼、與狀態(tài)代碼對應的簡短說明信息。在大多數(shù)情況下,除了Content-Type之外的所有應答頭都是可選的。但Content-Type是必需的,它描述的是后面文檔的MIME類型。雖然大多數(shù)應答都包含一個文檔,但也有一些不包含,例如對HEAD請求的應答永遠不會附帶文檔。有許多狀態(tài)代碼實際上用來標識一次失敗的請求,這些應答也不包含文檔(或只包含一個簡短的錯誤信息說明)。
    ??
    ?    Servlet可以利用狀態(tài)代碼來實現(xiàn)許多功能。例如,可以把用戶重定向到另一個網(wǎng)站;可以指示出后面的文檔是圖片、PDF文件或HTML文件;可以告訴用戶必須提供密碼才能訪問文檔;等等。這一部分我們將具體討論各種狀態(tài)代碼的含義以及利用這些代碼可以做些什么。
    ??
    ?    7.2 設置狀態(tài)代碼
    ??
    ?    如前所述,HTTP應答狀態(tài)行包含HTTP版本、狀態(tài)代碼和對應的狀態(tài)信息。由于狀態(tài)信息直接和狀態(tài)代碼相關,而HTTP版本又由服務器確定,因此需要Servlet設置的只有一個狀態(tài)代碼。
    ??
    ?    Servlet設置狀態(tài)代碼一般使用HttpServletResponse的setStatus方法。setStatus方法的參數(shù)是一個整數(shù)(即狀態(tài)代碼),不過為了使得代碼具有更好的可讀性,可以用HttpServletResponse中定義的常量來避免直接使用整數(shù)。這些常量根據(jù)HTTP 1.1中的標準狀態(tài)信息命名,所有的名字都加上了SC前綴(Status Code的縮寫)并大寫,同時把空格轉換成了下劃線。也就是說,與狀態(tài)代碼404對應的狀態(tài)信息是“Not Found”,則HttpServletResponse中的對應常量名字為SC_NOT_FOUND。但有兩個例外:和狀態(tài)代碼302對應的常量根據(jù)HTTP 1.0命名,而307沒有對應的常量。
    ??
    ?    設置狀態(tài)代碼并非總是意味著不要再返回文檔。例如,雖然大多數(shù)服務器返回404應答時會輸出簡單的“File Not Found”信息,但Servlet也可以定制這個應答。不過,定制應答時應當在通過PrintWriter發(fā)送任何內容之前先調用response.setStatus。
    ??
    ?    雖然設置狀態(tài)代碼一般使用的是response.setStauts(int)方法,但為了簡單起見,HttpServletResponse為兩種常見的情形提供了專用方法:sendError方法生成一個404應答,同時生成一個簡短的HTML錯誤信息文檔;sendRedirect方法生成一個302應答,同時在Location頭中指示新文檔的URL。
    ??
    ?    7.3 HTTP 1.1狀態(tài)代碼及其含義
    ??
    ?    下表顯示了常見的HTTP 1.1狀態(tài)代碼以及它們對應的狀態(tài)信息和含義。
    ??
    ?    應當謹慎地使用那些只有HTTP 1.1支持的狀態(tài)代碼,因為許多瀏覽器還只能夠支持HTTP 1.0。如果你使用了HTTP 1.1特有的狀態(tài)代碼,最好能夠檢查一下請求的HTTP版本號(通過HttpServletRequest的getProtocol方法)。 狀態(tài)代碼 狀態(tài)信息 含義
    ? 100 Continue 初始的請求已經(jīng)接受,客戶應當繼續(xù)發(fā)送請求的其余部分。(HTTP 1.1新)
    ? 101 Switching Protocols 服務器將遵從客戶的請求轉換到另外一種協(xié)議(HTTP 1.1新)
    ? 200 OK 一切正常,對GET和POST請求的應答文檔跟在后面。如果不用setStatus設置狀態(tài)代碼,Servlet默認使用202狀態(tài)代碼。
    ? 201 Created 服務器已經(jīng)創(chuàng)建了文檔,Location頭給出了它的URL。
    ? 202 Accepted 已經(jīng)接受請求,但處理尚未完成。
    ? 203 Non-Authoritative Information 文檔已經(jīng)正常地返回,但一些應答頭可能不正確,因為使用的是文檔的拷貝(HTTP 1.1新)。
    ? 204 No Content 沒有新文檔,瀏覽器應該繼續(xù)顯示原來的文檔。如果用戶定期地刷新頁面,而Servlet可以確定用戶文檔足夠新,這個狀態(tài)代碼是很有用的。
    ? 205 Reset Content 沒有新的內容,但瀏覽器應該重置它所顯示的內容。用來強制瀏覽器清除表單輸入內容(HTTP 1.1新)。
    ? 206 Partial Content 客戶發(fā)送了一個帶有Range頭的GET請求,服務器完成了它(HTTP 1.1新)。
    ? 300 Multiple Choices 客戶請求的文檔可以在多個位置找到,這些位置已經(jīng)在返回的文檔內列出。如果服務器要提出優(yōu)先選擇,則應該在Location應答頭指明。
    ? 301 Moved Permanently 客戶請求的文檔在其他地方,新的URL在Location頭中給出,瀏覽器應該自動地訪問新的URL。
    ? 302 Found 類似于301,但新的URL應該被視為臨時性的替代,而不是永久性的。注意,在HTTP1.0中對應的狀態(tài)信息是“Moved Temporatily”,而HttpServletResponse中相應的常量是SC_MOVED_TEMPORARILY,而不是SC_FOUND。
    ? 出現(xiàn)該狀態(tài)代碼時,瀏覽器能夠自動訪問新的URL,因此它是一個很有用的狀態(tài)代碼。為此,Servlet提供了一個專用的方法,即sendRedirect。使用response.sendRedirect(url)比使用response.setStatus(response.SC_MOVED_TEMPORARILY)和response.setHeader("Location",url)更好。這是因為:
    ??
    ? 首先,代碼更加簡潔。
    ? 第二,使用sendRedirect,Servlet會自動構造一個包含新鏈接的頁面(用于那些不能自動重定向的老式瀏覽器)。
    ? 最后,sendRedirect能夠處理相對URL,自動把它們轉換成絕對URL。
    ? 注意這個狀態(tài)代碼有時候可以和301替換使用。例如,如果瀏覽器錯誤地請求http://host/~user(缺少了后面的斜杠),有的服務器返回301,有的則返回302。
    ??
    ? 嚴格地說,我們只能假定只有當原來的請求是GET時瀏覽器才會自動重定向。請參見307。
    ??
    ? 303 See Other 類似于301/302,不同之處在于,如果原來的請求是POST,Location頭指定的重定向目標文檔應該通過GET提取(HTTP 1.1新)。
    ? 304 Not Modified 客戶端有緩沖的文檔并發(fā)出了一個條件性的請求(一般是提供If-Modified-Since頭表示客戶只想比指定日期更新的文檔)。服務器告訴客戶,原來緩沖的文檔還可以繼續(xù)使用。
    ? 305 Use Proxy 客戶請求的文檔應該通過Location頭所指明的代理服務器提取(HTTP 1.1新)。
    ? 307 Temporary Redirect 和302(Found)相同。許多瀏覽器會錯誤地響應302應答進行重定向,即使原來的請求是POST,即使它實際上只能在POST請求的應答是303時才能重定向。由于這個原因,HTTP 1.1新增了307,以便更加清除地區(qū)分幾個狀態(tài)代碼:當出現(xiàn)303應答時,瀏覽器可以跟隨重定向的GET和POST請求;如果是307應答,則瀏覽器只能跟隨對GET請求的重定向。
    ? 注意,HttpServletResponse中沒有為該狀態(tài)代碼提供相應的常量。(HTTP 1.1新)
    ??
    ? 400 Bad Request 請求出現(xiàn)語法錯誤。
    ? 401 Unauthorized 客戶試圖未經(jīng)授權訪問受密碼保護的頁面。應答中會包含一個WWW-Authenticate頭,瀏覽器據(jù)此顯示用戶名字/密碼對話框,然后在填寫合適的Authorization頭后再次發(fā)出請求。
    ? 403 Forbidden 資源不可用。服務器理解客戶的請求,但拒絕處理它。通常由于服務器上文件或目錄的權限設置導致。
    ? 404 Not Found 無法找到指定位置的資源。這也是一個常用的應答,HttpServletResponse專門提供了相應的方法:sendError(message)。
    ? 405 Method Not Allowed 請求方法(GET、POST、HEAD、DELETE、PUT、TRACE等)對指定的資源不適用。(HTTP 1.1新)
    ? 406 Not Acceptable 指定的資源已經(jīng)找到,但它的MIME類型和客戶在Accpet頭中所指定的不兼容(HTTP 1.1新)。
    ? 407 Proxy Authentication Required 類似于401,表示客戶必須先經(jīng)過代理服務器的授權。(HTTP 1.1新)
    ? 408 Request Timeout 在服務器許可的等待時間內,客戶一直沒有發(fā)出任何請求。客戶可以在以后重復同一請求。(HTTP 1.1新)
    ? 409 Conflict 通常和PUT請求有關。由于請求和資源的當前狀態(tài)相沖突,因此請求不能成功。(HTTP 1.1新)
    ? 410 Gone 所請求的文檔已經(jīng)不再可用,而且服務器不知道應該重定向到哪一個地址。它和404的不同在于,返回407表示文檔永久地離開了指定的位置,而404表示由于未知的原因文檔不可用。(HTTP 1.1新)
    ? 411 Length Required 服務器不能處理請求,除非客戶發(fā)送一個Content-Length頭。(HTTP 1.1新)
    ? 412 Precondition Failed 請求頭中指定的一些前提條件失敗(HTTP 1.1新)。
    ? 413 Request Entity Too Large 目標文檔的大小超過服務器當前愿意處理的大小。如果服務器認為自己能夠稍后再處理該請求,則應該提供一個Retry-After頭(HTTP 1.1新)。
    ? 414 Request URI Too Long URI太長(HTTP 1.1新)。
    ? 416 Requested Range Not Satisfiable 服務器不能滿足客戶在請求中指定的Range頭。(HTTP 1.1新)
    ? 500 Internal Server Error 服務器遇到了意料不到的情況,不能完成客戶的請求。
    ? 501 Not Implemented 服務器不支持實現(xiàn)請求所需要的功能。例如,客戶發(fā)出了一個服務器不支持的PUT請求。
    ? 502 Bad Gateway 服務器作為網(wǎng)關或者代理時,為了完成請求訪問下一個服務器,但該服務器返回了非法的應答。
    ? 503 Service Unavailable 服務器由于維護或者負載過重未能應答。例如,Servlet可能在數(shù)據(jù)庫連接池已滿的情況下返回503。服務器返回503時可以提供一個Retry-After頭。
    ? 504 Gateway Timeout 由作為代理或網(wǎng)關的服務器使用,表示不能及時地從遠程服務器獲得應答。(HTTP 1.1新)
    ? 505 HTTP Version Not Supported 服務器不支持請求中所指明的HTTP版本。(HTTP 1.1新)
    ??
    ??
    ?    7.4 實例:訪問多個搜索引擎
    ??
    ?    下面這個例子用到了除200之外的另外兩個常見狀態(tài)代碼:302和404。302通過sendRedirect方法設置,404通過sendError方法設置。
    ??
    ?    在這個例子中,首先出現(xiàn)的HTML表單用來選擇搜索引擎、搜索字符串、每頁顯示的搜索結果數(shù)量。表單提交后,Servlet提取這三個變量,按照所選擇的搜索引擎的要求構造出包含這些變量的URL,然后把用戶重定向到這個URL。如果用戶不能正確地選擇搜索引擎,或者利用其他表單發(fā)送了一個不認識的搜索引擎名字,則返回一個提示搜索引擎找不到的404頁面。
    ??
    ?    SearchEngines.java
    ??
    ?    注意:這個Servlet要用到后面給出的SearchSpec類,SearchSpec的功能是構造適合不同搜索引擎的URL。
    ? package hall;
    ??
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ? import java.net.*;
    ??
    ? public class SearchEngines extends HttpServlet {
    ?? public void doGet(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? // getParameter自動解碼URL編碼的查詢字符串。由于我們
    ?? // 要把查詢字符串發(fā)送給另一個服務器,因此再次使用
    ?? // URLEncoder進行URL編碼
    ?? String searchString =
    ?? URLEncoder.encode(request.getParameter("searchString"));
    ?? String numResults =
    ?? request.getParameter("numResults");
    ?? String searchEngine =
    ?? request.getParameter("searchEngine");
    ?? SearchSpec[] commonSpecs = SearchSpec.getCommonSpecs();
    ?? for(int i=0; i<commonSpecs.length; i++) {
    ?? SearchSpec searchSpec = commonSpecs[i];
    ?? if (searchSpec.getName().equals(searchEngine)) {
    ?? String url =
    ?? response.encodeURL(searchSpec.makeURL(searchString,
    ?? numResults));
    ?? response.sendRedirect(url);
    ?? return;
    ?? }
    ?? }
    ?? response.sendError(response.SC_NOT_FOUND,
    ?? "No recognized search engine specified.");
    ?? }
    ??
    ?? public void doPost(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? doGet(request, response);
    ?? }
    ? }
    ??
    ??
    ??
    ??
    ?    SearchSpec.java
    ? package hall;
    ??
    ? class SearchSpec {
    ?? private String name, baseURL, numResultsSuffix;
    ??
    ?? private static SearchSpec[] commonSpecs =
    ?? { new SearchSpec("google",
    ?? "http://www.google.com/search?q=",
    ?? "&num="),
    ?? new SearchSpec("infoseek",
    ?? "http://infoseek.go.com/Titles?qt=",
    ?? "&nh="),
    ?? new SearchSpec("lycos",
    ?? "http://lycospro.lycos.com/cgi-bin/pursuit?query=",
    ?? "&maxhits="),
    ?? new SearchSpec("hotbot",
    ?? "http://www.hotbot.com/?MT=",
    ?? "&DC=")
    ?? };
    ??
    ?? public SearchSpec(String name,
    ?? String baseURL,
    ?? String numResultsSuffix) {
    ?? this.name = name;
    ?? this.baseURL = baseURL;
    ?? this.numResultsSuffix = numResultsSuffix;
    ?? }
    ??
    ?? public String makeURL(String searchString, String numResults) {
    ?? return(baseURL + searchString + numResultsSuffix + numResults);
    ?? }
    ??
    ?? public String getName() {
    ?? return(name);
    ?? }
    ??
    ?? public static SearchSpec[] getCommonSpecs() {
    ?? return(commonSpecs);
    ?? }
    ? }
    ??
    ??
    ??
    ??
    ?    SearchEngines.html
    ??
    ?    下面是調用上述Servlet的HTML表單。
    ? <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
    ? <HTML>
    ? <HEAD>
    ?? <TITLE>訪問多個搜索引擎</TITLE>
    ? </HEAD>
    ??
    ? <BODY BGCOLOR="#FDF5E6">
    ??
    ? <FORM ACTION="/servlet/hall.SearchEngines">
    ?? <CENTER>
    ?? 搜索關鍵字:
    ?? <INPUT TYPE="TEXT" NAME="searchString"><BR>
    ?? 每頁顯示幾個查詢結果:
    ?? <INPUT TYPE="TEXT" NAME="numResults"
    ?? VALUE=10 SIZE=3><BR>
    ?? <INPUT TYPE="RADIO" NAME="searchEngine"
    ?? VALUE="google">
    ?? Google |
    ?? <INPUT TYPE="RADIO" NAME="searchEngine"
    ?? VALUE="infoseek">
    ?? Infoseek |
    ?? <INPUT TYPE="RADIO" NAME="searchEngine"
    ?? VALUE="lycos">
    ?? Lycos |
    ?? <INPUT TYPE="RADIO" NAME="searchEngine"
    ?? VALUE="hotbot">
    ?? HotBot
    ?? <BR>
    ?? <INPUT TYPE="SUBMIT" VALUE="Search">
    ?? </CENTER>
    ? </FORM>
    ??
    ? </BODY>
    ? </HTML>
    8.1 HTTP應答頭概述
    ??
    ?    Web服務器的HTTP應答一般由以下幾項構成:一個狀態(tài)行,一個或多個應答頭,一個空行,內容文檔。設置HTTP應答頭往往和設置狀態(tài)行中的狀態(tài)代碼結合起來。例如,有好幾個表示“文檔位置已經(jīng)改變”的狀態(tài)代碼都伴隨著一個Location頭,而401(Unauthorized)狀態(tài)代碼則必須伴隨一個WWW-Authenticate頭。
    ??
    ?    然而,即使在沒有設置特殊含義的狀態(tài)代碼時,指定應答頭也是很有用的。應答頭可以用來完成:設置Cookie,指定修改日期,指示瀏覽器按照指定的間隔刷新頁面,聲明文檔的長度以便利用持久HTTP連接,……等等許多其他任務。
    ??
    ?    設置應答頭最常用的方法是HttpServletResponse的setHeader,該方法有兩個參數(shù),分別表示應答頭的名字和值。和設置狀態(tài)代碼相似,設置應答頭應該在發(fā)送任何文檔內容之前進行。
    ??
    ?    setDateHeader方法和setIntHeadr方法專門用來設置包含日期和整數(shù)值的應答頭,前者避免了把Java時間轉換為GMT時間字符串的麻煩,后者則避免了把整數(shù)轉換為字符串的麻煩。
    ??
    ?    HttpServletResponse還提供了許多設置常見應答頭的簡便方法,如下所示:
    ??
    ? setContentType:設置Content-Type頭。大多數(shù)Servlet都要用到這個方法。
    ? setContentLength:設置Content-Length頭。對于支持持久HTTP連接的瀏覽器來說,這個函數(shù)是很有用的。
    ? addCookie:設置一個Cookie(Servlet API中沒有setCookie方法,因為應答往往包含多個Set-Cookie頭)。
    ? 另外,如上節(jié)介紹,sendRedirect方法設置狀態(tài)代碼302時也會設置Location頭。
    ?    8.2 常見應答頭及其含義
    ??
    ?    有關HTTP頭詳細和完整的說明,請參見http://www.w3.org/Protocols/ 規(guī)范。
    ??
    ? 應答頭 說明
    ? Allow 服務器支持哪些請求方法(如GET、POST等)。
    ? Content-Encoding 文檔的編碼(Encode)方法。只有在解碼之后才可以得到Content-Type頭指定的內容類型。利用gzip壓縮文檔能夠顯著地減少HTML文檔的下載時間。Java的GZIPOutputStream可以很方便地進行gzip壓縮,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。因此,Servlet應該通過查看Accept-Encoding頭(即request.getHeader("Accept-Encoding"))檢查瀏覽器是否支持gzip,為支持gzip的瀏覽器返回經(jīng)gzip壓縮的HTML頁面,為其他瀏覽器返回普通頁面。
    ? Content-Length 表示內容長度。只有當瀏覽器使用持久HTTP連接時才需要這個數(shù)據(jù)。如果你想要利用持久連接的優(yōu)勢,可以把輸出文檔寫入ByteArrayOutputStram,完成后查看其大小,然后把該值放入Content-Length頭,最后通過byteArrayStream.writeTo(response.getOutputStream()發(fā)送內容。
    ? Content-Type 表示后面的文檔屬于什么MIME類型。Servlet默認為text/plain,但通常需要顯式地指定為text/html。由于經(jīng)常要設置Content-Type,因此HttpServletResponse提供了一個專用的方法setContentTyep。
    ? Date 當前的GMT時間。你可以用setDateHeader來設置這個頭以避免轉換時間格式的麻煩。
    ? Expires 應該在什么時候認為文檔已經(jīng)過期,從而不再緩存它?
    ? Last-Modified 文檔的最后改動時間。客戶可以通過If-Modified-Since請求頭提供一個日期,該請求將被視為一個條件GET,只有改動時間遲于指定時間的文檔才會返回,否則返回一個304(Not Modified)狀態(tài)。Last-Modified也可用setDateHeader方法來設置。
    ? Location 表示客戶應當?shù)侥睦锶ヌ崛∥臋n。Location通常不是直接設置的,而是通過HttpServletResponse的sendRedirect方法,該方法同時設置狀態(tài)代碼為302。
    ? Refresh 表示瀏覽器應該在多少時間之后刷新文檔,以秒計。除了刷新當前文檔之外,你還可以通過setHeader("Refresh", "5; URL=http://host/path")讓瀏覽器讀取指定的頁面。
    ? 注意這種功能通常是通過設置HTML頁面HEAD區(qū)的<META HTTP-EQUIV="Refresh" CONTENT="5;URL=http://host/path">實現(xiàn),這是因為,自動刷新或重定向對于那些不能使用CGI或Servlet的HTML編寫者十分重要。但是,對于Servlet來說,直接設置Refresh頭更加方便。
    ??
    ? 注意Refresh的意義是“N秒之后刷新本頁面或訪問指定頁面”,而不是“每隔N秒刷新本頁面或訪問指定頁面”。因此,連續(xù)刷新要求每次都發(fā)送一個Refresh頭,而發(fā)送204狀態(tài)代碼則可以阻止瀏覽器繼續(xù)刷新,不管是使用Refresh頭還是<META HTTP-EQUIV="Refresh" ...>。
    ??
    ? 注意Refresh頭不屬于HTTP 1.1正式規(guī)范的一部分,而是一個擴展,但Netscape和IE都支持它。
    ??
    ? Server 服務器名字。Servlet一般不設置這個值,而是由Web服務器自己設置。
    ? Set-Cookie 設置和頁面關聯(lián)的Cookie。Servlet不應使用response.setHeader("Set-Cookie", ...),而是應使用HttpServletResponse提供的專用方法addCookie。參見下文有關Cookie設置的討論。
    ? WWW-Authenticate 客戶應該在Authorization頭中提供什么類型的授權信息?在包含401(Unauthorized)狀態(tài)行的應答中這個頭是必需的。例如,response.setHeader("WWW-Authenticate", "BASIC realm=\"executives\"")。
    ? 注意Servlet一般不進行這方面的處理,而是讓Web服務器的專門機制來控制受密碼保護頁面的訪問(例如.htaccess)。
    ??
    ??
    ??
    ?    8.3 實例:內容改變時自動刷新頁面
    ??
    ?    下面這個Servlet用來計算大素數(shù)。因為計算非常大的數(shù)字(例如500位)可能要花不少時間,所以Servlet將立即返回已經(jīng)找到的結果,同時在后臺繼續(xù)計算。后臺計算使用一個優(yōu)先級較低的線程以避免過多地影響Web服務器的性能。如果計算還沒有完成,Servlet通過發(fā)送Refresh頭指示瀏覽器在幾秒之后繼續(xù)請求新的內容。
    ??
    ?    注意,本例除了說明HTTP應答頭的用處之外,還顯示了Servlet的另外兩個很有價值的功能。首先,它表明Servlet能夠處理多個并發(fā)的連接,每個都有自己的線程。Servlet維護了一份已有素數(shù)計算請求的Vector表,通過查找素數(shù)個數(shù)(素數(shù)列表的長度)和數(shù)字個數(shù)(每個素數(shù)的長度)將當前請求和已有請求相匹配,把所有這些請求同步到這個列表上。第二,本例證明,在Servlet中維持請求之間的狀態(tài)信息是非常容易的。維持狀態(tài)信息在傳統(tǒng)的CGI編程中是一件很麻煩的事情。由于維持了狀態(tài)信息,瀏覽器能夠在刷新頁面時訪問到正在進行的計算過程,同時也使得Servlet能夠保存一個有關最近請求結果的列表,當一個新的請求指定了和最近請求相同的參數(shù)時可以立即返回結果。
    ??
    ?    PrimeNumbers.java
    ??
    ?    注意,該Servlet要用到前面給出的ServletUtilities.java。另外還要用到:PrimeList.java,用于在后臺線程中創(chuàng)建一個素數(shù)的Vector;Primes.java,用于隨機生成BigInteger類型的大數(shù)字,檢查它們是否是素數(shù)。(此處略去PrimeList.java和Primes.java的代碼。)
    ? package hall;
    ??
    ? import java.io.*;
    ? import javax.servlet.*;
    ? import javax.servlet.http.*;
    ? import java.util.*;
    ??
    ? public class PrimeNumbers extends HttpServlet {
    ?? private static Vector primeListVector = new Vector();
    ?? private static int maxPrimeLists = 30;
    ??
    ?? public void doGet(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? int numPrimes = ServletUtilities.getIntParameter(request, "numPrimes", 50);
    ?? int numDigits = ServletUtilities.getIntParameter(request, "numDigits", 120);
    ?? PrimeList primeList = findPrimeList(primeListVector, numPrimes, numDigits);
    ?? if (primeList == null) {
    ?? primeList = new PrimeList(numPrimes, numDigits, true);
    ?? synchronized(primeListVector) {
    ?? if (primeListVector.size() >= maxPrimeLists)
    ?? primeListVector.removeElementAt(0);
    ?? primeListVector.addElement(primeList);
    ?? }
    ?? }
    ?? Vector currentPrimes = primeList.getPrimes();
    ?? int numCurrentPrimes = currentPrimes.size();
    ?? int numPrimesRemaining = (numPrimes - numCurrentPrimes);
    ?? boolean isLastResult = (numPrimesRemaining == 0);
    ?? if (!isLastResult) {
    ?? response.setHeader("Refresh", "5");
    ?? }
    ?? response.setContentType("text/html");
    ?? PrintWriter out = response.getWriter();
    ?? String title = "Some " + numDigits + "-Digit Prime Numbers";
    ?? out.println(ServletUtilities.headWithTitle(title) +
    ?? "<BODY BGCOLOR=\"#FDF5E6\">\n" +
    ?? "<H2 ALIGN=CENTER>" + title + "</H2>\n" +
    ?? "<H3>Primes found with " + numDigits +
    ?? " or more digits: " + numCurrentPrimes + ".</H3>");
    ?? if (isLastResult)
    ?? out.println("<B>Done searching.</B>");
    ?? else
    ?? out.println("<B>Still looking for " + numPrimesRemaining +
    ?? " more<BLINK>...</BLINK></B>");
    ?? out.println("<OL>");
    ?? for(int i=0; i<numCurrentPrimes; i++) {
    ?? out.println(" <LI>" + currentPrimes.elementAt(i));
    ?? }
    ?? out.println("</OL>");
    ?? out.println("</BODY></HTML>");
    ?? }
    ??
    ?? public void doPost(HttpServletRequest request,
    ?? HttpServletResponse response)
    ?? throws ServletException, IOException {
    ?? doGet(request, response);
    ?? }
    ??
    ?? // 檢查是否存在同類型請求(已經(jīng)完成,或者正在計算)。
    ?? // 如存在,則返回現(xiàn)有結果而不是啟動新的后臺線程。
    ?? private PrimeList findPrimeList(Vector primeListVector,
    ?? int numPrimes,
    ?? int numDigits) {
    ?? synchronized(primeListVector) {
    ?? for(int i=0; i<primeListVector.size(); i++) {
    ?? PrimeList primes = (PrimeList)primeListVector.elementAt(i);
    ?? if ((numPrimes == primes.numPrimes()) &&
    ?? (numDigits == primes.numDigits()))
    ?? return(primes);
    ?? }
    ?? return(null);
    ?? }
    ?? }
    ? }?


    posted on 2006-09-01 17:20 阿成 閱讀(617) 評論(0)  編輯  收藏 所屬分類: JSP&SERVLET
    主站蜘蛛池模板: 最近免费中文字幕大全视频| 亚洲精品中文字幕乱码| 国产成人免费网站| 国产精品成人免费观看| 亚洲色成人网站WWW永久四虎| 亚洲AV永久精品爱情岛论坛| 亚洲AV蜜桃永久无码精品| 一二三四在线播放免费观看中文版视频| 三年片免费高清版| 色www免费视频| 亚洲另类无码专区首页| 亚洲欧洲精品在线| 精品亚洲成a人片在线观看| 亚洲无人区一区二区三区| 免费在线观看一级毛片| 日日夜夜精品免费视频| 在线观看免费人成视频色9| 最近2019免费中文字幕视频三 | 日韩在线播放全免费| 久久精品中文字幕免费| 四虎国产精品免费永久在线| 乱淫片免费影院观看| 深夜特黄a级毛片免费播放| 国产99久久亚洲综合精品| 91在线亚洲综合在线| 亚洲成年网站在线观看| 亚洲人成网站18禁止久久影院| 精品日韩亚洲AV无码| 亚洲综合色丁香麻豆| 亚洲一区二区电影| 亚洲人成网站影音先锋播放| 亚洲国产女人aaa毛片在线| 久久亚洲国产精品| 亚洲黄色片在线观看| 亚洲精品综合久久中文字幕| 久久99亚洲网美利坚合众国| 亚洲免费网站在线观看| 国产精品高清视亚洲一区二区| 在线精品亚洲一区二区| 亚洲大码熟女在线观看| 疯狂做受xxxx高潮视频免费|