OmniFaces 1.8.3 has finally been released!
Also this release had some unscheduled delay for various reasons. Great programmers are also just humans with a "life" next to all the development work. I personally had after all a lot more time needed to acclimatize myself to the Netherlands after having lived in CuraƧao for almost 6 years (still don't really feel home here outside the working hours, still want to go back once the kids grow out the house). Arjan had among others also some unforeseen issues with his new home.
As usual, in the What's new page of the showcase site you can find an overview of all what's been added/changed/fixed for 1.8. The three most useful additions are the <o:deferredScript>
, <o:massAttribute>
and @Eager
.
Installation
Non-Maven users: download OmniFaces 1.8.3 JAR and drop it in /WEB-INF/lib
the usual way, replacing the older version if any.
Maven users: use <version>1.8.3</version>
.
<dependency>
<groupId>org.omnifaces</groupId>
<artifactId>omnifaces</artifactId>
<version>1.8.3</version>
</dependency>
Defer loading and parsing of JavaScript files
If you've ever analyzed the performance of your website using a tool like Google PageSpeed, then you'll probably recognize the recommendation to defer loading and parsing of JavaScript files. Basically, the recommendation is to load JavaScript files only when the browser is finished with rendering of the page. This is to be achieved by dynamically creating a <script>
element via document.createElement()
during window.onload
. Please note that this is not the same as just moving the scripts to the bottom of the page using <h:outputScript target="body">
! That would speed up downloading of other resources, but still block the rendering of the HTML in most browsers (read: everything expect IE).
OmniFaces now comes with a <o:deferredScript>
component for this very purpose which works just like <h:outputScript>
with a library
and name
attribute.
<h:head>
...
<o:deferredScript library="libraryname" name="resourcename.js" />
</h:head>
You can also use it on for example PrimeFaces scripts, but some additional work needs to be done. For detail, refer this Stack Overflow Question and Answer: Defer loading and parsing of PrimeFaces JavaScript files. At a production site, this approach has proven to decrease the average time until "DOM content loaded" from ~3s to ~1s on a modern client machine.
Set a common attribute on multiple components
The new <o:massAttribute>
taghandler allows you to set a common attribute on multiple components. So, instead of for example:
<h:inputText ... disabled="#{someBean.disabled}" />
<h:inputText ... disabled="#{someBean.disabled}" />
<h:inputText ... disabled="#{someBean.disabled}" />
<h:inputText ... disabled="#{someBean.disabled}" />
<h:inputText ... disabled="#{someBean.disabled}" />
You can just do:
<o:massAttribute name="disabled" value="#{someBean.disabled}">
<h:inputText ... />
<h:inputText ... />
<h:inputText ... />
<h:inputText ... />
<h:inputText ... />
</o:massAttribute>
The advantage speaks for itself.
Eagerly instantiate a CDI managed bean
When using the standard JSF managed bean facility via @ManagedBean
which is since JSF 2.2 semi-official deprecated (not documented as such, but the JSF team is clearly pushing toward it given the total absence of new features around JSF managed bean facility, instead they are all in CDI managed bean facility), it was possible to declare an application scoped JSF managed bean to be eagerly instantiated during application's startup like so:
import javax.faces.bean.ApplicationScoped;
import javax.faces.bean.ManagedBean;
@ManagedBean(eager=true)
@ApplicationScoped
public class Bean {
// ...
}
However, this isn't possible with standard CDI, not even with the one as available in Java EE 7. So OmniFaces has added the @Eager
and @Startup
annotations for the very purpose. The @Startup
is just a stereotype for @Eager @ApplicationScoped
.
So, both beans below are equivalent:
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Named;
import org.omnifaces.cdi.Eager;
@Named
@Eager
@ApplicationScoped
public class Bean {
// ...
}
import javax.inject.Named;
import org.omnifaces.cdi.Startup;
@Named
@Startup
public class Bean {
// ...
}
An additional bonus of OmniFaces @Eager
is that it not only works on application scoped CDI managed beans, but also on request and session scoped CDI managed beans and on beans annotated with OmniFaces @ViewScoped
(thus not the JSF 2.2 one yet, that will come in the upcoming OmniFaces 2.0 which will target JSF 2.2, OmniFaces 1.x is namely JSF 2.0 targeted).
Having @Eager
on a request scoped bean may look somewhat strange, but this makes an interesting use case possible: eagerly and asynchronously fetch some data from a DB in the very beginning of the request, long before the FacesServlet
is invoked, it runs even before the servlet filters are hit (it's initiated via a ServletRequestListener
). Depending on the server hardware used, the available server resources, all code running between the invocation of the first servlet filter and entering the JSF render response, this may give you a time space of 10ms ~ 500ms (or perhaps more if you've some inefficient code in the pipeline ;) ) to fetch some data from DB in a different thread parallel with the HTTP request and thus a speed improvement equivalent to the time the DB needs to fetch the data. Below is an example of how such an approach can look like:
The asynchronous service (this silly example fetches the entire table; just do whatever DB or any other relatively long-lasting service task you want to do as long as the method is annotated with @Asynchronous
and you return an AsyncResult
as Future
; the container will all by itself worry about managing the threads):
package com.example;
import java.util.List;
import java.util.concurrent.Future;
import javax.ejb.AsyncResult;
import javax.ejb.Asynchronous;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
@Stateless
public class MyEntityService {
@PersistenceContext
private EntityManager em;
@Asynchronous
public Future<List<MyEntity>> asyncList() {
List<MyEntity> entities = em
.createQuery("SELECT e FROM MyEntity e", MyEntity.class)
.getResultList();
return new AsyncResult<>(entities);
}
}
The @Eager
request scoped bean (note the requestURI
attribute, this must exactly match the context-relative request URI without any path fragments and query strings, this example assumes a /test.xhtml
page (with a FacesServlet
mapping of *.xhtml
); wildcards like *
are not supported yet, this may come in the future if there's demand)
package com.example;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import javax.annotation.PostConstruct;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.inject.Named;
import org.omnifaces.cdi.Eager;
@Named
@Eager(requestURI="/test.xhtml")
@RequestScoped
public class MyEagerRequestBean {
private Future<List<MyEntity>> entities;
@Inject
private MyEntityService service;
@PostConstruct
public void init() {
entities = service.asyncList();
}
public List<MyEntity> getEntities() {
try {
return entities.get();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new FacesException(e);
} catch (ExecutionException e) {
throw new FacesException(e);
}
}
}
This way, when you request /test.xhtml
with something like this:
<h:dataTable value="#{myEagerRequestBean.entities}" var="entity">
<h:column>#{entity.property}</h:column>
</h:dataTable>
... then the above bean will be constructed and initialized far before the FacesServlet
is invoked. Note that this thus also means that the FacesContext
is not available inside the @PostConstruct
! From that point on, both JSF and JPA will do their jobs simultaneously in separate threads until JSF calls the getter for the first time. The JSF thread (the HTTP request thread) will then block until JPA has returned the result, or perhaps it's already returned at that moment and then JSF can just advance immediately without waiting for JPA.
An overview of all additions/changes/bugfixes in OmniFaces 1.8
Taken over from the What's new? page on showcase:
Added in OmniFaces 1.8
WebXml#getFormErrorPage()
to get web.xml configured location of the FORM authentication error page-
<o:deferredScript>
which is capable of deferring JavaScript resources towindow.onload
-
Faces#addResponseCookie()
got 2 new overloaded methods whereby domain and path defaults to current request domain and current path -
Components#isRendered()
which also checks the rendered attribute of all parents of the given component -
<o:massAttribute>
which sets the given attribute on all nested components -
FacesMessageExceptionHandler
which sets any caught exception as a global FATAL faces message -
<o:cache>
has new disabled attribute to temporarily disable the cache and pass-through children directly -
@Eager
annotation to eagerly instantiate request-, view-, session- and application scoped beans -
<o:viewParam>
skips converter for null model values so that query string doesn't get polluted with an empty string - Small amount of utility methods and classes, e.g. method to check CDI annotations recursively in stereotypes, shortcut method to obtain VDL, etc
Changed in OmniFaces 1.8
CombinedResourceHandler
now also recognizes and combines<o:deferredScript>
-
UnmappedResourceHandler
now also recognizes PrimeFaces dynamic resources usingStreamedContent
Fixed in OmniFaces 1.8
- Assume
RuntimeException
inBeanManager#init()
as CDI not available (fixes deployment error on WAS 8.5 without CDI enabled) -
Use "-" (hyphen) instead of
null
as default option value to workaroundnoSelectionOption
fail withGenericEnumConverter
-
<o:param>
shouldn't silently convert the value toString
(fixes e.g.java.util.Date
formatting fail in<o:outputFormat>
) -
Fixed
javax.enterprise.inject.AmbiguousResolutionException
in subclassed@FacesConverter
and@FacesValidator
-
<o:messages>
failed to find thefor
component when it's not in the same parent -
<o:conditionalComment>
shouldn't XML-escape the if value, enabling usage of&
character -
UnmappedResourceHandler
broke state saving when partial state saving is turned off -
CombinedResourceHandler
didn't properly deal with MyFaces-managed resources
Maven download stats
Here are the Maven download stats:
- January 2014: 3537
- February 2014: 3580
- March 2014: 3892
- April 2014: 3572
- May 2014: 3971
Below is the version pie of May 2014:
Last but not least
For the case you missed it: OmniFaces repo, wiki and issue tracking (basically: everything) has moved from Google Code to GitHub, along with a "brand new" homepage in GitHub style at omnifaces.org. The downloads will from now just point directly to Maven via links at the homepage.