pushlets: 反推:10年前作者的描述

These days, developers increasingly turn to servlets and JavaServer Pages (JSPs) as Web-based frontends that integrate backend databases, Enterprise JavaBeans, or legacy applications. It is, however, difficult to keep client Web browsers up-to-date when the state of backend application data changes after the client has loaded the HTML page. For example, how do you keep one user up-to-date when she views the contents of a database table in her browser while another user simultaneously updates that same table on the server?

最近开发者使用servlet和jsp通过整合后端数据库,Javabean作为前后端应用的技术。但是在客户端已经载入了HTML页面后,浏览器无法及时更新数据。例如,当一个用户正在浏览数据时,另外一个用户已经改变了这些数据,怎样才能更新数据?

Indeed, applications such as live stock feeds, flight-arrival information, or weather-condition updates, in which the server streams or pushes live data, are difficult to create with servlets or JSPs.

的确,许多诸如股市,航班到达信息,天气状态更新等需要及时发布动态数据的应用,无法通过创建servlet来做到。

How can we ensure these applications will be capable of notifying the browser after HTML page loading? Or, how can we selectively update parts of a page, such as updating only the stock whose price has changed? As seasoned Java developers, we often reflexively think of applets that use sockets or RMI/CORBA as the only server-to-client notification solution.

我们怎样才能让应用程序有这样的能力了?又或者说,怎样才能有选择性的更新数据,比如,股市系统中只更新那些已经变价了的页面部分。对于老道的java开发者,我们常会想到使用applets的RMI/CORBA来解决这种服务端到客户端的消息通知。

In this article we'll look at a solution that may better serve our purpose. First, I describe the current solutions for server-to-Web client notification. Next, I introduce pushlets as an alternative -- and possibly unconventional -- notification mechanism and then present a simple pushlet framework with some sample applications. Finally, I discuss some of the advantages and disadvantages of applying pushlets.

在这篇文章中,我们将会看到一个好的解决方案。首先,我会讲述当前server-to-client的解决方案。然后,我会介绍pushlets最为候选方案,并附上一些简单的例子。最后,我会总结使用pushlets的优缺点。

Note: This article's examples and complete source code can be viewed and downloaded fromhttp://www.fluidiom.com:8080

这篇文章中的例子和源代码可以在这个网址中下载。

Server-to-Web client notification: Current solutions

目前方案

Before we delve into the pushlet concept, let's review the existing server-to-Web client solutions. Assume we have a Java Web or application server from which we want to notify client browsers. Possible solutions can be categorized asHTML refresh, server-side callbacks, and messaging.

在我们讨论pushlets内容之前,我们先看下目前存在的servet-to-client的解决方案。假设我们有一个java web或者应用,我们希望发送消息给客户端浏览器。最有可能的解决方案分类成:Html刷新,服务端回调, 和 发送消息。

HTML refresh
HTML刷新

The simplest notification solution is the HTML refresh. By using <meta> tags in the header of the HTML document, the page automatically reloads every n seconds. If something has changed on the server since the last reload, we get the new content; otherwise, we get the original page. The following simple line, placed between<head> and </head> in an HTML page, does the trick:

最简单的解决方案是HTML刷新。通过使用<meta>标签,页面会自动每隔几秒刷新URL。如果服务器端的数据发生改变,我们会获取到新的内容。如下所示:

   <META HTTP-EQUIV="Refresh" CONTENT="4;URL=http://www.justobjects.nl">

While this solution is simple, we must still ask, how long should we make the refresh interval?

虽然方案简单,但是我们还是要问,每隔多久刷新一次才好?

Server-side callbacks
服务端调用

In server-side callbacks, a server object calls back a Java-applet client using RMI (Remote Method Invocation) or CORBA (Common Object Request Broker Architecture). Usually the client first passes a remote reference of an RMI or CORBA object to the server. The server keeps a list of those references and calls them sequentially at the time of notification. This solution has been discussed in detail in other JavaWorldarticles (see Resources).

在服务端调用方案中,一个服务对象通过远程方法调用或者通用对象请求代理结构来回调applet客户端。一般,客户端会首先传给一个远程RMI或者CORBA对象的引用给服务器。服务器保存这些对象的引用,继而在需要发出通知消息的时候调用它们。这些技术的具体实现已经在其他的文章中写出来了。

Messaging 
消息发送

In messaging, an applet is a client of a messaging server that pushes messages over a TCP/IP connection (java.net.Socket) or sends connectionless UDP messages (java.net.DatagramSocket), the latter possibly even with multicast (java.net.MulticastSocket). You can use messaging middleware (MOMs), such as SoftWired's iBus, IBM's MQSeries, and BEA Systems' WebLogic Events, or develop your own custom messaging with java.io.ObjectStream on top of TCP or UDP sockets. The Java Messaging Service (JMS) is an important standard for messaging.

Each of the above solutions has its advantages and disadvantages in complexity, security, performance, scalability, browser Java compatibility, and restrictions such as firewalls. The optimal solution strongly depends on the requirements of your application. For example, when users require a direct interaction with the state, such as in a shared whiteboard, server-side callbacks or messaging can be a powerful technique.

But we are still working within the confines of a browser. Unless the applet constitutes the entire client application, it is difficult to integrate the HTML content with the updates coming from the server. Considering this, how can we alter the content from within the applet when it gets the callback or message? A frequent solution is to refresh the page by calling  AppletContext.showDocument(URL)  within the callback method. This instructs the browser to change the page with the new URL.

关于消息发送,一个小的应用是消息发送服务器的客户端,通过TCP/IP连接发送消息或者发送非连接的UDP消息。你可以使用消息发送中间件,比如SoftWired的iBus,IBM的MQSeries,还有BEA系统的webLogic事件,或者基于TCP,UDP套接字开发自己的消息发送器。J MS就是一个标准版本的消息发送器。

上面所有的方案在复杂度、安全性、展现、扩展性、浏览器兼容性,以及防火墙限制上都有各自的优缺点。最优的方案取决于应用需求。比如,用户需要直接向一张白板上写数据,那么服务端调用或者消息发送就是好的选择。

但是我们仍然限制在浏览器的范围内。除非applet构成应用的所有部分,否则很难把服务器消息和Html整合到一起。考虑这点,我们怎样才能更新消息?最常用的方法是调用AppletContext.showDocument(URL)来刷新页面。

Enter pushlets

进入pushlets

Since HTML is a powerful layout language, wouldn't it be nice to be able directly to alter parts of the HTML content with incremental data coming from the server? This would be an ideal scheme for Web applications where content on the server changes dynamically and the required user-to-server interaction -- for example, that driven by HTML forms.

HTML最为最强的布置型语言,难道它自己不能修改内容吗?我们可以构想,当服务器数据改变时,用户就会与服务器交互。

The pushlet mechanism I developed is lightweight and thin on the client; it requires no applets or plug-ins, directly integrates with scripting and HTML, and uses standard HTTP connections; and it can be deployed (in theory!) in any Java-servlet server. It is certainly not meant to replace the traditional solutions outlined above. On the contrary, pushlets may be another option in your toolbox. As a Java architect or developer, you can determine the trade-offs and choose what is best for your particular application.

我开发的pushlet是轻量级的,不需要applet或者插件,而是集成于脚本和HTML中,使用标准的HTTP连接,并且理论上可以发布在任何java服务器上。当然,这不是说要完全取代上述其它方案。相反,pushlet也许只是你工具中一项而已。最为java架构师或者开发人员,你可以依据你的需求平衡最好的方案。

Pushlet basics

pushlet基础
So what are a pushlet and how does it work? In its basic form a pushlet is extremely simple. I will show the basics through a few examples. It's also time to see some code!

什么是pushlet,它是怎么运行的?下面会有一些例子和代码。

HTTP streaming
HTTP 流

Pushlets are based on HTTP streaming, a technique sometimes used in multimedia viewing applications such as QuickTime or RealAudio. Instead of closing the HTTP connection after fetching an HTML page, the connection remains open while fresh data is pushed to the client.

pushlet基于HTTP流实现,一项技术有时会运用到多媒体中,比如QUICKTIME或者RealAudio。在获取到一个html页面后,连接并不会关闭,这样服务器会发送新的数据过来。

Taking the idea of HTTP streaming, we could develop a JSP or servlet that continuously sends new HTML content back to the client in a timer loop, as seen in the example below:

为了展现HTTP流的理论,我们开发了一个JSP,通过定时器循环发送HTML到客户端,如下:

<HTML> <HEAD> <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> <META HTTP-EQUIV="Pragma" CONTENT="no-cache"> </HEAD> <BODY BGCOLOR="blue" TEXT="white"> <% 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>"); } %> </BODY> </HTML>

To see this example in action, from the Pushlets page, click on Examples, then Basics. Then click Run from the HTML Push section. The above example is not terribly useful, since the pushed content is continuously appended to the page while our intention was to alter the loaded content (that is, to show only the actual value of the counter).

