Thursday, February 2, 2017

OmniFaces 2.6 enables saving entire @ViewScoped bean in client side state

OmniFaces 2.6 has been released!

The main new things are a bunch of general purpose converters for strings and collections, a new implicit EL object #{faces} which delegates to Faces utility class and last but not least a new attribute for @ViewScoped which triggers saving the entire physical bean in JSF view state when client side state saving is enabled, hereby making it effectively to be not expirable and thus live forever as long as the associated page lives, even when cached/copied.

You can find the complete list of additions, changes and fixes at What's new in OmniFaces 2.6? list in showcase.

Installation

Non-Maven users: download OmniFaces 2.6 JAR and drop it in /WEB-INF/lib the usual way, replacing the older version if any.

Maven users: use <version>2.6</version>.

<dependency>
    <groupId>org.omnifaces</groupId>
    <artifactId>omnifaces</artifactId>
    <version>2.6</version>
</dependency>

For CDI-less users, the latest 1.1x version is still 1.14.

If you need some arguments to move to CDI:

  • Upcoming JSF 2.3 will require a CDI dependency
  • JSF own @ManagedBean and @XxxScope annotations are marked deprecated in JSF 2.3 (already as of 2.3-m06)
  • Spring 3.x already supports javax.inject API from CDI, so Spring services (which are often used to substitute the lack of EJB support in Tomcat) can seamlessly be injected in CDI managed beans.

@ViewScope(saveInViewState=true)

The @ViewScoped annotation got a new attribute: saveInViewState. When you're using client side state saving by having the javax.faces.STATE_SAVING_METHOD context parameter set to client along with a valid jsf/ClientSideSecretKey in web.xml as below...

<context-param>
    <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
    <param-value>client</param-value>
</context-param>
<env-entry>
    <env-entry-name>jsf/ClientSideSecretKey</env-entry-name>
    <env-entry-type>java.lang.String</env-entry-type>
    <env-entry-value><!-- See http://stackoverflow.com/q/35102645/157882 --></env-entry-value>
</env-entry>

...and you explicitly want to store the whole view scoped bean instance in the JSF view state instead of in the HTTP session, then set the annotation's saveInViewState attribute to true.

import javax.inject.Named;
import org.omnifaces.cdi.ViewScoped;

@Named
@ViewScoped(saveInViewState=true)
public class OmniCDIViewScopedBean implements Serializable {}

It's very important that you understand that this setting has potentially a major impact on the size of the JSF view state, certainly when the view scoped bean instance holds "too much" data, such as a collection of entities for a data table, and that such beans will in fact never expire as they are stored entirely in the javax.faces.ViewState hidden input field in the HTML page. Moreover, the @PreDestroy annotated method on such bean will explicitly never be invoked, even not on an unload as it's quite possible to save or cache the page source and re-execute it at a (much) later moment.

It's therefore strongly recommended to use this setting only on a view scoped bean instance which is exclusively used to keep track of the dynamically controlled form state, such as disabled, readonly and rendered attributes which are controlled by ajax events.

New converters

A bunch of new converters have been added. It's after all a bit embarrassing why they were not added to OmniFaces much sooner as they are all quite "general purpose" and have been repeated in all our projects for years. Anyway, better late than never ;)

  • omnifaces.TrimConverter: the getAsObject() trims any whitespace from submitted string values. The getAsString() does effectively nothing.
  • omnifaces.ToLowerCaseConverter: the getAsObject() lowercases any character in submitted string values based on current locale. The getAsString() does effectively nothing.
  • omnifaces.ToUpperCaseConverter: the getAsObject() uppercases any character in submitted string values based on current locale. The getAsString() does effectively nothing.
  • omnifaces.ToCollectionConverter: the getAsObject() converts any commaseparated string to a collection, along with trimming away any whitespace around the delimiters. The delimiter is configurable via an attribute. The getAsString() does effectively the reverse, but when using for output only, you could also use the existing #{of:joinXxx()} functions instead.

#{faces}

The well-known Faces utility class got a counterpart in EL with help of FacesELResolver. All methods which start with get or is, and take no parameters, and return either String or boolean, and are not related to response nor to session or flash (for which already implicit EL objects #{session} and #{flash} exist), will be available as properties of the implicit object #{faces}. Examples are:

  • #{faces.development}
  • #{faces.serverInfo}
  • #{faces.ajaxRequest}
  • #{faces.requestBaseURL}
  • #{faces.requestURLWithQueryString}

To find out all of them, check the javadoc whether the method explicitly states This is also available in EL as #{faces.xxx}.

OmniFaces 3.x

The 2.6 will be the latest OmniFaces release for JSF 2.2. If everything goes well and no 2.6.1 is necessary, then focus will be moved to OmniFaces 3.0 for JSF 2.3. JSF 2.3 itself is scheduled to be finalized this quarter. Major change will be the upgrade from Java 7 to —finally— Java 8 with all of its awesomeness. With this new major release, a bunch of OmniFaces artifacts which are already taken over in JSF 2.3 will be deprecated (except of <o:socket> as it still allows room for new ideas and improvements), and the ones already deprecated in OmniFaces 2.x will be removed.

To learn what's new in JSF 2.3, head to this complete overview of new JSF 2.3 features in Arjan Tijms' blog.