Last week I was warned by one of my users about the slow performance of web pages generated by my application. At first, I got annoyed by this comment, but then I thought that it was worth the effort trying to find the cause for this problem.

Get busy! I went to this person's desk and asked her about the problem. Once she explained me her feelings about the application I realized that everything was due to the fact that the page was being always completely refreshed; so the response time between the client and the server was not really the problem disturbing her, but the page being refreshed every time (with the annoying flicker effect, as she told me).

Then, a lightbulb turned on over my head... "What about that framework I read the other day... Ajaxanywhere, I think" - I told to myself. Thought and done. I downloaded the latest version, which now integrates with JSF, and began to do some testing.

I must admit I really got frustrated at first, due to the fact that there's a lack of documentation in the web regarding the integration with JSF (only a basic example where everything is "ajaxed"). I was not able to make it work, and I posted several messages in the forum to guess what was happening. Answers from other users and from one of Ajaxanywhere's creator, Vitaliy Shevchuk, pointed me the direction to go... and I finally solved all my problems and made it work like a charm!

So here I want to share my knowledge with you (so you can save some headaches as I couldn't do). Steps to take:

1) Download latest version of Ajaxanywhere (currently 1.1.0.6). This jar has a faces-config.xml file inside the META-INF folder which you must know about (in case strange errors appear in your application, check it is there).

2) Put it the JAR in your application's classpath and edit web.xml

3) Define a new filter in your filter chain for the AAFilter class: this filter is a wrapper for the ServletResponse object. But be careful, the order in which you declare this filter will make it work or not. Let me explain you the problem:

As comented, Ajaxanywhere uses a ServletResponse wrapper to handle AJAX requests. If used in combination with other web filters which also wrap the ServletResponse object, it causes conflicts. I haven't investigated deeper this problem, but it seems it has to be with the fact that the AAFilter receives the already wrapped ServletResponse, and sometimes calls its writer, which is null when being wrapped. I detected this problem with the Extension filter used by Myfaces (but maybe the problem is not with the AAFilter, but with the ExtensionFilter itself). The solution is just to define the AAFilter before the Myfaces extension filter, and vualĂ !

4) As I'm using Tiles with Myfaces, I will explain my particular scenario, which can be easily extrapolated to your particular scenario.

Take your root template. Mine is a typical one made of 4 sections: header, left menu, center, and footer. In my case, only the center zone changes on every request (obviously all zones are dynamically generated, but you know what I'm saying). So I decided to "ajaxifize" the center zone. Take a look at this code snippet:

<f:facet name="body">

</f:facet>

As you can see, you will need to indicate the aa.js file somewhere available to each of your "ajaxed" pages: this JS file is the heart of the matter. It creates the Ajaxanywhere object, and does all the replacing needed to capture AJAX requests which results in all this "magic".

Then, the ZoneJSF tag will allow you to mark the "to-be-ajaxed" zone, so Ajaxanywhere can know about it.

Once the whole center zone of the application has been marked as "ajaxed", it's time to give the particular information for each page (as maybe not always you want to "ajaxify" the whole page ;-)

5) Always give an ID to your HTML form --> <h:form id="ajaxedForm" .... > (notice that if this form is within another component, you should give the complete ID in the terms of id1:id2:id3... - we don't have the forceId for the form tag)

6) In case you only want some buttons to make an AJAX request, give them also an ID (in this case, yes, you can use the forceId attribute, which help to reference them after) --> <t:commandButton id="ajaxButtonSave" ... />

7) At the very end of the page, insert a piece of JS code to inform Ajaxanywhere about your intentions with AJAX requests. Let's see a snipped JS code (by the way, I use HTMLin from JSF Tutorials - a modified version by me indeed):

<htm:script type="text/javascript">

</htm:script>

First notice the getZonesToReload callback. Here you must tell Ajaxanywhere what zone to reload (if any) based on information of the submitted button. In this example, you tell Ajaxanywhere to reload the zone named "ajaxZone", just in case a submit button which ID starts with "ajaxButton" is clicked (a little trick not to have to ask for every submit button in your page).

If you don't want to Ajaxanywhere to do its role, the simply return null.

Then, below you find some extended information for Ajaxanywhere about the exact submit buttons you want to be processed by Ajaxanwhere. I experienced some problems here because I was not passing the array of buttons to the substituteSubmitButtonsBehaviour function (I supposed that not using that argument would cause all the buttons to be considered). But now I'm not sure whether this was really a problem, or a side-effect of the problem I had with the AAFilter (see above).

If you, like in my scenario, want evertyhing to be "ajaxed", from submit buttons to data scroller links, then simply return always "ajaxZone"; but you'll have to continue using the buttons array for the substituteSubmitButtonsBehavior(true, elements):

ajaxAnywhere.getZonesToReload = function(url, submitButton) {

}

8) Review your navigation configuration for JSF, as no redirects are allowed for Ajaxanywhere (if it finds them, then a normal sendRedirect request will be handled, loosing all your "ajaxification".

9) Just for the sake of testing if everything's working, add to your main root page, this JS code snippet taken from the examples given by Ajaxanywhere:

<span id=cnt>0</span> seconds since last page refresh. <script>

</script>

As can be seen, this JS code snippet will show a counter on top of every of your pages, so you can see when the page gets really refreshed (it shouldn't be never from now!!!!!!).

So this are all the steps to take when working with Ajaxanywhere together with Myfaces. Notice that you will say goodbye to those annoying problems regarding the user clicking the back button once and again ;-)

Integrating_Ajaxanywhere (last edited 2009-09-20 23:01:48 by localhost)