在官网上你可以看到这个例子的运行效果。上面的例子不是很有用,它只是持续的打出数据,但我们的目的是修改载入的内容。

In our next example, we jump right into the pushlet mechanics. Again from the Pushlets Examples-Basics page, click Run from the JavaScript Push section. Notice that the page refreshes every 3 seconds. How is that done?

The JavaScript Push example consists of three files: push-js-stream.htmlpush-js-stream-pusher.jsp, and push-js-stream-display.html. The main page, push-js-stream.html, contains each of the two other files in HTML frames. Let's just follow the route of the events.

下面的例子中,我们直接进入pushlet机制。再一次去官网中运行javascript的例子。结果显示页面每3秒钟会刷新数据。这是怎么做到的?代码包含三个文件:push-js-stream.html, push-js-stream-pusher.jsp 和 push-js-stream-display.html。主页是push-js-stream.html,嵌入了另外两个页面。

When requested, push-js-stream-pusher.jsp, a JavaServer Page, executes on the server. The file's main body is listed below:

当我们发送请求后,push-js-stream-pusher.jsp,最为服务页面,在服务器上开始运行。下面是BODY中的代码:


<%
  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: %>

In line 21 above, we see a timer loop that prints HTML to the browser every 3 seconds. But wait, it's pushing JavaScript, not HTML! What does this mean? Effectively, it pushes a line such as <script language=JavaScript >parent.push('Page 4')</script>.

在21行,有个Timer每隔三秒循环执行代码。但为什么发送脚本,而不是HTML!这意味着什么?

What does it mean for the browser? The browser, with its JavaScript engine running, obediently executes each line coming in. It calls the parent.push() JavaScript function. Now the parent is the parent of the frame it is in, which is the push-js-stream.html file:

浏览器的脚本引擎在接收到这些数据后,会乖乖的运行脚本,调用parent.push()方法。parent是指puash-js-stream.html。

<script LANGUAGE="JavaScript"> var pageStart="<HTML><HEAD></HEAD><BODY BGCOLOR=blue TEXT=white><H2>Server pushes: <P>"; 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> </HEAD>

<FRAMESET BORDER=0 COLS="*,0"> <!-- frame to display the content pushed by the pushlet --> <FRAME SRC="push-js-stream-display.html" NAME="displayFrame" BORDER=0 SCROLLING=no>

<!-- Hidden frame with the pushlet that pushes lines of JavaScript--> <FRAME SRC="push-js-stream-pusher.jsp" NAME="pushletFrame" BORDER=0 SCROLLING=no> </FRAMESET>

The push() function called from within the JSP frame (pushletFrame) writes whatever it gets passed in its content argument into the displayFrame. The function push(content)illustrates some dynamic HTML: you can refresh the content of a frame or window by calling writeln of its document object. So the displayFrame is the real view where the content is displayed. It initially displays "Wait..." until the server pushes the first content.

Push方法向displayFrame写入任何数据。你可以在这个方法里面动态的写写JS来修改HTML,然后就会数据就可以展现出来了。在服务器发送数据过来之前,可以显示些数据,比如:“等待中。。。”。

<HTML>
<BODY BGCOLOR=black TEXT=white>
<H1>WAIT...</H1>
</BODY>
</HTML>

The example above demonstrates the whole idea of pushlets: we stream in lines of JavaScript from a servlet (or JSP in the example above) into a hidden frame. The browser executes the lines and may do something interesting. So effectively we have a callback from Java in the server to JavaScript in the client browser! Phew, that was easy!

上面的例子展示了pushlet的整个机制:我们用一个servlet或者jsp给一个隐藏的frame写入数据。浏览器会执行解析这些数据,然后做些事情,所以我们从java后端发送数据到了js前端。

The example above demonstrates the mechanics, but a couple of issues still have to be solved and features have to be added. For that reason I've built a simple event framework that supports pushlets -- but first I will pull you off the Java track with something called dynamic HTML.

还有一些问题需要去解决。所以我就开发了一个简单的事件框架来支持pushlet。不过,首先我得向你展示些调用了动态HTML的东西。

Not just Java: Dynamic HTML 

不仅仅是java: 动态HTML

