孤独的猫

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

本章中我们将讨论servlet链,它是JvavServer体系结构的高级特征之一。

4.1 什么是servlet链

  与UNIX和DOS命令中的管道类似,你也可以将多个servlet以特定顺序链接起来。在servlet链中,一个servlet的输出被当作下一个servlet的输入,而链中最后一个servlet的输出被返回到浏览器。

4.2 servlet链接的实例:表过滤器(Table Filter)

  让我们马上就看看如何编写一个可以被用于链接的servlet吧。这个表过滤器servlet将分析另一个servlet的输出,查找含有特殊表格式指令的HTML注释,这些指令包括表有多少列、是否显示表头等等。在该表格式指令之后的所有行将会被格式化成一个HTML表格。这样,链中的前一个servlet只要简单地将数据用逗号分割,数据就可以一行一行地直接输出了,而无须将这些数据格式化成HTML表格。同样,当你决定修改这个表格的格式时,你就不必修改产生数据的servlet,而直接修改这个表过滤器servlet就可以了。
这个表过滤器servlet实现了HTTP服务的方法。首先,它必须重复(echo)上一个servlet中设置的头信息。这些信息包括内容类型、调用的URL、远程主机等等。

package javaservlets.samples;

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

public class TableFilter extends HttpServlet
{
/**
*

Performs an HTTP service request * * @param req The request from the client * @param resp The response from the servlet */ public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, java.io.IOException { // Get all headers set by the previous servlet and echo them java.util.Enumeration e = req.getHeaderNames(); while (e.hasMoreElements()) { String header = (String)e.nextElement(); String value = req.getHeader(header); resp.setHeader(header, value); }

  接下来的步骤是取得一个可以从前一个servlet的输出流中讲读取数据的输入流,如果该输入流的内容类型是某种我们可以分析的类型(如HTML),我们就继续下一步骤;否则,我们就从输入流中读出所有字节并毫不修改地把它们写回浏览器。

// Get the input and output streams
ServletInputStream in = req.getInputStream(); 
ServletOutputStream out = resp.getOutputStream();
// Only process if this is a recognized MIME type
String type = req.getContentType();
if (type.equals("text/html") ||
type.equals("text/table") ||
type.equals("application/x-www-form-urlencoded")) {
resp.setContentType("text/html");
// Create a buffered reader that we can use to read
// a single line at a time
BufferedReader br =
new BufferedReader(new InputStreamReader(in));
boolean inTable = false;
int tableCols = 0;
boolean headerRow = false;
// Read until no more data exists
while (true) {
String s = br.readLine();
// null indicates end of file
if (s == null) {
break;
}
// If we are in the middle of a table command, process
// the line
if (inTable) {
// Search for the end of the table  if (s.startsWith("<!--end table")) {
out.println("</table></center>");
inTable = false;
}
else {
// We've got a row of a table - format it
s = formatRow(s, tableCols, headerRow);
headerRow = false;
}
 }
else {
// Search for the start of a table if (s.startsWith("<!--table")) {  int pos = s.indexOf("columns="); cols = cols.substring(0, endPos); } tableCols = Integer.parseInt(cols); } // Get the header flag. If 'yes' the first // row of data is actually a header pos = s.indexOf("header="); if(pos >= 0) { String flag = s.substring(pos + 7); headerRow = flag.startsWith("yes"); } // If we have a valid number of columns, format // the table if (tableCols > 0) { out.println(s);  s = "<center><table border>"; inTable = true; } } } out.println(s); } } else { // Unsupported MIME type; echo the contents unchanged while (true) { int b = in.read(); if (b == -1) { break; } out.write(b); } } out.close(); } /** * <p>Formats the given line into a table row */ private String formatRow(String line, int cols, boolean header) {   String s = "<tr>"; int pos = line.indexOf(","); int lastPos = 0; // Loop for each column for (int i = 0; i < cols; i++) { if (pos < 0) { pos = line.length(); } // Insert the proper HTML tag if (header) { s += "<th>"; } else {   s += "<td>"; } // Find the next column data if (pos > 0) { s += line.substring(lastPos, pos); lastPos = pos; if (pos < line.length()) { lastPos = pos + 1; pos = line.indexOf(",", lastPos); } else { pos = 0; } } // Insert the proper HTML tag if (header) { s += "</th>"; } else {  s += "</td>"; } } // Return the formatted line return s; } /** * <p>Initialize the servlet. This is called once when the * servlet is loaded. It is guaranteed to complete before any * requests are made to the servlet * * @param cfg Servlet configuration information */ public void init(ServletConfig cfg) throws ServletException { super.init(cfg); } /**  * <p>Destroy the servlet. This is called once when the servlet * is unloaded. */ public void destroy() { super.destroy(); } /**  * <p>Returns information about this servlet    */ public String getServletInfo() { return "Table Filter for Chaining"; } }

  接下来的输入流分析就简单得多了。我们只要一行一行地读入表格中的数据,直至找到文件结束标记为止。对每一行,通过查找逗号来取得每一个域,然后将它们格式化成表格的一行。

4.3 触发一个servlet链

  在你将要链接在一起的servlet组织好之后,你可以通过别名、MIME类型或者HTML请求来触发这个servlet链。每一种方式都其特殊的配置,下面我就让我们分别看看如何用Java Web服务器和Live Software的JRun来配置这些触发方法。其他服务器的设置也大致相同。

4.3.1 servlet别名

servlet别名使你可以设置一个servlet名字或别名来表示一个或多个servlet。servlet链可以用servlet列表表示,该列表中的servlet用逗号分开,并按调用次序的先后排列在一起。

  Java Web Server
在配置servlet别名以触发servlet链之前,一定要确认servlet链接功能已经被启动。这里,你可以启用servlet链接功能。
增加一个servlet别名是非常直截了当的事。当服务器接收到对“/Elemetns”的请求,它将调用“javaservlets.samples.Elements”servlet,取得输出后将其传给servlet“javaservlets.samples.TableFilter”作为输入,最后将TableFilter的输出返回给浏览器。你只要简单地将servlet名用逗号分隔开来,就可以将任意数量的servlet链接在一起。
注意,在本书写作时,Java Web Server 1.1还不能完全正确地支持servlet链接。该问题将在后续版本中得到解决。

  JRun
在JRun中,servlet链接的配置是通过设置servlet映射来实现的。JRun中servlet别名与servlet是一一对应的,一个servlet只能与惟一的别名对应,所以servlet别名不能支持servlet链接。而servlet映射可以让你将一个名字与一系列servlet或者servlet别名项对应。

  servlet别名链接的例子:Elements
为了说明如何使用servlet别名来触发servlet链接,我们先编一个用HTML表格列出元素周期表的servlet。Elements Servlet要实现doGet()来响应HTML GET命令。我们还要设置内容类型然后输出HTML首部信息。Elements Servlet没有格式化输出HTML表格,我们将让它输出表过滤器servlet所需的表格格式信息,并简单地一行一行输出用逗号分隔的数据,

 package javaservlets.samples;
import javax.servlet.*;
import javax.servlet.http.*;
/**
* <p>This is a simple servlet that will return a list of
* periodic elements.
*/
public void doGet(HttpServletRequest req,
HttpServletResponse resp)
throws ServletException, java.io.IOException
{
// Create a PrintWriter to write the response
java.io.PrintWriter out =
new java.io.PrintWriter(resp.getOutputStream());
// Set the content type of the response
resp.setContentType("text/html");
// Print the HTML header
out.println("<html>");
out.println("<head>");
out.println("<title>Java Servlets Sample - " +
"Periodic Elements</title>");
out.println("</head>");
out.println("<h2><center>");
out.println("The Periodic Elements</center></h2>");
out.println("<br>");
// Output special table formatting instructions for
// the TableFilter servlet 
out.println("<!--table columns=2 header=yes-->");
// Output the table
out.println("Symbol,Element");
out.println("Ac,Actinium");
out.println("Ag,Silver");
out.println("Al,Aluminum");
//Etc... 
out.println("Y,Yttrium");
out.println("Yb,Ytterbium");
out.println("Zn,Zinc");
out.println("Zr,Zirconium");
out.println("<!--end table-->");
// Wrap up
out.println("</html>");
out.flush();
out.close();
}

  在你浏览器的URL中输出“/Elements”调用我们在Jrun中配置的servlet映射。
在刷新时,浏览器向Web服务器发出URL请求,Web服务器找到与这个URL信息对应的servlet映射,然后调用Elemetns Servlet。Elements Servlet处理GET请求并返回未格式化的元素周期表数据给Web服务器。之后,Web服务器发现存在servlet链接,于是将Elements Servlet的输出重定向为servlet链接中下一个servlet,也就是表过滤器的输入。表过滤器重新设置所有HTTP首部,以适应表过滤器的需要,然后读入所有元素周期表数据,表过滤器使用指定的表格格式信息来分析和处理这些数据,最后生成一个格式化的元素周期表。

4.3.2 Mime类型


  触发servlet链接的另外一种方法是将一个servlet与特定Mime类型联系起来。当这种Mime类型的应答产生时,输出就会被发送给与之相联系的servlet。由于MIME类型是在servlet向输出流中写入时才确定的,所以用这种方法你可以轻易地将servlet的输出重定向到其他servlet。

  Java Web Server
如前所述,在所有的工作之前,你必须确认servlet链接功能已经启用。截止到本书发稿,还没有可以管理MIME类型和servlet映射的图形用户接口(GUI),所以你不得不手工编辑“mimeservlets.properties”文件。这个文件位于目录“/<server_root>/properties/server/javawebserver/webpageservice”。值得注意的是,MIME类型所映射的servlet名字实际上是该servlet的别名。

# This file maps mime-types to the servlets which process them
# This is used by the filter manager to set up chains of servlets
# where the ouput of one servlet gets piped to the input of
# another servlet based on the mime-type that the servlet specifies
# with setContentType("mime-type")
#
# The default servlet for all mime-types is file.Do not set this
# explicitly.
#
# Entries in this file should be of the form
# mime-type/servletname
# ie.
# foo/bar=fooServlet
# where fooServlet is defined in servlets.properties
java-internal/parsed-html=ssi
java-internal/template-content=template

  JRun
在JRun中,你可以通过系统管理应用程序设置MIME类型映射。你可以把一个servlet和特定的MIME类型联系起来。

  MIME类型链接的例子:Indy 500
为了说明如何通过MIME类型来触发servlet链接,让我们编写一个列出Indianapolis 500自1911年起的所有优胜者。就像Elements Servlet一样,我们直接将输入用逗号分隔的各行数据,并用表过滤器将其格式化成HTML表格形式输出。惟一的不同在于我们设置了一个不同的MIME类型,通过这个MIME类型,Web服务器将Indy 500 Servlet的输出重定向为表过滤器servlet的输入。

package javaservlets.samples;

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

/**
* <p>This is a simple servlet that will return a list of
* past Indianapolis 500 winners
*/

public class Indy500 extends HttpServlet
{
/**
* <p>Performs the HTTP GET operation
*
* @param req The request from the client
* @param resp The response from the servlet
*/
public void doGet(HttpServletRequest req,HttpServletResponse resp)
throws ServletException, java.io.IOException{
// Create a PrintWriter to write the response
java.io.PrintWriter out =
new java.io.PrintWriter(resp.getOutputStream());
// Set the content type of the response
resp.setContentType("text/table");
// Print the HTML header
out.println("<html>");
out.println("<head>");
out.println("<title>Java Servlets Sample - " +
"Past Indianapolis 500 Winners</title>");
out.println("</head>");
out.println("<h2><center>");
out.println("Past Indianapolis 500 Winners</center></h2>");
out.println("<br>");
// Output special table formatting instructions for
// the TableFilter servlet
out.println("<!--table columns=3 header=yes-->");
out.println("Year,Driver,Average Speed");
out.println("1997,Arie Luyendyk,145.827");
out.println("1996,Buddy Lazier,147.956");
out.println("1995,Jacques Villenueve,153.616");
//Etc... 
out.println("1912,Joe Dawson,78.719");
out.println("1911,Ray Harroun,74.602");
out.println("<!--end table-->");
// Wrap up
out.println("</html>");
out.flush();
out.close();
} 

  通过使用JRun中配置的MIME类型映射,调用Indy 500 Servlet的结果将会是格式化了的Indianapolis 500优胜者列表。值得注意的是,我们只要设置servlet的别名就可以了,而无须指定它的全名。
再次重申,Web浏览器向Web服务器发送的是含有servlet名字的HTTP请求,Web服务器调用了servlet(Indy 500),servlet设置了MIME类型“text/table”,而我们已经将这个MIME类型映射到表过滤器servlet。于是,Indy 500所产生的输出将被重定向为表过滤器servlet的输入,表过滤器servlet将数据格式化为HTML表格的形式,并将输出返回给Web服务器。最后Web服务器把这些HTML页发送给浏览器。

4.3.3.HTTP请求


  触发servlet链接的另一种方法是在HTTP请求中指定servlet链接。不过不是所有的Web服务器都支持这种方法。为了说明HTTP请求中的servlet链接,我们再来编写一个简单的servlet(Solar System),它将返回我们太阳系中所有行星的信息。

package javaservlets.samples;
  import javax.servlet.*;
  import javax.servlet.http.*;
  /**
   * <p>This is a simple servlet that will return a list of
   * the planets in our solar system
   */
  public class SolarSystem extends HttpServlet{
   /**
    * <p>Performs the HTTP GET operation
    *
    * @param req The request from the client
    * @param resp The response from the servlet
    */

   public void doGet(HttpServletRequest req,
      HttpServletResponse resp)
      throws ServletException, java.io.IOException{
    // Set the content type of the response
    resp.setContentType("text/html");
    // Create a PrintWriter to write the response
    java.io.PrintWriter out = new java.io.PrintWriter(resp.getOutputStream());
    // Print the HTML header
    out.println("<html>");
    out.println("<head>");
    out.println("<title>Java Servlets Sample - " +
      "Planets In Our Solar System</title>");
    out.println("</head>");
    out.println("<h2><center>");
    out.println("Planets In Our Solar System</center></h2>");
    out.println("<br>");
    // Output special table formatting instructions for
    // the TableFilter servlet
    out.println("<!--table columns=5 header=yes-->");
    out.println("Planet,Avg. Distance from Sun," +
      "Time to orbit,Time to spin,Moons");
    out.println("Mercury,58 million km,88 days,58.6 days,0");
    out.println("Venus,108 million km,225 days,243 days,0");
    out.println("Earth,150 million km,365.25 days,24 hours,1");
    out.println("Mars,228 million km,687 days,24.62 hours,2");
    out.println("Jupiter,778 million km,11.9 years,9.83 hours,16");
    out.println("Saturn,1427 million km,29.5 years,10.65 hours,19");
    out.println("Uranus,2870 million km,84 years,17.23 hours,15");
    out.println("Neptune,4497 million km,164.8 years,16 hours,8");
    out.println("Pluto,5913 million km,248 years,6.375 days,1");
    out.println("<!--end table-->");
    // Wrap up
    out.println("</html>");
    out.flush();
    out.close();
   }
   /**
    * <p>Initialize the servlet. This is called once when the
    * servlet is loaded. It is guaranteed to complete before any
    * requests are made to the servlet
    *
    * @param cfg Servlet configuration information
    */

   public void init(ServletConfig cfg)
    throws ServletException{
    super.init(cfg);
   }

   /**
    * <p>Destroy the servlet. This is called once when the servlet
    * is unloaded.
    */

   public void destroy()
    super.destroy();
   }
  }

  和前面的servlet一样,Solar System Servlet将直接输出无格式的数据而将格式化的工作交由表过滤器servlet来完成。由于Java Web Server不支持用HTTP请求触发servlet链接,我们将使用JRun。
请注意调用servlet时使用的URL,在这种情况下,URL中包含了一个链接在一起的servlet名字,它们之间用逗号来间隔。

4.4 小结

  在本章中,我们讨论了servlet链接,这是JavaServer体系结构的高级特征之一。servlet链接提供了将一个servlet的输出重定向为另一个servlet的输入的能力。这样,你就可以划分工作,从而使用一系列servlet来实现它。另外,你还可以将servlet组织在一起以提供新的功能。
接下来,我们将致力于JavaServer体系结构的另一个高级特征:Server-Side Include。这一特征使你可以在HTML文档中嵌入servlet。

posted on 2012-06-15 20:03  孤独的猫  阅读(1089)  评论(0编辑  收藏  举报