Leonardo Uribe created MYFACES-3711: ---------------------------------------
Summary: Add alwaysRecompile mode for EL Expression Cache Mode Key: MYFACES-3711 URL: https://issues.apache.org/jira/browse/MYFACES-3711 Project: MyFaces Core Issue Type: New Feature Components: JSR-314 Reporter: Leonardo Uribe Assignee: Leonardo Uribe 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. -- This message is automatically generated by JIRA. If you think it was sent incorrectly, please contact your JIRA administrators For more information on JIRA, see: http://www.atlassian.com/software/jira