Uploaded image for project: 'MyFaces Core'
  1. MyFaces Core
  2. MYFACES-3711

Add alwaysRecompile mode for EL Expression Cache Mode

    XMLWordPrintableJSON

Details

    • New Feature
    • Status: Closed
    • Major
    • Resolution: Fixed
    • None
    • 2.1.12
    • JSR-314
    • None

    Description

      In MYFACES-3160, EL Expression Cache Mode was introduced but soon it was seen a
      problem found on MYFACES-3169 (ui:param and c:set implementations does not
      work as expected).

      There are two problems that limit the scope where EL Expression Cache can
      be used:

      1. Facelets user tags cannot cache EL Expressions.
      2. Inclusions using ui:param must always contains the same number of
      parameters.

      To understand the reasons it is worth to remember this example:

      a.xhtml
      <ui:composition template="c.xhtml">
      <ui:param name="var1" value="value1"/>
      </ui:composition>

      b.xhtml
      <ui:composition template="c.xhtml">
      <ui:param name="var1" value="value1"/>
      <ui:param name="var2" value="value2"/>
      </ui:composition>

      c.xhtml
      <ui:composition>
      <h:outputText value="#

      {var1}

      />
      <h:outputText value="#

      {var2}

      />
      </ui:composition>

      When facelet c.xhtml is constructed from a.xhtml, "var2" is not recognized as
      a parameter so all EL expressions inside c.xhtml holding refereces to "var2"
      will be cached. Later, facelet c.xhtml is reused from b.xhtml but since
      some EL expressions are cached the passed value in "var2" is not taken into
      account and the error arise.

      In this point it is good to remember that ui:include or ui:decorate or user
      tags are build view time tags, so they are executed only when the view is
      built. Parameters or attributes passed by ui:param or as user tag attributes
      follows the same principle, they are calculated on build view time through
      VariableMapper and the evaluation is stored inside the EL Expression. This
      means all EL Expressions holding references to these variables cannot be
      cached and needs to be generated each time the view is built.

      There is no way to know beforehand which references are affected, because
      in a template or an user tag there is no declaration of the parameters or
      attributes. But from user point of view that's good, because in this context
      a declaration of the parameters is just not necessary.

      The problem is ui:param and user tags are very useful features, widely used.
      A solution to this problem will improve performance in those cases.

      I have been thinking for a long time how to solve this, trying different
      strategies. Use some kind of concurrency algorithm inside TagAttributeImpl
      does not work because it is too expensive, or use a central storage for
      cache the expressions by the cost involved in the comparisons.

      The objective of cache EL expressions inside facelets abstract syntax tree
      (AST) is minimize the calculations required to get a valid expression. EL
      implementations has already an internal map that cache that information,
      but that code usually has synchronized blocks or similar things. In that
      sense, the idea is rely on that storage in those EL expressions where
      there is no choice and they need to be recreated.

      After doing many experiments in this part, I came up with a solution, which
      involves the following points:

      1. Associate to a facelet, the parameters that were considered as passed
      through ui:param or as a user tag attribute. If in some point of time
      we know for example c.xhtml uses var1, just consider it as c.xhtml(var1).

      2. Use DefaultVariableMapper to track the parameters that are passed through
      ui:param or as a user tag attribute. When the EL expression is created, if
      it uses at least one parameter, mark the expression as not cacheable.

      3. Override FaceletCache implementation and force a recompilation of a
      facelet if a new parameter is detected that was not considered the first
      time the template was created.

      4. A facelet stored in the cache can be used if and only if all the
      parameters used for the template where considered when it was compiled at
      first time.

      In the example proposed, when facelet c.xhtml is constructed from a.xhtml,
      we say that c.xhtml was built with var1 as a known parameter, or
      c.xhtml(var1). when we try to reuse facelet c.xhtml from b.xhtml, we discover
      that var2 is also a parameter, but since the cached facelet is c.xhtml(var1),
      the algorithm discard the facelet and create a new one, but taking into
      account var2 too, so the new facelet becomes c.xhtml(var1,var2). If there
      is a call to c.xhtml with no params, it is considered that c.xhtml(var1,var2)
      can be used in that case.

      The final effect is just some extra compilations of the same facelet at
      startup but in the medium/long term, the information we need is calculated
      and associated with the facelet url. Nice!. Facelet is very fast doing those
      extra compilation steps, and the final effect over performance really pays
      off. We could even set this mode as default.

      The only disadvantage of this strategy is the current contract of FaceletCache
      is insuficient. As it has been described in MYFACES-3705, there are
      implementation details inside MyFaces Core and in our facelets implementation,
      that needs to be exposed in a proper way. We need to create a custom
      AbstractFaceletCache and specify how to implement it.

      Attachments

        Issue Links

          Activity

            People

              lu4242 Leonardo Uribe
              lu4242 Leonardo Uribe
              Votes:
              0 Vote for this issue
              Watchers:
              1 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved: