小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Think in Pushlet

 looline 2007-01-29
server端向?yàn)g覽器client發(fā)送通知這種通訊模式在J2EE應(yīng)用中很常見(jiàn),通常使用采用RMI、CORBA或者自定義TCP/IP信息的applet來(lái)實(shí)現(xiàn)。這些技術(shù)往往由于復(fù)雜而產(chǎn)生諸多不利之處:技術(shù)難以實(shí)現(xiàn)、存在防火墻限制(因?yàn)樾枰蜷_(kāi)非HTTP的通訊端口)、需要額外的server開(kāi)發(fā)和維護(hù)。并且除了刷新整個(gè)頁(yè)面或者完全采用applet展示內(nèi)容之外,很難找到別的方法...

 Google

Think in Pushlet

作者:cleverpig

image


介紹

        server端向?yàn)g覽器client發(fā)送通知這種通訊模式在J2EE應(yīng)用中很常見(jiàn),通常使用采用RMI、CORBA或者自定義TCP/IP信息的applet來(lái)實(shí)現(xiàn)。這些技術(shù)往往由于復(fù)雜而產(chǎn)生諸多不利之處:技術(shù)難以實(shí)現(xiàn)、存在防火墻限制(因?yàn)樾枰蜷_(kāi)非HTTP的通訊端口)、需要額外的server開(kāi)發(fā)和維護(hù)。并且除了刷新整個(gè)頁(yè)面或者完全采用applet展示內(nèi)容之外,很難找到別的方法將client端applet的狀態(tài)和瀏覽器的頁(yè)面內(nèi)容集成在一起。

        Pushlet是一種comet實(shí)現(xiàn):在Servlet機(jī)制下,數(shù)據(jù)從server端的Java對(duì)象直接推送(push)到(動(dòng)態(tài))HTML頁(yè)面,而無(wú)需任何Java applet或者插件的幫助。它使server端可以周期性地更新client的web頁(yè)面,這與傳統(tǒng)的request/response方式相悖。瀏覽器client為兼容JavaScript1.4版本以上的瀏覽器(如Internet Explorer、FireFox),并使用JavaScript/Dynamic HTML特性。而低層實(shí)現(xiàn)使用一個(gè)servlet通過(guò)Http連接到JavaScript所在的瀏覽器,并將數(shù)據(jù)推送到后者。有關(guān)JavaScript版本的知識(shí)請(qǐng)參看Mozilla開(kāi)發(fā)中心提供的《JavaScript核心參考》Stephen Chapman編寫的《What Version of Javascript》

        這種機(jī)制是輕量級(jí)的,它使用server端的servlet連接管理、線程工具、javax.servlet API,并通過(guò)標(biāo)準(zhǔn)Java特性中Object的wait()和notify()實(shí)現(xiàn)的生產(chǎn)者/消費(fèi)者機(jī)制。原則上,Pushlet框架能夠運(yùn)行在任何支持servlet的server上、防火墻的后面。當(dāng)在client中使用JavaScript/DHTML時(shí),Pushlet提供了通過(guò)腳本快速建立應(yīng)用、使用HTML/CSS特性集成和布局新內(nèi)容的便利方法。

動(dòng)機(jī)

        目前越來(lái)越多的servlet和JSP用來(lái)部署web,于是便出現(xiàn)了在頁(yè)面已經(jīng)裝載完畢后由于server端某些對(duì)象的狀態(tài)變化而產(chǎn)生對(duì)client瀏覽器進(jìn)行通知和同步的需要。

        這些狀態(tài)變化的原因很復(fù)雜:可能由于用戶通過(guò)訪問(wèn)servlet或者修改數(shù)據(jù)庫(kù)記錄、更新EJB造成,或是在多用戶應(yīng)用(比如聊天室和共享白板)中的事件導(dǎo)致數(shù)據(jù)狀態(tài)變化。這些類型的應(yīng)用常常使用一種分布式的MVC模板:模式層位于server上(可能緩存在client中),控制層和視圖層位于client中(這兩個(gè)層可能合為一體)。

        當(dāng)然,這里也存在需要訂閱server端動(dòng)態(tài)內(nèi)容的應(yīng)用:那些動(dòng)態(tài)內(nèi)容不停地從server端推送過(guò)來(lái)。例如股票實(shí)時(shí)情報(bào)、系統(tǒng)狀態(tài)報(bào)告、天氣情況或者其它的監(jiān)測(cè)應(yīng)用。它遵循觀察者(Observer)模板(也稱為發(fā)布/訂閱模板),這種模板中的遠(yuǎn)程client注冊(cè)成為關(guān)注于server端對(duì)象變化的觀察者。關(guān)于設(shè)計(jì)模板的知識(shí)請(qǐng)看Matrix Wiki上的介紹。

        那么在HTML頁(yè)面已經(jīng)被裝載后如何通知瀏覽器客戶端?或者如果有選擇地更新頁(yè)面中一些部分的話,那該怎么做?比如只更新在HTML Table中的那些價(jià)格發(fā)生變化的股票列?

多種通知解決方案

        讓我們對(duì)應(yīng)用進(jìn)行這樣的假設(shè):擁有一個(gè)Java web server或者Java應(yīng)用server,我們?cè)噲D從server發(fā)送通知給client端瀏覽器。這里的解決方案可以分為:“輪詢(polling)”、“服務(wù)端回調(diào)(server-side callbacks)”和“消息(messaging)”三類。

輪詢

        最簡(jiǎn)單的解決方案便是“定時(shí)刷新頁(yè)面”。在HTML文檔的頭部使用HTML META標(biāo)簽,頁(yè)面便可以每隔N秒自動(dòng)reload一次。如果在此期間server端數(shù)據(jù)發(fā)生變化,那么我們可以獲得新的內(nèi)容,否則將得到相同的頁(yè)面。雖然方法很簡(jiǎn)單,但是如何設(shè)置刷新間隔是讓人頭疼的大問(wèn)題。

服務(wù)端回調(diào)

        因?yàn)槲覀兪恰吧斫?jīng)百戰(zhàn)”的Java開(kāi)發(fā)老手,所以經(jīng)常會(huì)用到“服務(wù)端回調(diào)”。這種方式通過(guò)RMI或者CORBA將Server端的對(duì)象傳輸?shù)絁ava applet客戶端。

消息(MOM)

        這種解決方案采用一個(gè)作為client的applet,它使用TCP/IP或者無(wú)連接的UDP、甚至多播協(xié)議來(lái)建立與消息中間鍵server的通訊,然后由server推送消息給client。你可以從例如SoftWired的iBusIBM的MQSeries、BEA的WebLogic Event這些消息產(chǎn)品中直接挑選,或者自己使用基于socket的java.io.ObjectStream定制開(kāi)發(fā)消息軟件。

討論(MOM)

        上面三種解決方案在復(fù)雜性、安全性、性能、可測(cè)量性、瀏覽器兼容性、防火墻限制上各有優(yōu)勢(shì)、劣勢(shì)。最佳解決方案依賴于你的應(yīng)用需求。例如,在共享白板應(yīng)用中,用戶需要直接與“狀態(tài)”交互,那么server端回調(diào)或者消息很可能會(huì)大顯身手。

        但在瀏覽器環(huán)境下,除非完全使用applet作為整個(gè)client應(yīng)用,否則把來(lái)自于server的更新信息集成到頁(yè)面中并非易事。如何在applet收到回調(diào)或者消息時(shí)變更頁(yè)面相關(guān)內(nèi)容?一個(gè)很“痛快”而又“痛苦”的解決方案就是在回調(diào)方法中使用AppletContext.showDocument(URL)方法來(lái)刷新整個(gè)頁(yè)面。

        由于HTML代碼可以直接影響頁(yè)面布局,直接使用來(lái)自server的數(shù)據(jù)更改HTML部分內(nèi)容不是更好嗎?這是web應(yīng)用的理想方案,在server上內(nèi)容動(dòng)態(tài)改變時(shí),從用戶到server所需的交互是最小化的。作為對(duì)上面的解決方案的補(bǔ)充,我開(kāi)發(fā)了Pushlet這種輕量級(jí)、瘦客戶端的技術(shù),它無(wú)需applet或者插件而直接與腳本/HTML集成在一起、使用標(biāo)準(zhǔn)HTTP連接、理論上可以部署到任何支持Java servlet的server上。但這并不意味著它將替換對(duì)前面解決方案,而是在你的開(kāi)發(fā)“工具箱”中添加另一種選擇。作為Java構(gòu)架者/開(kāi)發(fā)者,你可以自行權(quán)衡、選擇、決定哪種適合應(yīng)用的解決方案。

Pushlet原理

        Pushlet的基本使用形式是極為簡(jiǎn)單的。后面的一些示例會(huì)說(shuō)明這一點(diǎn)。

HTTP流

image
極富生活韻味的“Urban Stream”把我們Connecting Together

        Pushlet基于HTTP流,這種技術(shù)常常用在多媒體視頻、通訊應(yīng)用中,比如QuickTime。與裝載HTTP頁(yè)面之后馬上關(guān)閉HTTP連接的做法相反,Pushlet采用HTTP流方式將新數(shù)據(jù)源源不斷地推送到client,再此期間HTTP連接一直保持打開(kāi)。有關(guān)如何在Java中實(shí)現(xiàn)這種Keep-alive的長(zhǎng)連接請(qǐng)參看Sun提供的《HTTP Persistent Connection》W3C的《HTTP1.1規(guī)范》。

