10 Replies Latest reply on Aug 6, 2008 9:25 AM by Daniel Lichtenberger

    s:cache and compile-time view creation

    Daniel Lichtenberger Newbie

      Hi,


      I'm using s:cache to cache a fairly complex (read-only, plain HTML) navigation tree that is built recursively using Facelets template components. In my first approach I encapsulated the navigation area directly, i.e.


      <s:cache region="/test" key="tree-#{id}">
         <!-- Renders the navigation tree -->
         <my:navigationTree .../>
      </s:cache>



      The navigationTree component requires (relatively) expensive database calls to build the navigation for the current node. I did not experience a lot of performance gains with this, and using a profiler I noticed that the entire component tree of the navigation area was being built as before - it skipped only the render phase, but did not save me of the expensive view construction phase. It makes perfectly sense since this is essentially a recursive view declaration, and the entire tree is constructed at compile- and not at render-time.


      My workaround is to add a compile-time tag around the body content that proceeds with view building only if the page fragment is not cached yet:


      <s:cache region="/test" key="tree-#{id}">
         <c:if test="#{not myPageCache.cached('/test', 'tree-#{id}')}">
           <!-- Renders the navigation tree -->
           <my:navigationTree .../>
         </c:if>
      </s:cache>



      myPageCache.cached returns true if the given region/key combination is present in PojoCache. This skips the expensive view building entirely if the fragment is already cached and probably works only as long as my:navigationTree does not use JSF components. Any thoughts on this? Is there an easier way to achieve complete caching?


      Daniel

        • 1. Re: s:cache and compile-time view creation
          Sebastian Hennebrueder Newbie

          Well, actually not.


          I submitted a patch proposal in the dev list, which will reduce EL calles for rendered by 80 % in the cache but this is more in case of post requests.


          The problem is that we cache rendering which happens at the end of all phases and when the object tree is created, we don't know, if it is cached.


          Anyway, I will take your post as encouragement to have another look at the cache.


          Regards


          Sebastian




          • 2. Re: s:cache and compile-time view creation
            Sebastian Hennebrueder Newbie

            just to be sure, your db queries are of course not redone. it it just the restoring of the state.


            If not, please add some more informations like:
            post or get request and a code snippet showing what you are doing


            Regards


            Sebastian

            • 3. Re: s:cache and compile-time view creation
              Daniel Lichtenberger Newbie

              In my case the DB queries are triggered through EL expressions in the tree template, so they were resubmitted (unless using a c:if guard as described above). Maybe using POST-requests would have helped, but I have to use RESTful URLs and pure HTTP-GET by specification.


              I think the documentation of s:cache (especially regarding specific Facelets issues like render-time vs. compile-time components) is a little sparse, some examples of what will (and will not) be cached would be useful.


              Daniel

              • 4. Re: s:cache and compile-time view creation
                Pete Muir Master

                Sebastian, it would be great if you could write your findings up for this, and your post to the dev list, up in JIRA so that we have the info in one place.

                • 5. Re: s:cache and compile-time view creation
                  Sebastian Hennebrueder Newbie

                  I would not expect that DB queries are still generated.
                  Could you provide a code snippet showing the behaviour. If possible with normal components. If only your component issue this behaviour, it would be great to have the source.


                  @Pete
                  The subject was
                  'Caching issues with JSF rendered attribute'
                  on the dev list.


                  Best Regards


                  Sebastian Hennebrueder

                  • 6. Re: s:cache and compile-time view creation
                    Pete Muir Master

                    Yes dev list != JIRA ;-)


                    Seriously, if you just write stuff to the dev list, it gets lost. The place for this is JIRA.

                    • 8. Re: s:cache and compile-time view creation
                      Daniel Lichtenberger Newbie

                      I created a testcase that illustrates the behaviour. It's a recursive template that gets the displayed nodes from some backing bean, which will cache the values in request/page scope.


                      Note that if you refresh the page the DB-access message gets printed although the content is rendered from the cache (indicated by a comment in the source code or by debugging CacheRenderer).


                      /testCache.xhtml, which includes the s:cache tag and inserts a template:


                      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
                      <html xmlns="http://www.w3.org/1999/xhtml"
                            xmlns:ui="http://java.sun.com/jsf/facelets"
                            xmlns:s="http://jboss.com/products/seam/taglib"
                            xmlns:c="http://java.sun.com/jstl/core">
                      
                          <body>
                      
                              <s:cache region="/test" key="key" enabled="true">
                                  <ui:include src="/testCacheChild.xhtml">
                                      <ui:param name="node" value="0"/>
                                  </ui:include>
                              </s:cache>
                      
                          </body>
                      
                      </html>



                      /testCacheChild.xhtml, which represents the navigation component in my project:


                      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                              "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
                      <html xmlns="http://www.w3.org/1999/xhtml"
                            xmlns:ui="http://java.sun.com/jsf/facelets"
                            xmlns:c="http://java.sun.com/jstl/core">
                      
                      <ui:composition>
                          <ul>
                              <c:forEach var="i" items="#{testBean.loadChildren(node)}">
                                  <li>
                                      #{i}.
                                      <ui:include src="/testCacheChild.xhtml">
                                          <ui:param name="node" value="#{i}"/>
                                      </ui:include>
                                  </li>
                              </c:forEach>
                          </ul>
                      </ui:composition>
                      
                      </html>



                      And finally, the backing bean, TestBean.java:


                      @Name("testBean")
                      @Scope(ScopeType.PAGE)
                      public class TestBean {
                          private Map<Integer, List<Number>> dataCache = new HashMap<Integer, List<Number>>();
                      
                          public List<Number> loadChildren(int id) {
                              if (!dataCache.containsKey(id)) {
                                  System.out.println("Accessing DB for id " + id + "...");
                                  final List<Number> data = new ArrayList<Number>();
                                  if (id <= 1) {
                                      // create test data (only for two nodes)
                                      for (int i = id + 1; i < 10; i++) {
                                          data.add(i);
                                      }
                                  }
                                  dataCache.put(id, data);
                              }
                              return dataCache.get(id);
                          }
                      }



                      Actually, there is an even simpler example that exhibits the same behaviour:


                              <s:cache region="/test" key="key" enabled="true">
                                  <c:forEach var="node" items="#{testBean.loadChildren(0)}">
                                      #{node},
                                  </c:forEach>
                              </s:cache>



                      The issue is the compile-time c:forEach tag. If replaced with ui:repeat, no backing bean lookups will be made for the cached version (because it won't be rendered). However, since c:forEach is called during view construction, the s:cache tag fails to prevent its execution (unless the s:cache body is wrapped in a c:if construct as described in my original post).


                      Of course, in the last example it's easy to replace c:forEach with ui:repeat, but it's not that easy (if possible at all) with more complex, recursively nested templates.


                      Cheers, Daniel

                      • 9. Re: s:cache and compile-time view creation
                        Sebastian Hennebrueder Newbie

                        mmh,


                        I cannot reproduce it here. If I enable caching the EL is not called.


                        Can you validate that your cache is running?


                        Which version of Seam do you use?
                        Whcih version of JSTL?


                        How do you access your page (s:link or ...? )


                        Best Regards


                        Sebastian Hennebrueder
                        http://www.laliluna.de

                        • 10. Re: s:cache and compile-time view creation
                          Daniel Lichtenberger Newbie

                          Seam 2.0.0.GA, the cache is running (CacheRendererBase writes the cached content). The page is accessed directly via GET.


                          I'm running on oc4j 10, the JSTL version of the server seems to be 1.1.2 (is this actually relevant when I'm using Facelets?).


                          Cheers,
                          Daniel