Java中实现Comet风格的Web应用 (三)

2014-11-24 01:34:34 · 作者: · 浏览: 3

  }

  }

  }

  这个类基本上是样板代码,与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 加上少量的java script 就足够了。清单5 显示了该代码。

  清单5. 客户机 Comet 代码

  <%@page contentType="text/html" pageEncoding="UTF-8"%> 

  "http://www.w3.org/TR/html4/loose.dtd"> 

 

 

   

     

    Comet Weather 

    <SCRIPT TYPE="text/java script"> 

      function go(){ 

        var url = "http://localhost:8484/WeatherServer/Weather" 

        var request = new XMLHttpRequest(); 

        request.open("GET", url, true); 

        request.setRequestHeader("Content-Type","application/x-java script;"); 

        request.onreadystatechange = function() { 

          if (request.readyState == 4) { 

            if (request.status == 200){ 

              if (request.responseText) { 

                document.getElementById("forecasts").innerHTML = 

request.responseText; 

              } 

            } 

            go(); 

          } 

        }; 

        request.send(null); 

      } 

     

   

   

    

Rapid Fire Weather

 

     

    

 

   

 

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

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

  Jetty 和Comet

  Jetty 服务器使用稍微不同的技术来支持Comet 的可伸缩的实现。Jetty 支持被称作continuations 的编程结构。其思想很简单。请求先被暂停,然后在将来的某个时间点再继续。规定时间到期,或者某种有意义的事件发生,都可能导致请求继续。当请求被暂停时,它的线程被释放。

  可以使用Jetty 的org.mortbay.util.ajax.ContinuationSupport 类为任何HttpServletRequest 创建org.mortbay.util.ajax.Continuation 的一个实例。这种方法与Comet 有很大的不同。但是,continuations 可用于实现逻辑上等效的Comet。清单6 显示清单2 中的weather servlet “移植” 到Jetty 后的代码。

  清单6. Jetty Comet servlet

  public class JettyWeatherServlet extends HttpServlet {

  private MessageSender messageSender = null;

  private static final Integer TIMEOUT = 5 * 1000;

  public void begin(HttpServletRequest request, HttpServletResponse response)

  throws IOException, ServletException {