DWR详解(二)——反向AJAX

DWR详解(二)——反向AJAX

上一篇文章(DWR详解一)提到DWR允许javascript访问服务器端的Java方法,这使得AJAX使用起来会比较容易,而在DWR2.0里面添加了一个非常强大的功能——反向AJAX,也就是说,服务器端的Java方法可以取得浏览器端的Web上下文,并可以调用javascript的方法,将服务器端的数据异步地传输给浏览器。本文将通过一个demo展示这种特性。这个demo实现了类似股票交易系统中实时更新数据的功能,事实上是通过发布-订阅模式去实现的。也就是说,客户端订阅一个主题,服务器端通过一个线程向订阅这个主题的浏览器定时、异步地发送数据,从而实现了这种实时更新的功能。工程代码请点击这里 下载reverseAjaxDemo

    我们知道,客户端浏览器可以随时连接到web服务器,并向服务器请求资源,而服务器却没有这种能力,它不能主动地于客户端浏览器建立连接,并主动地将数据发送给浏览器。DWR支持3种从服务器端发送数据给客户端的方式:

1、轮询。客户端在每个时间周期内向服务器发送请求,看看服务器端有没有数据更新,如果有,就向服务器请求数据。

2、Comet:基于HTTP长连接的服务器推动方式。客户端向服务器发送请求后,服务器将数据通过response发送给客户端,但并不会将此response关闭,而是一直通过response将最新的数据发送给客户端浏览器,直到客户端浏览器关闭。

3、PiggyBack(回传)。服务器端将最新的数据排成队列,然后等待客户端下一次请求,接收到请求后就将等待更新的数据发给客户端。

这3种方式各有优劣,而DWR可以同时支持轮询和Comet。

首先,我们要让DWR程序支持反向AJAX。只需要在web.xml中DWRServlet里添加一个初始化参数:

<servlet>
<servlet-name>dwr-invoker</servlet-name>
<servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
<init-param>
<param-name>activeReverseAjaxEnabled</param-name>
<param-value>true</param-value>
</init-param>
...
</servlet>

另外,这个demo使用了TIBCO General Interface(GI)的AJAX框架,因此需要引入JSX这个组件库以及在gidemo目录下在config.xml和appCanvas.xml定义需要使用的组件(主要是用到Matrix数据表格这个组件),关于TIBCO GI,请参考http://www.tibco.com/devnet/index.html

这个demo的核心之处在于服务器端的发布者——Publisher.java,在这个类里面,首先通过org.directwebremoting.WebContext来获得访问这个应用的Web上下文:

WebContext webContext = WebContextFactory.get();
ServletContext servletContext = webContext.getServletContext();
serverContext = ServerContextFactory.get(servletContext);

webContext.getScriptSessionsByPage("");

这里主要通过WebContext类获得DWR应用的WEB上下文,用ServletContext获得DWRServlet的上下文,以及通过WEB上下文获取访问本应用的客户端浏览器的ScriptSession。通过ScriptSession,可以在服务器端向客户端浏览器发出执行js方法的指令,并把服务器端数据传送给js方法,具体的用法如下:

Collection sessions = serverContext.getScriptSessionsByPage("/reverseAjaxDemo/index.html");
ScriptProxy proxy = new ScriptProxy(sessions);
Corporation corp = corporations.getNextChangedCorporation();
proxy.addFunctionCall("OpenAjax.publish", "gidemo", "corporation", corp);

这段代码首先通过getScriptSessionsByPage方法获得所有访问/reverseAjaxDemo/index.html这个资源的客户端浏览器的ScriptSession,并为这些session建立代理(ScriptProxy),通过这个代理,让客户端执行OpenAjax.publish的js方法(见OpenAjax.js)。其中addFunctionCall就是向客户端发送执行js方法的服务器端方法,第一个参数是js方法的签名,后面的都是js方法的参数。其中"gidemo"是服务器端发布的主题(topic),"coporation"是要发布的变量,而corp则是要发布的即时数据。corp这个对象是随机生成的(见Corporation和Corporations类),Publish.java这个类启动了一个线程(worker),这个线程不断地生成corporation的数据,并发布给客户端。

以下是html页面的核心部分的代码:

<div style="width:100%; height:280px;" id="gidemo">
      <script. type="text/javascript"
              src="JSX/js/JSX30.js"
              jsxapppath="gidemo"
               jsxlt="true"> </script>
</div>

这一块代码主要是使用了GI的matrix组件,该组件可以动态加载数据。另外,页面引入了index.js,里面有两个个主要方法:


function giLoaded() {
  OpenAjax.subscribe("gidemo", "corporation", objectPublished);
  dwr.engine.setActiveReverseAjax(true);
}

function objectPublished(prefix, name, handlerData, corporation) {
  var matrix = giApp.getJSXByName("matrix");
  var inserted = matrix.getRecordNode(corporation.jsxid);
  matrix.insertRecord(corporation, null, inserted == null);

  // There are many ways to get a table to repaint.
  // One easy way is to ask it to repaint:
  // matrix.repaintData();

  // But we are going for a fancy option that does highlighting
  for (var property in corporation) {
    if (property != "jsxid") {
      var x = matrix.getContentElement(corporation.jsxid, property);
      if (ox) {
        var current = ox.innerHTML;
        if (current != "" + corporation[property]) {
          ox.style.backgroundColor = "#FDE4EB";
          var callback = function(ele) {
            return function() { ele.style.backgroundColor = "#FFFFFF"; };
          }(ox);
          setTimeout(callback, 1000);
          ox.innerHTML = corporation[property];
        }
      }
    }
  }
}

giLoaded方法通过OpenAjax.subscribe方法订阅主题为"gidemo"的数据(这些数据由服务器端的Java方法进行发布)。其中objectPublished是一个回调方法,表示取得数据后页面的改变。该回调方法非常简单,只是将matrix组件中发生变化的数据改变一下颜色,实现了实时提醒数据更新的功能。

另外,服务器端还有一个监听器PublisherServletContextListener,这是为了在适当的时候关闭发布者的线程。这个监听器要结合其他两个DWR的监听器使用,只需在web.xml里面声明就行了:

<listener>
     <listener-class>org.directwebremoting.servlet.EfficientShutdownServletContextAttributeListener</listener-class>
</listener>
<listener>
        <listener-class>org.directwebremoting.servlet.EfficientShutdownServletContextListener</listener-class>
    </listener>
    <listener>
        <listener-class>gidemo.PublisherServletContextListener</listener-class>
    </listener>

最后,看一下dwr的映射关系dwr.xml:

<dwr>
  <allow>
    <create creator="new" javascript="JDate">
      <param name="class" value="java.util.Date"/>
    </create>
    <create creator="new" javascript="Publisher" scope="application">
      <param name="class" value="gidemo.Publisher"/>
    </create>
   <convert converter="bean" match="gidemo.Corporation"/>

    <!-- this is a bad idea for live, but can be useful in testing -->
    <convert converter="exception" match="java.lang.Exception"/>
    <convert converter="bean" match="java.lang.StackTraceElement"/>
  </allow>
</dwr>

注意红色部分的配置,dwr允许将自定义的Java类型与js对象进行相互转换,但要声明转换器。

以下是程序运行的结果:

 

 

这个例子比较复杂,旨在让大家对反向AJAX的原理有所了解。下一篇文章开始,将用一些简单的例子来说明DWR反向AJAX的用法。

参考资料:

http://getahead.org/dwr/reverse-ajax

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值