I'm quite new to ZK and the concept of event queues. What I'm trying to do is run a long operation in the server and update the UI of the progress in real-time, instead of blocking the UI while the long operation runs. So for example, if there are 3 tasks (this number is not fixed) to do in the long operation, it should update the UI by updating a "log trace" textbox and a progress bar that same number of times.
My code structure looks like:
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())) {
//simulate a long operation
doTask1();
eq.publish(new Event("printStatus", null, "Task1 completed."));
doTask2();
eq.publish(new Event("printStatus", null, "Task2 completed."));
doTask3();
eq.publish(new Event("printStatus", null, "Task3 completed."));
result = "success";
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("printStatus".equals(evt.getName())) {
printToTextbox((String)evt.getData()); //appends value to the log textbox
}
if ("endLongOp".equals(evt.getName())) {
print(result); //show the result to the browser
EventQueues.remove("longop");
}
}
}); //synchronous
eq.publish(new Event("doLongOp")); //kick off the long operation
This didn't work. All the printStatus events happen AFTER the long operation is finished. The only thing this fixed is that the UI is not getting blocked whenever the long operation runs. I was assuming that since the long operation thread is asynch, it will still send the events to the queue and the synch UI thread will be able to handle them as soon as they happen. So after several hours of trial and error, and after noticing that the server push is NOT used in a desktop scope queue, I changed the scope to application and explicitly enabled server push:
EventQueue<Event> eq = EventQueues.lookup("longop", EventQueues.APPLICATION, true);
desktop.enableServerPush(true);
it just worked. I know that ZK CE only has the client polling, which is fine for my use case. But why is it that in desktop scope, server push is not used? How can we accomplish such task if we don't want the queue to be shared application-wide? I want each desktop to have their own event queue.
It might also be worth mentioning that I have enabled the event thread. And that I tried disabling it but the result was the same. So it looks to me that it doesn't affect my problem.
Any help is greatly appreciated.
PS: I am using ZK CE 7.0.3
There are many possible solutions for your situation.
Please take a look at this section of ZK documents.
You can use the piggyback, but when the user doesn't do anything, you also have no updates on the screen.
So I suggest go for the echoEvents
.
So you have to do task 1, update screen and echo onTask2
.
In OnTask2
do your stuff, update screen and echo onTask3
.
And for onTask3
do task 3 and update the screen.
Edit :
The scope doesn't have to be application scope. The application scope event queue has already server push build in (And I believe Session also). For the desktop you have to do it manually(or other approach). (your desktop.enableServerPush
isn't needed for application scope)
If you want to work simple with the eventqueue look here.
Use the EventQueue.subscribe(EventListener, EventListener)
what is the async and sync Eventlistener.
The only thing is, in the Sync listener you need to call your Task 2 with again the sync listener for refreshing GUI and start task 3 in same way.
The other way is passing the desktop to the async listener so you can enable (and disable) server push there.(async listener never has reference to desktop, it's a complete new thread)