View Pooling (Going beyond JSF 2.2 stateless views)
In JSF 2.2 now it is possible to define a view as "stateless" just writing something like this on the top level page:
<f:view transient="true" ...>
but the research done in MyFaces community has led to the conclusion that from performance perspective, there is no a real gain in speed or memory by multiple reasons:
- The size of a view in the session is very small.
- Save or restore the state is quite fast, compared with the work spent creating or rendering the view.
- The view still needs to be built.
There are two ways to enable this feature:
1. Add an entry into a faces-config.xml file like this:
<faces-config-extension>
<view-pool-mapping>
<url-pattern>/*</url-pattern>
<parameter>
<name>org.apache.myfaces.VIEW_POOL_MAX_POOL_SIZE</name>
<value>5</value>
</parameter>
</view-pool-mapping>
</faces-config-extension>
With this mode you can override the global web config parameters.
2. Encapsulate your top level page with
<f:view oamEnableViewPool=true>
A previous blog talks about the fundamentals of the technique. Please note third party libraries needs to be compatible with view pooling, so for now only the components included in JSF 2.2 and some MyFaces Tomahawk components support this mode.
SPI intefaces for custom flow and view scope
Many members of MyFaces community prefer use CDI and specially Apache OpenWebBeans and Apache TomEE for its projects, but the community is also aware of people who use other alternate technologies. In JSF 2.2 some CDI annotations were added like @FlowScoped and @ViewScoped, but it was not provided a general solution for users who don't want or can use CDI. In Myfaces 2.2 we have added the following SPI interfaces:
- org.apache.myfaces.spi.ViewScopeProvider
- org.apache.myfaces.spi.FacesFlowProvider
In that way it is possible to fill the gap and create implementations of those scopes. Inside MyFaces code there are 2 implementations of each SPI interface, one using CDI and one without CDI, so I hope it will be easy for the interested developers to fill the gap according to their needs.
Compatibility mode for Facelets 1.1.x / Mojarra syntax
In Facelets 1.1.x and in current versions of Mojarra, there is still some tags that does not behave properly. For example:
- Declarations like <c:set var="..." value="..."> applies for everything below the declaration and pass through any facelet, including composite components, breaking encapsulation principle.
- <ui:param ...> implementation is the same as <c:set ...>, and it should theorically add a template scope parameter.
- <ui:include ...> is not isolated properly, mixing declarations of <ui:define ...> and <ui:insert ...> from outer levels. In cases where this is valid <ui:decorate ...> should be used instead.
With this web config parameter:
<context-param>
<param-name>org.apache.myfaces.STRICT_JSF_2_FACELETS_COMPATIBILITY</param-name>
<param-value>true</param-value>
</context-param>
A special mode is enabled that allows facelets 1.1.x / Mojarra ugly syntax. Anyway, I encourage users to fix their apps and use the syntax available in MyFaces, because it follows the spec more strictly and it keeps compatibility with Mojarra. But keep in mind, from MyFaces perspective, the current syntax in Mojarra does not follow the spec wording.
Partial State Saving algorithm is applied even in dynamic sections and programatically created components
The current algorithm implemented inside MyFaces 2.2 uses Partial State Saving (PSS) technique in every possible situation:
- Using dynamic facelet tags like <c:if ...>, <c:choose ...>, <c:forEach>, <ui:include src="#{...}">, <ui:decorate template="#{...}"> or <ui:composition template="#{...}">.
- Composite components that use <cc:insertChildren ...> or <cc:insertFacet ...>
- Components created by effect of ViewDeclarationLanguage.createComponent(...)
ViewDeclarationLanguage.createComponent(...) supports non JSF component tags
ViewDeclarationLanguage.createComponent(...) was added in JSF 2.2 to allow create composite components programatically. But it is possible to extend that syntax a little bit to allow use other non component tags like <ui:include ...> as if they were components. For example:
ViewDeclarationLanguage vdl = facesContext.getApplication().
getViewHandler().getViewDeclarationLanguage(
facesContext, facesContext.getViewRoot().getViewId());
Map<String, Object> attributes = new HashMap<String, Object>();
attributes.put("src", "/addSimpleIncludeVDL_1_1.xhtml");
UIComponent component = vdl.createComponent(facesContext,
"http://java.sun.com/jsf/facelets",
"include", attributes);
panel.getChildren().add(component);
The idea is create automatically a wrapper component when it is necessary. In this way it is possible to avoid a hack over FaceletFactory that in my opinion it will never work well.
<c:forEach ...> has been fixed once for all
It is well known that <c:forEach> tags sometimes does not work as it should. For example, if the collection iterated by <c:forEach> tag changes somehow, the state of the inner components are not restored correctly. The reason is the original algorithm from facelets 1.1.x don't include the necessary logic to deal with this problem, and it was required many big changes inside facelets algorithm before make an attempt to fix it. In MyFaces 2.2.0 we finally have all necessary changes in place, so the long awaited fix has been finally done. The only thing you need to remember is, the elements in the iterated collection must be Serializable and implement hashCode() and equals(...) method properly, otherwise the old algorithm is used.
This tag has been also aligned with PSS algorithm, so now it is possible to use it in combination to dynamic facelet tags and keep the state small. Please remember in previous versions of MyFaces Core, the components created by effect of <c:forEach> tag needs to be saved and restored fully. See MYFACES-3811 for details.
View slots are reused for ajax request and the information stored in view scope is discarded when the view is removed from state on server side state saving
In MyFaces Core 2.0 and 2.1, each ajax request requires one slot in the view storage on server side state saving. A change was added to reuse view slots in those cases, reducing the space required in session, and in that way make a better use of the available space.
Additionally, it was included an algorithm that detect and discard view scopes when they are no longer used, keeping the session size as small as possible.
Early Flush for resources inside <head> or <h:head> tag
Using the following web config parameter:
<context-param>
<param-name>org.apache.myfaces.EARLY_FLUSH_ENABLED</param-name>
<param-value>true</param-value>
</context-param>
flush all resources that are rendered in html <head> tag early, which allows web browsers to fetch them as soon as the flush is done but while the page is loaded, improving response time.
CDI Injection for Converter and Validators
Using these two web config parameters:
<context-param>
<param-name>org.apache.myfaces.CDI_MANAGED_CONVERTERS_ENABLED</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>org.apache.myfaces.CDI_MANAGED_VALIDATORS_ENABLED</param-name>
<param-value>true</param-value>
</context-param>
MyFaces activates a built-in mode that enables CDI Injection for JSF Converters and Validators. This was implemented using an override over Application.createConverter(...) and Application.createValidator(...).
For example:
@FacesConverter("cdiConverter")
public class ConverterAsCdiBean implements Converter {
@Inject
private ConverterService converterService;
/*... */
}
in the page:
<h:inputText id="name" value="#{helloWorld.name}" required="true">
<f:converter converterId="cdiConverter"/>
</h:inputText>
That's it. But there are some considerations:
- JSF use a wrapper around the converter/validator to keep control of the instantiation or lookup, but you can get the real instance through a cast to FacesWrapper and calling getWrapped().
- The lifetime of a converter like the one from the example by default is request scope, because in this case CDI manages the lifecycle of the converter/validator, so in that sense it is detached from the view. For example, by default @PostConstruct and @PreDestroy are called at each request.
> This is necessary for improvements like EL expression caching and view pooling, but some users found these differences annoying specially when they try to migrate from Mojarra to MyFaces.
ResponderEliminar> Anyway, I encourage users to fix their apps and use the syntax available in MyFaces, because it follows the spec more strictly and it keeps compatibility with Mojarra. But keep in mind, from MyFaces perspective, the current syntax in Mojarra does not follow the spec wording.
+1 and yes, it was a 7-day (or almost 1-week) journey/mission for me when I migrated from Mojarra 2.1.8 to MyFaces 2.1.8 sometime in August/September 2012. I did have to 'fix' my beans as well as xhtml (I think), but I think most of the work was in my beans. Definitely an interesting and awarding experience.
When I first started using MyFaces, I replaced Mojarra in Glassfish 3.1.2.2 with MyFaces 2.1.8, and 2 or 3 months later, I decided to migrate from Glassfish 3.1.2.2 to TomEE 1.5.1-snapshot. that was also an interesting and rewarding experience.
I must say, I was very very impressed with the speed/performance of Glassfish 3.1.2.2 + MyFaces 2.1.8. wow, it performed really well, and it 'almost' made it hard for me turn my back on Glassfish, when migrating to TomEE, because, initially, my app was 'not' performing well running on TomEE/OpenWebBeans (since I also migrated from JSF managed beans to CDI managed beans, at the same time I migrated from glassfish to tomee). I could not get my app working at all with Glassfish/Weld, so TomEE/OpenWebBeans definitely gave me the warm-and-fuzzy to migrate from JSF-managed-beans to CDI-managed-beans. Of course, MyFaces and TomEE committers were a huge help... thank you, guys!
> @PostConstruct and @PreDestroy does not work. Use a view scope bean if you need them.
ResponderEliminarhmmm, can you explain/elaborate on this? Are you saying that @PostConstruct and @PreDestroy will not work on CDI @SessionScoped, CDI @ApplicationScoped, and CDI @RequestScoped?
I do remember testing and posting my test results about @PostConstruct and @PreDestroy on @ViewScoped. We had to work the kinks out before this stated working as expected. :)
Howard, it wont work in the instances created by JSF. But who uses those annotations in ie converters anyway...
ResponderEliminarI am seeing the following in my tomee server log:
EliminarJan 15, 2014 2:14:05 AM org.apache.myfaces.view.facelets.ViewPoolProcessor initialize
INFO: org.apache.myfaces.CACHE_EL_EXPRESSIONS web config parameter is set to "always". To enable view pooling this param must be set to "alwaysRecompile". View Pooling disabled.
and I saw the following in this blog:
> But on the way, it was found a way to reuse JSF views effectively and get a good improvement in performance, adding some additional complexity at the time to create components. Please note MyFaces Core even without the view pool gives a very good performance, so most users will not bother about enable this feature, but for some others this can be useful.
so, now, i'm wondering if I should try to tap into this performance enhancement, and/or will it work with PrimeFaces (4.x).
Third party libraries requires to be compatible with the view pool, so I do not expect PrimeFaces (4.x) work out of the box. But it shouldn't be hard to make it work, only those component that are special requires some hack, because al the logic is already in UIComponentBase.
EliminarYes, I was talking about the converter/validators. Gerhard Petracek clarified the issue (thanks to him for its help), the hack done takes into account @PostConstruct and @PreDestroy, but the lifecycle of the injected converter/validators are managed by CDI and a wrapper is filling the gap between JSF and CDI, so you don't need to worry about that. I have fixed the blog entry.
ResponderEliminar> Early Flush for resources inside head h:head tag
ResponderEliminaris there more of a white paper available for this, benchmarks when using it and when not using it, etc...
Just type "early flush html" and you will see example of the intention of the optimization. The idea is simple: send a part of the document early, usually what's inside "head" so other js or css files start to be downloaded immediately at the same time the document is downloaded. Find a benchmark is difficult because it depends on the page structure.
Eliminar