Saturday, July 18, 2020

OmniFaces 4.0-M1 ready for testing!

This is the first milestone release of OmniFaces 4.0, which is the first version developed specifically for JSF 3.0 from Jakarta EE 9 which is currently scheduled to be released at September 2020! OmniFaces 4.x is NOT backwards compatible with earlier Java EE / JSF versions because of the migration of the javax.* package to jakarta.* package.

Noted should be that Jakarta EE 9 initially went for Java 11 as minimum Java version, as can be seen in pom.xml of RC1, but this was unfortunately since RC2 switched back to Java 1.8 due to time constraints. Hence OmniFaces 4.0 is also still at Java 1.8.

The integration tests currently run successfully on GlassFish 6.0.0.M1.

Breaking changes:

  • Obviously, the migration from javax.* package to jakarta.* package. E.g. javax.faces.context.FacesContext is now available at jakarta.faces.context.FacesContext. This is however a general change in Jakarta EE itself. Jakarta EE 8 did still use the javax.* package, but Jakarta EE 9 uses now its own jakarta.* package. OmniFaces own package org.omnifaces.* is still the same as it is.
  • The date beans #{now} and #{startup} have been migrated from java.util.Date to java.time.Instant. This means that e.g. #{now.time} does now not work anymore as the java.time.Instant does not have a getTime() method. EL 4.0 will throw the following exception:
    jakarta.el.ELException: /test.xhtml: The class 'java.time.Instant' does not have the property 'time'.
    Sorry about that, but it was high time to migrate to the much better java.time API! You need to modify your JSF files to catch up this. Use e.g. #{now.epochSecond}, #{now.nano}, or #{now.toEpochMilli()} instead.
    UPDATE: as per the upcoming 4.0-M2 these will continue supporting #{now.time} format! And additionally these will offer #{now.instant} and #{now.zonedDateTime} properties to give you concrete instances of java.time.Instant and java.time.ZonedDateTime.
  • The <o:form includeViewParams="true"> which was deprecated since 3.0 has now been removed because this is already the default behavior.
  • The WebXml.INSTANCE and FacesConfigXml.INSTANCE which were deprecated since 3.1 have now been removed. Use WebXml.instance() and FacesConfigXml.instance() instead. This was done because they can then be @Inject'ed in a CDI bean.
  • The script omnifaces:fixviewstate.js which was deprecated since 3.0 has now been removed. This script is unnecessary since JSF 2.2 as it was fixed over there.

No new things are added, so the existing features are basically the same as with the current 3.7.1 version. JSF 3.0 itself is technically also basically the same as JSF 2.3, except for the change of the package from javax.faces.* to jakarta.faces.*. In other words, it has just been 'jakartified' ;)

Also note that the showcase still runs 3.7.1 (for now). Once WildFly comes with a final JEE9 version, it'll be updated over there as well.

Installation

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

Maven users: use <version>4.0-M1</version>.

<dependency>
    <groupId>org.omnifaces</groupId>
    <artifactId>omnifaces</artifactId>
    <version>4.0-M1</version>
</dependency>

If you have found any issues or comments related to OmniFaces 4.0, please by all means report an issue.

Migration of your project from Java EE 8 / Jakarta EE 8 to Jakarta EE 9

Use this dependency (note: keep an eye on newer RC/M versions and bump accordingly):

<dependency>
    <groupId>jakarta.platform</groupId>
    <artifactId>jakarta.jakartaee-api</artifactId>
    <version>9.0.0-RC2</version>
    <scope>provided</scope>
</dependency>

Then it's really basically a matter of performing a find&replace of the literal string "javax." to "jakarta." throughout the entire project! Perhaps a few things will not compile because they are still javax.*, but it's a matter of renaming them back, which you can then do a bit more specific. For example, javax.xml.* and javax.sql.* are still in Java SE. You should then find&replace e.g. the "jakarta.xml." back to "javax.xml.". Oh, don't forget to rename the files in META-INF/services, if any ;)

Monday, July 13, 2020

OmniFaces 3.7 adds autogenerated sw.js, o:inputHidden and of:stripTags()

