Servlet 3.0 异步传输的使用
AjaxCometServlet.java
ajax.js
index.html
servlet形成异步处理的过程
AjaxCometServlet.java
- import java.io.IOException;
- import java.io.PrintWriter;
- import java.util.Queue;
- import java.util.concurrent.ConcurrentLinkedQueue;
- import java.util.concurrent.BlockingQueue;
- import java.util.concurrent.LinkedBlockingQueue;
- import javax.servlet.AsyncContext;
- import javax.servlet.AsyncEvent;
- import javax.servlet.AsyncListener;
- import javax.servlet.ServletConfig;
- import javax.servlet.ServletException;
- import javax.servlet.annotation.WebServlet;
- import javax.servlet.http.HttpServlet;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- @WebServlet(urlPatterns = {"/chat"}, asyncSupported = true)
- public class AjaxCometServlet extends HttpServlet {
- private static final Queue<AsyncContext> queue = new ConcurrentLinkedQueue<AsyncContext>();
- private static final BlockingQueue<String> messageQueue = new LinkedBlockingQueue<String>();
- private static final String BEGIN_SCRIPT_TAG = "<script type='text/javascript'>\n";
- private static final String END_SCRIPT_TAG = "</script>\n";
- private static final long serialVersionUID = -2919167206889576860L;
- private Thread notifierThread = null;
- @Override
- public void init(ServletConfig config) throws ServletException {
- Runnable notifierRunnable = new Runnable() {
- public void run() {
- boolean done = false;
- while (!done) {
- String cMessage = null;
- try {
- cMessage = messageQueue.take();
- for (AsyncContext ac : queue) {
- try {
- PrintWriter acWriter = ac.getResponse().getWriter();
- acWriter.println(cMessage);
- acWriter.flush();
- } catch(IOException ex) {
- System.out.println(ex);
- queue.remove(ac);
- }
- }
- } catch(InterruptedException iex) {
- done = true;
- System.out.println(iex);
- }
- }
- }
- };
- notifierThread = new Thread(notifierRunnable);
- notifierThread.start();
- }
- @Override
- protected void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
- res.setContentType("text/html");
- res.setHeader("Cache-Control", "private");
- res.setHeader("Pragma", "no-cache");
- PrintWriter writer = res.getWriter();
- // for IE
- writer.println("<!-- Comet is a programming technique that enables web servers to send data to the client without having any need for the client to request it. -->\n");
- writer.flush();
- final AsyncContext ac = req.startAsync();
- ac.setTimeout(10 * 60 * 1000);
- ac.addListener(new AsyncListener() {
- public void onComplete(AsyncEvent event) throws IOException {
- queue.remove(ac);
- }
- public void onTimeout(AsyncEvent event) throws IOException {
- queue.remove(ac);
- }
- public void onError(AsyncEvent event) throws IOException {
- queue.remove(ac);
- }
- public void onStartAsync(AsyncEvent event) throws IOException {
- }
- });
- queue.add(ac);
- }
- @Override
- @SuppressWarnings("unchecked")
- protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
- res.setContentType("text/plain");
- res.setHeader("Cache-Control", "private");
- res.setHeader("Pragma", "no-cache");
- req.setCharacterEncoding("UTF-8");
- String action = req.getParameter("action");
- String name = req.getParameter("name");
- if ("login".equals(action)) {
- String cMessage = BEGIN_SCRIPT_TAG + toJsonp("System Message", name + " has joined.") + END_SCRIPT_TAG;
- notify(cMessage);
- res.getWriter().println("success");
- } else if ("post".equals(action)) {
- String message = req.getParameter("message");
- String cMessage = BEGIN_SCRIPT_TAG + toJsonp(name, message) + END_SCRIPT_TAG;
- notify(cMessage);
- res.getWriter().println("success");
- } else {
- res.sendError(422, "Unprocessable Entity");
- }
- }
- @Override
- public void destroy() {
- queue.clear();
- notifierThread.interrupt();
- }
- private void notify(String cMessage) throws IOException {
- try {
- messageQueue.put(cMessage);
- } catch(Exception ex) {
- IOException t = new IOException();
- t.initCause(ex);
- throw t;
- }
- }
- private String escape(String orig) {
- StringBuffer buffer = new StringBuffer(orig.length());
- for (int i = 0; i < orig.length(); i++) {
- char c = orig.charAt(i);
- switch (c) {
- case '\b':
- buffer.append("\\b");
- break;
- case '\f':
- buffer.append("\\f");
- break;
- case '\n':
- buffer.append("<br />");
- break;
- case '\r':
- // ignore
- break;
- case '\t':
- buffer.append("\\t");
- break;
- case '\'':
- buffer.append("\\'");
- break;
- case '\"':
- buffer.append("\\\"");
- break;
- case '\\':
- buffer.append("\\\\");
- break;
- case '<':
- buffer.append("<");
- break;
- case '>':
- buffer.append(">");
- break;
- case '&':
- buffer.append("&");
- break;
- default:
- buffer.append(c);
- }
- }
- return buffer.toString();
- }
- private String toJsonp(String name, String message) {
- return "window.parent.update({ name: \"" + escape(name) + "\", message: \"" + escape(message) + "\" });\n";
- }
- }
ajax.js
- var count=0;
- $(document).ready(function () {
- var url='/ajax/chat';
- $('#login-name').focus();
- $('#comet-frame')[0].src=url + '?' + count;
- count ++;
- $("#login-button").click(function () {
- var name = $('#login-name').val();
- if(! name.length > 0) {
- $('#system-message').css("color","red");
- $('#login-name').focus();
- return;
- }
- $('#system-message').css("color","#2d2b3d") ;
- $('#system-message').html(name + ':');
- $('#login-button').disabled = true;
- $('#login-form').css("display","none");
- $('#message-form').css("display","");
- var query =
- 'action=login' +
- '&name=' + encodeURI($('#login-name').val());
- $.ajax({
- type:"post",
- url:url,
- data:query,
- success:function (data,status) {
- $('#message').focus();
- },
- error:function () {
- alert("error occured!!!");
- }
- });
- });
- $("#post-button").click(function () {
- var message = $('#message').val();
- if(!message > 0) {
- return;
- }
- $('#message').disabled = true;
- $('#post-button').disabled = true;
- var query =
- 'action=post' +
- '&name=' + encodeURI($('#login-name').val()) +
- '&message=' + encodeURI(message);
- $.ajax({
- type:"post",
- url:url,
- data:query,
- success:function (data,status) {
- $('#message').disabled = false;
- $('#post-button').disabled = false;
- $('#message').focus();
- $('#message').val("");
- },
- error:function () {
- alert("error occured!!!");
- }
- });
- }
- )
- });
- function update(data){
- var p = document.createElement('p');
- p.innerHTML = data.name + ':<br/>' + data.message;
- $('#display')[0].appendChild(p);
- }
index.html
- <?xml version="1.0" encoding="UTF-8" ?>
- <!DOCTYPE html
- PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
- <title>Glassfish Chat</title>
- <link rel="stylesheet" href="css/default.css" type="text/css" />
- <script type="text/javascript" src="js/jquery-1.4.min.js"></script>
- <script type="text/javascript" src="js/ajax.js"></script>
- </head>
- <body>
- <div id="container">
- <div id="container-inner">
- <div id="header">
- <h1>Glassfish Chat</h1>
- </div>
- <div id="main">
- <div id="display">
- </div>
- <div id="form">
- <div id="system-message">Please input your name:</div>
- <div id="login-form">
- <input id="login-name" type="text" />
- <br />
- <input id="login-button" type="button" value="Login" />
- </div>
- <div id="message-form" style="display: none;">
- <div>
- <textarea id="message" name="message" rows="2" cols="40"></textarea>
- <br />
- <input id="post-button" type="button" value="Post Message" />
- </div>
- </div>
- </div>
- </div>
- </div>
- </div>
- <iframe id="comet-frame" style="display: none;"></iframe>
- </body>
- </html>
servlet形成异步处理的过程
- 在init()方法里,开启一条线程从消息队列中读取消息,并且在AsyncContext(异步上下文)中处理这些消息
- PrintWriter acWriter = ac.getResponse().getWriter();
- acWriter.println(cMessage);
- acWriter.flush();
前面的代码写入一个信息,每个异步请求对应AsyncContext。 - 在doGet方法里,设置超时时间为10分钟,开始异步并且增加AsyncContext到队列中
- final AsyncContext ac = req.startAsync();
- ac.setTimeout(10 * 60 * 1000);
- queue.add(ac);
对AsyncContext注册AsyncListener 监听器,在这种情况下,只有从队列中清理AsyncContext
- ac.addListener(new AsyncListener() {
- public void onComplete(AsyncEvent event) throws IOException {
- queue.remove(ac);
- }
- public void onTimeout(AsyncEvent event) throws IOException {
- queue.remove(ac);
- }
- public void onError(AsyncEvent event) throws IOException {
- queue.remove(ac);
- }
- public void onStartAsync(AsyncEvent event) throws IOException {
- }
- });
- 在doPost方法里,将消息加入到消息队列里,消息在init方法里开启线程执行
问题:不明白在ajax.js中为什么要加上$('#comet-frame')[0].src=url + '?' + count;这么一句,不加上的话,页面显示不了!