ZK 5: Chat with Event Queue

From Documentation
DocumentationSmall Talks2009AugustZK 5: Chat with Event Queue
ZK 5: Chat with Event Queue

Author
Tom Yeh, Potix Corporation
Date
August 4, 2009
Version
ZK 5.0.0

Overview

The best way to use the server push is not to know the server push at all.

When the server push was introduced in ZK 3, we minimized the server-push API to two methods: activate and deactivate. Once activated, a developer can access any component in any way he like. It is straightforward. However, it still requires some knowledge of thread programming[1] A couple weeks ago, Henri told me we can and shall encapsulate the use of server push in form of event firing and listening, and the event queue (introduced in ZK 3.5) is a good candidate.

Here is the result: application-scope event queue (org.zkoss.zk.ui.event.EventQueue).

We extends the event queue to support the concept of scope. Currently, there are two scopes: desktop and application. With a desktop scope, an event queue is visible only to the associated desktop. With an application scope, an event queue is visible to the whole application and any thread (not just event listeners) can access it. The application-scope event queue is based on the server push but the developer doesn't need to know that and he doesn't need to do any thread programming, which is handled automatically by the event queue.

The event queue is extensible. For example, you can extend it to support the communication across different virtual machine by use of, say, JMS or other service.


  1. Robbie wrote a tutorial about server push: Simple and Intuitive Server Push with a Chat Room Example.

What is an event queue?

An event queue is an event-based publish-subscribe solution for application information delivery and messaging. It provides asynchronous communications for different modules/roles in a loosely-coupled and autonomous fashion.

By publishing, a module (publisher) sends out messages without explicitly specifying or having knowledge of intended recipients. By subscribing, a receiving module (subscriber) receives messages that the subscriber has registered an interest in, without explicitly specifying or knowing the publisher.

Eventqueue-concept.jpg

The scope of an event queue

Depending on the visibility of the event queues, there are two scopes: desktop and application. A desktop-scope event queue is visible only within a given desktop, while an application-scope event queue is visible across the whole application.

How to locate an event queue

An event queue is identified by an unique name. To locate one, just invoke the lookup method of EventQueues. For example, we can locate an application-scope event queue as follows.

EventQueue eq = EventQueues.lookup("foo", EventQueues.APPLICATION, true);

where the first argument is the name of the event queue, and the third argument is whether to create one automatically if not found.

How to publish an event

To publish, just invoke the publish method of the EventQueue interface (returned by lookup). For example,

EventQueues.lookup("my super queue", EventQueues.APPLICATION, true)
  .publish(new Event("onSomethingHapping", null, new SomeAdditionInfo()));

The message used to communicate among publishers and subscribes are the event instances, so you can use any event you prefer.

How to subscribe to an event queue

The event being published will be sent to each subscriber by calling the event listener the subscriber subscribe. To subscribe, just invoke the subscribe method. For example,

EventQueues.lookup("my super queue", EventQueues.APPLICATION, true).subscribe(
  new EventListener() {
    public void onEvent(Event evt) {
       //handle the event just like any other event listener
    }
  });

The event listener is invoked just like a normal event. You can manipulate the component or do whatever as you want.

An example: chat

Let us put them together with a chat application. Here is the code snippet:

<window title="Chat" border="normal">
	<zscript><![CDATA[
	import org.zkoss.zk.ui.event.*;
	EventQueue que = EventQueues.lookup("chat", EventQueues.APPLICATION, true);
	que.subscribe(new EventListener() {
		public void onEvent(Event evt) {
			new Label(evt.getData()).setParent(inf);
		}
	});	
	void post(Textbox tb) {
		String text = tb.value;
		if (text.length() > 0) {
			tb.value = "";
			que.publish(new Event("onChat", null, text));
		}
	}
	]]></zscript>

	Say <textbox onOK="post(self)" onChange="post(self)"/>
	<separator bar="true"/>
	<vbox id="inf"/>
</window>

Subscribe and publish. Straightforward, isn't it? No multi-thread programming, no explicit server-push.

Then, you can chat with two or more different computers.

More about event queues

More about the scope of event queues

In the example above, we use the application-scope event queue since it is a queue shared by different desktops[1]. This can be tested by opening to different instances of browsers and chatting between them.

Here is a list of differences:

Desktop-scope Application-scope
visibility visible only to the same desktop visible to the whole application
publish it can be invoked only in an event listener, or when the current execution is available. it can be invoked anytime, anywhere
subscribe it can be invoked only in an event listener, or when the current execution is available. it can be invoked only in an event listener, or when the current execution is available.
multi-thread Not used Used but transparent to the user
server-push Not used Used but transparent to the user

  1. In ZK terminology, a browser window is a desktop

Event queues and server push

When an application-scope event queue is used, the server push is enabled for each desktop that subscribers belong to. In additions, a thread is created to forward the event to subscribers.

ZK has two kinds of server push: client-polling and comet[1]. The client-polling server push is implemented with an implicit timer at the client. The interval of the timer depends on the loading of the server. For example, the interval becomes longer if the time to get a response has become longer.

On the other hand, the comet server push is implemented with a pre-established and 'virtual' permanent connection. It is like sending a taxi to the server, and waiting in the server until there is data to send back. Meanwhile, the client-polling server is like sending a taxi periodically to the server, and leave immediately if no data is available.

By default, the comet server push is used. If you prefer to use the client-polling approach, just specify the following in WEB-INF/zk.xml[2].

<device-config>
	<device-type>ajax</device-type>
	<server-push-class>org.zkoss.zk.ui.impl.PollingServerPush</server-push-class>
</device-config>

  1. Comet Programming
  2. Like most ZK features, you can provide your own implementation if you like.

How to extend the event queue

Like most ZK features, the event queue is extensible. The location and creation of an event queue is actually done by a so-called event queue provider. An event-queue provider must implement the EventQueueProvider interface.

To customize it, just provide an implementation, and then specify the class name in the property called org.zkoss.zk.ui.event.EventQueueProvider.class.

For example, let us say we want to introduce the JMS scope, then we can implement as follows (only pseudo-code):

public class MyEventQueueProvider extends org.zkoss.zk.ui.event.impl.EventQueueProviderImpl {
  public EventQueue lookup(String name, String scope, boolean autocreate) {
    if ("jms".equals(scope)) {
      //create an event queue based on JMS's name
    } else
      return super.lookup(name, scope, autocreate);
  }
  public boolean remove(String name, String scope) {
    if ("jms".equals(scope)) {
      //remove the event queue based on JMS's name
    } else
      return super.removename, scope);
  }
}

Then, specify the property in WEB-INF/zk.xml

<library-property>
	<name>org.zkoss.zk.ui.event.EventQueueProvider.class</name>
	<value>MyEventQueueProvider</value>
</library-property>



Copyright © Potix Corporation. This article is licensed under GNU Free Documentation License.