示例1

        我們利用HTTP流開(kāi)發(fā)一個(gè)JSP頁(yè)面(因?yàn)樗子诓渴穑宜趙eb server中也是作為servlet對(duì)待的),此頁(yè)面在一個(gè)定時(shí)器循環(huán)中不斷地發(fā)送新的HTML內(nèi)容給client:

<%
  int i = 1;
    
  try {
    while (true) {
       out.print("<h1>"+(i++)+"</h1>");
       out.flush();
      
       try {
            Thread.sleep(3000);
       } catch (InterruptedException e) {
       out.print("<h1>"+e+"</h1>");
        }
     }
   } catch (Exception e) {
       out.print("<h1>"+e+"</h1>");
   }
%>


        在Pushlet源代碼中提供了此頁(yè)面(examples/basics/push-html-stream.jsp)。上面的頁(yè)面并不是十分有用,因?yàn)樵谖覀兯⑿马?yè)面時(shí),新內(nèi)容機(jī)械地、持續(xù)不斷地被添加到頁(yè)面中,而不是server端更新的內(nèi)容。

示例2

        現(xiàn)在讓我們步入Pushlet工作機(jī)理中一探究竟。通過(guò)運(yùn)行Pushlet的示例源代碼(examples/basics/ push-js-stream.html),我們會(huì)看到這個(gè)每3秒刷新一次的頁(yè)面。那么它是如何實(shí)現(xiàn)的呢?

        此示例中包含了三個(gè)文件:push-js-stream.html、push-js-stream-pusher.jsp、push-js-stream-display.html。

        其中push-js-stream.html是主框架文件,它以HTML Frame的形式包含其它兩個(gè)頁(yè)面。

        push-js-stream-pusher.jsp是一個(gè)JSP,它執(zhí)行在server端,此文件內(nèi)容如下:

  7: <%
  8:   /** Start a line of JavaScript with a function call to parent frame. */
  9:   String jsFunPre = "<script language=JavaScript >parent.push(‘";
10:  
11:   /** End the line of JavaScript */
12:   String jsFunPost = "‘)</script> ";
13:  
14:   int i = 1;
15:   try {
16:  
17:     // Every three seconds a line of JavaScript is pushed to the client
18:     while (true) {
19:    
20:        // Push a line of JavaScript to the client
21:        out.print(jsFunPre+"Page "+(i++)+jsFunPost);
22:        out.flush();
23:        
24:        // Sleep three secs
25:        try {
26:             Thread.sleep(3000);
27:        } catch (InterruptedException e) {
28:             // Let client display exception
29:             out.print(jsFunPre+"InterruptedException: "+e+jsFunPost);
30:        }
31:      }
32:    } catch (Exception e) {
33:             // Let client display exception
34:             out.print(jsFunPre+"Exception: "+e+jsFunPost);
35:    }
36: %>

        注意在示例1和示例2中使用JSP時(shí)都存在一個(gè)問(wèn)題:一些servlet引擎在某個(gè)client離開(kāi)時(shí)會(huì)“吃掉”IOException,以至于JSP頁(yè)面將永不拋出此異常。所以在這種情況下,頁(yè)面循環(huán)將會(huì)永遠(yuǎn)執(zhí)行下去。而這正是Pushlet實(shí)現(xiàn)采用servlet的原因之一:可以捕獲到IOException。

        在上面代碼的第21行中可以看到在一個(gè)定時(shí)器循環(huán)(3秒/周期)中打印了一些HTML并將它們輸出到client瀏覽器。請(qǐng)注意,這里推送的并非HTML而是Javascript!這樣做的意義何在?

        它把類似“<script language=JavaScript >parent.push(‘Page 4‘)</script>”的一行代碼推送到瀏覽器;而具有JavaScript引擎的瀏覽器可以直接執(zhí)行收到的每一行代碼,并調(diào)用parent.push()函數(shù)。而代碼中的Parent便是瀏覽器頁(yè)面中所在Frame的Parent,也就是push-js-stream.html。讓我們看看都發(fā)生了什么?

<script LANGUAGE="JavaScript">
var pageStart="<HTML><HEAD></HEAD><BODY BGCOLOR=blue TEXT=white><H2>Server pushes: <para>";
var pageEnd="</H2></BODY></HTML>";
  // Callback function with message from server.
  // This function is called from within the hidden JSP pushlet frame
  function push(content) {

    // Refresh the display frame with the content received
    window.frames[‘displayFrame‘].document.writeln(pageStart+content+pageEnd);
    window.frames[‘displayFrame‘].document.close();
  }

