Event Queues"

From Documentation
m
Line 1: Line 1:
{{ZKComponentReferencePageHeader}}
+
{{ZKDevelopersReferencePageHeader}}
  
 
= Overview =
 
= Overview =
Line 20: Line 20:
 
You could locate an event queue by invoking one of the <tt>lookup</tt> method of <javadoc>org.zkoss.zk.ui.event.EventQueues</javadoc>. For example,
 
You could locate an event queue by invoking one of the <tt>lookup</tt> method of <javadoc>org.zkoss.zk.ui.event.EventQueues</javadoc>. For example,
  
<source lang="java">
+
<syntax lang="java">
 
EventQueues.lookup("myQueue"); //assumes the desktop scope
 
EventQueues.lookup("myQueue"); //assumes the desktop scope
 
EventQueues.lookup("anotherQueue", EventQueues.SESSION);
 
EventQueues.lookup("anotherQueue", EventQueues.SESSION);
 
EventQueues.lookup("anotherQueue", session);
 
EventQueues.lookup("anotherQueue", session);
</source>
+
</syntax>
  
 
Notice that if you want to locate an event queue in a working thread (rather than an event listener), you have to use <javadoc method="lookup(java.lang.String, org.zkoss.zk.ui.Session)">org.zkoss.zk.ui.event.EventQueues</javadoc> or <javadoc method="lookup(java.lang.String, org.zkoss.zk.ui.Application)">org.zkoss.zk.ui.event.EventQueues</javadoc>, depending on your requirement.
 
Notice that if you want to locate an event queue in a working thread (rather than an event listener), you have to use <javadoc method="lookup(java.lang.String, org.zkoss.zk.ui.Session)">org.zkoss.zk.ui.event.EventQueues</javadoc> or <javadoc method="lookup(java.lang.String, org.zkoss.zk.ui.Application)">org.zkoss.zk.ui.event.EventQueues</javadoc>, depending on your requirement.
Line 79: Line 79:
 
To publish, just invoke one of the <tt>publish</tt> methods of <javadoc>org.zkoss.zk.ui.event.EventQueue</javadoc> (returned by <tt>lookup</tt>). For example,
 
To publish, just invoke one of the <tt>publish</tt> methods of <javadoc>org.zkoss.zk.ui.event.EventQueue</javadoc> (returned by <tt>lookup</tt>). For example,
  
<source lang="java">
+
<syntax lang="java">
 
EventQueues.lookup("my super queue", EventQueues.APPLICATION, true)
 
EventQueues.lookup("my super queue", EventQueues.APPLICATION, true)
 
   .publish(new Event("onSomethingHapping", null, new SomeAdditionInfo()));
 
   .publish(new Event("onSomethingHapping", null, new SomeAdditionInfo()));
</source>
+
</syntax>
  
 
The message used to communicate among publishers and subscribes are the event instances, so you can use any event you prefer.
 
The message used to communicate among publishers and subscribes are the event instances, so you can use any event you prefer.
Line 90: Line 90:
 
The event being published will be sent to each subscriber by calling the event listener the subscriber subscribe. To subscribe, just invoke one of the <tt>subscribe</tt> methods of <javadoc>org.zkoss.zk.ui.event.EventQueue</javadoc> (returned by <tt>lookup</tt>). For example,
 
The event being published will be sent to each subscriber by calling the event listener the subscriber subscribe. To subscribe, just invoke one of the <tt>subscribe</tt> methods of <javadoc>org.zkoss.zk.ui.event.EventQueue</javadoc> (returned by <tt>lookup</tt>). For example,
  
<source lang="java">
+
<syntax lang="java">
 
EventQueues.lookup("my super queue", EventQueues.APPLICATION, true).subscribe(
 
EventQueues.lookup("my super queue", EventQueues.APPLICATION, true).subscribe(
 
   new EventListener() {
 
   new EventListener() {
Line 96: Line 96:
 
       //handle the event just like any other event listener
 
       //handle the event just like any other event listener
 
     }
 
     }
   });</source>
+
   });
 +
</syntax>
  
 
The event listener is invoked just like a normal event. You can manipulate the component or do whatever as you want.
 
The event listener is invoked just like a normal event. You can manipulate the component or do whatever as you want.
Line 104: Line 105:
 
Here is an example: chat.
 
Here is an example: chat.
  
<source lang="xml">
+
<syntax lang="xml">
 
<window title="Chat" border="normal">
 
<window title="Chat" border="normal">
 
