10 Replies Latest reply on Jan 6, 2010 1:21 PM by Francisco Jose Peredo Noguez

    Proposal: a4j:rerenderCache to cache rerenders.

    Francisco Jose Peredo Noguez Master

      When I do HTML/AJAX coding "by hand" (or using DWR or SeamWebRemoting) I can do something I have found to me almost impossible to do with Richfaces: AJAX Caching.

      Here is an example (this also happens with chained selectOneMenus, tree expanding, tab panel switching, and many other places, but this is the most simple meaningful example I could think of:

      Lets say I have a an input box, where I can write any number, and an an output text that shows its text representation, so for example if I write "1" in the outputext I see "One", if I write "2" in the inputext I see "Two".

      The code would look like this:

      <h:inputText value="#{numberToLetterConverter.number}" >
       <a4j:support event="onblur" action="#{numberToLetterConverter.convert}"
       reRender="text"/>
      </h:inputText>
      
      <h:outputText id="text" value="#{numberToLetterConverter.text}" />
      


      Now, after I have written: 1, and 2, and 3, if I write 1 again, it will go back to the database to fetch is "letter" representantation, but it has already done that! If I were coding this with plain JS and AJAX, I could have kept some kind of "dictionary" collection that would save me the network roundtrip.

      How to achieve that in Richfaces?

      Well, what do you think about this (note the <a4j:rerenderCache> tag and the id of the inputText):

      <h:inputText id="input" value="#{numberToLetterConverter.number}" >
       <a4j:support event="onblur" action="#{numberToLetterConverter.convert}"
       reRender="text"/>
      </h:inputText>
      <a4j:rerenderCache cacheBy="input.value">
       <h:outputText id="text" value="#{numberToLetterConverter.text}" />
      </a4j:rerenderCache>
      


      the a4j:rerenderCache would keep its own dictionary of values, matching what is going to be rerendered with the las value (or comma separated values) that are specified in its cacheBy property, if the value is one that with which a re-render has already been triggered, the it will not go fecth the xhtml fragment to the database, it would just use a local javascript based cache.

      What do you think? Is it doable? (I have almost zero experience building JSF controls, the way I want to build this might be flawed, but I think it is an idea with a good spirit, that could help making richfaces performance a lot better when compared with built by hand AJAX aplications)

        • 1. Re: Proposal: a4j:rerenderCache to cache rerenders.
          Francisco Jose Peredo Noguez Master

          Another (perhaps even more flexible idea?) could be to leave the decision of going the server to a javascript function:

          <h:inputText id="input" value="#{numberToLetterConverter.number}" >
           <a4j:support event="onblur" action="#{numberToLetterConverter.convert}"
           reRender="text"/>
          </h:inputText>
          <a4j:rerenderCache cacheIf="return someJavascriptFunction();">
           <h:outputText id="text" value="#{numberToLetterConverter.text}" />
          </a4j:rerenderCache>
          
          


          In this case, the javascript function could go and extract the value of the inputText control, and then use a caching api specifically created to support this scenario, where it could find the xhtml fragments that have been cached before for a particular key, and return true if there is a cached fragment for that input value, and false if there isn't (this would have the added advantage the decision would be programmatic and complex client side logic could be used to decide if the AJAX call is triggered)

          • 2. Re: Proposal: a4j:rerenderCache to cache rerenders.
            Kerdudou Ronan Apprentice

            I had the idea of ajax lazzyLoading 2 years ago, that would enable to have about the same but for part of the page.
            For exemple a tabPanel where the tab loads in ajax when you click on it, and if you go back to previous tab, it wouldn't request it again to the server...
            but yet nobody developed it.

            • 3. Re: Proposal: a4j:rerenderCache to cache rerenders.
              Francisco Jose Peredo Noguez Master

              Hi!
              Thanks for answering

              "RonanKER" wrote:
              I had the idea of ajax lazzyLoading 2 years ago, that would enable to have about the same but for part of the page.
              For exemple a tabPanel where the tab loads in ajax when you click on it, and if you go back to previous tab, it wouldn't request it again to the server...
              but yet nobody developed it.


              Sounds very similar to what I am proposing in this post.
              Did you create a JIRA with your proposal? (or do you have a link to your original forum post? I would be happy to read and see if we can come up with a generic solution for this kind of problem)

              I while ago I proposed something less ambitious a while ago in RF-7280 a dynamic way to switch between client side and ajax mode in some controls, but it also did not draw much atention. I am hopping that his more ambitious idea seems more interesting to implement (but since I am no expert in the internals of Richfaces I am not even sure it is possible, do you think it is doable?)

              • 4. Re: Proposal: a4j:rerenderCache to cache rerenders.
                Francisco Jose Peredo Noguez Master

                Well, I hope it is doable, here is the new JIRA: RF-7906

                • 5. Re: Proposal: a4j:rerenderCache to cache rerenders.
                  Francisco Jose Peredo Noguez Master

                  Maybe creating a new tag is not the right way to approach this problem, maybe it is a better idea to modify existing tags, something like:

                  <h:inputText value="#{numberToLetterConverter.number}" >
                   <a4j:support event="onblur" action="#{numberToLetterConverter.convert}"
                   reRender="text" cacheReRenderByValue="true"/>
                  </h:inputText>
                  
                  <h:outputText id="text" value="#{numberToLetterConverter.text}" />
                  
                  


                  Please note the new cacheReRenderByValue in a4j:support it would go an check if a rerender has been triggered before by the inputText with the same value, and if it did it would to the AjaxCache (instead of all the way to the server) to obtain the changes that were stored there the last time the inputText had that value.




                  • 6. Re: Proposal: a4j:rerenderCache to cache rerenders.
                    Francisco Jose Peredo Noguez Master

                    Another interesting option could be to have a global cache anonymous cache (for cases when the default ajax caching configuration specified in web.xml is good enough) and specific named caches (using areRenderCache tag) when customization is needed:


                    
                    <a:reRenderCache id="cache1" duration="60000"/>
                    
                    <h:inputText id="inputText" value="#{numberToLetterConverter.number}" >
                     <a4j:support event="onblur" action="#{numberToLetterConverter.convert}"
                     reRender="text" cacheReRenderByValue="true" rerenderCache="cache1"/>
                    </h:inputText>
                    
                    <h:outputText id="text" value="#{numberToLetterConverter.text}" />
                    
                    


                    The duration attribute would be used to specify that we want to have the cache "cache1" evicted after 1 minute.

                    It would also allow the developers to, for example, programmatically evict the client side cache using javascript code using the id to identify a particular cache by name:

                    
                    function someJSFunction(){
                    
                    ajaxCacheManager.getCacheById('cache1').evictAll();
                    
                    }
                    
                    
                    


                    or even evict only the values for some controls:

                    
                    function someJSFunction(){
                    
                    ajaxCacheManager.getCacheById('cache1').evictByControlId('inputText');
                    
                    }
                    
                    
                    



                    Any comments? Is anything like this even remotely doable for Richfaces 4.0? Or this is just a really wild dream?

                    • 7. Re: Proposal: a4j:rerenderCache to cache rerenders.
                      Ilya Shaikovsky Master

                      Really interesting feature to review and think about.. Thanks guys for your efforts! I could add some additional thoughts :

                      about first idea with the renderCache which wraps the elements for reRender.

                      * Idea which defined in first post snippet not looks very native for me because support knows nothing in this case about the caching of element to reRender. (caching defined at the control for caching). And these elements could be placed even in different templates. So maintaining could also became very difficult for end developer.

                      about the separate attribute sample snippets.

                      * cache result could be depended from not the only the input as it shown in snippet. As you defining not ajaxSingle support - then the population of new pages part performs by processing all inputs/selects inside region. So the cached result could depends on some subset of inputs/selects values.
                      * suppose we using a:command* or just support for event="onclick" or something like that.. In that case we would have additional question what should be the condition of cache usage/request firing.

                      *conversation to be continued... :)

                      In general talking about the problem with fetching some data from db and ajax. Seems it's clear for all of us that your model on server side should have some caching itself. Ajaxified application communicate with server much more frequently in difference with traditional one. So it seems bad idea in general that the db call could be required for every call if the client side caching not present.

                      But again, this idea seems very good one and we will discuss it also internally and think about possible solutions with you guys. B.t.w. concrete components (like tabPanel) caching funnctionality already under our discussions for 4.x implementation.

                      • 8. Re: Proposal: a4j:rerenderCache to cache rerenders.
                        Francisco Jose Peredo Noguez Master

                         

                        "ilya_shaikovsky" wrote:
                        Really interesting feature to review and think about.. Thanks guys for your efforts! I could add some additional thoughts :

                        about first idea with the renderCache which wraps the elements for reRender.

                        * Idea which defined in first post snippet not looks very native for me because support knows nothing in this case about the caching of element to reRender. (caching defined at the control for caching). And these elements could be placed even in different templates. So maintaining could also became very difficult for end developer.


                        Yes, I am starting to like the second option more.


                        about the separate attribute sample snippets.

                        * cache result could be depended from not the only the input as it shown in snippet. As you defining not ajaxSingle support - then the population of new pages part performs by processing all inputs/selects inside region. So the cached result could depends on some subset of inputs/selects values.


                        So maybe something like:
                        <h:inputText id="inputText" value="#{numberToLetterConverter.number}" >
                         <a4j:support event="onblur" action="#{numberToLetterConverter.convert}"
                         reRender="text" cacheReRenderBy="inputText.value"/>
                        </h:inputText>
                        
                        <h:outputText id="text" value="#{numberToLetterConverter.text}" />
                        


                        In the case of multiple controls affecting the rerendering it coud be something like (in this example the values of 2 inputTexts are added in the server and presented in an output text, but we do not want to go to the server if we have already written those numbers before):

                        <h:inputText id="inputText1" value="#{numberToLetterConverter.firstNumber}" >
                         <a4j:support event="onblur" action="#{numberToLetterConverter.convert}"
                         reRender="sumResultText" cacheReRenderBy="inputText1.value,inputText2.value"/>
                        </h:inputText>
                        +
                        <h:inputText id="inputText2" value="#{numberToLetterConverter.secondNumber}" >
                         <a4j:support event="onblur" action="#{numberToLetterConverter.convert}"
                         reRender="sumResultText" cacheReRenderBy="inputText1.value,inputText2.value"/>
                        </h:inputText>
                        =
                        <h:outputText id="sumResultText" value="#{numberToLetterConverter.text}" />
                        


                        The cacheReRenderBy attributes makes it possible to list all the component properties that will be used to search in the client side ajax cache


                        * suppose we using a:command* or just support for event="onclick" or something like that.. In that case we would have additional question what should be the condition of cache usage/request firing.


                        Perhaps with some way to reference the current state of other controls in the page... I need to think more about this...


                        *conversation to be continued... :)

                        In general talking about the problem with fetching some data from db and ajax. Seems it's clear for all of us that your model on server side should have some caching itself. Ajaxified application communicate with server much more frequently in difference with traditional one. So it seems bad idea in general that the db call could be required for every call if the client side caching not present.


                        I agree with that, but in web applications when the app server and the computers with the webbrowser are connected by a slow network, having a cache in the server side, in my experience, helps but only a little. I mean, the performance is of course better than with no cache, but applications built with "hand made AJAX" perform a lot better, precisely because it is relatively easy to cache stuff in the client side.


                        But again, this idea seems very good one and we will discuss it also internally and think about possible solutions with you guys.


                        It would be really nice if richfaces 4.0 could have client side caching, AFAIK it is a very important feature that right now is only available if you are willing to write javascript code (and a feature that makes javascript/flex/silverlight based sites faster than those implemented with JSF or ASP.NET components)

                        B.t.w. concrete components (like tabPanel) caching funnctionality already under our discussions for 4.x implementation.


                        That is great!


                        • 9. Re: Proposal: a4j:rerenderCache to cache rerenders.
                          Francisco Jose Peredo Noguez Master

                           

                          "ilya_shaikovsky" wrote:

                          * suppose we using a:command* or just support for event="onclick" or something like that.. In that case we would have additional question what should be the condition of cache usage/request firing.


                          Well, for the case of a:command I think that a cacheReRenderBy attribute could also be the answer. Lets go back to the example of of 2 inputTexts are added in the server and presented in an output text, but this time, lets call the ajax rerender with a button, the code would look like this:

                          <h:inputText id="inputText1" value="#{calculator.firstNumber}" >
                          </h:inputText>
                          +
                          <h:inputText id="inputText2" value="#{calculator.secondNumber}" >
                          </h:inputText>
                          =
                          <a:commandButton value="Sum" action="calculator.sum()" rerender="sumResultText"
                          cacheReRenderBy="inputText1.value,inputText2.value"/>
                          
                          <h:outputText id="sumResultText" value="#{calculator.result}" />
                          
                          


                          This way, the rerender triggered by the a:commandButton is cached using the values ofthe inputTexts as the keys to retrieve the rerender fragment from the client side cache.

                          If we had several buttons, acting over the same inputs, then we could add the id of the button to the cacheReRenderBy. Example:

                          
                          <h:inputText id="inputText1" value="#{calculator.firstNumber}" >
                          </h:inputText>
                          
                          <a:commandButton id="buttonSum" value="Sum (+)" action="calculator.sum()" rerender="resultText"
                          cacheReRenderBy="buttonSum.id,inputText1.value,inputText2.value"/>
                          
                          <a:commandButton id="buttonSubs" value="Substract (-)" action="calculator.substract()" rerender="resultText"
                          cacheReRenderBy="buttonSubs.id,inputText1.value,inputText2.value"/>
                          
                          
                          <h:inputText id="inputText2" value="#{calculator.secondNumber}" >
                          </h:inputText>
                          
                          =
                          
                          <h:outputText id="resultText" value="#{calculator.result}" />