Jetty的org.mortbay.util.ajax.Continuation 接口:
suspend(long timeout) Suspend handling. // 相当于Object的wait
resume() Resume the request. // 相当于Object的notify
......
Jetty 的Streaming Comet实现:
/**
*
*/
package comet.first;
import java.io.IOException;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mortbay.util.ajax.Continuation;
import org.mortbay.util.ajax.ContinuationSupport;
/**
* Streaming Comet
*
* @author yangwm Sep 26, 2010 11:47:02 AM
*/
public class JettyCometStreaming extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 8098872819945693191L;
private static final int TIMEOUT = 3 * 1000;
public void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// handle the time request
// get the jetty continuation
Continuation con = ContinuationSupport.getContinuation(req, null);
// set the header
resp.setContentType("text/html");
resp.getWriter().print("service begin " + new Date() + ", ");
resp.getWriter().flush();
// write time periodically
while (resp.getWriter().checkError() == false) { // validity of the response's connection
resp.getWriter().print("first... ");
resp.getWriter().flush();
/*
* 每次被唤醒后恢复执行两次while循环开始 到con.suspend之前的代码 ,
* 因为con.suspend() equivalence con.reset() if con.isPending() is true
* 所以timeout之后重新从service开始执行--> con.suspend() --> con.suspend之后的代码 --> 继续while循环开始到con.suspend之前的代码,
*/
con.suspend(TIMEOUT); // suspend the response
resp.getWriter().print("end!");
resp.getWriter().flush();
}
// ...
}
}
Jetty 的Long Polling Comet实现:
/**
*
*/
package comet.second;
import java.io.IOException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mortbay.util.ajax.Continuation;
import org.mortbay.util.ajax.ContinuationSupport;
/**
* Long Polling Comet
*
* @author yangwm Sep 28, 2010 11:47:02 AM
*/
public class JettyCometLongPolling extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 8098872819945693191L;
private static final int TIMEOUT = 30 * 1000;
public void service(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
// handle the time request
// get the jetty continuation
Continuation con = ContinuationSupport.getContinuation(req, null);
JettyContinuationManager.add("yangwm", con); // id is yangwm
// set the header
resp.setContentType("text/html");
// write continuation resume
if(con.suspend(TIMEOUT)) {
System.out.println(con + ", get data: " + con.getObject());
resp.getWriter().write(con + ", get data: " + con.getObject());
resp.getWriter().flush();
} else {
System.out.println(con + ", timeout");
resp.getWriter().write(con + ", timeout");
resp.getWriter().flush();
JettyContinuationManager.clear("yangwm"); // id is yangwm
}
// ...
}
}
/**
*
*/
package comet.second;
import java.io.IOException;
import java.util.Date;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.mortbay.util.ajax.Continuation;
/**
* Notify Comet
*
* @author yangwm Sep 28, 2010 11:47:02 AM
*/
public class JettyCometNotify extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 8098872819945693191L;
public void service(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
// handle the time request
Continuation con = JettyContinuationManager.get("yangwm"); // id is yangwm
System.out.println(con + ", resume: ");
resp.getWriter().write(con + ", resume: ");
resp.getWriter().flush();
if (con != null) {
con.setObject(new Date() + ", it works!");
con.resume();
}
// ...
}
}
package comet.second;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.mortbay.util.ajax.Continuation;
/**
* Continuation Manager
*
* @author yangwm Sep 28, 2010 11:47:02 AM
*/
public class JettyContinuationManager {
private static ConcurrentMap<String, Continuation> map = new ConcurrentHashMap<String, Continuation>();
public static void add(String key, Continuation con) {
map.put(key, con);
}
public static void clear(String key) {
map.remove(key);
}
public static Continuation get(String key){
return map.get(key);
}
}
Jetty 的Streaming Comet与Long Polling实现的配置:
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<display-name>Welcome to testcomet</display-name>
<description>
Welcome to testcomet
</description>
<servlet>
<servlet-name>CometStreaming</servlet-name>
<servlet-class>comet.first.JettyCometStreaming</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CometStreaming</servlet-name>
<url-pattern>/stream</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>CometLongPolling</servlet-name>
<servlet-class>comet.second.JettyCometLongPolling</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CometLongPolling</servlet-name>
<url-pattern>/longPoll</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>CometNotify</servlet-name>
<servlet-class>comet.second.JettyCometNotify</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>CometNotify</servlet-name>
<url-pattern>/notify</url-pattern>
</servlet-mapping>
</web-app>
部署到jetty容器中。
1.Streaming方式:
D:/>curl http://127.0.0.1:8080/testcomet/stream
service begin Tue Sep 28 11:55:08 CST 2010, first... service begin Tue Sep 28 11:55:10 CST 2010, first... end!first...
第一次输出为:service begin Tue Sep 28 11:55:08 CST 2010, first...,也就是从service方法开始执行并不会执行suspend之后的代码。
之后输出为:service begin Tue Sep 28 11:55:10 CST 2010, first... end!first...,也就是从service方法开始执行并会执行suspend之后的代码。
2.Long Polling方式:
D:/>curl http://127.0.0.1:8080/testcomet/longPoll
RetryContinuation@9876930, get data: Tue Sep 28 11:57:04 CST 2010, it works
D:/>curl http://127.0.0.1:8080/testcomet/notify
RetryContinuation@9876930,pending,parked, resume:
先http://127.0.0.1:8080/testcomet/longPoll访问, 再访问http://127.0.0.1:8080/testcomet/notify。
学习参考资料:
TimYang的comet server之java实现:asyncweb,jetty,tomcat,http://hi.baidu.com/jabber/blog/item/f556c7fcbaa76286b801a042.html
Cometd & Jetty Continuations:http://geeklu.com/2010/07/cometd-jetty-continuations/
jetty javadoc: http://jetty.codehaus.org/jetty/jetty-6/apidocs/index.html
Comet (programming) - Wikipedia, the free encyclopedia:http://en.wikipedia.org/wiki/Comet_%28programming%29
Continuations - Jetty - Codehaus:http://docs.codehaus.org/display/JETTY/Continuations
Cometd (aka Bayeux) - Jetty - Codehaus:http://docs.codehaus.org/display/JETTY/Cometd+%28aka+Bayeux%29
Welcome to CometD Project @ The Dojo Foundation | cometd.org:http://www.cometd.org/