</script>



     <!-- frame to display the content pushed by the pushlet -->
    
    
     <!-- Hidden frame with the pushlet that pushes lines of JavaScript-->
    
</FRAMESET>

        可以看到push-js-stream.html中的push()函數(shù)被名為pushletFrame的JSP Frame調(diào)用:把傳入的參數(shù)值寫入到displayFrame(此Frame為push-js-stream-display.html)。這是動(dòng)態(tài)HTML的一個(gè)小技巧:使用document對(duì)象的writeln方法刷新某個(gè)Frame或者Window的內(nèi)容。

        于是displayFrame成為了用于顯示內(nèi)容的、真正的視圖。displayFrame初始化為黑色背景并顯示“wait…”直到來(lái)自server的內(nèi)容被推送過(guò)來(lái):

<H1>WAIT...</H1>


        這便是Pushlet的基本做法:我們從servlet(或者從示例中的JSP)把JavaScript代碼作為HTTP流推送到瀏覽器。這些代碼被瀏覽器的JavaScript引擎解釋并完成一些有趣的工作。于是便輕松地完成了從server端的Java到瀏覽器中的JavaScript的回調(diào)。

        上面的示例展示了Pushlet原理,但這里存在一些等待解決的問(wèn)題和需要增添的特性。于是我建立了一個(gè)小型的server端Pushlet框架(其類結(jié)構(gòu)圖表將會(huì)展示在下面),添加了一些用在client中的JavaScript庫(kù)。由于client需要依賴更多的DHTML特性(比如Layers),我們將首先粗略地溫習(xí)一些DHTML知識(shí)。示例代碼見(jiàn)examples/dhtml。

DHTML(動(dòng)態(tài)HTML)

        DHTML(動(dòng)態(tài)HTML)提供了在瀏覽器中維護(hù)內(nèi)容、進(jìn)行用戶交互的擴(kuò)展能力。就像Java開(kāi)發(fā)者使用servlet和JSP那樣,DHTML也應(yīng)該是你的工具箱中的一部分。

        DHTML涉及到HTML、級(jí)聯(lián)樣式表(CSS)、JavaScript和DOM。傳統(tǒng)的頁(yè)面只能通過(guò)重新裝載來(lái)自server新頁(yè)面的方式進(jìn)行更新。DHTML提供了在頁(yè)面被裝載完畢后對(duì)瀏覽器內(nèi)的HTML文檔的完全控制。你應(yīng)該見(jiàn)過(guò)一些帶有“圖像翻滾”、彈出內(nèi)容、可收縮菜單功能的web頁(yè)面,它們便是使用DHTML技術(shù)實(shí)現(xiàn)的。盡管存在一些標(biāo)準(zhǔn)上的差異(見(jiàn)下面的“跨瀏覽器DHTML”),多數(shù)兼容JavaScript1.4版本的瀏覽器(后面將簡(jiǎn)稱為“版本4的瀏覽器”)都支持DHTML。

        從開(kāi)發(fā)者的角度審視瀏覽器中的整個(gè)文檔,比如Frame、圖片、表格等,它們都可以表示為具有層次的對(duì)象模式——DOM。通過(guò)使用JavaScript可以維護(hù)DOM的成員,不但可以改變文檔的內(nèi)容和外觀,而且還可以捕捉例如鼠標(biāo)移動(dòng)、form提交這些用戶事件,而后對(duì)DOM進(jìn)行相應(yīng)修改。例如鼠標(biāo)移動(dòng)到圖片的上方可以產(chǎn)生“mouse-over”事件,這時(shí)通過(guò)顯示高亮版本的圖片或者彈出解釋性文字的方式修改頁(yè)面外觀。這聽(tīng)起來(lái)不錯(cuò)吧!我們現(xiàn)在就熟悉一下DHTML標(biāo)準(zhǔn)!但是誰(shuí)定義了DHTML標(biāo)準(zhǔn)?

        這是一些DHTML初學(xué)者首先遇到的問(wèn)題。首先,你需要一個(gè)版本4以上的瀏覽器。DHTML相關(guān)規(guī)范的官方標(biāo)準(zhǔn)出自World Wide Web Consortium (W3)。但是微軟和Netscape出品的版本4以上的瀏覽器都有一些私有的DHTML擴(kuò)展,這是你必須注意的。