Long gone (in Internet time) are the days when a Website could be produced by the local sysop who scanned in some images from the company's brochure and knew a few HTML tags. The possibilities for manipulating content and user interaction within the browser are expanding through dynamic HTML (DHTML). As a Java programmer using servlets and JSPs, you should make DHTML part of your toolkit. Be aware that you will often have to make trade-offs when you divide functionality among the servlets or JSPs and the DHTML pages executed in the client browser. That is often not an easy decision, but knowing the advantages and disadvantages of DHTML will help you make a better one.

很久以前,那些知道些HTML标签的系统操作员就可以构建一个网站。后来因为需要动态修改内容以及用户交互,才有了动态的HTML(DHTML)。作为一个java开发人员,你应该把DHTML放入工具袋里。你会依据需要平衡使用哪种技术。这样的决策不是那么容易的,不过知道DHTML的优缺点会帮助你做的更好。

Dynamic HTML refers to a combination of HTML, Cascading Style Sheets (CSS), JavaScript, and the browser's Document Object Model (DOM). Traditionally, a page could be altered only by reloading a new page from the server. DHTML allows full control of an HTML document within a browser after the page has been loaded. You may have seen such examples as image rollovers, pop-up content, and collapsible menus. Most browsers at version 4.0 and later support DHTML, albeit with some differences in standards (see below).

动态HTML使用HTML,CSS,JS,DOM的组合。很久以前,我们只能通过刷新页面来请求新的页面。DHTML可以在页面已经载入之后,完成文档的所有控制。你也许已经看到了诸如图片滚动,上翻内容和可折叠菜单等例子。很多浏览器要在4.0才支持这些特性。

From a programmer's point of view, the entire document in the browser -- its frames, images, paragraphs, tables, and so on -- is represented as a hierarchical object model, the DOM (not to be confused with the XML DOM). Through JavaScript you can manipulate the elements of the DOM and thereby change the content or appearance of the document. Moreover, you can capture user events such as mouse moves and form submissions, and subsequently process those events to modify DOM elements. For example, a mouse moving over an image may produce a mouseover event that, when processed, changes the image by popping up explanatory text. It sounds great, doesn't it? We just need to get familiar with the DHTML standard and off we go! Hmm, but who defines the DHTML standard?

从开发者的角度来讲,整个文档包含frame,image,paragraph,table等等元素。使用树形结构展示出来。虽然js可以操作DOM元素并且改变内容或者展示文档。但你可以捕获用户事件来操作DOM元素。比如,鼠标移动到图片上后,图片上显示解释性的文字。我们需要熟悉DHTML标准,可是谁定义这个标准了?

That question hits an issue that has inhibited many developers from embracing DHTML. First, the browser must be version 4.0 or later. The official standards body for DHTML-related specifications is the World Wide Web Consortium (W3C). However, the Microsoft and Netscape browsers now each have proprietary DHTML extensions that you must track as well.

这个问题刚好关联了一个问题,就是抑制开发者遵照DHTML。首先,浏览器必须在4.0以上。然后就是微软和网景公司都有自己版本的DHTML扩展,但你又必须遵循。

Luckily, the situation has become much better. Most users by now have the later browsers, and some people (in particular, the "Dannymen," Dan Steinman and Danny Goodman, seeResources) have done good work on cross-browser DHTML libraries that you can reuse. As a Java programmer, you may appreciate the fact that you can do reasonably clean object-based or even object-oriented programming in JavaScript. You will find examples in my basic DHTML demos (www.fluidiom.com:8080; choose Examples, then DHTML), but it is worthwhile to check the DHTML resources. Once you have browser issues hidden behind a facade of cross-browser libraries, DHTML programming becomes real fun.

幸运的是,问题朝着好的方向发展。很多用户使用后者浏览器,还有些人在做跨平台DHTML库,你都可以使用。作为java开发者,你也许很庆幸自己可以开发纯对象化的程序。

So, with Java gaining more market presence on the server and with DHTML providing powerful features on the client, I decided to couple those two great technologies in a direct way, using pushlets. For that I implemented a lightweight framework for the server and some DHTML libraries for the client. These are discussed next.

我为服务器端写了一些轻量级的框架,为前端写了一个DHTML库。下面会讨论到。

A pushlet framework

pushlet框架

The pushlet framework allows clients to subscribe to subjects within a server from which they subsequently receive events. The framework's basic design pattern -- Publish, Subscribe -- has both server and client components:

