8 Replies Latest reply on Mar 14, 2007 6:30 PM by sergeysmirnov

    Problems converting from AjaxAnywhere to Ajax4Jsf

    andrew.rw.robinson

      I finally have the time to do a little research of converting our project from AjaxAnywhere to Ajax4Jsf. For the most part, it is working, but there are some issues.

      Environment:
      Ajax4Jsf 1.1.0
      JBoss-Seam 1.1.6
      Facelets 1.0.14
      MyFaces 1.1.3 (patched)
      MyFaces Tomahawk 1.1.3 (patched)

      What I did to convert:
      Added an a4j:region at the root level of my template (entire page basically)
      Changed all <aa:zoneJSF id="..."> to <a4j:outputPanel id="...">
      Added global status with JavaScript hooks for the start & stop.

      Here is part of the code:

      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
      <t:document
       xmlns="http://www.w3.org/1999/xhtml"
       ...>
       <f:loadBundle basename="jsfmessages" var="msg" />
       <body
       onload="zfpOnLoad();"
       onresize="zfpOnResize();">
       <t:documentBody state="start" />
       <a4j:region id="zfpAjaxRegion" selfRendered="false" keepTransient="false">
       ... PAGE BODY HERE ....
       <a4j:status id="ajaxStatus" for="zfpAjaxRegion"
       onstart="onAjaxRequest();"
       onstop="onAjaxResponse();"
       startText=""
       stopText="" />
       </a4j:region>
       <t:documentBody state="end" />
       </body>
      </t:document>
      


      With this approach, the UILiteralText and UIText of facelets always cause:
      16:32:48,870 DEBUG [NavigationHandlerImpl] handleNavigation fromAction=#{contentMgr.load} outcome=loaded no matching navigation-case found, staying on current ViewRoot
      16:32:48,929 INFO [[/OSoft]] WARNING: Component _id366 just got an automatic id, because there was no id assigned yet. If this component was created dynamically (i.e. not by a JSP tag) you should assign it an explicit static id or assign it the id you get from the createUniqueId from the current UIViewRoot component right after creation!


      Also it seems like that the view may be getting re-created each request (but not sure).

      If I change the region to:
      <a4j:region id="zfpAjaxRegion" selfRendered="true" keepTransient="true">

      Then all my "zones" do not get reloaded, and the area is left blank and there are some errors about not finding images (perhaps a problem with the message bundle loaded with a plain loadBundle?).

      I cannot break the regions up at this time as it is too much effort at this time, so the one region for the entire page is unfortunately our best option at the moment.

      Any ideas?


        • 1. Re: Problems converting from AjaxAnywhere to Ajax4Jsf

          First advice - drop the a4j:region out until you realize when it is helpful

          Seconds, f:loadBundle is not a jsf tag actually, it is just loads a map to the request scope. As soon as each Ajax request has its own request scope, the data loaded during the first request just gone already. a4j:loadBundle is created to solve this problem

          Third, Ajax4jsf and AA have not a symmetric architectures. So, I am really pessimistic that magic will happen if you replace ones tag with another. Let's just dig into the concrete page to see what should be done

          • 2. Re: Problems converting from AjaxAnywhere to Ajax4Jsf
            andrew.rw.robinson

            Without an a4j:page or an a4j:region, how am I to execute the JavaScript code that I need on a new AJAX request and when the AJAX response is complete? Our application depends on these hooks for a lot of code to be run.

            a4j:loadBundle did help some, but that was not my issue. The selfRendered is definitely not working.

            Perhaps, I can describe our layout. We have a very complex web client. Our site is designed like a thick-client, complete with toolbars, and a panel with functionality that changes based on the current page. Beyond that we have many dialogs that also have AJAX areas that are loaded modally using DIV tags and z-index orders. Our layout consists of two main areas, a working-area (left) and an action pane (small-right, like a wizard). It looks something like:

            ----------------------
            | | B |
            | |-----|
            | | |
            | A | C |
            | | |
            | | |
            | |-----|
            | | D |
            ----------------------

            Most refreshing takes place in "A" and "C". When A is updated, D is updated as well (it provides context sensitive help). When C is updated, B is also (it provides navigation help for C). Links from A or C may lead to navigation in A and/or C based on custom navigation rules.

            We use a custom navigation handler to navigate these two areas. Sometimes only the wizard is navigated, sometimes both are. Here is an example from our custom navigation xml:
             <action-view-navigation>
             <parent-view-id>
             /pages/insight/dashboard/radarChart.xhtml
             </parent-view-id>
             <action-view-id>
             /pages/insight/dashboard/actionPane/radarChartAp.xhtml
             </action-view-id>
             <result>
             <from-outcome>returnToDashboard</from-outcome>
             <to-parent-view-id>
             /pages/insight/dashboard/landing.xhtml
             </to-parent-view-id>
             <redirect/>
             </result>
             </action-view-navigation>
            


            So, links in the action pane (C) often submit data from within just C, but not always, sometimes they submit data from A. As mentioned earlier, we have a lot of JavaScript that is run on the start and completion of AJAX requests, so I need a global hook, or at least one a4j:status that is always used. ~90% of all AJAX is through PPR, but some is through JBoss-Seam remoting calls.

            Under AjaxAnywhere, we had many zones and we had custom controls (extending the MyFaces controls) that added AJAX zones to the responses programmatically.

            The main page is a template that includes many other facelet files (so to render one page, somewhere around 10 xhtml files are used if not more).

            AjaxAnywhere was working fine, but it was slow and very hard to extend (I had to make a lot of hacks) especially since everything in it is mostly private and not protected.

            The one region with the AJAX zones replaced by output panels seems to be working, except for the UILiteralText and UIText automatic ID errors.

            Do you foresee issues with this with Ajax4JSF or do you think it can handle it?

            • 3. Re: Problems converting from AjaxAnywhere to Ajax4Jsf
              andrew.rw.robinson

              Okay, I should have tried a little more before my post. Without the region, the status is being found somehow (perhaps just its presence in the page?) and I am not getting the duplicate ID errors. It looks like I am good, and can just add regions where I want the "subForm" type of functionality of MyFaces Sandbox (where only certain values are sent instead of the entire page).

              • 4. Re: Problems converting from AjaxAnywhere to Ajax4Jsf
                andrew.rw.robinson

                I am now having a lot of issues with Seam. Even though I have the seam redirect filter and the phase listener setup correctly, I am loosing my conversation continuously. It is as if Seam is not restoring the conversation correctly for calls that are going through Ajax4Jsf. I have been looking into it for a few hours now but I have been unable to find why the conversation is not being saved.

                I have debugged into the Seam code and I see that the long running conversations are being created and stored into the view, but subsequent requests do not seem to be using those conversation IDs, and instead falling back onto a temporary conversation.

                The URL never seems to have the conversationId parameter in it either. I setup a4j and seam from the developer's guide.

                • 5. Re: Problems converting from AjaxAnywhere to Ajax4Jsf
                  andrew.rw.robinson

                  It looks like there is a bug in the a4j developers guide. The developers guide shows the Seam redirect filter outside (before) the A4J filter. If this is done, it looks like the Seam filter is never called for a redirect during an A4J request. Once I moved the Seam filter below the A4J filter I started getting the conversation propagating.

                  <filter>
                   <display-name>Ajax4jsf Filter</display-name>
                   <filter-name>ajax4jsf</filter-name>
                   <filter-class>org.ajax4jsf.Filter</filter-class>
                   </filter>
                   <filter-mapping>
                   <filter-name>ajax4jsf</filter-name>
                   <servlet-name>Faces Servlet</servlet-name>
                   <dispatcher>REQUEST</dispatcher>
                   <dispatcher>FORWARD</dispatcher>
                   <dispatcher>INCLUDE</dispatcher>
                   </filter-mapping>
                   <filter>
                   <filter-name>SeamRedirectFilter</filter-name>
                   <filter-class>org.jboss.seam.servlet.SeamRedirectFilter</filter-class>
                   </filter>
                   <filter-mapping>
                   <filter-name>SeamRedirectFilter</filter-name>
                   <servlet-name>Faces Servlet</servlet-name>
                   <dispatcher>REQUEST</dispatcher>
                   <dispatcher>FORWARD</dispatcher>
                   <dispatcher>INCLUDE</dispatcher>
                   </filter-mapping>


                  • 6. Re: Problems converting from AjaxAnywhere to Ajax4Jsf

                    Documentation says that Ajax4jsf filter should be first. Seam-gen also put it at the beginning of the web.xml. Where is the bug in the documentation ?

                    • 8. Re: Problems converting from AjaxAnywhere to Ajax4Jsf

                      Ok. This page will be corrected