OmniFaces 3.7 has been released!

In this version, the WebAppManifestResourceHandler got a new feature: auto-generating an offline-aware service worker file sw.js based on welcome files in web.xml and the configuration in your custom WebAppManifest implementation. With this new feature, the resource handler was renamed to PWAResourceHandler. Further a new component <o:inputHidden> component and a new EL function #{of:stripTags(string)} have been added.

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

Installation

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

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

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

PWAResourceHandler

A WebAppManifestResourceHandler was in OmniFaces 3.6 introduced. This PWAResourceHandler takes it a step further. Previously, it generated only a manifest.json file, which is one of the minimum requirements for a Progressive Web Application (PWA), but now it will also generate an offline-aware sw.js along it which should satisfy the PWA test done by Chrome's Lighthouse tool.

Basically, it will collect all welcome files found in web.xml, build them as JSF views, collect all JSF resource dependencies (any x:outputStylesheet, x:outputScript and x:graphicImage components) found in their component trees, and finally register all of them as offline-available. I.e. the home page should now respond 200 to Lighthouse.

All you need to do is to extend from the org.omnifaces.resourcehandler.WebAppManifest class, implement and override the desired getter methods, put a CDI scope annotation on it such as @ApplicationScoped, and finally reference it in your Facelet template exactly as below:

<link rel="manifest" href="#{resource['omnifaces:manifest.json']}" />

Furthermore, you will also be given the opportunity to explicitly register a special fallback/error page for the "You are offline!" condition. You can do this by overridding the WebAppManifest#getOfflineViewId() method to return the desired JSF view ID.

@Override
public String getOfflineViewId() {
    return "/offline.xhtml";
}

See also the showcase.

If you however would like to make use of the manifest.json alone and disable the offline-aware sw.js feature altogether, then simply override the WebAppManifest#getCacheableViewIds() to return an empty collection. By default, it returns the JSF view IDs of the welcome files encountered in web.xml.

@Override
public Collection<String> getCacheableViewIds() {
    return Collections.emptyList();
}

o:inputHidden

The existing <h:inputHidden> is slightly unintuitive. When the JSF form it is enclosed in is submitted, and another input component within the same form fails conversion or validation, then the value of the <h:inputHidden> won't be set in the backing bean. This makes sense for values submitted by the client, but this doesn't make sense for values submitted by server-controlled code. This way the <h:inputHidden> isn't terribly useful in order to retain request scoped bean properties across postbacks. One solution would be to convert the request scoped bean to a view scoped bean, but this isn't always desireable. It would be undesired when the JSF page in question is required to be entirely stateless.

The new <o:inputHidden> solves this by immediately performing the conversion/validation and updating the model values during the apply request values phase. Of course you could also use a plain vanilla HTML <input type="hidden"> for this in combination with manually grabbing the request parameter, but this is cumbersome and it doesn't transparently support JSF converters.

See also the live demo.

of:stripTags

In case you have a bean property representing a string with potentially some HTML tags, which should be used as some preview string, then you can use the new of:stripTags() function to strip out all HTML tags, leaving only plain text.

Usage example:

#{of:stripTags(bean.html)}

See also the showcase.

What happened to OmniFaces 3.7?

Directly after the 3.7 release I noticed that the sw.js was performing horribly on the showcase site :( These performance problems were unfortunately not immediately visible while developing and testing at localhost. I considered these performance issues to be important enough to warrant a quick hotfix release, so 3.7.1 was released the same day with performance fixes.

So, do not use 3.7 if you plan to make use of the PWAResourceHandler for the autogenerated offline-aware sw.js :)

How about OmniFaces 2.x and 1.1x?

The 2.x got the same bugfixes as 3.7.1 and has been released as 2.7.6. This version is for JSF 2.2 users with CDI. In case you've already migrated to JSF 2.3, use 3.x instead.

The 1.1x is basically already since 2.5 in maintenance mode. I.e. only critical bugfix versions will be released. It's currently still at 1.14.1 (May 2017), featuring the same features as OmniFaces 2.4, but without any JSF 2.2 and CDI things and therefore compatible with CDI-less JSF 2.0/2.1.