框架允许客户端向服务端提交东西,以便后续接收事件。框架的基本设计方式包含前后两部分。

  1. A server-side collection of Java classes designed around the Publisher class andSubscriber interface (see Figure 1, a Unified Model Language, or UML, class diagram)
  2. 服务端类
  3. A client-side reusable JavaScript library (pushlet.js) and HTML (pushlet.html) for receiving events within DHTML clients and passing them to the application
  4. 客户端Js库和html
  5. Client-side Java classes (JavaPushletClient.java and JavaPushletClientListener.java) for receiving events within Java clients
  6. 接收事件的客户端java类
  7. Cross-browser DHTML client utility libraries (layer.jslayer-grid.js, and layer-region.js) for displaying content in DHTML layers
  8. 跨平台js库
  9. Generators for test events (EventGenerators.java) and example applications (www.fluidiom.com:8080; choose Pushlet, then Examples)
  10. 事件生成器,方便测试用
Server-side class design

On the server side, the key classesas are the Pushlet servlet, the Publisher class, theSubscriber interface, and the Event class, as seen in Figure 1.

在服务端,核心类是Pushlet servllet, Publisher 类,Subscriber 接口, Event类。

 

Figure 1. UML class diagram: Server-side pushlet framework

服务端pushlet框架

By invoking the Pushlet servlet through an HTTP request, clients subscribe to receive Events. The request indicates:

通过HTTP请求调用Pushlet servlet, 客户端接收事件。请求包含:

  1. For which subject they would like to receive Events
  2. 使用哪个subject接收事件
  3. In which format -- JavaScript calls (the default), XML, or Java-serialized objects -- Events should be sent
  4. Events应该发送哪种格式的数据,是Js,XML,还是java 序列化对象
  5. Through which receiver protocol (currently HTTP only; additional protocols in future versions) they should be sent
  6. 它们应该使用哪种协议发送数据,目前只是HTTP

A sample request for receiving Amsterdam Exchange (AEX) stock rates formatted as XML through an HTTP response stream would be:

一个简单的例子表明使用XML格式,通过HTTP响应流来接收AEX数据。

http://www.fluidiom.com:8080/servlet/pushlet?subject="/stocks/aex"&format="XML"&protocol="HTTP-stream"

Subject identifiers are organized as a hierarchical topic tree. For example, /stocks identifies all Events related to stock rates, while /stocks/aex identifies stock rates for the Amsterdam Exchange. Likewise, the subject / indicates all events.

Currently, the only receiver protocol is a client HTTP response stream. In a future extension other receiver protocols such as TCP, UDP, RMI, HTTP POSTing, or even SMTP (email) will be able to be specified.

使用树形结构来标识subject。例如,/stocks标识所有与stock有关的事件,/stock/aex标识AEX。同样的,“/”标识所有事件。当前仅仅支持HTTP响应协议。将来会扩展到TCP,UDP,RMI,HTTP POSTING ,SMTP协议。

An Event is merely a set of name-value string pairs (implemented withjava.util.Properties). The Publisher has an interface through which classes that generate Events can publish them. The Publisher keeps a list of Subscribers and sends each Event to those Subscribers whose subject matches the Event's subject. Events may be originating within the server through EventGenerators, which may listen to external events such as a stock feed. In addition, clients may publish Events through HTTP with the Postlet servlet. The responsibilities of other classes in Figure 1,PushletSubscriber and its contained classes, can best be explained through scenarios.

事件仅仅是name-value的字符串对,使用java.util.Properties实现。Publisher有一个接口,那些生成事件的类通过这个接口发布他们。Publisher有一个Subscriber的集合,发送事件到与之事件匹配的Subscriber.通过EventGenerator可以生成事件。另外,客户端可以通过Postlet servlet 发布事件。


Figure 2 shows a UML sequence diagram for a client browser subscribing for Events from the Publisher.

图2 使用UML序列图显示客户端浏览器提交事件。

Figure 2. UML sequence diagram: Client event subscription

Pushlet is invoked with the servlet doGet() method. Because multiple clients may invoke the same Pushletobject, it should itself not be a Subscriber. Instead, it delegates all subscription (and subsequent Eventhandling) by creating a new PushletSubscriber object for each doGet() and letting it run until finished witheventLoop(). The PushletSubscriber that implements theSubscriber interface registers for Events with thePublisher through the join() method. To deal with different client formats and protocols, it creates a specialized ClientAdapter object, in this case aBrowserPushletAdapter. For browsers supporting Multipart MIME, such as Netscape 4.0 or later, this would be aMultipartBrowserClientAdapter. The final call in this scenario is a "wait for event loop." Note that thedeQueue() method suspends the execution of the current thread until an Eventbecomes available (indicated with a half arrow). This is explained through the next scenario in Figure 3.

图2.UML时序图:客户端事件提交