幸運(yùn)的是大多數(shù)用戶都有版本4以上的瀏覽器,而且一些開(kāi)發(fā)者(Dannymen、Dan SteinmanDanny Goodman)建造了跨越瀏覽器的、可重用的DHTML庫(kù)。

        作為一名Java開(kāi)發(fā)者,你要接受這個(gè)事實(shí):你應(yīng)該適當(dāng)?shù)孛靼谆趯?duì)象、甚至面向?qū)ο蟮腏avaScript編程。在我的DHTML中你將找到一些示例,但了解更多的DHTML資源也是很值得的。尤其在使用跨越瀏覽器的DHTML庫(kù)對(duì)付那些頑固的瀏覽器問(wèn)題時(shí),一切都變得有趣、而不是枯燥。

        就如Java獲得在廣闊的server端市場(chǎng)、DHTML在client領(lǐng)域具有許多強(qiáng)大特性那樣,Pushlet以一種直接的方式將這兩項(xiàng)偉大的技術(shù)捆綁在一起。下一個(gè)章節(jié)將詳細(xì)討論P(yáng)ushlet這個(gè)server端輕量級(jí)框架和client端DHTML庫(kù)。

框架的設(shè)計(jì)

        注意:本章節(jié)僅反映了Pushlet server端框架的1.0版本(隨著版本升級(jí)可能還會(huì)重新構(gòu)造)。

        Pushlet框架允許client訂閱在server端的主題(subject),而server則接收訂閱,然后在server端的訂閱主題所對(duì)應(yīng)的數(shù)據(jù)變化時(shí)推送數(shù)據(jù)到client。此框架的基本設(shè)計(jì)模板是發(fā)布/訂閱(Publish/Subscrib),也被稱為觀察者(Observer)。它具有server和client兩部分組建而成:

        Server端:
        由圍繞著Pushlet類的Java類集合構(gòu)成(見(jiàn)下面的UML類設(shè)計(jì)圖表)。

        Client端:
        腳本與頁(yè)面:可重用的JavaScript庫(kù)(pushlet.js)和用來(lái)在DHTML client(這里指瀏覽器)中接收事件的HTML(pushlet.html)組成。

        Client端Java類:
        JavaPushletClient.java和JavaPushletClientListener.java,負(fù)責(zé)在Java client中接收事件。

        跨越瀏覽器的DHTML工具庫(kù):
        layer.js, layer-grid.js, layer-region.js,用來(lái)在DHTML層中顯示數(shù)據(jù)內(nèi)容。

        最后,還有用于測(cè)試事件的生成工具類EventGenerators.java以及一些示例應(yīng)用。

server端類設(shè)計(jì)

        下面是server端Java類的UML圖表:

image
Pushlet框架Java類UML圖


        關(guān)鍵的類:Pushlet、Publisher類、Subscriber接口和Event類。通過(guò)HTTP請(qǐng)求調(diào)用Pushlet這個(gè)servlet,client訂閱事件并接收事件。

        Client發(fā)送訂閱請(qǐng)求時(shí)需要表明的內(nèi)容如下:
        1.訂閱事件的主題
        2.接收事件所采用的格式:默認(rèn)為JavaScript調(diào)用,還有XML或者Java序列化對(duì)象者三種。目前Pushlet 2.0.2版已經(jīng)支持AJAX。
        3.使用哪種接收協(xié)議(將來(lái)實(shí)現(xiàn)):TCP/IP、UDP、Multicast。

        示例:用于接收AEX股票價(jià)格的請(qǐng)求,默認(rèn)使用JavaScript調(diào)用作為格式。

http://www.:8080/servlet/pushlet?subject="/stocks/aex"


        主題(subject)表示為具有層次的“主題樹(shù)”(topic-tree)形式。例如:“/stocks”表示與股票價(jià)格相關(guān)的所有事件,而“/stocks/aex”表示Amsterdam Exchange公司的股票價(jià)格?!?”表示所有事件。這并不時(shí)硬性規(guī)定,而是由開(kāi)發(fā)者根據(jù)應(yīng)用自行定義。

        當(dāng)前只有接收方協(xié)議是發(fā)送到client的HTTP回應(yīng)流(response stream)。在將來(lái)的擴(kuò)展版本中,接收方協(xié)議能夠提供多種選擇,比如TCP、UDP、RMI、HTTP POST甚至只SMTP。

        Event(事件)類:僅僅是name/value的字符串對(duì)(使用java.util.Properties實(shí)現(xiàn))的集合。

        產(chǎn)生Event的方式:Publisher類為生成的Event提供了發(fā)布接口,它內(nèi)部保存了訂閱者(那些實(shí)現(xiàn)Subscriber接口的類)列表,并把每個(gè)Event發(fā)送給那些主題與Event匹配的訂閱者。Event在server端也可以通過(guò)能夠偵聽(tīng)外部Event的EventGenerators類來(lái)生成。另外client可以通過(guò)基于HTTP通訊的Postlet類來(lái)發(fā)布Event。

        在上面的圖表中,為了適配不同請(qǐng)求源(瀏覽器、Java client程序),PushletSubscriber以及它所包含的那些類提供了多種訂閱者的實(shí)現(xiàn)。

