Task: Update the content of the page if the content is changed on server-side, in other words – show the most-fleshiest content to the user.
The most simple solution that comes to mind is to use <a4j:poll/> component which will periodically call some action which will load fresh content and re-render the content on the page. But that not very optimal since you will create the content on server-side and send it back to the client each time you execute the polling request.
a4j:push component looks like the one who was created to avoid that every-time re-rendering. Here is what demo-site says about it:
The main difference between a4j:push and a4j:poll components is that a4j:push makes request to minimal code only (not to JSF tree) in order to check the presence of messages in the queue. If the message exists the complete request will be performed. The component doesn’t poll registered beans but registers EventListener which receives messages about events.
Looks very promising, but in reality it’s a kind of tricky to integrate it with your real application because some code on the server side should execute something like that:
synchronized (listener) {
listener.onEvent(new EventObject(this));
}
But, to do so we should have something in background which will periodically check for updates and generate the events and send them to our listener. Starting a Thread is not good option since we have to worry about the way to stop it, as well who start Thread’s in web-apps ? @Asynchronous task is better one option, but still has the same issues – we have to worry about it lifecycle and stop updates at some point (user may leave the page or close his browser) so we actually has no good way to manage the lifecycle of launched background tasks.
At first I try to use a4j:push with action=”#{myActionBean.checkForUpdates}” but action is not called by a4j:push, it just checks if he has new events and only if it has such – execute action and do reRendering (all that JSF lifecycle)
But it looks like combination of a4j:poll and a4j:push could work here in that way.
- a4j:poll execute an action which check for updates in the data and fire an Event if they are exists
- a4j:push periodically check for new events and re-render content on new event
So, a4:poll play role of assistant, actually it’s like the managed background task on the serverside which periodically poll for the data, but it’s live on the client-side, so in case user will close his browser – server will not execute the requests to datsource anymore.
I call it a “conditional re-rendering”. Here is the JSF code I use to make it working.
<a4j:form id="pollForm" eventsQueue="queue"> <a4j:region id="pollRegion" selfRendered="true" renderRegionOnly="true"> <a4j:poll action="#{myActionBean.checkForNewData}" id="datasetChangedChecker" interval="#{myActionBean.pollInterval}" ajaxSingle="true" limitToList="true" immediate="true" bypassUpdates="true"> </a4j:poll> </a4j:region> <!-- the function below execute an ajax-call to server which enable the push and re-render <a4j:push /> to start it --> <a4j:jsFunction name="startPushUpdates" action="#{myActionBean.startPushUpdates}" reRender="dataSetReRendererPush" ajaxSingle="true"/> </a4j:form> <a4j:form id="pushForm" eventsQueue="queue"> <a4j:push id="dataSetReRendererPush" ajaxSingle="true" eventProducer="#{myActionBean.addUpdatesListener}" interval="#{myActionBean.pollInterval}" reRender="contentPanel,datasetChangedChecker,dataSetReRendererPush" enabled="#{myActionBean.pushUpdatesStarted}" /> </a4j:form> <a4j:outputPanel id="contentPanel"> <!-- Updated content goes here --> </a4j:outputPanel>
The only issue here is that we have to ensure that a4j:push is executed AFTER a4j:poll so action called in a4j:poll will generate the event before we will execute the a4j:push. Unfortunately we don’t have the way to manage it in JSF with attributes, but that could be done with help of postponed launching of a4j:push (with some reasonable gap which should be bigger than time required to execute check for data-updates)
The postponed a4j:push execution is achieved with enabling it with ‘enabled=”#{myActionBean.pushUpdatesStarted}”‘ attribute and calling the javascript with timeout
jQuery(document).ready(function() { window.setTimeout(startPushUpdates, 5000); });
so, having all that JSF code and MyActionBean.java we have quite optimal solution
I didn’t include java-code here but I believe if you read this line than you most probably got the idea and capable to implement it on your own
Future notes: it looks that IceFaces propose their own vision of ajax-push and it’s not based on periodical polling of server from server-side.
As well they have Direct-To-Dom rendering which looks like much more optimal way of re-Rendering. Haven’t tried it yet, but for me it looks ajax-push at IceFaces and re-rendering is a more optimal solution.
It would be nice to hear from the ones who try both RichFaces and IceFaces ajax push approaches.
BTW, just find that RichFaces team prepare some updates to a4j:push in RF-4.0
With this release we have added a few new components, redesigned a4j:push to use advanced Comet implementation and JMS servers