Pushlet被调用了它的doGet方法。由于多客户端可能会调用同一个Pushlet对象,所以它自己不可以作为一个Subscriber。而是为每一个doGet请求创建一个新的PushletSubscriber对象,并且让它运行知道循环结束。PushletSubscriber实现了Subscriber接口,并通过Publisher的join方法注册事件。为了处理不同的客户端和协议,就创建了一个ClientAdapter对象。这里创建的是BrowserPushletAdapter.为了能够支持更多的浏览器,比如网景公司的4.0版本或者之后,应该是MultipartBrowserClientAdapter。最后是一个循环,doQueue()方法挂起了当前线程,直到有一个事件变得可用。

Figure 3. UML sequence diagram: Event publishing and client notification

事件发布,通知客户端

The scenario illustrated in Figure 3 shows how anEventGenerator creates an Event and callsPublisher.publish() to have it dispatched. The Publisher walks through its list ofSubscribers and asks each whether theEvent matches its subscription criteria (currently only the subject). If the Event matches, Publisher calls the send() method on the Subscriber. Each PushletSubscriber object has a GuardedQueueobject in which it queues incoming Events when send() is called.

EventGenerator创建了一个事件,然后调用Publisher.publish()方法。这个Publisher会询问所有的Subscriber,是否匹配这个事件,如果匹配了,就会调用Subscriber的send()方法。每一个PushletSubscriber都有一个GuardedQueue对象,这个对象会缓存发送过来的事件。

So why doesn't Publisher directly push the Event to the BrowserPushletAdapter? First, we want to suspend execution of the BrowserPushletAdapter thread until an Eventbecomes available; that is, we don't want to do a busy-wait (also known as polling). Second, a Publisher may notify multiple clients. Having a synchronous send() method call a slow client on the other end of the line may block all the other clients to be notified next. This is a design pitfall I also observed in RMI or CORBA callback examples in which a list of clients is called back synchronously -- client number 13 on a slow connection and a 386 processor may block the remaining callbacks

为什么Publisher不直接推送事件到BrowserPushletAdapter中?首先,我们想在一个事件被激活前,能够挂起BrowserPushletAdapter线程。我们不希望出现繁忙等待。其次,一个Publisher也许会通知多个客户端。使用同步的send方法通知一个慢的要命的客户端会影响后续通知其他客户端的工作。我也观察到RMI或者CORBA也是使用异步的方式通知客户端的。

The GuardedQueue, a utility object, uses the readers-writers pattern to enqueue or dequeue Objects, with guarded suspension using the java.lang.Object.wait() and notifyAll()methods. The thread of a client of GuardedQueue callingdeQueue() is suspended (using wait()) until an Object is queued. Likewise, a client enqueuing an Object will be suspended as long as the queue is full. When the clients are fast enough, the GuardedQueue never fills up.

这个GuardedQueue作为一个工具,使用读写模式去载入或者载出对象。使用wait和notifyAll挂起。GuardedQueue的线程在一个对象被载入之前,线程处于挂起状态。

After the BrowserPushletSubscriber has dequeued an Eventobject, it calls the push() method on the BrowserPushletAdapter, which formats the Event to a JavaScript element and send it to the browser. For example, for a Royal Philips Electronics stock rate of 123.45, the JavaScript element would look like this:

在Subscriber移除一个事件对象后,它会调用Adapter的Push()方法,这个方法会格式化事件成为js元素,并且送到浏览器中。

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

Now we have arrived at the client browser side. We assigned the Pushlet itself to a hidden HTML frame. Within this frame, parent.push() calls thepush() function in its parent. This is effectively the callback that originated within the server. The parent frame has to implement the push() method. Since this is a common task for all browser clients, two reusable files for the client are provided:pushlet.html and pushlet.js

pushlet.html is meant to be included in a frame within the application-specific client HTML document. It can be made a parameter with the subject identifier and a background color (such that it remains invisible). Most important, it implements the JavaScript push() method as follows:

我们已经到达浏览器这边 了,指定Pushlet到一个隐藏的HTML frame。 在这个frame中,parent.push()方法会调用push()方法。父frame要实现push方法。这是所有浏览器客户端都要做到事情,这有两个客户端文件。

pushlet.html需要放到一个frame中。

