源地址链接: http://www.ibm.com/developerworks/cn/web/wa-cometjava/
import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; import java.net.URL; import java.util.ArrayList; import java.util.List; import javax.servlet.ServletException; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.catalina.CometEvent; import org.apache.catalina.CometProcessor; import com.sun.syndication.feed.synd.SyndEntry; import com.sun.syndication.feed.synd.SyndFeed; import com.sun.syndication.io.SyndFeedInput; public class TomcatWeatherServlet extends HttpServlet implements CometProcessor { private MessageSender messageSender = null; private static final Integer TIMEOUT = 60 * 1000; @Override public void destroy() { log("destroy()!!!"); messageSender.stop(); messageSender = null; } @Override public void init() throws ServletException { log("init()!!!"); 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); 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"); } } private class Weatherman { private final List<URL> zipCodes; private final String YAHOO_WEATHER = "http://weather.yahooapis.com/forecastrss?p="; public Weatherman(Integer... zips) { zipCodes = new ArrayList<URL>(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 start() { Runnable r = new Runnable() { 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++; } } }; Thread t = new Thread(r); t.start(); } private String entryToHtml(SyndEntry entry){ StringBuilder html = new StringBuilder("<h2>"); html.append(entry.getTitle()); html.append("</h2>"); html.append(entry.getDescription().getValue()); return html.toString(); } } private class MessageSender implements Runnable { protected boolean running = true; protected final ArrayList<String> messages = new ArrayList<String>(); private ServletResponse connection; private synchronized void setConnection(ServletResponse connection){ this.connection = connection; notify(); } public void stop() { running = false; } /** * Add message for sending. */ public void send(String message) { synchronized (messages) { messages.add(message); log("Message added #messages=" + messages.size()); messages.notify(); } } public void run() { log("start running!!!"); while (running) { if (messages.size() == 0) { try { synchronized (messages) { log("start waiting!!!"); 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] + "<br>"; writer.println(forecast); log("Writing:" + forecast); } writer.flush(); writer.close(); //connection = null; log("Closing connection"); } catch (IOException e) { log("IOExeption sending message", e); } } } } }
<%@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:8080/comet1/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; } }else{ document.getElementById("forecasts").innerHTML = "Error!!!!"; } //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>
做如下修改:
一、JSP页面中客户端多次调用go(),改为一次,把循环调用go()语句注释掉。
二、每次request只有一次write,在MessageSender中注释掉 connection = null; 这样保证每次request有多次response。
Tomcat上说明Comet的模型是“BEGIN -> READ -> READ -> READ -> ERROR/TIMEOUT”,这里使用的get请求,不是post请求,没有使用到Read事件。