場(chǎng)景1: 事件訂閱

image
瀏覽器client訂閱程序圖


        上面的UML程序圖中,瀏覽器client通過(guò)Publisher訂閱Event。

        Pushlet作為servlet,通過(guò)doGet/doPost方法被調(diào)用。由于多個(gè)client可以同時(shí)調(diào)用同一個(gè)Pushlet,所以Pushlet本身不能作為訂閱者。取而代之的是,它派發(fā)所有的訂閱:在每一次調(diào)用doGet()/doPost()時(shí),新建PushletSubscriber對(duì)象、并使之運(yùn)行直至事件循環(huán)(eventLoop)結(jié)束。PushletSubscriber作為一個(gè)實(shí)現(xiàn)Subscriber接口的對(duì)象,通過(guò)join()方法向Publisher類進(jìn)行注冊(cè)的方式將自身添加到Publisher的內(nèi)部列表。

        面對(duì)不同的client類型和協(xié)議,PushletSubscriber建立一個(gè)相對(duì)的ClientAdapter對(duì)象,在這個(gè)場(chǎng)景中是BrowserPushletAdapter對(duì)象。而對(duì)于支持Multipart MIME的瀏覽器,將建立MultipartBrowserClientAdapter對(duì)象。

        最后的deQueue()調(diào)用是一個(gè)“等待Event的循環(huán)”,deQueue的意思為入隊(duì)。注意此方法將掛起當(dāng)前線程直到PushletSubscriber的GuardedQueue隊(duì)列中存入有效的Event。

場(chǎng)景2: 發(fā)送和派發(fā)事件

image
事件發(fā)布程序圖


        上圖顯示了發(fā)送一個(gè)事件所要經(jīng)歷的程序。它展現(xiàn)了Event如何被生成、被派發(fā)給瀏覽器client。在這個(gè)場(chǎng)景中,EventGenerator建立了一個(gè)Event對(duì)象,并調(diào)用Publisher.publish()將其派發(fā)到client。Publisher遍歷它內(nèi)部的訂閱者列表,詢問(wèn)這個(gè)Event是否匹配訂閱標(biāo)準(zhǔn)(目前只是主題匹配)。如果發(fā)現(xiàn)與之匹配的訂閱者,則調(diào)用該訂閱者的send()方法。

        每個(gè)PushletSubscriber對(duì)象都有一個(gè)GuardedQueue對(duì)象,在其中以隊(duì)列的形式保存著調(diào)用send()方法時(shí)傳入的Event。那么它為什么不直接將Event推送給BrowserPushletAdapter呢?最重要的原因是我們期望掛起B(yǎng)rowserPushletAdapter線程,直到GuardedQueue中存在有效的Event,這樣就避免了“忙于等待”或者“輪詢”方式所帶來(lái)的負(fù)面影響。第二原因是Publisher可以通知多個(gè)client,如果在執(zhí)行同步的send()調(diào)用時(shí),某個(gè)慢速的client可能會(huì)堵塞所有其它正在等待通知的client。這正是我在RMI或者CORBA提供的一組client進(jìn)行同步回調(diào)的示例中所看到的設(shè)計(jì)缺陷。

        GuardedQueue是個(gè)工具類,它使用了讀/寫模板(readers-writers pattern),此模板采取java.lang.Object.wait()/notifyAll()方法實(shí)現(xiàn)可被監(jiān)控的掛起。通過(guò)使用讀/寫模板,使GuardedQueue類具有進(jìn)行對(duì)象入隊(duì)/出隊(duì)(enqueue/dequeue)操作的能力。當(dāng)隊(duì)列為空時(shí),GuardedQueue調(diào)用deQueue()方法時(shí),此時(shí)調(diào)用線程將被掛起,直到有對(duì)象入隊(duì)為止。相反,當(dāng)隊(duì)列已滿時(shí)調(diào)用enQueue(),線程也將掛起。在BrowserPushletSubscriber獲得出隊(duì)的Event對(duì)象后,它將調(diào)用BrowserPushletAdapter的push()方法,后者將格式化Event為JavaScript代碼或者XML以及其它格式),并將它發(fā)送到瀏覽器。比如Philips股票價(jià)格為123.45的JavaScript代碼格式如下:

<SCRIPT language=JavaScript >parent.push(‘subject‘, ‘/stocks/aex‘, ‘philips‘, ‘123.45‘) </SCRIPT>


Client端框架

        由于這是對(duì)于所有瀏覽器client的通用任務(wù),所以Pushlet Client端框架提供了兩個(gè)可重用的文件:
pushlet.html和pushlet.js。
        Pushlet.html本身是被附著在一個(gè)隱藏的HTML Frame中。這個(gè)Frame的parent調(diào)用并實(shí)現(xiàn)push()方法。

        pushlet.html :被包含在client端的HTML文檔中的Frame中。它可以傳入主題標(biāo)識(shí)和背景顏色兩個(gè)參數(shù)。而它所做的最重要的工作是下面的push方法:

function push() {
        // 根據(jù)傳入的參數(shù)建立PushletEvent object
        // push.arguments是來(lái)自server的Event數(shù)據(jù)
        pushletEvent = new PushletEvent(push.arguments)

        // 更新?tīng)顟B(tài)Frame:顯示閃光表示接收數(shù)據(jù)
        updateStatusFrame();
        
        // parent frame是否準(zhǔn)備好接收Event?
        if (!parent.onPush) {
                return;
        }
        
        // 把Event轉(zhuǎn)發(fā)給parent frame指定的處理方法
        parent.onPush(pushletEvent);
}

        Push()函數(shù)首先根據(jù)傳入的參數(shù)建立了一個(gè)JavaScript對(duì)象——pushletEvent。接著使用updateStatusFrame()顯示閃光,表示我們正在接收Event數(shù)據(jù),如果parent frame存在onPush()函數(shù),則將前面建立的PushletEvent對(duì)象作為參數(shù)調(diào)用parent frame指定的處理方法。

        在pushlet.js 中的PushletEvent類代碼如下:

/* Object to represent nl.justobjects.pushlet.Event in JavaScript.
   Arguments are an array where args[i] is name and args[i+1] is value
*/
function PushletEvent(args) {
   // Map存放Name/Value pairs
   this.map = new Map();
  
   // 設(shè)置成員方法
   this.getSubject = PushletEventGetSubject
   this.put = PushletEventPut
   this.get = PushletEventGet
   this.toString = PushletEventToString
   this.toTable = PushletEventToTable

   // 將傳入的參數(shù)值放入到map中
   for (var i=0; i < args.length; i++) {
     this.put(args[i], args[++i] );
   }
}

// 獲取事件主題
function PushletEventGetSubject() {
  return this.map.get(‘subject‘)
}

// 獲取事件屬性
function PushletEventGet(name) {
  return this.map.get(name)
}

// 存放事件屬性
function PushletEventPut(name, value) {
  return this.map.put(name, value)
}

function PushletEventToString() {
  return this.map.toString();
}

// 將map內(nèi)容轉(zhuǎn)化為HTML Table
function PushletEventToTable() {
  return this.map.toTable();
}

        PushletEvent使用了一個(gè)我增加的Map JavaScript對(duì)象,它類似于java.util.Hashtable。

Pushlet協(xié)議

        詳見(jiàn)http://www./doc/protocol.html

應(yīng)用

        Pushlet可以開(kāi)發(fā)多種類型的web應(yīng)用。由于此框架允許client主動(dòng)更新事件(通過(guò)Postlet),所以應(yīng)用就并不只是被動(dòng)地推送數(shù)據(jù)了。每個(gè)Pushlet應(yīng)用都可以根據(jù)下面進(jìn)行分類:

        事件由server發(fā)起、還是client發(fā)起或者兩者都有可能;狀態(tài)是否保持在server、還是在client或者兩者都有可能。

        由于事件不但被做成了對(duì)JavaScript有效,而且也是其它腳本化的插件能夠接收實(shí)時(shí)的事件更新。例如你可以腳本化Macromedia Flash或者VRML應(yīng)用。

        為了說(shuō)明Pushlet應(yīng)用的范圍,下面提供了一些簡(jiǎn)單的demo。

監(jiān)控

        例如股票、天氣、投票、機(jī)場(chǎng)到達(dá)系統(tǒng),這些應(yīng)用都可以采用Pushlet對(duì)實(shí)時(shí)數(shù)據(jù)進(jìn)行監(jiān)控。

        這是一個(gè)實(shí)時(shí)FX股票/新聞應(yīng)用:www. (IE only). 另一個(gè)部署Pushlet的實(shí)時(shí)股票/新聞應(yīng)用:www.marketnews.com.

游戲
        從象棋到描述危機(jī)和壟斷者的游戲。

