In one of the previous post I show an example of how a4j-push and a4j-poll were used together. It help to solve several things and optimize code/requests a bit. But in reality it was not “real” push – it just execute periodic lightweight requests (ping server) and asks him – “do you have something new for me ?”. It has serious cons:
- number of requests to server is linearly depends on the number of browser windows opened, so more simultaneous users we have connected to the server – the higher loading it will have. So, actually server can be just “overloaded” by that simple requests
- with regards to previous point we have to increase the period between requests (to make less impact on the server) – but it is not good for user experience, since it will see the updates with a larger delay
What I expect from Push is that server itself initiate the push-notification to the client. So it does that right at the moment he have to say something new to the client. There is no need to say that such server-to-client communication open a lot of great features and possibilities to web apps.
Here is the diagram from the IcePush docs
It probably not that visible, but in short – “It works great”. IcePush client (javascript) send the list of subscribed topics he want to listen, server genberate the unique ID for him, after that client establish the connection with the server which listen for response from server. Server will notify the client once he will have notifications to send to him. Client call the callback function in which you may do anything (you may call any javascript function as well the a4j:jsFunction if you want to do something through JSF)
As a result, the client browser will be notified right after the event will occur on the server. It will be immediately pushed to the client and after that client is free to do anything he want to react to the notification.
The good thing is that it’s fairly easy to integrate IcePush technology with existent java-web application. The requirement here is just to have a servlet container.
In my case it was existent Richfaces/Seam-2/JSF application (I was afraid that IcePush may conflict with Richfaces, but it is not the case, IcePush can be integrated completely transparent to the JSF/Seam application, since it integrates on Servlet level).
IcePush has a number of ways to integrate, but I’ve chooses the jQuery Integration
You can find all the necessary information about IcePush integration here. I will show the example of integration of IcePush with existent web-application (Seam/Richfaces). I think the integration could be the same for GWT or any other app which is deployed in the Servlet-Container.
- Downloaded jQuery integration package
- Added the icepush.jar to the WEB-INF/lib
- Added the following definition to web.xml
<servlet> <servlet-name>icepush</servlet-name> <servlet-class>org.icepush.servlet.ICEpushServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>icepush</servlet-name> <url-pattern>*.icepush</url-pattern> </servlet-mapping>
- on the web-page initialized the IcePush with
//loaded required scripts <script type="text/javascript" src="#{request.contextPath}/code.icepush"></script> <script type="text/javascript" src="#{request.contextPath}/js/icepush/jquery.icepush.js"></script> //initialized the icepush client <script type="text/javascript"> var myPushNotificationCallback = function(){ //do something on push callback. //it may be ajax request to the server based on jQuery-ajax or a4j:jsFunction, whatever //for simplicity I will just call alert alert("Push notification arrived"); } jQuery(document).ready(function () { //set the uriPrefix so icepush will make requests to the correct URL ice.push.configuration.uriPrefix = '#{baseAppPath}/'; //register the icepush client, based on the user-primary-key jQuery.push.listenToGroup("myPushGroup_UsR_#{loggedInUser.pk}", myPushNotificationCallback); }); </script>
- On the server side call the IcePush in the places I want to send notification (as a reaction to some event)
We can call method pushToUserList directly, but I wrapped it so it become asynchronous call, so it don’t slow-down the http-request processing (it may send notifications to several clients and as long it is network operation, it’s may cause a significant delay)@Name("pushAsyncExecutor") @Scope(ScopeType.APPLICATION) public class PushAsyncExecutor implements Serializable { private static Log log = Logging.getLog(PushAsyncExecutor.class); @Asynchronous @SuppressWarnings("unused") public QuartzTriggerHandle pushToUserList (@Expiration Date when, String pushGroup, Collection<Long> userIds) { pushToUserList(pushGroup, userIds); return null; } public static void pushToUserList(String pushGroup, Collection<Long> userIds) { log.trace("Started pushToUserListImpl"); try { PushContext pushContext = PushContext.getInstance(SeamComponentFactory.getServletContext()); for (Long userId : userIds) { pushContext.push(getUserNotificationGroupName(pushGroup, userId)); } } catch (Throwable ex){ log.error("Failed to push updates", ex); } log.trace("Finished pushToUserListImpl"); } /** * this method generate the push-id for the userId and "pushGroup" */ private static String getUserNotificationGroupName(String pushGroup, Long userId) { return new StringBuilder(pushGroup.length() + 16).append(pushGroup).append("_UsR_").append(userId).toString(); } }
