点击这里给我发消息 点击这里给我发消息

使用Java实现Comet风格的Web应用(二)

添加时间:2013-12-7
    相关阅读: 设计 程序 HTML JavaScript JSP

    CometProcessor 接口要求实现 event 方法。这是用于 Comet 交互的一个生命周期方法。Tomcat 将使用不同的 CometEvent 实例调用。通过检查 CometEvent 的 eventType,可以判断正处在生命周期的哪个阶段。当请求第一次传入时,即发生 BEGIN 事件。READ 事件表明数据正在被发送,只有当请求为 POST 时才需要该事件。遇到 END 或 ERROR 事件时,请求终止。

    在清单 2 的例子中,Servlet 使用一个 MessageSender 类发送数据。这个类的实例是在 servlet 的 init 方法中在其自身的线程中创建,并在 servlet 的 destroy 方法中销毁的。清单 3 显示了 MessageSender.

    清单 3. MessageSender

 private class MessageSender implements Runnable {
  protected boolean running = true;
  protected final ArrayList messages = new ArrayList();
  private ServletResponse connection;
  private synchronized void setConnection(ServletResponse connection){
  this.connection = connection;
  notify();
  }
  public void send(String message) {
  synchronized (messages) {
  messages.add(message);
  log("Message added #messages=" + messages.size());
  messages.notify();
  }
  }
  public void run() {
  while (running) {
  if (messages.size() == 0) {
  try {
  synchronized (messages) {
  messages.wait();
  }
  } catch (InterruptedException e) {
  // Ignore
  }
  }
  String[] pendingMessages = null;
  synchronized (messages) {
  pendingMessages = messages.toArray(new String[0]);
  messages.clear();
  }
  try {
  if (connection == null){
  try{
  synchronized(this){
  wait();
  }
  } catch (InterruptedException e){
  // Ignore
  }
  }
  PrintWriter writer = connection.getWriter();
  for (int j = 0; j < pendingMessages.length; j++) {
  final String forecast = pendingMessages[j] + "
";
  writer.println(forecast);
  log("Writing:" + forecast);
  }
  writer.flush();
  writer.close();
  connection = null;
  log("Closing connection");
  } catch (IOException e) {
  log("IOExeption sending message", e);
  }
  }
  }
  }

    这个类基本上是样板代码,与 Comet 没有直接的关系。但是,有两点要注意。这个类含有一个 ServletResponse 对象。回头看看清单 2 中的 event 方法,当事件为 BEGIN 时,response 对象被传入到 MessageSender 中。在 MessageSender 的 run 方法中,它使用 ServletResponse 将数据发送回客户机。注意,一旦发送完所有排队等待的消息后,它将关闭连接。这样就实现了长轮询。如果要实现流风格的 Comet,那么需要使连接保持开启,但是仍然刷新数据。

    回头看清单 2 可以发现,其中创建了一个 Weatherman 类。正是这个类使用 MessageSender 将数据发送回客户机。这个类使用 Yahoo RSS feed 获得不同地区的天气信息,并将该信息发送到客户机。这是一个特别设计的例子,用于模拟以异步方式发送数据的数据源。清单 4 显示了它的代码。

    清单 4. Weatherman

 private class Weatherman implements Runnable{
  private final List zipCodes;
  private final String YAHOO_WEATHER = "http://weather.yahooapis.com/forecastrss?p=";
  public Weatherman(Integer... zips) {
  zipCodes = new ArrayList(zips.length);
  for (Integer zip : zips) {
  try {
  zipCodes.add(new URL(YAHOO_WEATHER + zip));
  } catch (Exception e) {
  // dont add it if it sucks
  }
  }
  }
  public void run() {
  int i = 0;
  while (i >= 0) {
  int j = i % zipCodes.size();
  SyndFeedInput input = new SyndFeedInput();
  try {
  SyndFeed feed = input.build(new InputStreamReader(zipCodes.get(j)
  .openStream()));
  SyndEntry entry = (SyndEntry) feed.getEntries().get(0);
  messageSender.send(entryToHtml(entry));
  Thread.sleep(30000L);
  } catch (Exception e) {
  // just eat it, eat it
  }
  i++;
  }
  }
  private String entryToHtml(SyndEntry entry){
  StringBuilder html = new StringBuilder("
");
  html.append(entry.getTitle());
  html.append("
");
  html.append(entry.getDescription().getValue());
  return html.toString();
  }
  }

    这个类使用 Project Rome 库解析来自 Yahoo Weather 的 RSS feed.如果需要生成或使用 RSS 或 Atom feed,这是一个非常有用的库。此外,这个代码中只有一个地方值得注意,那就是它产生另一个线程,用于每过 30 秒钟发送一次天气数据。最后,我们再看一个地方:使用该 Servlet 的客户机代码。在这种情况下,一个简单的 JSP 加上少量的 JavaScript 就足够了。清单 5 显示了该代码。

    清单 5. 客户机 Comet 代码

 <%@page contentType="text/html" pageEncoding="UTF-8"%> 
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" 
  "http://www.w3.org/TR/html4/loose.dtd"> 
 
<html> 
  <head> 
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 
    <title>Comet Weather</title> 
    <SCRIPT TYPE="text/Javascript"> 
      function go(){ 
        var url = "http://localhost:8484/WeatherServer/Weather" 
        var request = new XMLHttpRequest(); 
        request.open("GET", url, true); 
        request.setRequestHeader("Content-Type","application/x-javascript;"); 
        request.onreadystatechange = function() { 
          if (request.readyState == 4) { 
            if (request.status == 200){ 
              if (request.responseText) { 
                document.getElementById("forecasts").innerHTML = 
request.responseText; 
              } 
            } 
            go(); 
          } 
        }; 
        request.send(null); 
      } 
    </SCRIPT> 
  </head> 
  <body> 
    <h1>Rapid Fire Weather</h1> 
    <input type="button" onclick="go()" value="Go!"></input> 
    <div id="forecasts"></div> 
  </body> 
</html> 

 

    该代码只是在用户单击 Go 按钮时开始长轮询。注意,它直接使用 XMLHttpRequest 对象,所以这在 Internet Explorer 6 中将不能工作。您可能需要使用一个 Ajax 库解决浏览器差异问题。除此之外,惟一需要注意的是回调函数,或者为请求的 onreadystatechange 函数创建的闭包。该函数粘贴来自服务器的新的数据,然后重新调用 go 函数。

    现在,我们看过了一个简单的 Comet 应用程序在 Tomcat 上是什么样的。有两件与 Tomcat 密切相关的事情要做:一是配置它的连接器,二是在 Servlet 中实现一个特定于 Tomcat 的接口。您可能想知道,将该代码 “移植” 到 Jetty 有多大难度。接下来我们就来看看这个问题。

[1] [2] [3] 下一页

咨询热线:020-85648757 85648755 85648616 0755-27912581 客服:020-85648756 0755-27912581 业务传真:020-32579052
广州市网景网络科技有限公司 Copyright◎2003-2008 Veelink.com. All Rights Reserved.
广州商务地址:广东省广州市黄埔大道中203号(海景园区)海景花园C栋501室
= 深圳商务地址:深圳市宝源路华丰宝源大厦606
研发中心:广东广州市天河软件园海景园区 粤ICP备05103322号 工商注册