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

2014-11-24 01:34:34 · 作者: · 浏览: 1
at 的配置文件server.XML 稍作修改。默认情况下启用的是更典型的同步IO 连接器。现在只需将它切换成异步版本,如清单1 所示。

  清单1. 修改Tomcat 的server.xml

 

   

coyote.http11.Http11NioProtocol" redirectPort="8443"/> 

Servlet。这显然是Tomcat 特有的一个接口。清单2 显示了一个这样的例子。

  清单2. Tomcat Comet servlet

 

 public class TomcatWeatherServlet extends HttpServlet implements CometProcessor {

  private MessageSender messageSender = null;

  private static final Integer TIMEOUT = 60 * 1000;

  @Override

  public void destroy() {

  messageSender.stop();

  messageSender = null;

  }

  @Override

  public void init() throws ServletException {

  messageSender = new MessageSender();

  Thread messageSenderThread =

  new Thread(messageSender, "MessageSender[" + getServletContext()

  .getContextPath() + "]");

  messageSenderThread.setDaemon(true);

  messageSenderThread.start();

  }

  public void event(final CometEvent event) throws IOException, ServletException {

  HttpServletRequest request = event.getHttpServletRequest();

  HttpServletResponse response = event.getHttpServletResponse();

  if (event.getEventType() == CometEvent.EventType.BEGIN) {

  request.setAttribute("org.apache.tomcat.comet.timeout", TIMEOUT);

  log("Begin for session: " + request.getSession(true).getId());

  messageSender.setConnection(response);

  Weatherman weatherman = new Weatherman(95118, 32408);

  new Thread(weatherman).start();

  } else if (event.getEventType() == CometEvent.EventType.ERROR) {

  log("Error for session: " + request.getSession(true).getId());

  event.close();

  } else if (event.getEventType() == CometEvent.EventType.END) {

  log("End for session: " + request.getSession(true).getId());

  event.close();

  } else if (event.getEventType() == CometEvent.EventType.READ) {

  throw new UnsupportedOperationException("This servlet does not accept

  data");

  }

  }

  }

  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);

  }