function push() { // Create a PushletEvent object from the arguments passed in. // push.arguments is event data coming from the Server pushletEvent = new PushletEvent(push.arguments)

// Show blinking light as data is coming in updateStatusFrame();

// Is parent ready to receive events? if (!parent.onPush) { return; }

// Forward the event to the parent frame that should do // application-specific handling of the event parent.onPush(pushletEvent); }

The function push() first creates a JavaScript object from the parameters passed in. Yes, you can do object-based programming in JavaScript. Reminiscent of varargs in C/C++, JavaScript functions may have a variable number of arguments. A PushletEventobject is created with whatever arguments were passed to the push() method from the server. Let's take a look at PushletEvent implemented in pushlet.js:

函数push首先会创建一个javascript对象。你可以在javascript中做面向对象编程。回忆下C/C++,javascript可以有多个可变参数。无论参数是什么,都会创建PushletEvent对象。下面是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) { // Member variable setup; the Map stores the N/V pairs this.map = new Map();

// Member function setup this.getSubject = PushletEventGetSubject this.put = PushletEventPut this.get = PushletEventGet this.toString = PushletEventToString this.toTable = PushletEventToTable

// Put the arguments' name/value pairs in the Map for (var i=0; i < args.length; i++) { this.put(args[i], args[++i] ); } }

// Get the subject attribute function PushletEventGetSubject() { return this.map.get('subject') }

// Get event attribute function PushletEventGet(name) { return this.map.get(name) }

// Put event attribute function PushletEventPut(name, value) { return this.map.put(name, value) }

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

// Convert content to HTML TABLE function PushletEventToTable() { return this.map.toTable(); }

In turn, pushlet.js uses a Map JavaScript object, ajava.util.Hashtable-like utility object.

Next, push() calls the updateStatusFrame() method to show a blinking light to indicate we are still receiving events. If aparent.onPush() function exists, push() calls that function with the PushletEvent. The parent.onPush() method is the application-specific, event-handling function that in this case may update the philips stock-related layer in a DHTML page.

That ends the description of the basic framework design. Now let's turn our attention to situations where we can apply pushlets and look at their strengths and weaknesses.

Pushlet applications

Pushlets allow many types of Web applications to be developed. Since the framework also allows for clients to upload events (through the Postlet), applications can do more than passively push data. Possible applications can be categorized according to:

  • Whether the events originate from the server, from clients, or both
  • Whether state is kept on the server, within the clients, or both

Moreover, since live events are made available to JavaScript, you can create scriptable plug-ins able to receive live updates. You may script your Macromedia Flash or VRML World application, for example.

Let's examine some possible applications.

Monitoring

With pushlets, various data sources -- stocks, weather, votes, flight arrivals, systems -- may be monitored live.

Gaming

Developers can create simple move-display games, such as a two-user tic-tac-toe and more elaborate chess, Risk, and Monopoly-like games. Players can make moves through postlets. An extended server could implement the game's logic and push changes in the game's state to the clients. In an upcoming version of the pushlet framework I plan to implement a generic multiuser session so that the players' presence and actions are communicated.

Distributed Model View Controller (MVC)

MVC refers to a design pattern often found in user-interface frameworks such as Java Swing and Microsoft MFC. In the distributed variant, a model (often the data) resides on a server, while clients hold the views and controls. The model is modified through the controls. The model then notifies all attached views, which subsequently refresh themselves.

Many applications may have a Web frontend through which data on the server is updated by multiple users -- flight reservation systems, for example. If one client makes an update, the others won't see it unless they continuously refresh their pages. In some cases that is a simple and workable solution, but in other cases users need to be synchronized with the update as it happens. That is easily done with pushlets pushing a URL as the single event. When a client receives the URL, it refreshes the page.

Enterprise JavaBeans (EJBs) server is an example. Although Java clients are able to talk directly to an EJB (through RMI or CORBA), more often servlets and JSPs are used as a frontend. In this case notification becomes much harder. With pushlets, an EJB could notify its attached Web clients whenever its state changes.

Web presentations

When referring to Web presentations, I once quipped, "Are you being surfed?" In fact, Web presentations, also known as Web tours, inspired me to develop pushlets. I abandoned PowerPoint in making Java course content and developed a content-management framework based on XML with slide presentations in HTML. Since classrooms frequently had no projector but all students had a networked computer, I developed a simple application (WebPres) that enable me to change HTML slides with all students automatically.

Web presentations can also be useful for call centers, banks, or help desks. For example, when I phone in with a question, a call-center agent may guide me to URLs with solutions, offers, or other information.


Community tools

Community tools include various applications where multiple users can join a live session. I originally had a prototype of the framework where I created a multiuser session as a collection of HTTP Session objects. I plan to extend the pushlet framework with such capabilities. For now, I have a simple Web chat and what I call WCQ (in Dutch this sounds almost like "We Seek You"), a simple ICQ-like desktop window where you can monitor the presence of friends. Other applications in this area include live forums and shared document editing.

The consequences of using pushlets

Like any other mechanism or design pattern, pushlets present obvious advantages and disadvantages when compared with Java-based applets that use messaging or RMI/CORBA client callbacks.

Advantages
  • Direct integration with DHTML within the browser: data generated by the server can be immediately integrated into the page content of the browser. Therefore, all the layout capabilities of DHTML can be directly applied. With RMI or CORBA it is difficult to integrate the applet with its containing Web page.
  • Standard HTTP port and protocols: messaging and RMI or CORBA use dedicated ports that may not work through firewalls, and browser security restrictions may prevent callbacks or data receipt over UDP.
  • Low client weight: Java applets with RMI/CORBA often make the client heavier in startup and resource consumption; pushlets don't.
  • No extra server: messaging and RMI/CORBA often require a dedicated server product or at least a registry running RMI. pushlets can, in theory, run on any servlet engine, using functions such as connection management and multithreading (which may be a disadvantage, as well, as seen below).
  • Small protocol overhead.

Disadvantages
  • Cross-browser DHTML: some effort is required to make cross-browser DHTML libraries that work in all browser versions on all platforms.
  • Scalability: when hundreds of clients connect through pushlets, they may hog resources such as threads and sockets. On this scale it would be better to serve pushlets off a dedicated servlet server optimized for this type of HTTP usage. However, scalability is also an issue with CORBA callbacks.
  • Web-server issues: a Web server is usually not designed for long-lived connections. One solution is the same as that applied above under Scalability. For large-scale applications, a pushlet applet client that receives notifications as UDP messages may be used. The HTTP request is then used only for subscription.
  • Proxy buffering: As one reviewer of the pushlet concept pointed out, some proxy servers may buffer HTTP data.
  • Quality of service: the server gets no direct confirmation that the event has been processed successfully.

Further work

I am further testing the viability of pushlets. The pushlet framework is a generic Publish-Subscribe pattern, with HTTP-based pushlet clients as a special case. I am developing the framework with the following extensions:

  • Additional client receiver-protocols such as TCP and UDP. One interesting possibility is calling an applet that receives UDP messages from within the pushlet frame on the client. That would mean we don't keep the HTTP connection. In general, clients will subscribe with a HTTP request on which they indicate through which protocol (TCP, UDP, RMI, HTTP-stream, or HTTP POST) they want to receive events and in which format (XML, Java-serialized objects, and so on). The address of the receiver is specified, where required. For example, to receive XML-formatted stock events through UDP on port 5001, the request would look like this:

    http://www.fluidiom.com:8080/servlet/pushlet?subject="/stocks/aex"&format="XML"&protocol="UDP"&port="5001"

    With UDP a lease time needs to be specified (since the server would never know when the client leaves). If the lease is not renewed within this time, the server will drop the client.

  • Additional client sender-protocols. Currently, events are generated internally or through the postlet by using the HTTP POST request. Additional protocols such as RMI, TCP, and UDP may be applied.

  • Subject state. Currently, a subject is just a hierarchical string. A client that subscribes gets no history that may have built up -- a chatroom discussion, for example. In a next version subjects become first-class objects, so that when clients subscribe they may implicitly get the current state or may explicitly request the state.
  • Multiuser. although some multiuser applications are possible, there is no multiuser session control on the server. In earlier versions I experimented with combining multiple HTTP sessions into a multiuser HTTP session.

Conclusions

In this article we have covered quite some ground: server-side Java, servlets, DHTML, design patterns, and a framework. In its essence the pushlet technology is very simple, and, given its advantages and disadvantages, it may serve as an alternative to RMI or CORBA for certain Web applications.

The pushlet framework is in its early stages and being evaluated by a number of users. You may visit the pushlet Website through http://www.justobjects.nl to see how development is progressing, to run examples, and to download the framework source. Since I have planned it as an open source project, you are welcome to participate in development and review.

Just van den Broecke of Just Objects (http://www.justobjects.nl) is a freelance architect, developer, and trainer specializing in object-oriented design, distributed Java, and Web technology. His Java projects range from banking applications to multiuser multimedia tools for performing artists (http://www.keyworx.org). He believes Java is most effectively applied when combined with other technologies such as dynamic HTML and XML.

Learn more about this topic




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值