分布式MVC

        這涉及到了在用戶接口框架(例如Java Swing和微軟MFC)中常見(jiàn)的設(shè)計(jì)模板。在分布式MVC的各種變體中,模式層位于server,而client控制著是視圖層和控制層。Client通過(guò)控制進(jìn)而修改模式,然后模式將通知所有依附的視圖,而視圖將進(jìn)行自我刷新。

        一些應(yīng)用具有web前端(front end),其數(shù)據(jù)存放在server上可被多個(gè)用戶更新。比如預(yù)訂系統(tǒng)和登記系統(tǒng)。如果一個(gè)client完成一次更新,而其它c(diǎn)lient卻不能馬上見(jiàn)到變化直至刷新頁(yè)面。在某些情況下,這是很簡(jiǎn)單、可行的解決方案,但同時(shí)也存在著用戶需要同步變化的情況。這種情況下,應(yīng)用可以使用Pushlet簡(jiǎn)單地將URL作為單一事件推送到client,client接收到這個(gè)URL后將刷新頁(yè)面。

        另外一點(diǎn)值得注意的示例是爭(zhēng)議頗多的EJB。盡管Java client能夠直接和EJB對(duì)話(通過(guò)RMI或者CORBA),但多數(shù)情況下則是由servlet和作為client前端的JSP來(lái)完成。在這種情況下,“通知”工作變得很艱難。使用Pushlet,EJB可以在其狀態(tài)發(fā)生改變時(shí)通知依附于它的web client。

Web表示層

        在放棄使用PowerPonit作Java課程講解工具后,我開(kāi)發(fā)了一個(gè)基于XML的內(nèi)容管理框架。由于在某些情形下,教室沒(méi)有“卷軸工”,但是所有的學(xué)生人手一臺(tái)網(wǎng)絡(luò)計(jì)算機(jī),所以我開(kāi)發(fā)了這個(gè)簡(jiǎn)單的應(yīng)用,它使我能夠同步改變學(xué)生和我的頁(yè)面內(nèi)容。

用戶輔助

        這種類型的應(yīng)用可用于call center、銀行、幫助桌面、電子商務(wù)web應(yīng)用。當(dāng)你由于問(wèn)題而撥打call center電話時(shí),代理程序可以使你通過(guò)上網(wǎng)的方式瀏覽解決方案、供貨等信息。

        使用EJB作為后臺(tái)和JSP作為前臺(tái),client可以買/賣外幣。一個(gè)“AutoTrader”對(duì)象自動(dòng)提供處理,如果自動(dòng)處理失敗或者client請(qǐng)求人工處理時(shí),一個(gè)“處理干預(yù)”將發(fā)生,處理者將被通知并提供相應(yīng)的服務(wù)。

社區(qū)工具

        這是一種多用戶參加實(shí)時(shí)會(huì)話的應(yīng)用。我正在計(jì)劃擴(kuò)充Pushlet框架,使其支持多用戶session的特性。目前可以實(shí)現(xiàn)簡(jiǎn)單的web聊天,我稱之為WCQ,大家可以在Pushlet源代碼的example中見(jiàn)到它。

比較

        本章節(jié)對(duì)Pushlet與基于CORBA/RMI的Java applet解決方案進(jìn)行一下比較。
優(yōu)勢(shì)
        直接與瀏覽器中的DHTML集成。

        標(biāo)準(zhǔn)的HTTP端口和協(xié)議:消息和RMI/CORBA使用非標(biāo)準(zhǔn)端口(相對(duì)HTTP標(biāo)準(zhǔn)端口而言),遇到“防火墻”、“禁止回調(diào)”、“禁止接收UDP數(shù)據(jù)”的瀏覽器安全限制時(shí)可能無(wú)法工作。

        client負(fù)載:基于CORBA/RMI的Java applet使client在啟動(dòng)時(shí)更加沉重,并消耗更多的資源。

        無(wú)需額外的server:消息和RMI/CORBA需要單獨(dú)的server產(chǎn)品。Pushlet理論上可以在任何server引擎上運(yùn)行,并具備連接管理和多線程能力。

缺點(diǎn)
        跨越瀏覽器的DHTML:Pushlet需要使用能工作在任何平臺(tái)、所有瀏覽器版本的DHTML庫(kù)。

        可測(cè)量性:當(dāng)100個(gè)以上的client通過(guò)Pushlet連接到server時(shí),server上的線程和socket資源都將出現(xiàn)緊張。而解決這一問(wèn)題的方式就是使用單獨(dú)的Pushlet服務(wù)器。

        Web server問(wèn)題:一般的web server往往不是為長(zhǎng)連接而設(shè)計(jì)的。針對(duì)這一問(wèn)題的解決方案與上面的可測(cè)量性相同。

        代理緩存:一些代理服務(wù)器可能緩存HTTP數(shù)據(jù)。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多