<zscript><![CDATA[
 
<zscript><![CDATA[
Line 127: Line 128:
 
<vbox id="inf"/>
 
<vbox id="inf"/>
 
</window>
 
</window>
</source>
+
</syntax>
  
 
Then, you can chat among two or more different computers.
 
Then, you can chat among two or more different computers.
Line 144: Line 145:
 
For example,
 
For example,
  
<source lang="xml">
+
<syntax lang="xml">
 
<window title="test of long operation" border="normal">
 
<window title="test of long operation" border="normal">
 
<html><![CDATA[
 
<html><![CDATA[
Line 195: Line 196:
 
  <vbox id="inf"/>
 
  <vbox id="inf"/>
 
</window>
 
</window>
</source>
+
</syntax>
  
 
An asynchronous event listener is <i>not</i> allowed to access the desktop, but it is allowed to invoke <javadoc method="publish(org.zkoss.zk.ui.event.Event)">org.zkoss.zk.ui.event.EventQueue</javadoc> to publish an event.
 
An asynchronous event listener is <i>not</i> allowed to access the desktop, but it is allowed to invoke <javadoc method="publish(org.zkoss.zk.ui.event.Event)">org.zkoss.zk.ui.event.EventQueue</javadoc> to publish an event.
Line 203: Line 204:
 
While subscribing the asynchronous and synchronous event listeners separately is, as illustrated above, the general approach, the event queue provides a simple method to allow you register them in one invocation: <javadoc method="subscribe(org.zkoss.zk.ui.event.EventListener, org.zkoss.zk.ui.event.EventListener)">org.zkoss.zk.ui.event.EventQueue</javadoc>. In additions, you don't need to publish an event at the end of the asynchronous event listener -- the synchronous event listener is invoked automatically.
 
While subscribing the asynchronous and synchronous event listeners separately is, as illustrated above, the general approach, the event queue provides a simple method to allow you register them in one invocation: <javadoc method="subscribe(org.zkoss.zk.ui.event.EventListener, org.zkoss.zk.ui.event.EventListener)">org.zkoss.zk.ui.event.EventQueue</javadoc>. In additions, you don't need to publish an event at the end of the asynchronous event listener -- the synchronous event listener is invoked automatically.
  
<source lang="xml">
+
<syntax lang="xml">
 
<window title="test of long operation" border="normal">
 
<window title="test of long operation" border="normal">
 
<zscript>
 
<zscript>
Line 239: Line 240:
 
  <vbox id="inf"/>
 
  <vbox id="inf"/>
 
</window>
 
</window>
</source>
+
</syntax>
  
 
= More About Scopes =
 
= More About Scopes =
Line 292: Line 293:
 
For example, let us say we want to introduce the JMS scope, then we can implement as follows (only pseudo-code):
 
For example, let us say we want to introduce the JMS scope, then we can implement as follows (only pseudo-code):
  
<source lang="java">
+
<syntax lang="java">
 
public class MyEventQueueProvider extends org.zkoss.zk.ui.event.impl.EventQueueProviderImpl {
 
public class MyEventQueueProvider extends org.zkoss.zk.ui.event.impl.EventQueueProviderImpl {
 
   public EventQueue lookup(String name, String scope, boolean autocreate) {
 
   public EventQueue lookup(String name, String scope, boolean autocreate) {
Line 306: Line 307:
 
       return super.removename, scope);
 
       return super.removename, scope);
 
   }
 
   }
}</source>
+
}
 +
</syntax>
  
 
Then, specify the property in <tt>WEB-INF/zk.xml</tt>
 
Then, specify the property in <tt>WEB-INF/zk.xml</tt>
  
<source lang="xml">
+
<syntax lang="xml">
 
<library-property>
 
<library-property>
 
<name>org.zkoss.zk.ui.event.EventQueueProvider.class</name>
 
<name>org.zkoss.zk.ui.event.EventQueueProvider.class</name>
 
<value>MyEventQueueProvider</value>
 
<value>MyEventQueueProvider</value>
 
</library-property>
 
</library-property>
</source>
+
</syntax>
  
 
== Event Queues and Server Push ==
 
== Event Queues and Server Push ==
Line 327: Line 329:
 
By default, the comet server push is used. If you prefer to use the client-polling approach, just specify the following in <tt>WEB-INF/zk.xml</tt><ref>Like most ZK features, you can provide your own implementation if you like.</ref>.
 
By default, the comet server push is used. If you prefer to use the client-polling approach, just specify the following in <tt>WEB-INF/zk.xml</tt><ref>Like most ZK features, you can provide your own implementation if you like.</ref>.
  
<source lang="xml">
+
<syntax lang="xml">
 
<device-config>
 
<device-config>
 
<device-type>ajax</device-type>
 
<device-type>ajax</device-type>
 
<server-push-class>org.zkoss.zk.ui.impl.PollingServerPush</server-push-class>
 
<server-push-class>org.zkoss.zk.ui.impl.PollingServerPush</server-push-class>
 
</device-config>
 
</device-config>
</source>
+
</syntax>
  
 
<blockquote>
 
<blockquote>
Line 349: Line 351:
 
|}
 
|}
  
{{ZKComponentReferencePageFooter}}
+
{{ZKDevelopersReferencePageFooter}}

Revision as of 07:22, 17 September 2010

Overview

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 purpose of event queues are two folds:

  1. Simplify the many-to-many communication.
  2. Make the application independent of the underlining communication mechanism. The application remains the same, while the event queue can be implemented by use of Ajax, server push and even message queue.

Identification of an Event Queue

An event queue is identified by a name and a scope. The scope represents the visibility of an event queue. For example, a desktop-scoped event queue is visible in the same desktop, while application-scoped event queue is visible in the whole application.

Locate an Event Queue

You could locate an event queue by invoking one of the lookup method of EventQueues. For example,

<syntax lang="java"> EventQueues.lookup("myQueue"); //assumes the desktop scope EventQueues.lookup("anotherQueue", EventQueues.SESSION); EventQueues.lookup("anotherQueue", session); </syntax>

Notice that if you want to locate an event queue in a working thread (rather than an event listener), you have to use EventQueues.lookup(String, Session) or EventQueues.lookup(String, Application), depending on your requirement.

The Scope of an Event Queue

There are currently four different scopes: desktop, group, session and application. In additions, you add your own scope, such as message queue to communicate among several servers.

Name API Description
desktop EventQueues.lookup(String, String)

EventQueues.lookup(String, String, boolean)

The event queue is visible only in the same desktop.
group EventQueues.lookup(String, String)

EventQueues.lookup(String, String, boolean)

[since 5.0.4][ZK EE]

The event queue is visible only in a group of desktops that belongs to the same browser. It is formed if iframe or frameset is used. Some portal container might cause a group of desktops to be formed too. Unlike the session and application scope, the group scope doesn't require the server push, so the communication is more efficient.

session EventQueues.lookup(String, String)

EventQueues.lookup(String, String, boolean)
EventQueues.lookup(String, Session)
EventQueues.lookup(String, Session, boolean)

The event queue is visible only in the same session. The server push will be enabled automatically if it subscribes a session-scoped event queue.

Notice that the server push is disabled automatically if the current desktop doesn't subscribe to any session- or application-scoped event queue. Also notice that the locating and creating of an event queue and publishing an event won't start the server push.

When a server push is enabled, a working thread is instantiated and started. It means this feature cannot be used in the environment that doesn't allow working threads, such Google App Engine.

application EventQueues.lookup(String, String)

EventQueues.lookup(String, String, boolean)
EventQueues.lookup(String, Application)
EventQueues.lookup(String, Application, boolean)

The event queue is visible only in the whole application. The server push will be enabled automatically.

Notice that the server push is disabled automatically if the current desktop doesn't subscribe to any session- or application-scoped event queue. Also notice that the locating and creating of an event queue and publishing an event won't start the server push.

When a server push is enabled, a working thread is instantiated and started. It means this feature cannot be used in the environment that doesn't allow working threads, such Google App Engine.

Publish and Subscribe

Publish an Event

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

<syntax lang="java"> EventQueues.lookup("my super queue", EventQueues.APPLICATION, true)

 .publish(new Event("onSomethingHapping", null, new SomeAdditionInfo()));

</syntax>

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

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 one of the subscribe methods of EventQueue (returned by lookup). For example,

<syntax lang="java"> 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
   }
 });

</syntax>

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

An Example: Chat

Here is an example: chat.

<syntax lang="xml"> <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> </syntax>

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


Asynchronous Event Listener

By default, the subscribed event listeners are invoked the same way as invocation of the listeners for a posted event. They are invoked one-by-one. No two event listeners belonging to the same desktop will be invoked at the same time. In additions, it is invoked under an execution (i.e., Executions.getCurrent is never null). It is allowed to manipulate the components belonging to the current execution. For sake of description, we call them synchronous event listeners.

On the other hand, the event queue also supports the so-called asynchronous event listener, which is invoked asynchronously in another thread. There is no current execution available. It is not allowed to access any component. It is designed to execute a long operation without blocking the user.

A Long-Operation Example

A typical use case is to subscribe an asynchronous event listener for doing the long operation, and to subscribe a synchronous event listener to update the user interface. Then, when starting a long operation, an event is posted to the asynchronous event listener for processing. Since the invocation is asynchronous, the user can still interact with ZK smoothly. At the end of the invocation of the asynchronous event listener, it published an event to the synchronous event listener to update the result of the long operation back to the browser.

For example,

<syntax lang="xml"> <window title="test of long operation" border="normal"> <html><![CDATA[

  • Click the button it will start a long operation.
  • With this implementation, you can press the button again even if the long operation is still being processed

]]></html> <zscript> void print(String msg) { new Label(msg).setParent(inf); } </zscript> <button label="async long op"> <attribute name="onClick"><![CDATA[

  if (EventQueues.exists("longop")) {
    print("It is busy. Please wait");
    return; //busy
  }
  EventQueue eq = EventQueues.lookup("longop"); //create a queue
  String result;
  //subscribe async listener to handle long operation
  eq.subscribe(new EventListener() {
    public void onEvent(Event evt) {
      if ("doLongOp".equals(evt.getName())) {
        org.zkoss.lang.Threads.sleep(3000); //simulate a long operation
        result = "success"; //store the result
        eq.publish(new Event("endLongOp")); //notify it is done
      }
    }
  }, true); //asynchronous
  //subscribe a normal listener to show the resul to the browser
  eq.subscribe(new EventListener() {
    public void onEvent(Event evt) {
      if ("endLongOp".equals(evt.getName())) {
  	     print(result); //show the result to the browser
  	     EventQueues.remove("longop");
  	   }
  	 }
  }); //synchronous
  print("Wait for 3 seconds");
  eq.publish(new Event("doLongOp")); //kick off the long operation
 		]]></attribute>
	</button>
	<vbox id="inf"/>

</window> </syntax>

An asynchronous event listener is not allowed to access the desktop, but it is allowed to invoke EventQueue.publish(Event) to publish an event.

A Simpler Approach

While subscribing the asynchronous and synchronous event listeners separately is, as illustrated above, the general approach, the event queue provides a simple method to allow you register them in one invocation: EventQueue.subscribe(EventListener, EventListener). In additions, you don't need to publish an event at the end of the asynchronous event listener -- the synchronous event listener is invoked automatically.

<syntax lang="xml"> <window title="test of long operation" border="normal"> <zscript> void print(String msg) { new Label(msg).setParent(inf); } </zscript> <button label="async long op"> <attribute name="onClick"><![CDATA[

  if (EventQueues.exists("longop")) {
    print("It is busy. Please wait");
    return; //busy
  }
  EventQueue eq = EventQueues.lookup("longop"); //create a queue
  String result;
  //subscribe async listener to handle long operation
  eq.subscribe(new EventListener() {
    public void onEvent(Event evt) { //asynchronous
      org.zkoss.lang.Threads.sleep(3000); //simulate a long operation
      result = "success"; //store the result
    }
  }, new EventListener() { //callback
    public void onEvent(Event evt) {
      print(result); //show the result to the browser
  	   EventQueues.remove("longop");
  	 }
  });
  print("Wait for 3 seconds");
  eq.publish(new Event("whatever")); //kick off the long operation
 		]]></attribute>
	</button>
	<vbox id="inf"/>

</window> </syntax>

More About Scopes

Here is a summary of the differences.

desktop group session application
visibility desktop group session application
publish only in an event listener, or the current execution is available. only in an event listener, or the current execution is available. no limitation no limitation
subscribe only in an event listener, or the current execution is available. only in an event listener, or the current execution is available. only in an event listener, or the current execution is available. only in an event listener, or the current execution is available.
multi-thread Not used Not used Used (transparent) Used (transparent)
server-push Not used Not used Used (transparent) Used (transparent)

Extend Event Queue: Add a Custom Scope

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

<syntax lang="java"> 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);
 }

} </syntax>

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

<syntax lang="xml"> <library-property> <name>org.zkoss.zk.ui.event.EventQueueProvider.class</name> <value>MyEventQueueProvider</value> </library-property> </syntax>

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].

<syntax lang="xml"> <device-config> <device-type>ajax</device-type> <server-push-class>org.zkoss.zk.ui.impl.PollingServerPush</server-push-class> </device-config> </syntax>


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

Version History

Version Date Content
5.0.4 August 2010 The group scope was introduced to allow the communication among inline frames without Server Push (minimizing the network bandwidth consumption).



Last Update : 2010/09/17

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