myfaces-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Leonardo Uribe (JIRA)" <>
Subject [jira] [Created] (MYFACES-3825) [perf] Cache EL expressions using an indirection for ui:param and user tag attributes
Date Fri, 22 Nov 2013 23:51:35 GMT
Leonardo Uribe created MYFACES-3825:

             Summary: [perf] Cache EL expressions using an indirection for ui:param and user
tag attributes
                 Key: MYFACES-3825
             Project: MyFaces Core
          Issue Type: Improvement
          Components: JSR-344
            Reporter: Leonardo Uribe
            Assignee: Leonardo Uribe

I have been trying for some time to find new ways to improve the code inside
MyFaces. Working in MYFACES-3811 (fix c:forEach) I have realized that the way
how VariableMapper works allows us to cache EL expressions in those places where
we have thought EL caching was not possible. 

This fact is important because it changes the way how we have been thinking around
view pooling technique (See MYFACES-3664 for details). If all 
ValueExpression/MethodExpression instances in a view can be considered "static" or
in other words it does not change each time the view is built or refreshed, we can
be sure that with a plain visitTree call it is possible to "reset" any view and
reuse it safely, even in cases like when ui:param is used or user facelet tags.
If all components in a view support pooling (hard/soft reset using saveState 
method), any view using those components can be poolable. 

First of all, let's remember how VariableMapper works. Basically it is just a map
with var names as keys and ValueExpression as values. When a EL expression is
created, the variables that are on the context VariableMapper and are used
to solve the expression are copied and stored into an inner VariableMapper of
the created EL expression. For example if we have this:

<c:set var="item" value="Hello"/>
<c:set var="item2" value="#{item}"/>
<c:set var="item3" value="#{item2}"/>

the EL expression for item2 will have an inner VariableMapper with an EL
expression pointing to "Hello".

Now, we need to remember the problematic cases for EL caching:

1. Use combinations of c:set and c:if

    <c:if test="#{condition}">
        <c:set var="item" value="Hello"/>
    <h:outputText value="#{item}"/>

This case is unlikely, but most of all, it can be refactored very easily to avoid
the c:if and move the condition to the c:set EL Expression. It is common to found
this technique in old JSP pages. But it is clear with JSF, this kind of logic
should reside in a managed bean. So at the end it is not a big deal. Anyway, 
There is a mode called "strict" that disable EL caching for the whole page if 
c:set is found.

2. Use of ui:param

    <ui:decorate template="uiparamcache1_1.xhtml">
    <ui:decorate template="uiparamcache1_1.xhtml">
        <ui:param name="param1" value="ALFA"/>

The first time the template is called, it has no params, so all expressions are
cached inside the inner template. But once we call the same template again, those
cached expressions are now invalid and needs to be recalculated again. The hack
done with "alwaysRecompile" mode recompiles the facelet, but takes into account
the known parameters for the template. In this way, the EL expressions that are
affected by the param are not cached.

3. Use of facelet user tags

    <user:usertagtest1 var1="ALFA" id="comp1">
    <user:usertagtest1 var2="BETA" id="comp2">
    <user:usertagtest1 var1="GAMMA" var2="OMEGA" id="comp3">
This is quite the same to the case with ui:param, but in this case affect facelet tag 

4. An expression uses a variable resolved through VariableMapper

This is unlikely, because there are no standard tags using this strategy, but it is
possible to create a facelet tag that uses a VariableMapper wrapper. This is not 
something we should worry about.

In MYFACES-3811 (fix c:forEach), there is a part where a wrapper 
(IteratedValueExpression or MappedValueExpression) is required to hold 
the associated item and inject it into the VariableMapper. This is indeed a good idea,
because it shows that we can just put a wrapper inside VariableMapper and things will
keep working. 

If we can substitute the ValueExpression associated with a var with something else, we
can avoid the propagation effect that makes EL caching fail in 2 and 3. The trick is 
use an unique id associated with the facelet tag and put the real EL expression in
a central point like FaceletState object, which is stored in UIViewRoot. The resulting
structure can be generated over and over if PSS is enabled and if is disable, it needs
to be saved with the state. If the component tree changes dynamically, the generated
structure will change too.

The final effect will be that 100% of the EL Expressions managed by facelets using 
"alwaysRecompile" mode will be cacheable, which will be a great improvement. It also
removes one of the biggest disadvantages we had for include view pooling technique
into MyFaces 2.2.x.

This message was sent by Atlassian JIRA

View raw message