﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-小菜毛毛技术分享-文章分类-AJAX</title><link>http://www.blogjava.net/caizh2009/category/39395.html</link><description>与大家共同成长</description><language>zh-cn</language><lastBuildDate>Fri, 05 Feb 2010 10:10:20 GMT</lastBuildDate><pubDate>Fri, 05 Feb 2010 10:10:20 GMT</pubDate><ttl>60</ttl><item><title>Ajax 简单示例</title><link>http://www.blogjava.net/caizh2009/articles/311067.html</link><dc:creator>小菜毛毛</dc:creator><author>小菜毛毛</author><pubDate>Thu, 28 Jan 2010 05:36:00 GMT</pubDate><guid>http://www.blogjava.net/caizh2009/articles/311067.html</guid><wfw:comment>http://www.blogjava.net/caizh2009/comments/311067.html</wfw:comment><comments>http://www.blogjava.net/caizh2009/articles/311067.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/caizh2009/comments/commentRss/311067.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/caizh2009/services/trackbacks/311067.html</trackback:ping><description><![CDATA[<p>&lt;html xmlns="http://www.w3.org/1999/xhtml" &gt;<br />
&lt;head&gt;<br />
&nbsp;&nbsp; &lt;title&gt;xmlhttprequest ajax demo&lt;/title&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;script type ="text/javascript" language ="javascript" &gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var req; //定义变量，用来创建xmlhttprequest对象<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; function creatReq() // 创建xmlhttprequest,ajax开始<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var url="ajaxServer.aspx"; //要请求的服务端地址<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(window.XMLHttpRequest) //非IE浏览器及IE7(7.0及以上版本)，用xmlhttprequest对象创建<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; req=new XMLHttpRequest();<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if(window.ActiveXObject) //IE(6.0及以下版本)浏览器用activexobject对象创建,如果用户浏览器禁用了ActiveX,可能会失败.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; req=new ActiveXObject("Microsoft.XMLHttp");<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(req) //成功创建xmlhttprequest<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; req.open("GET",url,true); //与服务端建立连接(请求方式post或get，地址,true表示异步)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; req.onreadystatechange = callback; //指定回调函数<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; req.send(null); //发送请求<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; function callback() //回调函数，对服务端的响应处理，监视response状态<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(req.readystate==4) //请求状态为4表示成功<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(req.status==200) //http状态200表示OK<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Dispaly(); //所有状态成功，执行此函数，显示数据<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else //http返回状态失败<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; alert("服务端返回状态" + req.statusText);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else //请求状态还没有成功，页面等待<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; document .getElementById ("myTime").innerHTML ="数据加载中";<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; function Dispaly() //接受服务端返回的数据，对其进行显示<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; document .getElementById ("myTime").innerHTML =req.responseText;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &lt;/script&gt;<br />
&lt;/head&gt;<br />
&lt;body&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;div id="myTime"&gt;&lt;/div&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &lt;input id="Button1" type="button" value="Get Time"&nbsp; onclick ="creatReq();"/&gt;<br />
&lt;/body&gt;<br />
&lt;/html&gt;</p>
<p><br />
对于Ajax需要注意执行 的 顺序 </p>
<p>对于input 类型为text的 动作有 onChange() onPropertyChange() 后者比前者的反应 更加的敏感，有可能onChange()不会随着里面的改变而改变，但是onPropertyChange()肯定会随着value的值 改变而改变。 </p>
<p>对于返回的值，要把结果写在最后的条件都符合的情况下，就是注意函数的位置。 </p>
<p>操作xml的时候 用到的方法 和以前操作xml的是一样的，如getElementsByTagName(),还有.firstChild.data,nodeValue. </p>
<p>Ajax执行的顺序是open,onreadystatechange,send 都是基于流的形式传输。</p>
<p>对于servlet里面 注意 都是 用流的形式进行 传输和取值。resp.setContentType()，里面得相应的设置方式。</p>
<p>&nbsp;</p>
<p>本文来自CSDN博客，转载请标明出处：http://blog.csdn.net/zuowangxi/archive/2009/12/30/5104036.aspx</p>
<img src ="http://www.blogjava.net/caizh2009/aggbug/311067.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/caizh2009/" target="_blank">小菜毛毛</a> 2010-01-28 13:36 <a href="http://www.blogjava.net/caizh2009/articles/311067.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于 HTTP 长连接的“服务器推”技术</title><link>http://www.blogjava.net/caizh2009/articles/279721.html</link><dc:creator>小菜毛毛</dc:creator><author>小菜毛毛</author><pubDate>Tue, 02 Jun 2009 15:46:00 GMT</pubDate><guid>http://www.blogjava.net/caizh2009/articles/279721.html</guid><wfw:comment>http://www.blogjava.net/caizh2009/comments/279721.html</wfw:comment><comments>http://www.blogjava.net/caizh2009/articles/279721.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/caizh2009/comments/commentRss/279721.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/caizh2009/services/trackbacks/279721.html</trackback:ping><description><![CDATA[<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td width="100%">
            <h1>Comet：基于 HTTP 长连接的&#8220;服务器推&#8221;技术</h1>
            <img class="display-img" height="6" alt="" src="http://www.ibm.com/i/c.gif" width="1" /></td>
            <td class="no-print" width="192"><img height="18" alt="developerWorks" src="http://www.ibm.com/developerworks/i/dw.gif" width="192" /></td>
        </tr>
    </tbody>
</table>
<table cellspacing="0" cellpadding="0" width="100%" border="0">
    <tbody>
        <tr valign="top">
            <td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td>
            <td width="100%">
            <table class="no-print" cellspacing="0" cellpadding="0" width="160" align="right" border="0">
                <tbody>
                    <tr>
                        <td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td>
                        <td>
                        <table cellspacing="0" cellpadding="0" width="150" border="0">
                            <tbody>
                                <tr>
                                    <td class="v14-header-1-small">文档选项</td>
                                </tr>
                            </tbody>
                        </table>
                        <table class="v14-gray-table-border" cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td class="no-padding" width="150">
                                    <table cellspacing="0" cellpadding="0" width="143" border="0">
                                        <script language="JavaScript" type="text/javascript">
<!-- document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt="" /></td><td width="16"><img alt="将打印机的版面设置成横向打印模式" height="16" src="//www.ibm.com/i/v14/icons/printer.gif" width="16" vspace="3"  /></td><td width="122"><p><strong><a class="smallplainlink" href="javascript:print()">打印本页</a></strong></p></td></tr>');
//-->
</script>
                                        <tbody>
                                            <tr valign="top">
                                                <td width="8"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="8" /></td>
                                                <td width="16"><img height="16" alt="将打印机的版面设置成横向打印模式" src="http://www.ibm.com/i/v14/icons/printer.gif" width="16" vspace="3" /></td>
                                                <td width="122">
                                                <p><strong><a class="smallplainlink" href="javascript:print()" cmimpressionsent="1">打印本页</a></strong></p>
                                                </td>
                                            </tr>
                                            <noscript></noscript>
                                            <form name="email" action="https://www.ibm.com/developerworks/secure/email-it.jsp" cm1="1">
                                                <input type="hidden" value="很多应用譬如监控、即时通信、即时报价系统都需要将后台发生的变化实时传送到客户端而无须客户端不停地刷新、发送请求。本文首先介绍、比较了常用的&#8220;服务器推&#8221;方案，着重介绍了 Comet － 使用 HTTP 长连接、无须浏览器安装插件的两种&#8220;服务器推&#8221;方案：基于 AJAX 的长轮询方式；基于 iframe 及 htmlfile 的流方式。最后分析了开发 Comet 应用需要注意的一些问题，以及如何借助开源的 Comet 框架－pushlet 构建自己的&#8220;服务器推&#8221;应用。" name="body" cM3 cm1="1" cm2="0" /><input type="hidden" value="Comet：基于 HTTP 长连接的&#8220;服务器推&#8221;技术" name="subject" cM3 cm1="1" cm2="1" /><input type="hidden" value="cn" name="lang" cM3 cm1="1" cm2="2" /> <script language="JavaScript" type="text/javascript">
<!-- document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt="" /></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送"  /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><strong>将此页作为电子邮件发送</strong></a></p></td></tr>');
//-->
</script>
                                                <tr valign="top">
                                                    <td width="8"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="8" /></td>
                                                    <td width="16"><img height="16" alt="将此页作为电子邮件发送" src="http://www.ibm.com/i/v14/icons/em.gif" width="16" vspace="3" /></td>
                                                    <td width="122">
                                                    <p><a class="smallplainlink" href="javascript:document.email.submit();" cmimpressionsent="1"><strong>将此页作为电子邮件发送</strong></a></p>
                                                    </td>
                                                </tr>
                                                <noscript></noscript>
                                            </form>
                                        </tbody>
                                    </table>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        <!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- this content will be automatically generated across all content areas --><!--end RESERVED FOR FUTURE USE INCLUDE FILES--><br />
                        </td>
                    </tr>
                </tbody>
            </table>
            <p>级别： 中级</p>
            <p><a href="http://www.ibm.com/developerworks/cn/web/wa-lo-comet/#author" cmimpressionsent="1">周 婷</a> (<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#122;&#104;&#111;&#117;&#116;&#105;&#110;&#103;&#64;&#99;&#110;&#46;&#105;&#98;&#109;&#46;&#99;&#111;&#109;&#63;&#115;&#117;&#98;&#106;&#101;&#99;&#116;&#61;&#67;&#111;&#109;&#101;&#116;&#65306;&#22522;&#20110;&#32;&#72;&#84;&#84;&#80;&#32;&#38271;&#36830;&#25509;&#30340;&#8220;&#26381;&#21153;&#22120;&#25512;&#8221;&#25216;&#26415;" cmimpressionsent="1">zhouting@cn.ibm.com</a>), 软件工程师, IBM 中国软件开发技术实验室<br />
            </p>
            <p>2007 年 8 月 31 日</p>
            <blockquote>很多应用譬如监控、即时通信、即时报价系统都需要将后台发生的变化实时传送到客户端而无须客户端不停地刷新、发送请求。本文首先介绍、比较了常用的&#8220;服务器推&#8221;方案，着重介绍了 Comet － 使用 HTTP 长连接、无须浏览器安装插件的两种&#8220;服务器推&#8221;方案：基于 AJAX 的长轮询方式；基于 iframe 及 htmlfile 的流方式。最后分析了开发 Comet 应用需要注意的一些问题，以及如何借助开源的 Comet 框架－pushlet 构建自己的&#8220;服务器推&#8221;应用。</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
            <p><a name="N10042"><span class="atitle">&#8220;服务器推&#8221;技术的应用</span></a></p>
            <table cellspacing="0" cellpadding="0" width="40%" align="right" border="0">
                <tbody>
                    <tr>
                        <td width="10"><img height="1" alt="" src="http://www.ibm.com/i/c.gif" width="10" /></td>
                        <td>
                        <table cellspacing="0" cellpadding="5" width="100%" border="1">
                            <tbody>
                                <tr>
                                    <td bgcolor="#eeeeee">
                                    <p>请访问 <a href="http://www.ibm.com/developerworks/cn/ajax/" cmimpressionsent="1">Ajax 技术资源中心</a>，这是有关 Ajax 编程模型信息的一站式中心，包括很多文档、教程、论坛、blog、wiki 和新闻。任何 Ajax 的新信息都能在这里找到。
                                    <table cellspacing="0" cellpadding="0" width="80%" border="0">
                                        <tbody>
                                            <tr>
                                                <td colspan="2"><img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="1" border="0" /> </td>
                                            </tr>
                                            <tr valign="top" align="center">
                                                <td colspan="2"><img alt="" src="http://www.ibm.com/i/c.gif" width="5" border="0" /> </td>
                                                <td width="21"><a href="http://www.ibm.com/developerworks/cn/views/rss/customrssatom.jsp?zone_by=Web+services&amp;zone_by=Java+technology&amp;zone_by=XML&amp;zone_by=Open+source&amp;zone_by=WebSphere&amp;zone_by=Web+architecture&amp;type_by=Articles&amp;type_by=Tutorials&amp;search_by=Ajax&amp;day=1&amp;month=01&amp;year=2005&amp;max_entries=20&amp;feed_by=rss&amp;isGUI=true" cmimpressionsent="1"><img height="16" alt="RSS" src="http://www.ibm.com/i/v14/buttons/feed.gif" width="16" border="0" /> </a></td>
                                                <td><a href="http://www.ibm.com/developerworks/cn/views/rss/customrssatom.jsp?zone_by=Web+services&amp;zone_by=Java+technology&amp;zone_by=XML&amp;zone_by=Open+source&amp;zone_by=WebSphere&amp;zone_by=Web+architecture&amp;type_by=Articles&amp;type_by=Tutorials&amp;search_by=Ajax&amp;day=1&amp;month=01&amp;year=2005&amp;max_entries=20&amp;feed_by=rss&amp;isGUI=true" cmimpressionsent="1">订阅 Ajax 相关文章和教程的 RSS 提要</a> </td>
                                            </tr>
                                            <tr>
                                                <td colspan="2"><img height="5" alt="" src="http://www.ibm.com/i/c.gif" width="1" border="0" /> </td>
                                            </tr>
                                        </tbody>
                                    </table>
                                    </p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <p>传统模式的 Web 系统以客户端发出请求、服务器端响应的方式工作。这种方式并不能满足很多现实应用的需求，譬如：</p>
            <ul>
                <li>监控系统：后台硬件热插拔、LED、温度、电压发生变化；
                <li>即时通信系统：其它用户登录、发送信息；
                <li>即时报价系统：后台数据库内容发生变化； </li>
            </ul>
            <p>这些应用都需要服务器能实时地将更新的信息传送到客户端，而无须客户端发出请求。&#8220;服务器推&#8221;技术在现实应用中有一些解决方案，本文将这些解决方案分为两类：一类需要在浏览器端安装插件，基于套接口传送信息，或是使用 RMI、CORBA 进行远程调用；而另一类则无须浏览器安装任何插件、基于 HTTP 长连接。</p>
            <p>将&#8220;服务器推&#8221;应用在 Web 程序中，首先考虑的是如何在功能有限的浏览器端接收、处理信息：</p>
            <ol>
                <li>客户端如何接收、处理信息，是否需要使用套接口或是使用远程调用。客户端呈现给用户的是 HTML 页面还是 Java applet 或 Flash 窗口。如果使用套接口和远程调用，怎么和 JavaScript 结合修改 HTML 的显示。
                <li>客户与服务器端通信的信息格式，采取怎样的出错处理机制。
                <li>客户端是否需要支持不同类型的浏览器如 IE、Firefox，是否需要同时支持 Windows 和 Linux 平台。 </li>
            </ol>
            <br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
                        <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
                    </tr>
                </tbody>
            </table>
            <table class="no-print" cellspacing="0" cellpadding="0" align="right">
                <tbody>
                    <tr align="right">
                        <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                                    </td>
                                    <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/web/wa-lo-comet/#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="N100BD"><span class="atitle">基于客户端套接口的&#8220;服务器推&#8221;技术</span></a></p>
            <p><a name="N100C3"><span class="smalltitle">Flash XMLSocket</span></a></p>
            <p>如果 Web 应用的用户接受应用只有在安装了 Flash 播放器才能正常运行， 那么使用 Flash 的 XMLSocket 也是一个可行的方案。</p>
            <p>这种方案实现的基础是：</p>
            <ol>
                <li>Flash 提供了 XMLSocket 类。
                <li>JavaScript 和 Flash 的紧密结合：在 JavaScript 可以直接调用 Flash 程序提供的接口。 </li>
            </ol>
            <p>具体实现方法：在 HTML 页面中内嵌入一个使用了 XMLSocket 类的 Flash 程序。JavaScript 通过调用此 Flash 程序提供的套接口接口与服务器端的套接口进行通信。JavaScript 在收到服务器端以 XML 格式传送的信息后可以很容易地控制 HTML 页面的内容显示。</p>
            <p>关于如何去构建充当了 JavaScript 与 Flash XMLSocket 桥梁的 Flash 程序，以及如何在 JavaScript 里调用 Flash 提供的接口，我们可以参考 AFLAX（Asynchronous Flash and XML）项目提供的 Socket Demo 以及 SocketJS（请参见 <a href="http://www.ibm.com/developerworks/cn/web/wa-lo-comet/#resources" cmimpressionsent="1">参考资源</a>）。</p>
            <p>Javascript 与 Flash 的紧密结合，极大增强了客户端的处理能力。从 Flash 播放器 V7.0.19 开始，已经取消了 XMLSocket 的端口必须大于 1023 的限制。Linux 平台也支持 Flash XMLSocket 方案。但此方案的缺点在于：</p>
            <ol>
                <li>客户端必须安装 Flash 播放器；
                <li>因为 XMLSocket 没有 HTTP 隧道功能，XMLSocket 类不能自动穿过防火墙；
                <li>因为是使用套接口，需要设置一个通信端口，防火墙、代理服务器也可能对非 HTTP 通道端口进行限制； </li>
            </ol>
            <p>不过这种方案在一些网络聊天室，网络互动游戏中已得到广泛使用。</p>
            <p><a name="N100F3"><span class="smalltitle">Java Applet 套接口 </span></a></p>
            <p>在客户端使用 Java Applet，通过 <code>java.net.Socket</code> 或 <code>java.net.DatagramSocket</code> 或 <code>java.net.MulticastSocket</code> 建立与服务器端的套接口连接，从而实现&#8220;服务器推&#8221;。</p>
            <p>这种方案最大的不足在于 Java applet 在收到服务器端返回的信息后，无法通过 JavaScript 去更新 HTML 页面的内容。 </p>
            <br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
                        <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
                    </tr>
                </tbody>
            </table>
            <table class="no-print" cellspacing="0" cellpadding="0" align="right">
                <tbody>
                    <tr align="right">
                        <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                                    </td>
                                    <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/web/wa-lo-comet/#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="N1010A"><span class="atitle">基于 HTTP 长连接的&#8220;服务器推&#8221;技术</span></a></p>
            <p><a name="N10110"><span class="smalltitle">Comet 简介</span></a></p>
            <p>浏览器作为 Web 应用的前台，自身的处理功能比较有限。浏览器的发展需要客户端升级软件，同时由于客户端浏览器软件的多样性，在某种意义上，也影响了浏览器新技术的推广。在 Web 应用中，浏览器的主要工作是发送请求、解析服务器返回的信息以不同的风格显示。AJAX 是浏览器技术发展的成果，通过在浏览器端发送异步请求，提高了单用户操作的响应性。但 Web 本质上是一个多用户的系统，对任何用户来说，可以认为服务器是另外一个用户。现有 AJAX 技术的发展并不能解决在一个多用户的 Web 应用中，将更新的信息实时传送给客户端，从而用户可能在&#8220;过时&#8221;的信息下进行操作。而 AJAX 的应用又使后台数据更新更加频繁成为可能。</p>
            <br />
            <a name="fig001"><strong>图 1. 传统的 Web 应用模型与基于 AJAX 的模型之比较</strong></a><br />
            <img alt="图 1. 传统的 Web 应用模型与基于 AJAX 的模型之比较" src="http://www.ibm.com/developerworks/cn/web/wa-lo-comet/fig001.jpg" /> <br />
            <p>&#8220;服务器推&#8221;是一种很早就存在的技术，以前在实现上主要是通过客户端的套接口，或是服务器端的远程调用。因为浏览器技术的发展比较缓慢，没有为&#8220;服务器推&#8221;的实现提供很好的支持，在纯浏览器的应用中很难有一个完善的方案去实现&#8220;服务器推&#8221;并用于商业程序。最近几年，因为 AJAX 技术的普及，以及把 IFrame 嵌在&#8220;htmlfile&#8220;的 ActiveX 组件中可以解决 IE 的加载显示问题，一些受欢迎的应用如 meebo，gmail+gtalk 在实现中使用了这些新技术；同时&#8220;服务器推&#8221;在现实应用中确实存在很多需求。因为这些原因，基于纯浏览器的&#8220;服务器推&#8221;技术开始受到较多关注，Alex Russell（Dojo Toolkit 的项目 Lead）称这种基于 HTTP 长连接、无须在浏览器端安装插件的&#8220;服务器推&#8221;技术为&#8220;Comet&#8221;。目前已经出现了一些成熟的 Comet 应用以及各种开源框架；一些 Web 服务器如 Jetty 也在为支持大量并发的长连接进行了很多改进。关于 Comet 技术最新的发展状况请参考关于 Comet 的 wiki。</p>
            <p>下面将介绍两种 Comet 应用的实现模型。</p>
            <p><a name="N1012A"><span class="smalltitle">基于 AJAX 的长轮询（long-polling）方式</span></a></p>
            <p>如 <a href="http://www.ibm.com/developerworks/cn/web/wa-lo-comet/#fig001" cmimpressionsent="1">图 1</a> 所示，AJAX 的出现使得 JavaScript 可以调用 XMLHttpRequest 对象发出 HTTP 请求，JavaScript 响应处理函数根据服务器返回的信息对 HTML 页面的显示进行更新。使用 AJAX 实现&#8220;服务器推&#8221;与传统的 AJAX 应用不同之处在于：</p>
            <ol>
                <li>服务器端会阻塞请求直到有数据传递或超时才返回。
                <li>客户端 JavaScript 响应处理函数会在处理完服务器返回的信息后，再次发出请求，重新建立连接。
                <li>当客户端处理接收的数据、重新建立连接时，服务器端可能有新的数据到达；这些信息会被服务器端保存直到客户端重新建立连接，客户端会一次把当前服务器端所有的信息取回。 </li>
            </ol>
            <br />
            <a name="fig002"><strong>图 2. 基于长轮询的服务器推模型</strong></a><br />
            <img alt="图 2. 基于长轮询的服务器推模型" src="http://www.ibm.com/developerworks/cn/web/wa-lo-comet/fig002.jpg" /> <br />
            <p>一些应用及示例如 &#8220;Meebo&#8221;, &#8220;Pushlet Chat&#8221; 都采用了这种长轮询的方式。相对于&#8220;轮询&#8221;（poll），这种长轮询方式也可以称为&#8220;拉&#8221;（pull）。因为这种方案基于 AJAX，具有以下一些优点：请求异步发出；无须安装插件；IE、Mozilla FireFox 都支持 AJAX。</p>
            <p>在这种长轮询方式下，客户端是在 XMLHttpRequest 的 readystate 为 4（即数据传输结束）时调用回调函数，进行信息处理。当 readystate 为 4 时，数据传输结束，连接已经关闭。Mozilla Firefox 提供了对 Streaming AJAX 的支持， 即 readystate 为 3 时（数据仍在传输中），客户端可以读取数据，从而无须关闭连接，就能读取处理服务器端返回的信息。IE 在 readystate 为 3 时，不能读取服务器返回的数据，目前 IE 不支持基于 Streaming AJAX。</p>
            <p><a name="N10154"><span class="smalltitle">基于 Iframe 及 htmlfile 的流（streaming）方式</span></a></p>
            <p>iframe 是很早就存在的一种 HTML 标记， 通过在 HTML 页面里嵌入一个隐蔵帧，然后将这个隐蔵帧的 SRC 属性设为对一个长连接的请求，服务器端就能源源不断地往客户端输入数据。</p>
            <br />
            <a name="fig003"><strong>图 3. 基于流方式的服务器推模型</strong></a><br />
            <img alt="图 3. 基于流方式的服务器推模型" src="http://www.ibm.com/developerworks/cn/web/wa-lo-comet/fig003.jpg" /> <br />
            <p>上节提到的 AJAX 方案是在 JavaScript 里处理 XMLHttpRequest 从服务器取回的数据，然后 Javascript 可以很方便的去控制 HTML 页面的显示。同样的思路用在 iframe 方案的客户端，iframe 服务器端并不返回直接显示在页面的数据，而是返回对客户端 Javascript 函数的调用，如&#8220;<code>&lt;script type="text/javascript"&gt;js_func(&#8220;data from server &#8221;)&lt;/script&gt;</code>&#8221;。服务器端将返回的数据作为客户端 JavaScript 函数的参数传递；客户端浏览器的 Javascript 引擎在收到服务器返回的 JavaScript 调用时就会去执行代码。</p>
            <p>从 <a href="http://www.ibm.com/developerworks/cn/web/wa-lo-comet/#fig003" cmimpressionsent="1">图 3</a> 可以看到，每次数据传送不会关闭连接，连接只会在通信出现错误时，或是连接重建时关闭（一些防火墙常被设置为丢弃过长的连接， 服务器端可以设置一个超时时间， 超时后通知客户端重新建立连接，并关闭原来的连接）。</p>
            <p>使用 iframe 请求一个长连接有一个很明显的不足之处：IE、Morzilla Firefox 下端的进度栏都会显示加载没有完成，而且 IE 上方的图标会不停的转动，表示加载正在进行。Google 的天才们使用一个称为&#8220;htmlfile&#8221;的 ActiveX 解决了在 IE 中的加载显示问题，并将这种方法用到了 gmail+gtalk 产品中。Alex Russell 在 &#8220;What else is burried down in the depth's of Google's amazing JavaScript?&#8221;文章中介绍了这种方法。Zeitoun 网站提供的 comet-iframe.tar.gz，封装了一个基于 iframe 和 htmlfile 的 JavaScript comet 对象，支持 IE、Mozilla Firefox 浏览器，可以作为参考。（请参见 <a href="http://www.ibm.com/developerworks/cn/web/wa-lo-comet/#resources" cmimpressionsent="1">参考资源</a>）</p>
            <br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
                        <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
                    </tr>
                </tbody>
            </table>
            <table class="no-print" cellspacing="0" cellpadding="0" align="right">
                <tbody>
                    <tr align="right">
                        <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                                    </td>
                                    <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/web/wa-lo-comet/#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="N1017D"><span class="atitle">使用 Comet 模型开发自己的应用</span></a></p>
            <p>上面介绍了两种基于 HTTP 长连接的&#8220;服务器推&#8221;架构，更多描述了客户端处理长连接的技术。对于一个实际的应用而言，系统的稳定性和性能是非常重要的。将 HTTP 长连接用于实际应用，很多细节需要考虑。</p>
            <p><a name="N10186"><span class="smalltitle">不要在同一客户端同时使用超过两个的 HTTP 长连接</span></a></p>
            <p>我们使用 IE 下载文件时会有这样的体验，从同一个 Web 服务器下载文件，最多只能有两个文件同时被下载。第三个文件的下载会被阻塞，直到前面下载的文件下载完毕。这是因为 HTTP 1.1 规范中规定，客户端不应该与服务器端建立超过两个的 HTTP 连接， 新的连接会被阻塞。而 IE 在实现中严格遵守了这种规定。</p>
            <p>HTTP 1.1 对两个长连接的限制，会对使用了长连接的 Web 应用带来如下现象：在客户端如果打开超过两个的 IE 窗口去访问同一个使用了长连接的 Web 服务器，第三个 IE 窗口的 HTTP 请求被前两个窗口的长连接阻塞。</p>
            <p>所以在开发长连接的应用时， 必须注意在使用了多个 frame 的页面中，不要为每个 frame 的页面都建立一个 HTTP 长连接，这样会阻塞其它的 HTTP 请求，在设计上考虑让多个 frame 的更新共用一个长连接。</p>
            <p><a name="N10194"><span class="smalltitle">服务器端的性能和可扩展性</span></a></p>
            <p>一般 Web 服务器会为每个连接创建一个线程，如果在大型的商业应用中使用 Comet，服务器端需要维护大量并发的长连接。在这种应用背景下，服务器端需要考虑负载均衡和集群技术；或是在服务器端为长连接作一些改进。</p>
            <p>应用和技术的发展总是带来新的需求，从而推动新技术的发展。HTTP 1.1 与 1.0 规范有一个很大的不同：1.0 规范下服务器在处理完每个 Get/Post 请求后会关闭套接口连接； 而 1.1 规范下服务器会保持这个连接，在处理两个请求的间隔时间里，这个连接处于空闲状态。 Java 1.4 引入了支持异步 IO 的 java.nio 包。当连接处于空闲时，为这个连接分配的线程资源会返还到线程池，可以供新的连接使用；当原来处于空闲的连接的客户发出新的请求，会从线程池里分配一个线程资源处理这个请求。 这种技术在连接处于空闲的机率较高、并发连接数目很多的场景下对于降低服务器的资源负载非常有效。</p>
            <p>但是 AJAX 的应用使请求的出现变得频繁，而 Comet 则会长时间占用一个连接，上述的服务器模型在新的应用背景下会变得非常低效，线程池里有限的线程数甚至可能会阻塞新的连接。Jetty 6 Web 服务器针对 AJAX、Comet 应用的特点进行了很多创新的改进，请参考文章&#8220;AJAX，Comet and Jetty&#8221;（请参见 <a href="http://www.ibm.com/developerworks/cn/web/wa-lo-comet/#resources" cmimpressionsent="1">参考资源</a>）。</p>
            <p><a name="N101A6"><span class="smalltitle">控制信息与数据信息使用不同的 HTTP 连接</span></a></p>
            <p>使用长连接时，存在一个很常见的场景：客户端网页需要关闭，而服务器端还处在读取数据的堵塞状态，客户端需要及时通知服务器端关闭数据连接。服务器在收到关闭请求后首先要从读取数据的阻塞状态唤醒，然后释放为这个客户端分配的资源，再关闭连接。</p>
            <p>所以在设计上，我们需要使客户端的控制请求和数据请求使用不同的 HTTP 连接，才能使控制请求不会被阻塞。</p>
            <p>在实现上，如果是基于 iframe 流方式的长连接，客户端页面需要使用两个 iframe，一个是控制帧，用于往服务器端发送控制请求，控制请求能很快收到响应，不会被堵塞；一个是显示帧，用于往服务器端发送长连接请求。如果是基于 AJAX 的长轮询方式，客户端可以异步地发出一个 XMLHttpRequest 请求，通知服务器端关闭数据连接。</p>
            <p><a name="N101B4"><span class="smalltitle">在客户和服务器之间保持&#8220;心跳&#8221;信息</span></a></p>
            <p>在浏览器与服务器之间维持一个长连接会为通信带来一些不确定性：因为数据传输是随机的，客户端不知道何时服务器才有数据传送。服务器端需要确保当客户端不再工作时，释放为这个客户端分配的资源，防止内存泄漏。因此需要一种机制使双方知道大家都在正常运行。在实现上：</p>
            <ol>
                <li>服务器端在阻塞读时会设置一个时限，超时后阻塞读调用会返回，同时发给客户端没有新数据到达的心跳信息。此时如果客户端已经关闭，服务器往通道写数据会出现异常，服务器端就会及时释放为这个客户端分配的资源。
                <li>如果客户端使用的是基于 AJAX 的长轮询方式；服务器端返回数据、关闭连接后，经过某个时限没有收到客户端的再次请求，会认为客户端不能正常工作，会释放为这个客户端分配、维护的资源。
                <li>当服务器处理信息出现异常情况，需要发送错误信息通知客户端，同时释放资源、关闭连接。 </li>
            </ol>
            <p><a name="N101C8"><span class="smalltitle">Pushlet - 开源 Comet 框架</span></a></p>
            <p>Pushlet 是一个开源的 Comet 框架，在设计上有很多值得借鉴的地方，对于开发轻量级的 Comet 应用很有参考价值。</p>
            <p><strong>观察者模型</strong> </p>
            <p>Pushlet 使用了观察者模型：客户端发送请求，订阅感兴趣的事件；服务器端为每个客户端分配一个会话 ID 作为标记，事件源会把新产生的事件以多播的方式发送到订阅者的事件队列里。</p>
            <p><strong>客户端 JavaScript 库</strong> </p>
            <p>pushlet 提供了基于 AJAX 的 JavaScript 库文件用于实现长轮询方式的&#8220;服务器推&#8221;；还提供了基于 iframe 的 JavaScript 库文件用于实现流方式的&#8220;服务器推&#8221;。</p>
            <p>JavaScript 库做了很多封装工作：</p>
            <ol>
                <li>定义客户端的通信状态：<code>STATE_ERROR</code>、<code>STATE_ABORT</code>、<code>STATE_NULL</code>、<code>STATE_READY</code>、<code>STATE_JOINED</code>、<code>STATE_LISTENING</code>；
                <li>保存服务器分配的会话 ID，在建立连接之后的每次请求中会附上会话 ID 表明身份；
                <li>提供了 <code>join()</code>、<code>leave()</code>、<code>subscribe()</code>、 <code>unsubsribe()</code>、<code>listen()</code> 等 API 供页面调用；
                <li>提供了处理响应的 JavaScript 函数接口 <code>onData()</code>、<code>onEvent()</code>&#8230; </li>
            </ol>
            <p>网页可以很方便地使用这两个 JavaScript 库文件封装的 API 与服务器进行通信。</p>
            <p><strong>客户端与服务器端通信信息格式</strong> </p>
            <p>pushlet 定义了一套客户与服务器通信的信息格式，使用 XML 格式。定义了客户端发送请求的类型：<code>join</code>、<code>leave</code>、<code>subscribe</code>、<code>unsubscribe</code>、<code>listen</code>、<code>refresh</code>；以及响应的事件类型：<code>data</code>、<code>join_ack</code>、<code>listen_ack</code>、<code>refresh</code>、<code>heartbeat</code>、<code>error</code>、<code>abort</code>、<code>subscribe_ack</code>、<code>unsubscribe_ack</code>。 </p>
            <p><strong>服务器端事件队列管理</strong> </p>
            <p>pushlet 在服务器端使用 Java Servlet 实现，其数据结构的设计框架仍可适用于 PHP、C 编写的后台客户端。</p>
            <p>Pushlet 支持客户端自己选择使用流、拉（长轮询）、轮询方式。服务器端根据客户选择的方式在读取事件队列（fetchEvents）时进行不同的处理。&#8220;轮询&#8221;模式下 <code>fetchEvents()</code> 会马上返回。&#8221;流&#8220;和&#8221;拉&#8220;模式使用阻塞的方式读事件，如果超时，会发给客户端发送一个没有新信息收到的&#8220;heartbeat&#8220;事件，如果是&#8220;拉&#8221;模式，会把&#8220;heartbeat&#8221;与&#8220;refresh&#8221;事件一起传给客户端，通知客户端重新发出请求、建立连接。</p>
            <p><strong>客户服务器之间的会话管理</strong> </p>
            <p>服务端在客户端发送 <code>join</code> 请求时，会为客户端分配一个会话 ID， 并传给客户端，然后客户端就通过此会话 ID 标明身份发出 <code>subscribe</code> 和 <code>listen</code> 请求。服务器端会为每个会话维护一个订阅的主题集合、事件队列。</p>
            <p>服务器端的事件源会把新产生的事件以多播的方式发送到每个会话（即订阅者）的事件队列里。</p>
            <br />
            <table cellspacing="0" cellpadding="0" width="100%" border="0">
                <tbody>
                    <tr>
                        <td><img height="1" alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" /><br />
                        <img height="6" alt="" src="http://www.ibm.com/i/c.gif" width="8" border="0" /></td>
                    </tr>
                </tbody>
            </table>
            <table class="no-print" cellspacing="0" cellpadding="0" align="right">
                <tbody>
                    <tr align="right">
                        <td><img height="4" alt="" src="http://www.ibm.com/i/c.gif" width="100%" /><br />
                        <table cellspacing="0" cellpadding="0" border="0">
                            <tbody>
                                <tr>
                                    <td valign="middle"><img height="16" alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width="16" border="0" /><br />
                                    </td>
                                    <td valign="top" align="right"><a class="fbox" href="http://www.ibm.com/developerworks/cn/web/wa-lo-comet/#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="N10298"><span class="atitle">小结</span></a></p>
            <p>本文介绍了如何在现有的技术基础上选择合适的方案开发一个&#8220;服务器推&#8221;的应用，最优的方案还是取决于应用需求的本身。相对于传统的 Web 应用， 目前开发 Comet 应用还是具有一定的挑战性。</p>
            <p>&#8220;服务器推&#8221;存在广泛的应用需求，为了使 Comet 模型适用于大规模的商业应用，以及方便用户构建 Comet 应用，最近几年，无论是服务器还是浏览器都出现了很多新技术，同时也出现了很多开源的 Comet 框架、协议。需求推动技术的发展，相信 Comet 的应用会变得和 AJAX 一样普及。</p>
            <br />
            <br />
            <p><a name="resources"><span class="atitle">参考资料 </span></a></p>
            <strong>学习</strong><br />
            <ul>
                <li>developerWorks 文章&#8220; <a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/index.html" cmimpressionsent="1">面向 Java 开发人员的 Ajax: 使用 Jetty 和 Direct Web Remoting 编写可扩展的 Comet 应用程序</a>&#8221;：受异步服务器端事件驱动的 Ajax 应用程序实现较为困难，本文介绍了一种结合使用 Comet 模式和 Jetty 6 Continuations API 的解决方法。<br />
                <br />
                <li>&#8220;<a href="http://alex.dojotoolkit.org/?p=545" cmimpressionsent="1">Comet: Low Latency Data for the Browser</a>&#8221;：Alex Russell 是 Dojo Toolkit 的项目主管和 Dojo Foundation 的主席，他在这篇博客文章中提出了 <em>Comet</em> 这个术语。<br />
                <br />
                <li>&#8220;<a href="http://alex.dojotoolkit.org/?p=538" cmimpressionsent="1">What else is burried down in the depth&#8217;s of Google&#8217;s amazing JavaScript?</a>&#8221;（Alex Russel，2006 年 2 月）：Alex 在这篇文章里介绍了如何使用&#8220;htmlfile&#8221;ActiveX 控件解决 iframe 请求长连接时 IE 的加载显示问题。<br />
                <br />
                <li><a href="http://en.wikipedia.org/wiki/Comet_(programming)" cmimpressionsent="1">Comet wiki</a>：提供了很多开源 Comet 框架的链接。<br />
                <br />
                <li><a href="http://jetty.mortbay.org/" cmimpressionsent="1">Jetty</a>：Jetty 是一种开源的基于标准的 Web 服务器，完全使用 Java 语言实现。<br />
                <br />
                <li>&#8220;<a href="http://www.webtide.com/downloads/whitePaperAjaxJetty.html" cmimpressionsent="1">Ajax, Comet and Jetty</a>&#8221;（Greg Wilkins，Webtide，2006 年 1 月）：Wilkins 的这份白皮书讨论了扩展 Ajax 连接的 Jetty 架构方法。<br />
                <br />
                <li><a href="http://docs.codehaus.org/display/JETTY/Continuations" cmimpressionsent="1">Continuations</a>：了解更多关于 Jetty 的 Continuations 特性的信息。<br />
                <br />
                <li>&#8220;<a href="http://www.pushlets.com/" cmimpressionsent="1">pushlet</a>&#8221;：开源 comet 框架，使用了观察者模型。浏览器端提供了基于 AJAX 和 iframe 的 JavaScript 库，服务器端使用 Java Servlet。<br />
                <br />
                <li>&#8220;<a href="http://www.zeitoun.net/index.php?2007/06/22/46-how-to-implement-comet-with-php" cmimpressionsent="1">How to implement COMET with PHP</a>&#8221;：提供的 comet-iframe.tar.gz 使用 iframe/htmlfile 封装了一个 JavaScript comet 对象，支持 IE、Mozilla Firefox 浏览器。<br />
                <br />
                <li>&#8220;<a href="http://www.aflax.org/" cmimpressionsent="1">AFLAX</a>&#8221;：Asynchronous Flash and XML，提供了强大的 Flash、Javascript 库和很多范例。<br />
                <br />
                <li><a href="http://www.ibm.com/developerworks/cn/ajax/" cmimpressionsent="1">developerWorks Ajax 技术资源中心</a>：能找到更多关于 Ajax 技术的文章和教程。<br />
                <br />
                <li><a href="http://www.ibm.com/developerworks/cn/web/" cmimpressionsent="1">developerWorks Web 开发技术专区</a>：提供了关于 Web 开发和架构方面的大量文章。<br />
                <br />
                <li><a href="http://www.ibm.com/developerworks/cn/java/" cmimpressionsent="1">developerWorks Java 技术专区</a>：提供了关于 Java 编程各个方面的数百篇文章。<br />
                <br />
                <li>浏览 <a href="http://www.ibm.com/developerworks/apps/SendTo?bookstore=safari" cmimpressionsent="1">技术书店</a>，查阅有关本文所述主题以及其他技术主题的书籍。 </li>
            </ul>
            <p><br />
            <strong>Pushlet - 开源 Comet 框架</strong> <br />
            Pushlet 是一个开源的 Comet 框架，在设计上有很多值得借鉴的地方，对于开发轻量级的 Comet 应用很有参考价值。 <br />
            <br />
            <strong>观察者模型</strong> <br />
            Pushlet 使用了观察者模型：客户端发送请求，订阅感兴趣的事件；服务器端为每个客户端分配一个会话 ID 作为标记，事件源会把新产生的事件以多播的方式发送到订阅者的事件队列里。 <br />
            <br />
            <strong>客户端 JavaScript 库</strong> <br />
            <br />
            pushlet 提供了基于 AJAX 的 JavaScript 库文件用于实现长轮询方式的&#8220;服务器推&#8221;；还提供了基于 iframe 的 JavaScript 库文件用于实现流方式的&#8220;服务器推&#8221;。 <br />
            </p>
            </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/caizh2009/aggbug/279721.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/caizh2009/" target="_blank">小菜毛毛</a> 2009-06-02 23:46 <a href="http://www.blogjava.net/caizh2009/articles/279721.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>dwr实例2（级联菜单并确定默认值）</title><link>http://www.blogjava.net/caizh2009/articles/279694.html</link><dc:creator>小菜毛毛</dc:creator><author>小菜毛毛</author><pubDate>Tue, 02 Jun 2009 13:30:00 GMT</pubDate><guid>http://www.blogjava.net/caizh2009/articles/279694.html</guid><wfw:comment>http://www.blogjava.net/caizh2009/comments/279694.html</wfw:comment><comments>http://www.blogjava.net/caizh2009/articles/279694.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/caizh2009/comments/commentRss/279694.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/caizh2009/services/trackbacks/279694.html</trackback:ping><description><![CDATA[<p>&lt;%@ include file="/common/taglibs.jsp"%&gt;</p>
<p>&lt;title&gt;&lt;fmt:message key="menu.admin" /&gt;-&gt;&lt;fmt:message key="menu.admin.departmentsManager" /&gt;&lt;/title&gt;</p>
<p>&lt;link rel="stylesheet" type="text/css" media="all" href="&lt;c:url value='/styles/default.css'/&gt;" /&gt;<br />
&lt;link rel="stylesheet" type="text/css" media="all" href="&lt;c:url value='/styles/helptip.css'/&gt;" /&gt;<br />
&lt;link rel="stylesheet" type="text/css" media="print" href="&lt;c:url value='/styles/print.css'/&gt;" /&gt;</p>
<p>&lt;c:if test="${not empty errorReason}"&gt;<br />
&nbsp;&nbsp; &lt;div align="center" style="color: red"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;c:if test="${errorReason=='updateConstraint'}"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;fmt:message key="tcsBox.updateConstraint"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/c:if&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;c:if test="${errorReason=='addConstraint'}"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;fmt:message key="tcsBox.addConstraint"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/c:if&gt;<br />
&nbsp;&nbsp; &lt;/div&gt;<br />
&lt;/c:if&gt;<br />
&lt;html:form action="editBoxes.html?method=finishEdit" styleId="TcsboxForm" &gt;</p>
<p>&lt;table class="detail" valign="top"&gt;&nbsp; <br />
&lt;c:if test="${from == 'update' or param.handle =='update'}"&gt;<br />
&nbsp; &lt;input type="hidden" value="update"&nbsp; name="handle"/&gt;<br />
&lt;/c:if &gt; <br />
&lt;c:if test="${from == 'add'}"&gt;<br />
&nbsp; &lt;input type="hidden" value="add"&nbsp; name="handle"/&gt;<br />
&lt;/c:if &gt;&nbsp;&nbsp; <br />
&lt;!-- &lt;c:set var="pageButtons"&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td height="50"&gt;&lt;/td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td class="buttonBar"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;c:if test="${from == 'update'}"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &lt;input type="submit" value='&lt;fmt:message key="button.submit"/&gt;' class="button" /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &lt;input type="hidden" value="update"&nbsp; name="handle"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;/c:if &gt; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;c:if test="${from == 'add'}"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&lt;input type="submit" value='&lt;fmt:message key="button.submit"/&gt;' class="button" /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&lt;input type="hidden" value="add"&nbsp; name="handle"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/c:if &gt;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp; &lt;input type="reset" value='&lt;fmt:message key="button.reset"/&gt;' class="button"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;input type="button" value='&lt;fmt:message key="button.back"/&gt;' class="button" onclick="history.back()"/&gt; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />
&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br />
&lt;/c:set&gt; --&gt;<br />
&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;c:if test="${from=='update' or param.handle =='update'}"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;manhourpool:label key="TcsBoxForm.tcs_box_id"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;html:text property="tcs_box_id" styleId="tcs_box_id" readonly="true"&gt;&lt;/html:text&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/c:if&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;manhourpool:label key="TcsBoxForm.code"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;html:text property="tcs_box_code" styleId="tcs_box_code" &gt;&lt;/html:text&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;manhourpool:label key="TcsBoxForm.tcs_box_name"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;html:text property="tcs_box_name" styleId="tcs_box_name" &gt;&lt;/html:text&gt; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;manhourpool:label key="TcsBoxForm.companycode"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;html:select property="company_code" styleId="company_code" onchange="changeCompany_Code(this)"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;html:options collection="companyLst" property="code" labelProperty="name" /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/html:select&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;manhourpool:label key="TcsBoxForm.burea"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;html:select property="bureau_id"&nbsp; styleId="bureau_id" &gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;html:options collection="bureauLst" property="code" labelProperty="name" /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/html:select&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;manhourpool:label key="TcsBoxForm.district"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;html:select property="district_id"&nbsp; styleId="district_id" &gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;html:options collection="districtLst" property="code" labelProperty="name" /&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/html:select&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;manhourpool:label key="TcsBoxForm.address"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;html:text property="tcs_address" styleId="tcs_address" &gt;&lt;/html:text&gt; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;c:if test="${from=='update' or param.handle =='update'}"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;manhourpool:label key="chartem.state"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;html:select property="state" &gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;html:options collection="doms" property="column_value" labelProperty="description"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/html:select&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;/c:if&gt;<br />
&lt;/table&gt; <br />
&lt;table align="center"&gt;<br />
&lt;!-- &lt;c:out value="${pageButtons}"&nbsp; escapeXml="false"/&gt; --&gt;&lt;/table&gt;<br />
&lt;/html:form&gt; </p>
<p><br />
&lt;script type='text/javascript' src='dwr/interface/bureauManager.js'&gt;&lt;/script&gt;<br />
&lt;script type='text/javascript' src='dwr/interface/districtManager.js'&gt;&lt;/script&gt;<br />
&lt;script type='text/javascript' src='dwr/engine.js'&gt;&lt;/script&gt;<br />
&lt;script type='text/javascript' src='dwr/util.js'&gt;&lt;/script&gt;<br />
&lt;script type="text/javascript"&gt;<br />
&nbsp;&nbsp; var bureau_on_load=true;<br />
&nbsp;&nbsp; var bureau_id='&lt;c:out value="${tcsBoxForm.bureau_id}"/&gt;';<br />
&nbsp;&nbsp; window.onload=function(){<br />
&nbsp;&nbsp;&nbsp;&nbsp; window.changeCompany_Code(document.all.company_code);<br />
&nbsp;var ppage = window.parent.document.frames['theiframtwo'];<br />
&nbsp;if('&lt;c:out value="${handlesucc}" /&gt;'){<br />
&nbsp;&nbsp;ppage.document.getElementById('viewBoxes').submit();<br />
&nbsp;}<br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; function changeCompany_Code(sel_obj)<br />
&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp; var company_code=sel_obj.value;<br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; if(company_code=='')<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />
&nbsp;&nbsp;&nbsp;&nbsp; var param =new Object();<br />
&nbsp;&nbsp;&nbsp;&nbsp; param.company_code=company_code;<br />
&nbsp;&nbsp;&nbsp;&nbsp; bureauManager.getBureauListInDwr(param,showBureau); <br />
&nbsp;&nbsp;&nbsp;&nbsp; districtManager.getDistrictBycompany(company_code,showDistrict);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp; function showBureau(bureaus)<br />
&nbsp;&nbsp; {<br />
&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp; var bureau_id=$('bureau_id').value;<br />
&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp; if(window.bureau_on_load)<br />
&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bureau_id='&lt;c:out value="${param.bureau_id}"/&gt;';<br />
&nbsp;//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; window.bureau_on_load=false;<br />
//&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWRUtil.removeAllOptions('bureau_id');<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var op=document.createElement('option');<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; op.value='';<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; op.text='&lt;fmt:message key="task.default" /&gt;';<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $('bureau_id').options.add(op);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DWRUtil.addOptions('bureau_id',bureaus,'bureau_id','bureau_name');<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(var i=0;i&lt;$('bureau_id').options.length;i++)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if($('bureau_id').options[i].value==bureau_id)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $('bureau_id').selectedIndex=i;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp; function showDistrict(dis)<br />
&nbsp;&nbsp; {&nbsp; <br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp; var dis_code=$('district_id').value;<br />
&nbsp;&nbsp; if(window.isOnload)<br />
&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp; dis_code='&lt;c:out value="${taskInfoForm.district_id}"/&gt;';<br />
&nbsp;&nbsp;&nbsp;&nbsp; window.isOnload=false;<br />
&nbsp;&nbsp; } <br />
&nbsp;&nbsp;&nbsp;&nbsp; DWRUtil.removeAllOptions('district_id');<br />
&nbsp;&nbsp;&nbsp;&nbsp; var op=document.createElement('option');<br />
&nbsp;&nbsp;&nbsp;&nbsp; op.value='';<br />
&nbsp;&nbsp;&nbsp;&nbsp; op.text='&lt;fmt:message key="task.default" /&gt;';<br />
&nbsp;&nbsp;&nbsp;&nbsp; $('district_id').options.add(op);<br />
&nbsp;&nbsp;&nbsp;&nbsp; DWRUtil.addOptions('district_id',dis,'code','name');<br />
&nbsp;&nbsp;&nbsp;&nbsp; for(var i=0;i&lt;$('district_id').options.length;i++)<br />
&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if($('district_id').options[i].value==dis_code)<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $('district_id').selectedIndex=i;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; break;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp;&nbsp;&nbsp; }<br />
&nbsp;&nbsp; } <br />
&lt;/script&gt;<br />
</p>
<img src ="http://www.blogjava.net/caizh2009/aggbug/279694.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/caizh2009/" target="_blank">小菜毛毛</a> 2009-06-02 21:30 <a href="http://www.blogjava.net/caizh2009/articles/279694.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DWR级联菜单实例</title><link>http://www.blogjava.net/caizh2009/articles/279693.html</link><dc:creator>小菜毛毛</dc:creator><author>小菜毛毛</author><pubDate>Tue, 02 Jun 2009 13:28:00 GMT</pubDate><guid>http://www.blogjava.net/caizh2009/articles/279693.html</guid><wfw:comment>http://www.blogjava.net/caizh2009/comments/279693.html</wfw:comment><comments>http://www.blogjava.net/caizh2009/articles/279693.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/caizh2009/comments/commentRss/279693.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/caizh2009/services/trackbacks/279693.html</trackback:ping><description><![CDATA[<p>页面：<br />
&lt;script type='text/javascript' src='dwr/interface/timeLimitMVManager.js'&gt;&lt;/script&gt;<br />
&lt;script type='text/javascript' src='dwr/engine.js'&gt;&lt;/script&gt;<br />
&lt;script type='text/javascript' src='dwr/util.js'&gt;&lt;/script&gt;<br />
&lt;tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;fmt:message key="timelimitmv.tyep.name"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;select name="match_type_work_char_id" onchange="changeType(this)" id="match_type_work_char_id"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;option value=""&gt;&lt;fmt:message key="msgtem.default" /&gt;&lt;/option&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;option value="1"&gt;&lt;fmt:message key="timelimitmv.type1"/&gt;&lt;/option&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;option value="2"&gt;&lt;fmt:message key="timelimitmv.type2"/&gt;&lt;/option&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;option value="4"&gt;&lt;fmt:message key="timelimitmv.type4"/&gt;&lt;/option&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/select&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;fmt:message key="timelimitmv.value.name"/&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/th&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&lt;select name="begin_value" id="begin_value"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;option value="1" &gt;&lt;fmt:message key="timelimitmv.type1"/&gt;&lt;/option&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;option value="2"&gt;&lt;fmt:message key="timelimitmv.type2"/&gt;&lt;/option&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;option value="4"&gt;&lt;fmt:message key="timelimitmv.type4"/&gt;&lt;/option&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/select&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;</p>
function changeType(sel_obj)<br />
&nbsp;&nbsp; {<br />
&nbsp;&nbsp;&nbsp;&nbsp; var match_type_work_char_id=sel_obj.value;<br />
&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; if(match_type_work_char_id=='')<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return;<br />
&nbsp;&nbsp;&nbsp;&nbsp; timeLimitMVManager.getVNByTN(match_type_work_char_id,showValue);<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp; }<br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp; function showValue(dis)<br />
&nbsp;&nbsp; {&nbsp; <br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp; <br />
&nbsp;&nbsp;&nbsp;&nbsp; DWRUtil.removeAllOptions('begin_value');<br />
&nbsp;&nbsp;&nbsp;&nbsp; DWRUtil.addOptions('begin_value',dis,'begin_value','value_name');<br />
&nbsp;&nbsp;&nbsp; <br />
&nbsp;&nbsp; }<br />
&nbsp;dwr.xml：<br />
&nbsp;&lt;allow&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;create creator="spring" javascript="timeLimitMVManager"&gt;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;param name="beanName" value="timeLimitMVManager"/&gt;&nbsp;&nbsp; --由SPRING进行管理的MANAGER名称--<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;include method="getVNByTN"/&gt;&nbsp; --暴露给页面的方法--<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/create&gt;&nbsp; <br />
&nbsp;&nbsp;&nbsp; &lt;/allow&gt;<br />
<br />
manager方法：<br />
public List getVNByTN(String match_type_work_char_id) {<br />
&nbsp;&nbsp;// TODO Auto-generated method stub<br />
&nbsp;&nbsp;List obs=null;<br />
&nbsp;&nbsp;if(match_type_work_char_id!=null){<br />
&nbsp;&nbsp;if(match_type_work_char_id.equals("1")){<br />
&nbsp;&nbsp;&nbsp;obs=timeLimitMVDAO.getSBLXById();<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;if(match_type_work_char_id.equals("2")){<br />
&nbsp;&nbsp;&nbsp;obs=timeLimitMVDAO.getKUPPById();<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;if(match_type_work_char_id.equals("4")){<br />
&nbsp;&nbsp;&nbsp;obs=timeLimitMVDAO.getCXBZById();<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;}<br />
&nbsp;&nbsp;return obs;<br />
&nbsp;}
<img src ="http://www.blogjava.net/caizh2009/aggbug/279693.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/caizh2009/" target="_blank">小菜毛毛</a> 2009-06-02 21:28 <a href="http://www.blogjava.net/caizh2009/articles/279693.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DWR学习</title><link>http://www.blogjava.net/caizh2009/articles/279570.html</link><dc:creator>小菜毛毛</dc:creator><author>小菜毛毛</author><pubDate>Tue, 02 Jun 2009 05:56:00 GMT</pubDate><guid>http://www.blogjava.net/caizh2009/articles/279570.html</guid><wfw:comment>http://www.blogjava.net/caizh2009/comments/279570.html</wfw:comment><comments>http://www.blogjava.net/caizh2009/articles/279570.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/caizh2009/comments/commentRss/279570.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/caizh2009/services/trackbacks/279570.html</trackback:ping><description><![CDATA[<p>以下三篇文章,由林信良台湾的技术作家写的: <br />
DWR 入門與應用（一）: <br />
<a href="http://blog.csdn.net/caterpillar_here/archive/2006/09/06/1186566.aspx" target="blank"><font color="#800080">http://blog.csdn.net/caterpillar_here/archive/2006/09/06/1186566.aspx</font></a> <br />
DWR 入門與應用（二）: <br />
<a href="http://blog.csdn.net/caterpillar_here/archive/2006/09/18/1239538.aspx" target="blank">http://blog.csdn.net/caterpillar_here/archive/2006/09/18/1239538.aspx</a> <br />
DWR 入門與應用（三）: <br />
<a href="http://blog.csdn.net/caterpillar_here/archive/2006/09/30/1311605.aspx" target="blank">http://blog.csdn.net/caterpillar_here/archive/2006/09/30/1311605.aspx</a></p>
<p>DWR.xml配置文件说明书.doc <br />
DWR笔记.doc <br />
DWR技术分析.doc <br />
DWR开发培训.ppt <br />
DWR学习.doc <br />
<span>打包下载 </span><a href="http://www.javaeye.com/topic/32782">http://www.javaeye.com/topic/32782</a></p>
<p>DWR学习 <a href="http://www.javaeye.com/topic/16424">http://www.javaeye.com/topic/16424</a></p>
<p>DWR应用总结 <a href="http://www.javaeye.com/topic/33678">http://www.javaeye.com/topic/33678</a></p>
<img src ="http://www.blogjava.net/caizh2009/aggbug/279570.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/caizh2009/" target="_blank">小菜毛毛</a> 2009-06-02 13:56 <a href="http://www.blogjava.net/caizh2009/articles/279570.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DWR 入門與應用（三）</title><link>http://www.blogjava.net/caizh2009/articles/279569.html</link><dc:creator>小菜毛毛</dc:creator><author>小菜毛毛</author><pubDate>Tue, 02 Jun 2009 05:54:00 GMT</pubDate><guid>http://www.blogjava.net/caizh2009/articles/279569.html</guid><wfw:comment>http://www.blogjava.net/caizh2009/comments/279569.html</wfw:comment><comments>http://www.blogjava.net/caizh2009/articles/279569.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/caizh2009/comments/commentRss/279569.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/caizh2009/services/trackbacks/279569.html</trackback:ping><description><![CDATA[<p>來寫個AJAX版的聊天室吧！先看看直接使用AJAX要如何做到，首先需要一個簡單的聊天室Servlet&#8230; <br />
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455 package onlyfun.caterpillar; import java.io.IOException;import java.io.PrintWriter;import java.util.LinkedList;import java.util.List; import javax.servlet.ServletException;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;&nbsp; public class ChatRoomServlet extends javax.servlet.http.HttpServlet implements javax.servlet.Servlet {&nbsp;&nbsp;&nbsp;&nbsp; private static LinkedList&lt;Message&gt; messages = new LinkedList&lt;Message&gt;();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public ChatRoomServlet() {&nbsp;&nbsp;&nbsp; super();&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private List&lt;Message&gt; addMessage(String text) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (text != null &amp;&amp; text.trim().length() &gt; 0) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; messages.addFirst(new Message(text));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (messages.size() &gt; 10) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; messages.removeLast();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return messages;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp; private List&lt;Message&gt; getMessages() {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return messages;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; List&lt;Message&gt; list = null;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if("send".equals(request.getParameter("task"))) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; list = addMessage(request.getParameter("msg"));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if("query".equals(request.getParameter("task"))){&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; list = getMessages();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PrintWriter out = response.getWriter();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; response.setContentType("text/xml");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; response.setHeader("Cache-Control", "no-cache");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("&lt;response&gt;");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(int i = 0; i &lt; list.size(); i++) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String msg = list.get(i).getText();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("&lt;message&gt;" + msg + "&lt;/message&gt;");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.println("&lt;/response&gt;");&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp; </p>
<p><br />
Message物件如下&#8230; <br />
1234567891011121314151617181920212223 package onlyfun.caterpillar; public class Message {&nbsp;&nbsp;&nbsp; private long id = System.currentTimeMillis();&nbsp;&nbsp;&nbsp; private String text;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Message(String newtext) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; text = newtext;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (text.length() &gt; 256) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; text = text.substring(0, 256);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; text = text.replace('&lt;', '[');&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; text = text.replace('&amp;', '_');&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp; public long getId() {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return id;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp; public String getText() {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return text;&nbsp;&nbsp;&nbsp; }}&nbsp; </p>
<p><br />
Servlet接受訊息新增與查詢，判斷的方式是檢查請求參數task是send或query。 </p>
<p>Servlet會以XML傳回目前List當中的訊息，客戶端可以查詢或插入新訊息時，取得目前List中的訊息，接著在web.xml中設定一下&#8230; <br />
12345678910111213141516171819202122 &lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"&gt;&nbsp; &lt;servlet&gt;&nbsp;&nbsp;&nbsp; &lt;description&gt;&nbsp;&nbsp;&nbsp; &lt;/description&gt;&nbsp;&nbsp;&nbsp; &lt;display-name&gt;&nbsp;&nbsp;&nbsp; ChatRoomServlet&lt;/display-name&gt;&nbsp;&nbsp;&nbsp; &lt;servlet-name&gt;ChatRoomServlet&lt;/servlet-name&gt;&nbsp;&nbsp;&nbsp; &lt;servlet-class&gt;&nbsp;&nbsp;&nbsp; onlyfun.caterpillar.ChatRoomServlet&lt;/servlet-class&gt;&nbsp; &lt;/servlet&gt;&nbsp;&nbsp; &lt;servlet-mapping&gt;&nbsp;&nbsp;&nbsp; &lt;servlet-name&gt;ChatRoomServlet&lt;/servlet-name&gt;&nbsp;&nbsp;&nbsp; &lt;url-pattern&gt;/ChatRoomServlet&lt;/url-pattern&gt;&nbsp; &lt;/servlet-mapping&gt;&nbsp; &lt;session-config&gt;&nbsp;&nbsp;&nbsp; &lt;session-timeout&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 30&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/session-timeout&gt;&nbsp; &lt;/session-config&gt;&nbsp; &lt;/web-app&gt; </p>
<p><br />
在網頁中，使用者可以在輸入訊息後按下按鈕送出資訊，並在XML回應取得時，將訊息以一列一列的表格方式顯示出來，另外還設定了週期性的輪詢，即使不輸入新訊息，也可以週期性的取得新的聊天訊息&#8230; <br />
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697 &lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"&gt;&lt;html&gt;&lt;head&gt;&lt;meta http-equiv="Content-Type" content="text/html; charset=BIG5"&gt;&lt;title&gt;Chat Room&lt;/title&gt; &lt;script type="text/javascript"&gt;var xmlHttp; function createXMLHttpRequest() {&nbsp;&nbsp;&nbsp; if (window.ActiveXObject) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp; else if (window.XMLHttpRequest) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlHttp = new XMLHttpRequest();&nbsp;&nbsp;&nbsp; }} function sendMessage() {&nbsp; var msg = document.getElementById("text").value;&nbsp;&nbsp;&nbsp; if(msg == "") {&nbsp;&nbsp;&nbsp; refreshMessage();&nbsp;&nbsp;&nbsp; return;&nbsp; }&nbsp;&nbsp;&nbsp; var param = "task=send&amp;msg=" + msg;&nbsp; ajaxRequest(param);&nbsp; document.getElementById("text").value = "";} function queryMessage() {&nbsp; var param = "task=query";&nbsp; ajaxRequest(param);} function ajaxRequest(param) {&nbsp; var url = "ChatRoomServlet?timestamp" + new Date().getTime();&nbsp;&nbsp;&nbsp; createXMLHttpRequest();&nbsp; xmlHttp.onreadystatechange = refreshMessage;&nbsp;&nbsp;&nbsp; xmlHttp.open("POST", url, true);&nbsp; xmlHttp.setRequestHeader("Content-Type",&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "application/x-www-form-urlencoded;");&nbsp;&nbsp;&nbsp; xmlHttp.send(param);}&nbsp; function refreshMessage() {&nbsp; if(xmlHttp.readyState == 4) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if(xmlHttp.status == 200) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var messages = xmlHttp.responseXML.getElementsByTagName("message");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var table_body = document.getElementById("dynamicUpdateArea");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var length = table_body.childNodes.length;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for (var i = 0; i &lt; length; i++) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; table_body.removeChild(table_body.childNodes[0]);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var length = messages.length;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; for(var i = length - 1; i &gt;= 0 ; i--) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var message = messages[i].firstChild.data;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; var row = createRow(message);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; table_body.appendChild(row);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; setTimeout("queryMessage()", 2000);&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp; }} function createRow(message) {&nbsp;&nbsp;&nbsp; var row = document.createElement("tr");&nbsp;&nbsp;&nbsp; var cell = document.createElement("td");&nbsp;&nbsp;&nbsp; var cell_data = document.createTextNode(message);&nbsp;&nbsp;&nbsp; cell.appendChild(cell_data);&nbsp;&nbsp;&nbsp; row.appendChild(cell);&nbsp;&nbsp;&nbsp; return row;} &lt;/script&gt; &lt;/head&gt;&lt;body&gt; &lt;p&gt;&nbsp; Your Message:&nbsp; &lt;input id="text"/&gt;&nbsp; &lt;input type="button" value="Send"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; onclick="sendMessage()"/&gt;&lt;/p&gt; &lt;p&gt;Messages:&lt;/p&gt;&nbsp;&nbsp;&nbsp; &lt;table align="left"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tbody id="dynamicUpdateArea"&gt;&lt;/tbody&gt;&nbsp;&nbsp;&nbsp; &lt;/table&gt; &lt;/body&gt;&lt;/html&gt; </p>
<p><br />
簡單抓個畫面&#8230;&nbsp; </p>
<p><br />
直接用AJAX，後端用JSP/Servlet，您要對請求參數做些判斷，看看是新增訊息或查詢，並要自行輸出XML，有的沒的&#8230; </p>
<p>改成DWR的話，就很簡單了，寫個簡單的Java物件&#8230; <br />
1234567891011121314151617181920212223 package onlyfun.caterpillar; import java.util.LinkedList;import java.util.List; public class Chat {&nbsp; private static LinkedList&lt;Message&gt; messages = new LinkedList&lt;Message&gt;();&nbsp;&nbsp; public List addMessage(String text) {&nbsp;&nbsp;&nbsp; if (text != null &amp;&amp; text.trim().length() &gt; 0) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; messages.addFirst(new Message(text));&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (messages.size() &gt; 10) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; messages.removeLast();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp; return messages;&nbsp; }&nbsp;&nbsp; public List getMessages() {&nbsp;&nbsp;&nbsp; return messages;&nbsp; }}&nbsp; </p>
<p><br />
接著&#8230;在dwr.xml中開放一下&#8230; <br />
12345678910111213 &lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN" "http://www.getahead.ltd.uk/dwr/dwr10.dtd"&gt; &lt;dwr&gt;&nbsp; &lt;allow&gt;&nbsp;&nbsp;&nbsp;&nbsp; &lt;create creator="new" javascript="Chat"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param name="class" value="onlyfun.caterpillar.Chat"/&gt;&nbsp;&nbsp;&nbsp; &lt;/create&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;convert converter="bean" match="onlyfun.caterpillar.Message"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/allow&gt;&lt;/dwr&gt; </p>
<p><br />
使用者取得訊息時，是直接傳回List物件，而List中裝的是Message物件，Message物件是自訂物件，conterver設定為 bean，表示DWR會自動將Message的getter名稱轉換為傳回客戶端的JavaScript物件中的屬性，例如Message中有 getText()，則在客戶端可以用message.text這樣的方式來存取。 </p>
<p>接著是簡單的客戶端網頁&#8230; <br />
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647 &lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"&gt;&lt;html&gt;&lt;head&gt;&lt;meta http-equiv="Content-Type" content="text/html; charset=BIG5"&gt;&lt;title&gt;Insert title here&lt;/title&gt; &lt;script src="dwr/interface/Chat.js" type="text/javascript"&gt;&lt;/script&gt;&lt;script src="dwr/engine.js" type="text/javascript"&gt;&lt;/script&gt;&lt;script src="dwr/util.js" type="text/javascript"&gt;&lt;/script&gt; &lt;script type="text/javascript"&gt;function sendMessage() {&nbsp;&nbsp;&nbsp; var text = DWRUtil.getValue("text");&nbsp;&nbsp;&nbsp; DWRUtil.setValue("text", "");&nbsp;&nbsp;&nbsp; Chat.addMessage(text, gotMessages);} function gotMessages(messages) {&nbsp;&nbsp;&nbsp; var chatlog = "";&nbsp;&nbsp;&nbsp; for (var data in messages) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; chatlog = "&lt;div&gt;" + messages[data].text +&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "&lt;/div&gt;" + chatlog;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp; DWRUtil.setValue("chatlog", chatlog);&nbsp; setTimeout("queryMessage()", 2000);} function queryMessage() {&nbsp; Chat.getMessages(gotMessages);}&lt;/script&gt; &lt;/head&gt;&lt;body&gt; &lt;p&gt;&nbsp; Your Message:&nbsp; &lt;input id="text"/&gt;&nbsp; &lt;input type="button" value="Send"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; onclick="sendMessage()"/&gt;&lt;/p&gt; &lt;p&gt;Messages:&lt;/p&gt;&lt;div id="chatlog"&gt;&lt;/div&gt; &lt;/body&gt;&lt;/html&gt; </p>
<p><br />
當List物件傳回時，它成為gotMessages(messages)中的messages物件，而messages物件中包括 Message物件轉換後對應的JavaScript物件，由於我們已經設定了Converter，所以可以用messages[data].text來 取得傳回的訊息&#8230; </p>
<p>簡單抓個畫面&#8230; </p>
<p><br />
本文来自CSDN博客，转载请标明出处：http://blog.csdn.net/caterpillar_here/archive/2006/09/30/1311605.aspx</p>
<img src ="http://www.blogjava.net/caizh2009/aggbug/279569.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/caizh2009/" target="_blank">小菜毛毛</a> 2009-06-02 13:54 <a href="http://www.blogjava.net/caizh2009/articles/279569.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DWR 入門與應用（二）</title><link>http://www.blogjava.net/caizh2009/articles/279568.html</link><dc:creator>小菜毛毛</dc:creator><author>小菜毛毛</author><pubDate>Tue, 02 Jun 2009 05:54:00 GMT</pubDate><guid>http://www.blogjava.net/caizh2009/articles/279568.html</guid><wfw:comment>http://www.blogjava.net/caizh2009/comments/279568.html</wfw:comment><comments>http://www.blogjava.net/caizh2009/articles/279568.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/caizh2009/comments/commentRss/279568.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/caizh2009/services/trackbacks/279568.html</trackback:ping><description><![CDATA[<p>假設您要從資料庫中查詢出一些字串，然後填寫到表單的下拉選單中。 </p>
<p>例如一個示意的Java程式如下： <br />
12345678 package onlyfun.caterpillar; public class Option {&nbsp; public String[] getOptions() {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // 實際上這些字串是從資料庫中查到的啦&#8230;&nbsp;&nbsp;&nbsp; return new String[] {"良葛格", "毛美眉", "米小狗"};&nbsp;&nbsp; }}&nbsp; </p>
<p><br />
傳回的字串陣列，您要填寫到下拉選單中，當然，首先我們要在dwr.xml中開發這個物件&#8230; <br />
12345678910 &lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN" "http://www.getahead.ltd.uk/dwr/dwr10.dtd"&gt; &lt;dwr&gt;&nbsp; &lt;allow&gt;&nbsp;&nbsp;&nbsp; &lt;create creator="new" javascript="OPT"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param name="class" value="onlyfun.caterpillar.Option"/&gt;&nbsp;&nbsp;&nbsp; &lt;/create&gt;&nbsp;&nbsp;&nbsp; &lt;/allow&gt;&lt;/dwr&gt; </p>
<p><br />
這是我們的網頁&#8230; <br />
123456789101112131415 &lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"&gt;&lt;html&gt;&lt;head&gt;&lt;meta http-equiv="Content-Type" content="text/html; charset=BIG5"&gt;&lt;script src="option.js" type="text/javascript"&gt;&lt;/script&gt;&lt;script src="dwr/interface/OPT.js" type="text/javascript"&gt;&lt;/script&gt;&lt;script src="dwr/engine.js" type="text/javascript"&gt;&lt;/script&gt;&lt;script src="dwr/util.js" type="text/javascript"&gt;&lt;/script&gt; &lt;/head&gt; &lt;body&gt;&nbsp;&nbsp;&nbsp; 選項: &lt;select id="opts"&gt;&lt;/select&gt;&lt;/body&gt;&lt;/html&gt; </p>
<p><br />
傳回的字串陣列會填入opts這個select中，我們的option.js如下&#8230; <br />
12345678 window.onload = function() {&nbsp;&nbsp;&nbsp; OPT.getOptions(populate);&nbsp; }; function populate(list){&nbsp;&nbsp;&nbsp; DWRUtil.removeAllOptions("opts");&nbsp;&nbsp;&nbsp; DWRUtil.addOptions("opts", list);}&nbsp; </p>
<p><br />
夠簡單了&#8230;不需要解釋了&#8230; </p>
<p>看一下結果&#8230;&nbsp; </p>
<p><br />
好啦！我知道有人在說了，這個程式有夠無聊&#8230; </p>
<p>改一下！就是個不錯的範例了，例如連動方塊，唔！在Ajax in action中叫啥？Dynamic double combo？&#8230; </p>
<p>假設一個會去從資料庫中查詢資料的Java程式示意如下： <br />
123456789101112131415161718192021222324252627282930 package onlyfun.caterpillar; import java.util.Map;import java.util.TreeMap; public class Bike {&nbsp; private Map&lt;String, String[]&gt; bikes;&nbsp;&nbsp;&nbsp; public Bike() {&nbsp;&nbsp;&nbsp; bikes = new TreeMap&lt;String, String[]&gt;();&nbsp;&nbsp;&nbsp; bikes.put("2000", new String[] {"2000 T1", "2000 T2", "2000 T3"});&nbsp;&nbsp;&nbsp; bikes.put("2001", new String[] {"2001 A1", "2001 A2"});&nbsp;&nbsp;&nbsp; bikes.put("2002", new String[] {"2002 BW1", "2002 BW2", "2002 BW"});&nbsp;&nbsp;&nbsp; bikes.put("2003", new String[] {"2003 S320"});&nbsp;&nbsp;&nbsp; bikes.put("2004", new String[] {"2004 TA1", "2004 TA2", "2004 TA3"});&nbsp; }&nbsp;&nbsp;&nbsp; public String[] getYears() {&nbsp;&nbsp;&nbsp; String[] keys = new String[bikes.size()];&nbsp;&nbsp;&nbsp; int i = 0;&nbsp;&nbsp;&nbsp; for(String key : bikes.keySet()) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; keys[i++] = key;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp; return keys;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp; public String[] getBikes(String year) {&nbsp;&nbsp;&nbsp; return bikes.get(year);&nbsp; }}&nbsp; </p>
<p><br />
getYears()跟getBkies()分別表示產品的年份跟型號，這邊用Map模擬，實際上資料是來自資料庫的查詢。 </p>
<p>一樣的，在dwr.xml中設定： <br />
12345678910 &lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN" "http://www.getahead.ltd.uk/dwr/dwr10.dtd"&gt; &lt;dwr&gt;&nbsp; &lt;allow&gt;&nbsp;&nbsp;&nbsp; &lt;create creator="new" javascript="Bike" scope="application"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param name="class" value="onlyfun.caterpillar.Bike"/&gt;&nbsp;&nbsp;&nbsp; &lt;/create&gt;&nbsp; &lt;/allow&gt;&lt;/dwr&gt; </p>
<p><br />
我們會有個腳踏車年份與型號查詢頁面： <br />
123456789101112131415 &lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"&gt;&lt;html&gt;&lt;head&gt;&lt;meta http-equiv="Content-Type" content="text/html; charset=BIG5"&gt;&lt;title&gt;Insert title here&lt;/title&gt;&nbsp; &lt;script type='text/javascript' src='dwr/interface/Bike.js'&gt;&lt;/script&gt;&nbsp; &lt;script type='text/javascript' src='dwr/engine.js'&gt;&lt;/script&gt;&nbsp; &lt;script type='text/javascript' src='dwr/util.js'&gt;&lt;/script&gt;&nbsp; &lt;script type='text/javascript' src='bike.js'&gt;&lt;/script&gt;&lt;/head&gt;&lt;body onload="refreshYearList();"&gt;&nbsp; 年份：&lt;select id="years" onchange="refreshBikeList();"&gt;&lt;/select&gt;&lt;br/&gt;&lt;br/&gt;&nbsp; 型號：&lt;select id="bikes"&gt;&lt;/select&gt;&lt;br/&gt;&lt;/body&gt;&lt;/html&gt; </p>
<p><br />
注意，在選完第一個年份後，會觸發onchange事件，接著第二個下拉選單會自動填上對應年份的型號，而不是按鈕按下，再去取得第二個下拉選單，然後refresh...blah...blah... </p>
<p>bike.js如下&#8230; <br />
12345678910111213141516171819 function refreshYearList() {&nbsp;&nbsp;&nbsp; Bike.getYears(populateYearList);} function populateYearList(list){&nbsp;&nbsp;&nbsp; DWRUtil.removeAllOptions("years");&nbsp;&nbsp;&nbsp; DWRUtil.addOptions("years", list);&nbsp;&nbsp;&nbsp; refreshBikeList();} function refreshBikeList() {&nbsp;&nbsp;&nbsp; var year = $("years").value;&nbsp;&nbsp;&nbsp; Bike.getBikes(year, populateBikeList);} function populateBikeList(list){&nbsp;&nbsp;&nbsp; DWRUtil.removeAllOptions("bikes");&nbsp;&nbsp;&nbsp; DWRUtil.addOptions("bikes", list);}&nbsp; </p>
<p><br />
一樣很簡單&#8230; </p>
<p>看個無聊的畫面&#8230;XD </p>
<p>&nbsp;</p>
<p>本文来自CSDN博客，转载请标明出处：http://blog.csdn.net/caterpillar_here/archive/2006/09/18/1239538.aspx</p>
<img src ="http://www.blogjava.net/caizh2009/aggbug/279568.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/caizh2009/" target="_blank">小菜毛毛</a> 2009-06-02 13:54 <a href="http://www.blogjava.net/caizh2009/articles/279568.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>DWR 入門與應用（一）</title><link>http://www.blogjava.net/caizh2009/articles/279567.html</link><dc:creator>小菜毛毛</dc:creator><author>小菜毛毛</author><pubDate>Tue, 02 Jun 2009 05:53:00 GMT</pubDate><guid>http://www.blogjava.net/caizh2009/articles/279567.html</guid><wfw:comment>http://www.blogjava.net/caizh2009/comments/279567.html</wfw:comment><comments>http://www.blogjava.net/caizh2009/articles/279567.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/caizh2009/comments/commentRss/279567.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/caizh2009/services/trackbacks/279567.html</trackback:ping><description><![CDATA[<p>Java 開發人員與網頁設計人員的橋樑 DWR&#8230;呃！我懶得寫簡介了&#8230;直接來看看可以做什麼吧！&#8230; </p>
<p>請先到 http://getahead.ltd.uk/dwr/ 下載 dwr.jar，放到WEB-INF/lib下&#8230; </p>
<p>負責處理客戶端請求，並呼叫Java物件的是DWRServlet，DWR其實也有些Model 2的味道，只是View的這一層比較弱，因為放到客戶端的JavaScript應用程式中&#8230; </p>
<p>在web.xml中加入DWRServlet&#8230; <br />
1234567891011121314151617181920212223 &lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"&gt;&nbsp; &lt;display-name&gt;&nbsp; ajaxDWR&lt;/display-name&gt;&nbsp; &lt;servlet&gt;&nbsp;&nbsp;&nbsp; &lt;servlet-name&gt;dwr-invoker&lt;/servlet-name&gt;&nbsp;&nbsp;&nbsp; &lt;servlet-class&gt;uk.ltd.getahead.dwr.DWRServlet&lt;/servlet-class&gt;&nbsp;&nbsp;&nbsp; &lt;init-param&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;description&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/description&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-name&gt;debug&lt;/param-name&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-value&gt;true&lt;/param-value&gt;&nbsp;&nbsp;&nbsp; &lt;/init-param&gt;&nbsp; &lt;/servlet&gt;&nbsp; &lt;servlet-mapping&gt;&nbsp;&nbsp;&nbsp; &lt;servlet-name&gt;dwr-invoker&lt;/servlet-name&gt;&nbsp;&nbsp;&nbsp; &lt;url-pattern&gt;/dwr/*&lt;/url-pattern&gt;&nbsp; &lt;/servlet-mapping&gt;&lt;/web-app&gt; </p>
<p><br />
接下來寫個簡單的Hello吧！ <br />
1234567 package onlyfun.caterpillar; public class Hello {&nbsp; public String hello(String name) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "哈囉！" + name + "！您的第一個DWR！";&nbsp;&nbsp;&nbsp; }}&nbsp; </p>
<p><br />
客戶端要呼叫這個Java物件，傳給它參數，而後傳回一個字串，客戶端再顯示這個字串，神奇？其實是要告訴DWRServlet這件事，這需要一個dwr.xml： <br />
1234567891011 &lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN" "http://www.getahead.ltd.uk/dwr/dwr10.dtd"&gt; &lt;dwr&gt;&nbsp; &lt;allow&gt;&nbsp;&nbsp;&nbsp; &lt;create creator="new" javascript="Hello"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param name="class" value="onlyfun.caterpillar.Hello" /&gt;&nbsp;&nbsp;&nbsp; &lt;/create&gt;&nbsp; &lt;/allow&gt;&lt;/dwr&gt; </p>
<p><br />
creator設定為new，表示使用Hello的無參數建構子來生成物件，javascript設定為Hello，表示客戶端JavaScript程式可以使用Hello來呼叫對應的onlyfun.caterpillar.Hello物件。 </p>
<p>來寫個客戶端的網頁，當中有一個輸入欄位&#8230; <br />
12345678910111213141516171819 &lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"&gt;&lt;html&gt;&lt;head&gt;&lt;meta http-equiv="Content-Type" content="text/html; charset=BIG5"&gt;&nbsp; &lt;title&gt;第一個DWR程式&lt;/title&gt;&nbsp; &lt;script type='text/javascript' src='dwr/interface/Hello.js'&gt;&lt;/script&gt;&nbsp; &lt;script type='text/javascript' src='dwr/engine.js'&gt;&lt;/script&gt;&nbsp; &lt;script type='text/javascript' src='dwr/util.js'&gt;&lt;/script&gt;&nbsp; &lt;script type='text/javascript' src='hello.js'&gt;&lt;/script&gt;&lt;/head&gt;&lt;body&gt; &lt;input id="user" type="text" /&gt;&lt;input type='button' value='哈囉' onclick='hello();' /&gt;&nbsp; &lt;div id="result"&gt;&lt;/div&gt; &lt;/body&gt;&lt;/html&gt; </p>
<p><br />
dwr/interface/Hello.js是由DWRServlet根據dwr.xml中的設定生成的，engine.js負責客戶端伺服端溝通，util.js是一些好用的JavaScript程式，可以讓您少寫很多JavaScript。 </p>
<p>hello.js是我們自訂的函式，按下按鈕後，會呼叫當中的hello()函式： <br />
12345678 function hello() {&nbsp;&nbsp;&nbsp; var user = $('user').value;&nbsp;&nbsp;&nbsp; Hello.hello(user, callback);} function callback(msg) {&nbsp;&nbsp; DWRUtil.setValue('result', msg);}&nbsp; </p>
<p><br />
${'user'}取得輸入欄位的DOM物件，value取得當中的欄位值，而後呼叫Hello.hello()，並將value當作參數傳送&#8230; 結果是呼叫Server端的Hello Java物件，當結果傳回後，會呼叫JavaScript的callback函式，DWRUtil的setValue()方法會將傳回的msg設定給指定 id的DOM，結果就是&#8230;啥！AJAX的功能在哪&#8230;就這個而言就是發出非同步請求，而回應不用Refresh頁面啦！&nbsp; </p>
<p><br />
好啦！這個無聊的Hello DWR可以做啥！&#8230;XD </p>
<p>已經可以讓您做個簡單的文字提示功能了&#8230;像這個&#8230; <br />
http://caterpillar.onlyfun.net/Gossip/index.html </p>
<p>把滑鼠指到書的照片上，會顯示提示文字，這些提示文字本身不是存在網頁上的，而是在Server端，當滑鼠指到書上時，會用Request object去抓，然後顯示在框框中&#8230; </p>
<p>當然！我的網站只支援PHP，所以那不是DWR完成的功能，而且我是直接用Request object跟DOM去慢慢刻的&#8230;對初學者來說已經有些麻煩了&#8230;XD </p>
<p>不過！用DWR就可以很簡單完成這個功能&#8230; </p>
<p>先寫個Java類別吧！會抓properties檔案中的文字訊息，例如&#8230; <br />
123456789101112131415 package onlyfun.caterpillar; import java.util.ResourceBundle; public class Book {&nbsp; private ResourceBundle resource;&nbsp;&nbsp;&nbsp; public Book() {&nbsp;&nbsp;&nbsp; resource = ResourceBundle.getBundle("book");&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public String getDescription(String key) {&nbsp;&nbsp;&nbsp; return resource.getString(key);&nbsp; }}&nbsp; </p>
<p><br />
從程式中就知道，它會去抓book_zh_TW.properties的資料，這不是重點啦！只是Java的一個功能，我們要看的是DWR，不過先把book_zh_TW.properties準備好&#8230; <br />
123 java=Java 學習筆記的介紹 &#8230; BlaBla...spring=Spring 技術手冊的介紹&#8230;BlaBla...ajax=Ajax in action 中文版的介紹&#8230; </p>
<p><br />
唔！裏頭是中文字，自己用native2ascii轉換吧&#8230;這也不是重點&#8230;我們是要看DWR怎麼做到文字提示功能&#8230; </p>
<p>一樣的&#8230;要開放這個Book物件，在dwr.xml中&#8230; <br />
1234567891011 &lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 1.0//EN" "http://www.getahead.ltd.uk/dwr/dwr10.dtd"&gt; &lt;dwr&gt;&nbsp; &lt;allow&gt;&nbsp; &lt;create creator="new" javascript="Book" scope="application"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param name="class" value="onlyfun.caterpillar.Book"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/create&gt;&nbsp;&nbsp;&nbsp; &lt;/allow&gt;&lt;/dwr&gt; </p>
<p><br />
scope設定為application，表示這個Book物件在整個應用程式階段都活著。 </p>
<p>然後，客戶端寫個網頁&#8230; <br />
12345678910111213141516171819202122232425262728293031323334353637383940 &lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"&gt;&lt;html&gt;&lt;head&gt;&lt;meta http-equiv="Content-Type" content="text/html; charset=BIG5"&gt;&nbsp; &lt;script type='text/javascript' src='dwr/interface/Book.js'&gt;&lt;/script&gt;&nbsp; &lt;script type='text/javascript' src='dwr/engine.js'&gt;&lt;/script&gt;&nbsp; &lt;script type='text/javascript' src='dwr/util.js'&gt;&lt;/script&gt;&nbsp; &lt;script type='text/javascript' src='book.js'&gt;&lt;/script&gt;&lt;title&gt;個人著／譯作&lt;/title&gt;&lt;/head&gt;&lt;body&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;div id="ajax" onmouseover="getBookData(this);" onmouseout="clearData();"&gt;&lt;a href="http://www.gotop.com.tw/waweb2004/home/home.aspx?pg=HM010X&amp;bn=AXP011800"&gt;&lt;small&gt;&lt;img style="border: 0px solid ; width: 80px; height: 110px; float: left;" alt="Ajax in action 中文版" title="Ajax in action 中文版" src="images/ajax_in_action_c.jpg" hspace="10" vspace="2"&gt;&lt;/small&gt;&lt;/a&gt;&lt;/div&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;div id="spring" onmouseover="getBookData(this);" onmouseout="clearData();"&gt;&lt;a href="http://www.gotop.com.tw/waweb2004/home/home.aspx?pg=HM010X&amp;bn=ACL021000"&gt;&lt;small&gt;&lt;img style="border: 0px solid ; width: 80px; height: 110px; float: left;" alt="Spring 技術手冊" title="Spring 技術手冊" src="images/SpringTech_S.jpg" hspace="10" vspace="2"&gt;&lt;/small&gt;&lt;/a&gt;&lt;/div&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;div id="java" onmouseover="getBookData(this);" onmouseout="clearData();"&gt;&lt;a href="http://www.gotop.com.tw/waweb2004/home/home.aspx?pg=HM010X&amp;bn=ACL020931"&gt;&lt;small&gt;&lt;img style="border: 0px solid ; width: 80px; height: 110px; float: left;" alt="Java 學習筆記" title="Java 學習筆記" src="images/JavaGossip_Cover_Small.jpg" hspace="10" vspace="2"&gt;&lt;/small&gt;&lt;/a&gt;&lt;/div&gt;&nbsp;&nbsp;&nbsp;&nbsp; &lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;&nbsp;&nbsp;&nbsp;&nbsp; &lt;div id="info"&gt;&lt;/div&gt; &lt;/body&gt;&lt;/html&gt; </p>
<p><br />
重點在於onmouseover跟onmouseout，滑鼠移入與移出時會呼叫的函式，還有最下面的info，抓回來的書籍介紹會放到當中&#8230; </p>
<p>book.js如下，簡單的很&#8230; <br />
1234567891011 function getBookData(ele) {&nbsp; Book.getDescription(ele.id, setBookData);} function setBookData(description) {&nbsp; DWRUtil.setValue('info', description);} function clearData() {&nbsp; DWRUtil.setValue('info', '');}&nbsp; </p>
<p><br />
程式很簡單，我懶得解釋了&#8230;XD </p>
<p>看一下畫面好了&#8230;這是滑鼠移到 Ajax in action中文版 上的介紹畫面&#8230; </p>
<p><br />
本文来自CSDN博客，转载请标明出处：http://blog.csdn.net/caterpillar_here/archive/2006/09/06/1186566.aspx</p>
<img src ="http://www.blogjava.net/caizh2009/aggbug/279567.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/caizh2009/" target="_blank">小菜毛毛</a> 2009-06-02 13:53 <a href="http://www.blogjava.net/caizh2009/articles/279567.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>面向 Java 开发人员的 Ajax: 使用 Jetty 和 Direct Web</title><link>http://www.blogjava.net/caizh2009/articles/268972.html</link><dc:creator>小菜毛毛</dc:creator><author>小菜毛毛</author><pubDate>Tue, 05 May 2009 06:49:00 GMT</pubDate><guid>http://www.blogjava.net/caizh2009/articles/268972.html</guid><wfw:comment>http://www.blogjava.net/caizh2009/comments/268972.html</wfw:comment><comments>http://www.blogjava.net/caizh2009/articles/268972.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/caizh2009/comments/commentRss/268972.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/caizh2009/services/trackbacks/268972.html</trackback:ping><description><![CDATA[<blockquote>转载IBM开发社区<br />
受异步服务器端事件驱动的 Ajax 应用程序实现较为困难，并且难于扩展。Philip McCarthy 在其广受欢迎的 <a href="http://www.ibm.com/developerworks/cn/java/j-ajax/">系列文章</a> 中介绍了一种行之有效的方法：结合使用 Comet 模式（将数据推到客户机）和 Jetty 6 的 Continuations API（将 Comet 应用程序扩展到大量客户机中）。您可以方便地在 Direct Web Remoting
(DWR) 2 中将 Comet 和 Continuations 与 Reverse Ajax 技术结合使用。 </blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters -->
<!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
<p>作为一种广泛使
用的 Web 应用程序开发技术，Ajax 牢固确立了自己的地位，随之而来的是一些通用 Ajax 使用模式。例如，Ajax
经常用于对用户输入作出响应，然后使用从服务器获得的新数据修改页面的部分内容。但是，有时 Web
应用程序的用户界面需要进行更新以响应服务器端发生的异步事件，而不需要用户操作 —— 例如，显示到达 Ajax
聊天应用程序的新消息，或者在文本编辑器中显示来自另一个用户的改变。由于只能由浏览器建立 Web 浏览器和服务器之间的 HTTP
连接，服务器无法在改动发生时将变化 &#8220;推送&#8221; 给浏览器。</p>
<p>Ajax 应用程序可以使用两种基本的方法解决这一问题：一种方法是浏览器每隔若干秒时间向服务器发出轮询以进行更新，另一种方法是服务器始终打开与浏览器的连接并在数据可用时发送给浏览器。长期连接技术被称为 <em>Comet</em>（请参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#resources">参考资料</a>）。本文将展示如何结合使用 Jetty servlet 引擎和 DWR 简捷有效地实现一个 Comet Web 应用程序。</p>
<p><a name="N10070"><span class="atitle">为什么使用 Comet？</span></a></p>
<p>轮
询方法的主要缺点是：当扩展到更多客户机时，将生成大量的通信量。每个客户机必须定期访问服务器以检查更新，这为服务器资源添加了更多负荷。最坏的一种情
况是对不频繁发生更新的应用程序使用轮询，例如一种 Ajax 邮件
Inbox。在这种情况下，相当数量的客户机轮询是没有必要的，服务器对这些轮询的回答只会是
&#8220;没有产生新数据&#8221;。虽然可以通过增加轮询的时间间隔来减轻服务器负荷，但是这种方法会产生不良后果，即延迟客户机对服务器事件的感知。当然，很多应用程
序可以实现某种权衡，从而获得可接受的轮询方法。</p>
<p>尽管如此，吸引人们使用 Comet
策略的其中一个优点是其显而易见的高效性。客户机不会像使用轮询方法那样生成烦人的通信量，并且事件发生后可立即发布给客户机。但是保持长期连接处于打开
状态也会消耗服务器资源。当等待状态的 servlet 持有一个持久性请求时，该 servlet 会独占一个线程。这将限制 Comet 对传统
servlet 引擎的可伸缩性，因为客户机的数量会很快超过服务器栈能有效处理的线程数量。</p>
<br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br />
            <img alt="" src="http://www.ibm.com/i/c.gif" border="0" width="8" height="6" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" width="16" height="16" /><br />
                        </td>
                        <td align="right" valign="top"><a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N1007D"><span class="atitle">Jetty 6 有何不同</span></a></p>
<p>Jetty 6 的目的是扩展大量同步连接，使用 Java&#8482; 语言的非阻塞 I/O（<code>java.nio</code>）库并使用一个经过优化的输出缓冲架构（参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#resources">参考资料</a>）。Jetty 还为处理长期连接提供了一些技巧：该特性称为 <em>Continuations</em>。
我将使用一个简单的 servlet 对 Continuations 进行演示，这个 servlet
将接受请求，等待处理，然后发送响应。接下来，我将展示当客户机数量超过服务器提供的处理线程后发生的状况。最后，我将使用
Continuations 重新实现 servlet，您将了解 Continuations 在其中扮演的角色。</p>
<p>为了便于理解下面的示例，我将把 Jetty servlet 引擎限制在一个单请求处理线程。<a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#listing1">清单 1</a> 展示了 jetty.xml 中的相关配置。我实际上需要在 <code>ThreadPool</code> 使用三个线程：Jetty 服务器本身使用一个线程，另一线程运行 HTTP 连接器，侦听到来的请求。第三个线程执行 servlet 代码。</p>
<br />
<a name="listing1"><strong>清单 1. 单个 servlet 线程的 Jetty 配置</strong></a><br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                <br />
            &lt;?xml version="1.0"?&gt;<br />
            &lt;!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN"<br />
            "http://jetty.mortbay.org/configure.dtd"&gt;<br />
            &lt;Configure id="Server" class="org.mortbay.jetty.Server"&gt;<br />
            &lt;Set name="ThreadPool"&gt;<br />
            &lt;New class="org.mortbay.thread.BoundedThreadPool"&gt;<br />
            &lt;Set name="minThreads"&gt;3&lt;/Set&gt;<br />
            &lt;Set name="lowThreads"&gt;0&lt;/Set&gt;<br />
            &lt;Set name="maxThreads"&gt;3&lt;/Set&gt;<br />
            &lt;/New&gt;<br />
            &lt;/Set&gt;<br />
            &lt;/Configure&gt;<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>接下来，为了模拟对异步事件的等待，<a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#listing2">清单 2</a> 展示了 <code>BlockingServlet</code> 的 <code>service()</code> 方法，该方法将使用 <code>Thread.sleep()</code> 调用在线程结束之前暂停 2000 毫秒的时间。它还在执行开始和结束时输出系统时间。为了区别输出和不同的请求，还将作为标识符的请求参数记录在日志中。</p>
<br />
<a name="listing2"><strong>清单 2. BlockingServlet</strong></a><br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                <br />
            public class BlockingServlet extends HttpServlet {<br />
            <br />
            public void service(HttpServletRequest req, HttpServletResponse res)<br />
            throws java.io.IOException {<br />
            <br />
            String reqId = req.getParameter("id");<br />
            <br />
            res.setContentType("text/plain");<br />
            res.getWriter().println("Request: "+reqId+"\tstart:\t" + new Date());<br />
            res.getWriter().flush();<br />
            <br />
            try {<br />
            Thread.sleep(2000);<br />
            } catch (Exception e) {}<br />
            <br />
            res.getWriter().println("Request: "+reqId+"\tend:\t" + new Date());<br />
            }<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>现在可以观察到 servlet 响应一些同步请求的行为。<a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#listing3">清单 3</a> 展示了控制台输出，五个使用 <code>lynx</code> 的并行请求。命令行启动五个 <code>lynx</code> 进程，将标识序号附加在请求 URL 的后面。 </p>
<br />
<a name="listing3"><strong>清单 3. 对 BlockingServlet 并发请求的输出</strong></a><br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                <br />
            <br />
            $ for i in 'seq 1 5'  ; do lynx -dump localhost:8080/blocking?id=$i &amp;  done<br />
            Request: 1      start:  Sun Jul 01 12:32:29 BST 2007<br />
            Request: 1      end:    Sun Jul 01 12:32:31 BST 2007<br />
            <br />
            Request: 2      start:  Sun Jul 01 12:32:31 BST 2007<br />
            Request: 2      end:    Sun Jul 01 12:32:33 BST 2007<br />
            <br />
            Request: 3      start:  Sun Jul 01 12:32:33 BST 2007<br />
            Request: 3      end:    Sun Jul 01 12:32:35 BST 2007<br />
            <br />
            Request: 4      start:  Sun Jul 01 12:32:35 BST 2007<br />
            Request: 4      end:    Sun Jul 01 12:32:37 BST 2007<br />
            <br />
            Request: 5      start:  Sun Jul 01 12:32:37 BST 2007<br />
            Request: 5      end:    Sun Jul 01 12:32:39 BST 2007<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>
<a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#listing3">清单 3</a> 中的输出和预期一样。因为 Jetty 只可以使用一个线程执行 servlet 的 <code>service()</code> 方法。Jetty 对请求进行排列，并按顺序提供服务。当针对某请求发出响应后将立即显示时间戳（一个 <code>end</code> 消息），servlet 接着处理下一个请求（后续的 <code>start</code> 消息）。因此即使同时发出五个请求，其中一个请求必须等待 8 秒钟的时间才能接受 servlet 处理。</p>
<p>请注意，当 servlet 被阻塞时，执行任何操作都无济于事。这段代码模拟了请求等待来自应用程序不同部分的异步事件。这里使用的服务器既不是 CPU 密集型也不是 I/O 密集型：只有线程池耗尽之后才会对请求进行排队。 </p>
<p>现在，查看 Jetty 6 的 Continuations 特性如何为这类情形提供帮助。<a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#listing4">清单 4</a> 展示了 <a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#listing2">清单 2</a> 中使用 Continuations API 重写后的 <code>BlockingServlet</code>。我将稍后解释这些代码。</p>
<br />
<a name="listing4"><strong>清单 4. ContinuationServlet</strong></a><br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                <br />
            public class ContinuationServlet extends HttpServlet {<br />
            <br />
            public void service(HttpServletRequest req, HttpServletResponse res)<br />
            throws java.io.IOException {<br />
            <br />
            String reqId = req.getParameter("id");<br />
            <br />
            Continuation cc = ContinuationSupport.getContinuation(req,null);<br />
            <br />
            res.setContentType("text/plain");<br />
            res.getWriter().println("Request: "+reqId+"\tstart:\t"+new Date());<br />
            res.getWriter().flush();<br />
            <br />
            cc.suspend(2000);<br />
            <br />
            res.getWriter().println("Request: "+reqId+"\tend:\t"+new Date());<br />
            }<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>
<a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#listing5">清单 5</a> 展示了对 <code>ContinuationServlet</code> 的五个同步请求的输出；请与 <a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#listing3">清单 3</a> 进行比较。</p>
<br />
<a name="listing5"><strong>清单 5. 对 ContinuationServlet 的五个并发请求的输出</strong></a><br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                <br />
            $ for i in 'seq 1 5'  ; do lynx -dump localhost:8080/continuation?id=$i &amp;  done<br />
            <br />
            Request: 1      start:  Sun Jul 01 13:37:37 BST 2007<br />
            Request: 1      start:  Sun Jul 01 13:37:39 BST 2007<br />
            Request: 1      end:    Sun Jul 01 13:37:39 BST 2007<br />
            <br />
            Request: 3      start:  Sun Jul 01 13:37:37 BST 2007<br />
            Request: 3      start:  Sun Jul 01 13:37:39 BST 2007<br />
            Request: 3      end:    Sun Jul 01 13:37:39 BST 2007<br />
            <br />
            Request: 2      start:  Sun Jul 01 13:37:37 BST 2007<br />
            Request: 2      start:  Sun Jul 01 13:37:39 BST 2007<br />
            Request: 2      end:    Sun Jul 01 13:37:39 BST 2007<br />
            <br />
            Request: 5      start:  Sun Jul 01 13:37:37 BST 2007<br />
            Request: 5      start:  Sun Jul 01 13:37:39 BST 2007<br />
            Request: 5      end:    Sun Jul 01 13:37:39 BST 2007<br />
            <br />
            Request: 4      start:  Sun Jul 01 13:37:37 BST 2007<br />
            Request: 4      start:  Sun Jul 01 13:37:39 BST 2007<br />
            Request: 4      end:    Sun Jul 01 13:37:39 BST 2007<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>清单 5 中有两处需要重点注意。首先，每个 <code>start</code> 消息出现两次；先不要着急。其次，更重要的一点，请求现在不需排队就能够并发处理，注意所有 <code>start</code> 和 <code>end</code> 消息的时间戳是相同的。因此，每个请求的处理时间不会超过两秒，即使只运行一个 servlet 线程。</p>
<br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br />
            <img alt="" src="http://www.ibm.com/i/c.gif" border="0" width="8" height="6" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" width="16" height="16" /><br />
                        </td>
                        <td align="right" valign="top"><a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N10136"><span class="atitle">Jetty Continuations 机制原理</span></a></p>
<p>理解了 Jetty Continuations 机制的实现原理，您就能够解释 <a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#listing5">清单 5</a> 中的现象。要使用 Continuations，必须对 Jetty 进行配置，以使用其 <code>SelectChannelConnector</code> 处理请求。这个连接器构建在 <code>java.nio</code> API 之上，因此使它能够不用消耗每个连接的线程就可以持有开放的连接。当使用 <code>SelectChannelConnector</code> 时，<code>ContinuationSupport.getContinuation()</code> 将提供一个 <code>SelectChannelConnector.RetryContinuation</code> 实例。（然而，您应该只针对 <code>Continuation</code> 接口进行编码；请参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#side1">Portability and the Continuations API</a>。）当对 <code>RetryContinuation</code> 调用 <code>suspend()</code> 时，它将抛出一个特殊的运行时异常 ——  <code>RetryRequest</code> —— 该异常将传播到 servlet 以外并通过过滤器链传回，并由 <code>SelectChannelConnector</code> 捕获。 但是发生该异常之后并没有将响应发送给客户机，请求被放到处于等待状态的 <code>Continuation</code> 队列中，而 HTTP 连接仍然保持打开状态。此时，为该请求提供服务的线程将返回 <code>ThreadPool</code>，用以为其他请求提供服务。 </p>
<table align="right" border="0" width="40%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td width="10"><img alt="" src="http://www.ibm.com/i/c.gif" width="10" height="1" /></td>
            <td>
            <table border="1" width="100%" cellpadding="5" cellspacing="0">
                <tbody>
                    <tr>
                        <td bgcolor="#eeeeee">
                        <a name="side1"><strong>可移植性和 Continuations API</strong></a><br />
                        <p>我提到过应该使用 Jetty 的 <code>SelectChannelConnector</code> 来启用 Continuations 功能。然而，Continuations API 仍然可用于传统的 <code>SocketConnector</code>，这种情况下 Jetty 将回退到不同的 <code>Continuation</code> 实现，该实现使用 <code>wait()/notify()</code> 方法。您的代码仍然可以编译和运行，但是却失去了非阻塞 Continuations 的优点。如果您希望继续使用非 Jetty 服务器，您应该考虑编写自己的 <code>Continuation</code> 包装器，在运行时期使用反射检查 Jetty Continuations 库是否可用。DWR 就使用了这种策略。</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<p>暂停的请求将一直保持在等待状态的 <code>Continuation</code> 队列，直到超出指定的时限，或者当对 <code>resume()</code> 方法的 <code>Continuation</code> 调用 <code>resume()</code> 时（稍后将详细介绍）。出现上述任意一种条件时，请求将被重新提交到 servlet（通过过滤器链）。事实上，整个请求被重新进行处理，直到首次调用 <code>suspend()</code>。当执行第二次发生 <code>suspend()</code> 调用时，<code>RetryRequest</code> 异常不会被抛出，执行照常进行。 </p>
<p>现在应该可以解释 <a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#listing5">清单 5</a> 中的输出了。每个请求依次进入 servlet 的 <code>service()</code> 方法后，将发送 <code>start</code> 消息进行响应，<code>Continuation</code> 的 <code>suspend()</code> 方法引发 servlet 异常，将释放线程使其处理下一个请求。所有五个请求快速通过 <code>service()</code> 方法的第一部分，并进入等待状态，并且所有 <code>start</code> 消息将在几毫秒内输出。两秒后，当超过 <code>suspend()</code> 的时限后，将从等待队列中检索第一个请求，并将其重新提交给 <code>ContinuationServlet</code>。第二次输出 <code>start</code> 消息，立即返回对 <code>suspend()</code> 的第二次调用，并且发送 <code>end</code> 消息进行响应。然后将在此执行 servlet 代码来处理队列中的下一个请求，以此类推。</p>
<p>因此，在 <code>BlockingServlet</code> 和 <code>ContinuationServlet</code> 两种情况中，请求被放入队列中以访问单个 servlet 线程。然而，虽然 servlet 线程执行期间 <code>BlockingServlet</code> 发生两秒暂停，<code>SelectChannelConnector</code> 中的 <code>ContinuationServlet</code> 的暂停发生在 servlet 之外。<code>ContinuationServlet</code> 的总吞吐量更高一些，因为 servlet 线程没有将大部分时间用在 <code>sleep()</code> 调用中。</p>
<br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br />
            <img alt="" src="http://www.ibm.com/i/c.gif" border="0" width="8" height="6" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" width="16" height="16" /><br />
                        </td>
                        <td align="right" valign="top"><a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N10209"><span class="atitle">使 Continuations 变得有用</span></a></p>
<p>现在您已经了解到 Continuations 能够不消耗线程就可以暂停 servlet 请求，我需要进一步解释 Continuations API 以向您展示如何在实际应用中使用。</p>
<p>
<code>resume()</code> 方法生成一对 <code>suspend()</code>。可以将它们视为标准的 <code>Object wait()</code>/<code>notify()</code> 机制的 Continuations 等价体。就是说，<code>suspend()</code> 使 <code>Continuation</code>（因此也包括当前方法的执行）处于暂停状态，直到超出时限，或者另一个线程调用 <code>resume()</code>。<code>suspend()</code>/<code>resume()</code> 对于实现真正使用 Continuations 的 Comet 风格的服务非常关键。其基本模式是：从当前请求获得 <code>Continuation</code>，调用 <code>suspend()</code>，等待异步事件的到来。然后调用 <code>resume()</code> 并生成一个响应。 </p>
<p>然而，与 Scheme 这种语言中真正的语言级别的 continuations 或者是 Java 语言的 <code>wait()</code>/<code>notify()</code> 范例不同的是，对 Jetty <code>Continuation</code> 调用 <code>resume()</code> 并不意味着代码会从中断的地方继续执行。正如您刚刚看到的，实际上和 <code>Continuation</code> 相关的请求被重新处理。这会产生两个问题：重新执行 <a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#listing4">清单 4</a> 中的 <code>ContinuationServlet</code> 代码，以及丢失状态：即调用 <code>suspend()</code> 时丢失作用域内所有内容。</p>
<p>第一个问题的解决方法是使用 <code>isPending()</code> 方法。如果 <code>isPending()</code> 返回值为 true，这意味着之前已经调用过一次 <code>suspend()</code>，而重新执行请求时还没有发生第二次 <code>suspend()</code> 调用。换言之，根据 <code>isPending()</code> 条件在执行 <code>suspend()</code> 调用之前运行代码，这样将确保对每个请求只执行一次。在 <code>suspend()</code> 调用具有等幂性之前，最好先对应用程序进行设计，这样即使调用两次也不会出现问题，但是某些情况下无法使用 <code>isPending()</code> 方法。<code>Continuation</code> 也提供了一种简单的机制来保持状态：<code>putObject(Object)</code> 和 <code>getObject()</code> 方法。在 <code>Continuation</code> 发生暂停时，使用这两种方法可以保持上下文对象以及需要保存的状态。您还可以使用这种机制作为在线程之间传递事件数据的方式，稍后将演示这种方法。</p>
<br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br />
            <img alt="" src="http://www.ibm.com/i/c.gif" border="0" width="8" height="6" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" width="16" height="16" /><br />
                        </td>
                        <td align="right" valign="top"><a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N1029C"><span class="atitle">编写基于 Continuations 的应用程序</span></a></p>
<p>作
为实际示例场景，我将开发一个基本的 GPS 坐标跟踪 Web
应用程序。它将在不规则的时间间隔内生成随机的经纬度值对。发挥一下想象力，生成的坐标值可能就是临近的一个公共车站、随身携带着 GPS
设备的马拉松选手、汽车拉力赛中的汽车或者运输中的包裹。令人感兴趣的是我将如何告诉浏览器这个坐标。<a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#fig1">图 1</a> 展示了这个简单的 GPS 跟踪器应用程序的类图：</p>
<br />
<a name="fig1"><strong>图 1. 显示 GPS 跟踪器应用程序主要组件的类图</strong></a><br />
<img alt="GPS 跟踪器组件的 UML 类图" src="http://www.ibm.com/developerworks/cn/java/j-jettydwr/figure1.gif" width="545" height="308" />
<br />
<p>首先，应用程序需要某种方法来生成坐标。这将由 <code>RandomWalkGenerator</code> 完成。从一对初始坐标对开始，每次调用它的私有 <code>generateNextCoord()</code> 方法时，将从该位置移动随机指定的距离，并将新的位置作为 <code>GpsCoord</code> 对象返回。初始化完成后，<code>RandomWalkGenerator</code> 将生成一个线程，该线程以随机的时间间隔调用 <code>generateNextCoord()</code> 方法并将生成的坐标发送给任何注册了 <code>addListener()</code> 的 <code>CoordListener</code> 实例。<a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#listing6">清单 6</a> 展示了 <code>RandomWalkGenerator</code> 循环的逻辑：</p>
<br />
<a name="listing6"><strong>清单 6. RandomWalkGenerator's run() 方法</strong></a><br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                <br />
            public void run() {<br />
            <br />
            try {<br />
            while (true) {<br />
            int sleepMillis = 5000 + (int)(Math.random()*8000d);<br />
            Thread.sleep(sleepMillis);<br />
            dispatchUpdate(generateNextCoord());<br />
            }<br />
            } catch (Exception e) {<br />
            throw new RuntimeException(e);<br />
            }<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>
<code>CoordListener</code> 是一个回调接口，仅仅定义 <code>onCoord(GpsCoord coord)</code> 方法。在本例中，<code>ContinuationBasedTracker</code> 类实现 <code>CoordListener</code>。<code>ContinuationBasedTracker</code> 的另一个公有方法是 <code>getNextPosition(Continuation, int)</code>。<a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#listing7">清单 7</a> 展示了这些方法的实现：</p>
<br />
<a name="listing7"><strong>清单 7.  ContinuationBasedTracker 结构</strong></a><br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                <br />
            public GpsCoord getNextPosition(Continuation continuation, int timeoutSecs) {<br />
            <br />
            synchronized(this) {<br />
            if (!continuation.isPending()) {<br />
            pendingContinuations.add(continuation);<br />
            }<br />
            <br />
            // Wait for next update<br />
            continuation.suspend(timeoutSecs*1000);<br />
            }<br />
            <br />
            return (GpsCoord)continuation.getObject();<br />
            }<br />
            <br />
            <br />
            public void onCoord(GpsCoord gpsCoord) {<br />
            <br />
            synchronized(this) {<br />
            for (Continuation continuation : pendingContinuations) {<br />
            <br />
            continuation.setObject(gpsCoord);<br />
            continuation.resume();<br />
            }<br />
            <br />
            pendingContinuations.clear();<br />
            }<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>当客户机使用 <code>Continuation</code> 调用 <code>getNextPosition()</code> 时，<code>isPending</code> 方法将检查此时的请求是否是第二次执行，然后将它添加到等待坐标的 <code>Continuation</code> 集合中。然后该 <code>Continuation</code> 被暂停。同时，<code>onCoord</code> —— 生成新坐标时将被调用 —— 循环遍历所有处于等待状态的 <code>Continuation</code>，对它们设置 GPS 坐标，并重新使用它们。之后，每个再次执行的请求完成 <code>getNextPosition()</code> 执行，从 <code>Continuation</code> 检索 <code>GpsCoord</code> 并将其返回给调用者。注意此处的同步需求，是为了保护 <code>pendingContinuations</code> 集合中的实例状态不会改变，并确保新增的 <code>Continuation</code> 在暂停之前没有被处理过。</p>
<p>最后一个难点是 servlet 代码本身，如 <a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#listing8">清单 8</a> 所示：</p>
<br />
<a name="listing8"><strong>清单 8. GPSTrackerServlet 实现</strong></a><br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                <br />
            public class GpsTrackerServlet extends HttpServlet {<br />
            <br />
            private static final int TIMEOUT_SECS = 60;<br />
            private ContinuationBasedTracker tracker = new ContinuationBasedTracker();<br />
            <br />
            public void service(HttpServletRequest req, HttpServletResponse res)<br />
            throws java.io.IOException {<br />
            <br />
            Continuation c = ContinuationSupport.getContinuation(req,null);<br />
            GpsCoord position = tracker.getNextPosition(c, TIMEOUT_SECS);<br />
            <br />
            String json = new Jsonifier().toJson(position);<br />
            res.getWriter().print(json);<br />
            }<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>如您所见，servlet 只执行了很少的工作。它仅仅获取了请求的 <code>Continuation</code>，调用 <code>getNextPosition()</code>，将 <code>GPSCoord</code> 转换成 JavaScript Object Notation (JSON)，然后输出。这里不需要防止重新执行，因此我不必检查 <code>isPending()</code>。<a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#listing9">清单 9</a> 展示了调用 <code>GpsTrackerServlet</code> 的输出，同样，有五个同步请求而服务器只有一个可用线程： </p>
<br />
<a name="listing9"><strong>Listing 9. Output of GPSTrackerServlet</strong></a><br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                <br />
            $  for i in 'seq 1 5'  ; do lynx -dump localhost:8080/tracker &amp;  done<br />
            { coord : { lat : 51.51122, lng : -0.08103112 } }<br />
            { coord : { lat : 51.51122, lng : -0.08103112 } }<br />
            { coord : { lat : 51.51122, lng : -0.08103112 } }<br />
            { coord : { lat : 51.51122, lng : -0.08103112 } }<br />
            { coord : { lat : 51.51122, lng : -0.08103112 } }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>这个示例并不引人注意，但是提供了概念证明。发出请求后，它们将一直保持打开的连接直至生成坐标，此时将快速生成响应。这是 Comet 模式的基本原理，Jetty 使用这种原理在一个线程内处理 5 个并发请求，这都是 Continuations 的功劳。</p>
<br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br />
            <img alt="" src="http://www.ibm.com/i/c.gif" border="0" width="8" height="6" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" width="16" height="16" /><br />
                        </td>
                        <td align="right" valign="top"><a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N1037F"><span class="atitle">创建一个 Comet 客户机</span></a></p>
<p>现在您已经了解了如何使用 Continuations 在理论上创建非阻塞 Web 服务，您可能想知道如何创建客户端代码来使用这种功能。一个 Comet 客户机需要完成以下功能：
</p>
<ol>
    <li>保持打开 <code>XMLHttpRequest</code> 连接，直到收到响应。</li>
    <li>将响应发送到合适的 JavaScript 处理程序。</li>
    <li>立即建立新的连接。</li>
</ol>
更高级的 Comet 设置将使用一个连接将数据从不同服务推入浏览器，并且客户机和服务器配有相应的路由机制。一种可行的方法是根据一种 JavaScript 库，例如 Dojo，编写客户端代码，这将提供基于 Comet 的请求机制，其形式为 <code>dojo.io.cometd</code>。
<p>然而，如果服务器使用 Java 语言，使用 DWR 2 可以同时在客户机和服务器上获得 Comet 高级支持，这是一种不错的方法（参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#resources">参考资料</a>）。如果您并不了解 DWR 的话，请参阅本系列第 3 部分 &#8220;<a href="http://www.ibm.com/developerworks/cn/java/j-ajax3/">结合 Direct Web Remoting 使用 Ajax</a>&#8221;。DWR 透明地提供了一种 HTTP-RPC 传输层，将您的 Java 对象公开给网络中 JavaScript 代码的调用。DWR 生成客户端代理，将自动封送和解除封送数据，处理安全问题，提供方便的客户端实用工具库，并可以在所有主要浏览器上工作。 </p>
<br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br />
            <img alt="" src="http://www.ibm.com/i/c.gif" border="0" width="8" height="6" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" width="16" height="16" /><br />
                        </td>
                        <td align="right" valign="top"><a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N103A8"><span class="atitle">DWR 2: Reverse Ajax</span></a></p>
<p>DWR 2 最新引入了 <em>Reverse Ajax</em> 概念。这种机制可以将服务器端事件 &#8220;推入&#8221; 到客户机。客户端 DWR 代码透明地处理已建立的连接并解析响应，因此从开发人员的角度来看，事件是从服务器端 Java 代码轻松地发布到客户机中。</p>
<p>DWR 经过配置之后可以使用 Reverse Ajax 的三种不同机制。第一种就是较为熟悉的轮询方法。第二种称为 <em>piggyback</em>，
这种机制并不创建任何到服务器的连接，相反，将一直等待直至发生另一个 DWR 服务，piggybacks
使事件等待该请求的响应。这使它具有较高的效率，但也意味着客户机事件通知被延迟到直到发生另一个不相关的客户机调用。最后一种机制使用长期的、
Comet 风格的连接。最妙的是，当运行在 Jetty 下时，DWR 能够自动检测并切换为使用 Contiuations，实现非阻塞
Comet。</p>
<p>我将在 GPS 示例中结合使用 Reverse Ajax 和 DWR 2。通过这种演示，您将对 Reverse Ajax 的工作原理有更多的了解。</p>
<p>此时不再需要使用 servlet。DWR 提供了一个控制器 servlet，它将在 Java 对象之上直接转交客户机请求。同样也不需要显式地处理 Continuations，因为 DWR 将在内部进行处理。因此我只需要一个新的 <code>CoordListener</code> 实现，将坐标更新发布到到任何客户机浏览器上。</p>
<p>
<code>ServerContext</code> 接口提供了 DWR 的 Reverse Ajax 功能。<code>ServerContext</code> 可以察觉到当前查看给定页面的所有 Web 客户机，并提供一个 <code>ScriptSession</code> 进行相互通信。<code>ScriptSession</code> 用于从 Java 代码将 JavaScript 片段推入到客户机。<a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#listing10">清单 10</a> 展示了 <code>ReverseAjaxTracker</code> 响应坐标通知的方式，并使用它们生成对客户端 <code>updateCoordinate()</code> 函数的调用。注意对 DWR <code>ScriptBuffer</code> 对象调用 <code>appendData()</code> 将自动把 Java 对象封送给 JSON（如果使用合适的转换器）。</p>
<br />
<a name="listing10"><strong>清单 10. ReverseAjaxTracker 中的通知回调方法</strong></a><br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                <br />
            public void onCoord(GpsCoord gpsCoord) {<br />
            <br />
            // Generate JavaScript code to call client-side<br />
            // function with coord data<br />
            ScriptBuffer script = new ScriptBuffer();<br />
            script.appendScript("updateCoordinate(")<br />
            .appendData(gpsCoord)<br />
            .appendScript(");");<br />
            <br />
            // Push script out to clients viewing the page<br />
            Collection&lt;ScriptSession&gt; sessions = <br />
            sctx.getScriptSessionsByPage(pageUrl);<br />
            <br />
            for (ScriptSession session : sessions) {<br />
            session.addScript(script);<br />
            }   <br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>接下来，必须对 DWR 进行配置以感知 <code>ReverseAjaxTracker</code> 的存在。在大型应用程序中，可以使用 DWR 的 Spring 集成提供 Spring 生成的 bean。但是，在本例中，我仅使用 DWR 创建了一个 <code>ReverseAjaxTracker</code> 新实例并将其放到 <code>application</code> 范围中。所有后续请求将访问这个实例。</p>
<p>我还需告诉 DWR 如何将数据从 <code>GpsCoord</code> beans 封送到 JSON。由于 <code>GpsCoord</code> 是一个简单对象，DWR 的基于反射的 <code>BeanConverter</code> 就可以完成此功能。<a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#listing11">清单 11</a> 展示了 <code>ReverseAjaxTracker</code> 的配置：</p>
<br />
<a name="listing11"><strong>清单 11. ReverseAjaxTracker 的 DWR 配置</strong></a><br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                <br />
            &lt;dwr&gt;<br />
            &lt;allow&gt;<br />
            &lt;create creator="new" javascript="Tracker" scope="application"&gt;<br />
            &lt;param name="class" value="developerworks.jetty6.gpstracker.ReverseAjaxTracker"/&gt;<br />
            &lt;/create&gt;<br />
            <br />
            &lt;convert converter="bean" match="developerworks.jetty6.gpstracker.GpsCoord"/&gt;<br />
            &lt;/allow&gt;<br />
            &lt;/dwr&gt;<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>
<code>create</code> 元素的 <code>javascript</code> 属性指定了 DWR 用于将跟踪器公开为 JavaScript 对象的名字，在本例中，我的客户端代码没有使用该属性，而是将数据从跟踪器推入到其中。同样
，还需对 web.xml 进行额外的配置，以针对 Reverse Ajax 配置 DWR，如 <a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#listing12">清单 12</a> 所示：</p>
<br />
<a name="listing12"><strong>清单 12. DwrServlet 的 web.xml 配置</strong></a><br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                <br />
            &lt;servlet&gt;<br />
            &lt;servlet-name&gt;dwr-invoker&lt;/servlet-name&gt;<br />
            &lt;servlet-class&gt;<br />
            org.directwebremoting.servlet.DwrServlet<br />
            &lt;/servlet-class&gt;<br />
            &lt;init-param&gt;<br />
            &lt;param-name&gt;activeReverseAjaxEnabled&lt;/param-name&gt;<br />
            &lt;param-value&gt;true&lt;/param-value&gt;<br />
            &lt;/init-param&gt;<br />
            &lt;init-param&gt;<br />
            &lt;param-name&gt;initApplicationScopeCreatorsAtStartup&lt;/param-name&gt;<br />
            &lt;param-value&gt;true&lt;/param-value&gt;<br />
            &lt;/init-param&gt;<br />
            &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;<br />
            &lt;/servlet&gt;<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>第一个 servlet <code>init-param</code>，<code>activeReverseAjaxEnabled</code> 将激活轮询和 Comet 功能。第二个 <code>initApplicationScopeCreatorsAtStartup</code> 通知 DWR 在应用程序启动时初始化 <code>ReverseAjaxTracker</code>。这将在对 bean 生成第一个请求时改写延迟初始化（lazy initialization）的常规行为 —— 在本例中这是必须的，因为客户机不会主动对 <code>ReverseAjaxTracker</code> 调用方法。</p>
<p>最后，我需要实现调用自 DWR 的客户端 JavaScript 函数。将向回调函数 —— <code>updateCoordinate()</code> —— 传递 <code>GpsCoord</code> Java bean 的 JSON 表示，由 DWR 的 <code>BeanConverter</code> 自动序列化。该函数将从坐标中提取 <code>longitude</code> 和 <code>latitude</code> 字段，并通过调用 Document Object Model (DOM) 将它们附加到列表中。<a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#listing13">清单 13</a> 展示了这一过程，以及页面的 <code>onload</code> 函数。<code>onload</code> 包含对 <code>dwr.engine.setActiveReverseAjax(true)</code> 的调用，将通知 DWR 打开与服务器的持久连接并等待回调。</p>
<br />
<a name="listing13"><strong>清单 13. 简单 Reverse Ajax GPS 跟踪器的客户端实现</strong></a><br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">                <br />
            window.onload = function() {<br />
            dwr.engine.setActiveReverseAjax(true);<br />
            }<br />
            <br />
            function updateCoordinate(coord) {<br />
            if (coord) {<br />
            var li = document.createElement("li");<br />
            li.appendChild(document.createTextNode(<br />
            coord.longitude + ", " + coord.latitude)<br />
            );<br />
            document.getElementById("coords").appendChild(li);<br />
            }<br />
            }<br />
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<table align="right" border="0" width="30%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td width="10"><img alt="" src="http://www.ibm.com/i/c.gif" width="10" height="1" /></td>
            <td>
            <table border="1" width="100%" cellpadding="5" cellspacing="0">
                <tbody>
                    <tr>
                        <td bgcolor="#eeeeee">
                        <a name="N1048A"><strong>不使用 JavaScript 更新页面</strong></a><br />
                        如果希望最小化应用程序中使用的 JavaScript 代码的数量，可以使用 <code>ScriptSession</code> 编写 JavaScript 回调：将 <code>ScriptSession</code> 实例封装在 DWR <code>Util</code> 对象中。该类将提供直接操作浏览器 DOM 的简单 Java 方法，并在后台自动生成所需的脚本。
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<p>现在我可以将浏览器指向跟踪器页面，DWR 将在生成坐标数据时把数据推入客户机。该实现输出生成坐标的列表，如 <a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#fig2">图 2</a> 所示：</p>
<br />
<a name="fig2"><strong>图 2. ReverseAjaxTracker 的输出</strong></a><br />
<img alt="列出生成坐标的简单 Web 页面" src="http://www.ibm.com/developerworks/cn/java/j-jettydwr/figure2.jpg" width="536" height="427" />
<br />
<p>可以看到，使用 Reverse Ajax 创建事件驱动的 Ajax 应用程序非常简单。请记住，正是由于 DWR 使用了 Jetty Continuations，当客户机等待新事件到来时不会占用服务器上面的线程。</p>
<p>此时，集成来自 Yahoo! 或 Google 的地图部件非常简单。通过更改客户端回调，可轻松地将坐标传送到地图 API，而不是直接附加到页面中。<a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#fig3">图 3</a> 展示了 DWR Reverse Ajax GPS 跟踪器在此类地图组件上标绘随机路线：</p>
<br />
<a name="fig3"><strong>Figure 3. 具有地图 UI 的 ReverseAjaxTracker</strong></a><br />
<img alt="地图显示路线跟踪生成的坐标" src="http://www.ibm.com/developerworks/cn/java/j-jettydwr/figure3.jpg" width="536" height="427" />
<br />
<br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br />
            <img alt="" src="http://www.ibm.com/i/c.gif" border="0" width="8" height="6" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" width="16" height="16" /><br />
                        </td>
                        <td align="right" valign="top"><a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N104CB"><span class="atitle">结束语</span></a></p>
<p>通
过本文，您了解了如何结合使用 Jetty Continuations 和 Comet 为事件驱动 Ajax
应用程序提供高效的可扩展解决方案。我没有给出 Continuations
可扩展性的具体数字，因为实际应用程序的性能取决于多种变化的因素。服务器硬件、所选择的操作系统、JVM 实现、Jetty
配置以及应用程序的设计和通信量配置文件都会影响 Jetty Continuations 的性能。然而，Webtide 的 Greg
Wilkins（主要的 Jetty 开发人员）曾经发布了一份关于 Jetty 6 的白皮书，对使用 Continuations 和没有使用
Continuations 的 Comet 应用程序的性能进行了比较，该程序同时处理 10000 个并发请求（参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#resources">参考资料</a>）。在 Greg 的测试中，使用 Continuations 能够减少线程消耗，并同时减少了超过 10 倍的栈内存消耗。</p>
<p>您
还看到了使用 DWR 的 Reverse Ajax 技术实现事件驱动 Ajax 应用程序是多么简单。DWR
不仅省去了大量客户端和服务器端编码，而且 Reverse Ajax 还从代码中将完整的服务器-推送机制抽象出来。通过更改 DWR
的配置，您可以自由地在 Comet、轮询，甚至是 piggyback
方法之间进行切换。您可以对此进行实验，并找到适合自己应用程序的最佳性能策略，同时不会影响到自己的代码。</p>
<p>如果希望对自己的 Reverse Ajax 应用程序进行实验，下载并研究 DWR 演示程序的代码（DWR 源代码发行版的一部分，参阅 <a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#resources">参考资源</a>）将非常有帮助。如果希望亲自运行示例，还可获得本文使用的示例代码（参见 <a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#download">下载</a>）。</p>
<br />
<br />
<table border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" width="100%" height="1" /><br />
            <img alt="" src="http://www.ibm.com/i/c.gif" border="0" width="8" height="6" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" align="right" cellpadding="0" cellspacing="0">
    <tbody>
        <tr align="right">
            <td><img src="http://www.ibm.com/i/c.gif" alt="" width="100%" height="4" /><br />
            <table border="0" cellpadding="0" cellspacing="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" width="16" height="16" /><br />
                        </td>
                        <td align="right" valign="top"><a href="http://www.ibm.com/developerworks/cn/java/j-jettydwr/#main" class="fbox"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><span class="atitle"><a name="download">下载</a></span></p>
<table class="data-table-1" border="0" width="100%" cellpadding="0" cellspacing="0">
    <tbody>
        <tr>
            <th scope="col">描述</th><th scope="col">名字</th><th scope="col">大小</th><th scope="col">下载方法</th>
        </tr>
        <tr>
            <th class="tb-row" scope="row">示例代码</th>
            <td nowrap="nowrap">jetty-dwr-comet-src.tgz</td>
            <td nowrap="nowrap">8KB</td>
            <td nowrap="nowrap"><a class="fbox" href="http://download.boulder.ibm.com/ibmdl/pub/software/dw/java/j-jetty-dwr-comet-src.tgz"><strong>HTTP</strong></a></td>
        </tr>
    </tbody>
</table>
<table border="0" cellpadding="0" cellspacing="0">
    <tbody>
        <tr valign="top">
            <td colspan="5"><img alt="" src="http://www.ibm.com/i/c.gif" border="0" width="12" height="12" /></td>
        </tr>
        <tr>
            <td><img alt="" src="http://www.ibm.com/i/v14/icons/fw.gif" width="16" height="16" /></td>
            <td><a class="fbox" href="http://www.ibm.com/developerworks/cn/whichmethod.html">关于下载方法的信息</a></td>
            <td><img alt="" src="http://www.ibm.com/i/c.gif" width="50" height="1" /></td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="resources"><span class="atitle">参考资料 </span></a></p>
<strong>学习</strong><br />
<ul>
    <li>您可以参阅本文在 developerWorks 全球站点上的 <a href="http://www.ibm.com/developerworks/java/library/j-jettydwr/?S_TACT=105AGX52&amp;S_CMP=cn-a-j" target="_blank">英文原文</a> 。<br />
    <br />
    </li>
    <li>
    &#8220;<a href="http://alex.dojotoolkit.org/?p=545">Comet: Low Latency Data for the Browser</a>&#8221;：Alex Russell 是 Dojo Toolkit 的项目主管和 Dojo Foundation 的主席，他在这篇博客文章中提出了 <em>Comet</em> 这个术语。<br />
    <br />
    </li>
    <li>
    <a href="http://jetty.mortbay.org/">Jetty</a>：Jetty 是一种开源的基于标准的 Web 服务器，完全使用 Java 语言实现。<br />
    <br />
    </li>
    <li>
    &#8220;<a href="http://www.webtide.com/downloads/whitePaperAjaxJetty.html">Ajax, Comet and Jetty</a>&#8221;（Greg Wilkins，Webtide，2006 年 1 月）：Wilkins 的这份白皮书讨论了扩展 Ajax 连接的 Jetty 架构方法。<br />
    <br />
    </li>
    <li>
    <a href="http://docs.codehaus.org/display/JETTY/Continuations">Continuations</a>：了解更多关于 Jetty 的 Continuations 特性的信息。<br />
    <br />
    </li>
    <li>
    <a href="http://getahead.org/dwr">Direct Web Remoting (DWR)</a>: DWR 是用于编写 Ajax Web 应用程序的开源 Java 库，使浏览器中的 JavaScript 能够与服务器中的 Java 代码进行交互。<br />
    <br />
    </li>
    <li>
    &#8220;<a href="http://www.ibm.com/developerworks/cn/java/j-javaio/">Merlin 给 Java 平台带来了非阻塞 I/O</a>&#8221;（Aruna Kalagnanam 和 Balu G, developerWorks，2002 年 3 月）：阅读这个 Java 非阻塞 I/O 包，由 JDK 1.4 引入。<br />
    <br />
    </li>
    <li>
    <a href="http://www.ibm.com/developerworks/cn/java/">developerWorks Java 技术专区</a>：提供了关于 Java 编程各个方面的数百篇文章。<br />
    <br />
    </li>
    <li> 浏览 <a href="http://www.ibm.com/developerworks/apps/SendTo?bookstore=safari">技术书店</a>，查阅有关本文所述主题以及其他技术主题的书籍。  <br />
    <br />
    </li>
</ul>
<br />
<strong>获得产品和技术</strong><br />
<ul>
    <li>
    <a href="http://docs.codehaus.org/display/JETTY/Downloading+and+Installing#download">Jetty</a>：下载 Jetty。<br />
    <br />
    </li>
    <li>
    <a href="http://getahead.org/dwr/download">DWR</a>：下载 DWR。</li>
</ul>
<br />
<strong>讨论</strong><br />
查看 <a href="http://www.ibm.com/developerworks/blogs/?S_TACT=105AGX52&amp;S_CMP=cn-a-j">developerWorks blogs</a>，加入 <a aiotarget="false" aiotitle="developerWorks 社区" href="http://www.ibm.com/developerworks/community?S_TACT=105AGX52&amp;S_CMP=cn-a-j">developerWorks 社区</a>。
<img src ="http://www.blogjava.net/caizh2009/aggbug/268972.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/caizh2009/" target="_blank">小菜毛毛</a> 2009-05-05 14:49 <a href="http://www.blogjava.net/caizh2009/articles/268972.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>