Showing posts with label OmniFaces. Show all posts
Showing posts with label OmniFaces. Show all posts

Sunday, April 4, 2021

OmniFaces 3.11 released!

OmniFaces 3.11 has been released!

Nothing shocking. Just one new utility method and a small handful improvements/fixes. It's been more than 2 months since previous release, so it's about time for a new release even if it's relatively minor.

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

Installation

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

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

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

If you're already on Jakarta EE 9 (e.g. WildFly 22, OpenLiberty 21, etc), then use 4.0-M8 instead. It's the Jakartified version of 3.11.

How about OmniFaces 2.x and 1.1x?

The 2.x got the same bugfixes as 3.11 and has been released as 2.7.11. 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.

Sunday, January 24, 2021

OmniFaces 3.10 released!

OmniFaces 3.10 has been released!

It introduces among others the new ViewResourceHandler which enables using JSF components and EL expressions in non-Facelets files, such as /sitemap.xml and /robots.txt, a new <o:sitemapUrl> component, and a new lazy="true" attribute for the <o:graphicImage.

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

Installation

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

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

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

If you're already on Jakarta EE 9 (e.g. WildFly 22, OpenLiberty 21, etc), then use 4.0-M7 instead. It's the Jakartified version of 3.10.1.

Using JSF components and EL expressions in non-Facelets files

The ViewResourceHandler allows you to register a specific set of non-Facelets files as JSF views. This will allow you to use JSF components and EL expressions in among others /sitemap.xml and /robots.txt.

In order to install it, add it as resource handler in faces-config.xml:

<application>
    <resource-handler>org.omnifaces.resourcehandler.ViewResourceHandler</resource-handler>
</application>

The specific set of non-Facelets files can in turn be defined as the value of a web.xml context parameter with the name org.omnifaces.VIEW_RESOURCE_HANDLER_URIS which accepts a comma separated string:

<context-param>
    <param-name>org.omnifaces.VIEW_RESOURCE_HANDLER_URIS</param-name>
    <param-value>/robots.txt, /sitemap.xml</param-value>
</context-param>

A live demo can be seen on the ViewResourceHandler showcase page.

New component specifically for sitemap.xml

The <o:sitemapUrl> component is designed specifically for the /sitemap.xml. The prerequisite is that the above mentioned ViewResourceHandler is installed and configured for /sitemap.xml. It basically generates a sitemap-specific <url> element, whereby a given JSF view ID will be automatically converted to a bookmarkable URL in <loc>. Optionally the lastModified, changeFrequency and priority attributes can also be specified which then generate the associated <lastmod>, <changefreq> and <priority> elements.

A live demo can be seen on the ViewResourceHandler showcase page.

Lazy images

The existing <o:graphicImage> component has been enhanced to support the new lazy="true" attribute.

<o:graphicImage ... lazy="true" />

You can use it with any type of image and image source, as long as the dataURI is not set, else the lazy attribute is simply ignored.

When set, then the image will only be actually loaded when the window is finished loading, and the image is visible in the viewport. The trick is done by initially rendering an empty SVG image as data URI in src attribute and rendering the actual URL in the data-src attribute, along with a data-lazy="true" flag. Something like below (taken from the <o:graphicImage> showcase page):

<img src="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'/%3E" 
     data-src="/javax.faces.resource/Images_getSvgLogo.svg.xhtml?ln=omnifaces.graphic&v=1611510995216"
     data-lazy="true" />

The actual lazy loading work is done by the automatically included graphicimage.js script.

How about OmniFaces 2.x and 1.1x?

The 2.x got the same bugfixes as 3.10.1 and has been released as 2.7.10. 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.

Saturday, December 19, 2020

OmniFaces 3.9 released with new exception handlers and resource handler

OmniFaces 3.9 has been released!

In this version, among others two new exception handlers have been added: the ExceptionSuppressor and ViewExpiredExceptionHandler, as well as a new resource handler: the VersionedResourceHandler. All based on contributions of Lenny Primak.

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

Installation

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

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

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

If you're already on Jakarta EE 9 (e.g. GlassFish 6), then use 4.0-M4 instead. It's the Jakartified version of 3.9.

Suppressing inevitable exceptions

The ExceptionSuppressor allows you to completely suppress exceptions by simply refreshing the current page without leaving any trace in server logs. This is useful in case of inevitable exceptions such as those caused by an abruptly aborted network connection between client and server.

In order to install it, add it as last exception handler factory in faces-config.xml (because you don't want earlier registered exception handlers from possibly taking over the handling of the to-be-suppressed exceptions; the last registered one basically wraps the previous registered one and thus runs as the first):

<factory>
    ...
    ...
    <exception-handler-factory>org.omnifaces.exceptionhandler.ExceptionSuppressorFactory</exception-handler-factory>
</factory>

And configure the to-be-suppressed exceptions as a commaseparated string of fully qualified names of exception types in web.xml as the value of a context parameter with the name org.omnifaces.EXCEPTION_TYPES_TO_SUPPRESS:

<context-param>
    <param-name>org.omnifaces.EXCEPTION_TYPES_TO_SUPPRESS</param-name>
    <param-value>java.nio.channels.ClosedByInterruptException,java.nio.channels.IllegalSelectorException</param-value>
</context-param>

Suppressing view expired exceptions

The ViewExpiredExceptionHandler is a specialized subclass of ExceptionSuppressor which does basically the same but additionally sets a specific flash scoped attribute so that the view and/or the backing bean can consult whether the currently requested page was the consequence of a refresh after a ViewExpiredException.

In order to install it, add it as exception handler factory in faces-config.xml:

<factory>
    <exception-handler-factory>org.omnifaces.exceptionhandler.ViewExpiredExceptionHandlerFactory</exception-handler-factory>
</factory>

Noted should be that this is essentially a poor practice as this is very likely to cause bad user experience when the view was not expired as the consequence of an expired session. Although this is a rather rare case, if you want to properly deal with this condition, then better use a specific error page in web.xml which can be managed by FullAjaxExceptionHandler. From the error page on, you'll have more control over whether to actually refresh/redirect the request or not.

Adding cache bust query string to resources

The VersionedResourceHandler will automatically add version parameter with query string name v to all resource URLs with a value which can be obtained from a managed bean.

In order to install it, add it as last resource handler in faces-config.xml (because you don't want to disturb any other registered resource handlers which themselves already add a v query string parameter):

<application>
    ...
    ...
    <resource-handler>org.omnifaces.resourcehandler.VersionedResourceHandler</resource-handler>
</application>

The value of the version parameter can in turn be defined as the value of a web.xml context parameter with the name org.omnifaces.VERSIONED_RESOURCE_HANDLER_VERSION which in turn can represent an EL expression referring a managed bean property:

<context-param>
    <param-name>org.omnifaces.VERSIONED_RESOURCE_HANDLER_VERSION</param-name>
    <param-value>#{environment.version}</param-value>
</context-param>

How about OmniFaces 2.x and 1.1x?

The 2.x got the same bugfixes as 3.9 and has been released as 2.7.9. 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.

Sunday, May 3, 2020

OmniFaces 3.6 adds manifest.json generator, o:scriptParam, and o:pathParam

OmniFaces 3.6 has been released!

Next to a bunch of utility methods, this version adds a WebAppManifestResourceHandler which autogenerates the manifest.json based on properties of a CDI bean, the <o:scriptParam> which allows you to set evaluated JavaScript results in a managed bean, and a <o:pathParam> which can be used in UIOutcomeTarget components (such as <h:link>) to populate desired path parameters for pages covered by MultiViews.

You can find the complete list of additions, changes and fixes at What's new in OmniFaces 3.6? list in showcase. One of most important changes is that the of:formatDateXxx() functions now also support java.time.Temporal instances.

Installation

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

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

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

WebAppManifestResourceHandler

This resource handler takes care of automatically generating a manifest.json file, which is one of the minimum requirements for a Progressive Web Application (PWA). 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']}" />

And you're done! See also the showcase.

o:scriptParam

This new little brother of <hashParam> is capable of running some JavaScript code on page load, collect its results and populate the managed bean with them. The @PostScriptParam annotation can be used to invoke a method after the script params have been populated.

For example,

<f:metadata>
    <o:scriptParam script="new Date().timeZoneOffset()" value="#{bean.clientTimeZoneOffset}" />
</f:metadata>
private Integer clientTimeZoneOffset; // +getter +setter

@PostScriptParam
public void initScriptParams() {
    System.out.println("The client time zone offset is: " + clientTimeZoneOffset);
}

See also the live demo.

o:pathParam

In case you're using MultiViews, i.e. when you have for example a /users.xhtml page which can be opened as /users/42/john-doe, then you can use the new <o:pathParam> to safely URI-encode the path parameters into any link to /users.xhtml.

For example,

<ui:repeat value="#{bean.users}" var="user">
    <h:link outcome="/users" value="#{user.name}">
        <o:pathParam value="#{user.id}">
        <o:pathParam value="#{user.slug}">
    </h:link>
</ui:repeat>

See also the showcase.

How about OmniFaces 2.x and 1.1x?

The 2.x got the same bugfixes as 3.6 and has been released as 2.7.5. 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.

Sunday, May 5, 2019

OmniFaces 3.3 (finally) released!

OmniFaces 3.3 has been released!

After a little more than 9 months after 3.2 release, the 3.3 has finally been released. I waited a bit too long because there were not really exciting new things being added since 3.2 release. In total, there are only 2 minor additions, 2 minor changes and 7 bugfixes. But it has to be released anyway. So here it is :) One of the causes of the little work is that since I moved back from the Netherlands to CuraƧao in May 2018, I didn't anymore like spending an entire night or weekend behind a computer writing code. So I've as to writing open source code been on a really low profile the last year as compared to the years before. Also, I've been working on the spinoff projects OmniPersistence and OptimusFaces, which are basically "OmniFaces for JPA" and "OmniFaces for <p:dataTable>" respectively.

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

Installation

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

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

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

How about OmniFaces 2.x and 1.1x?

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

The 1.1x is basically 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.

Maven download stats

Here are the Maven download stats since previous release:

  • July 2018: 16307
  • August 2018: 15788
  • September 2018: 15531
  • October 2018: 16735
  • November 2018: 15487
  • December 2018: 12504
  • January 2019: 15568
  • February 2019: 17324
  • March 2019: 17787
  • April 2019: 17939

Sunday, July 29, 2018

OmniFaces 3.2 adds o:hashParam, CDNResource and UUID in exception logging

OmniFaces 3.2 has been released!

Next to a bunch of utility methods, this version adds a <o:hashParam> and a CDNResource, and the FullAjaxExceptionhandler and FacesExceptionFilter will from now log the exceptions with an UUID and user IP.

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

Installation

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

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

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

o:hashParam

This new brother of <f|o:viewParam> will less or more do the same things, but then with hash query string parameters instead of request query string parameters.

The "hash query string" is the part in URL after the # which could be formatted in the same format as a regular request query string (the part in URL after the ?). An example:

http://example.com/page.xhtml#foo=baz&bar=kaz

This specific part of the URL (also called hash fragment identifier) is by default not sent to the server. The <o:hashParam> will on page load and on every window.onhashchange event send it anyway so that the JSF model gets updated, and on every JSF ajax request update the hash query string when the corresponding JSF model value has changed.

Check out the live demo!

CDNResource

This is a new javax.faces.application.Resource subclass which can be used by your custom ResourceHandler implementation which automatically uploads the local resources to a CDN and then returns the CDN URL instead of the local URL. The CDNResource offers the CombinedResourceHandler the opportunity to automatically generate a fallback URL to the local resource into the <script> and <link rel="stylesheet"> elements generated by the associated <h:outputScript>, <o:deferredScript> and <h:outputStylesheet> components. This is for now indeed only useful when you have enabled the CombinedResourceHandler. A more general appliance may come in a future OmniFaces version which should also cover non-combined resources and images.

Imagine that you have the below custom resource handler configured as <resource-handler> in faces-config.xml which automatically uploads all local CSS/JS/image resources to a Amazon S3 based CDN:

public class AmazonS3ResourceHandler extends DefaultResourceHandler {

    @Inject
    private AmazonS3Service s3;

    public AmazonS3ResourceHandler(ResourceHandler wrapped) {
        super(wrapped);
    }

    @Override
    public Resource decorateResource(Resource resource, String resourceName, String libraryName) {
        if (resource != null && Utils.endsWithOneOf(resourceName, ".js", ".css", ".jpg", ".gif", ".png", ".svg")) {
            return new CDNResource(resource, s3.getURL(resource, resourceName, libraryName));
        }
        else {
            return resource;
        }
    }
}

Whereby the your custom AmazonS3Service looks something like this, using the com.amazonaws:aws-java-sdk-s3 library:

@ApplicationScoped
public class AmazonS3Service {

    // key = resource path, value = last modified timestamp
    private static final Map<String, Long> RESOURCES = new ConcurrentHashMap<>();

    private AmazonS3 client;
    private String bucket;
    private String baseURL;

    @PostConstruct
    private void init() {
        client = AmazonS3ClientBuilder.standard().build(); // Use your own builder of course.
        bucket = "cdn"; // Use your own S3 bucket name of course.
        baseURL = "https://cdn.example.com/"; // Use your own CDN URL of course.
    }

    public String getURL(Resource resource, String resourceName, String libraryName) {
        String path = Paths.get(Utils.coalesce(libraryName, "")).resolve(resourceName).toString();
        Long lastModified = RESOURCES.computeIfAbsent(path, k -> uploadIfNecessary(resource, path));
        return baseURL + path + "?v=" + lastModified;
    }

    private long uploadIfNecessary(Resource resource, String path) {
        Long s3LastModified = null;
        long localLastModified;

        try {
            if (resource instanceof DynamicResource) { // E.g. combined resource.
                localLastModified = ((DynamicResource) resource).getLastModified();
            }
            else {
                localLastModified = resource.getURL().openConnection().getLastModified();
            }

            if (client.doesObjectExist(bucket, path)) {
                s3LastModified = client.getObjectMetadata(bucket, path).getLastModified().getTime();

                if (localLastModified > s3LastModified) {
                    s3LastModified = null;
                }
            }

            if (s3LastModified == null) {
                s3LastModified = localLastModified;
                upload(resource, path, s3LastModified);
            }
        }
        catch (Exception e) {
            throw new FacesException(e);
        }

        return s3LastModified;
    }

    private void upload(Resource resource, String path, long lastModified) throws Exception {
        String filename = Paths.get(path).getFileName().toString();
        byte[] content = Utils.toByteArray(resource.getInputStream());

        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentType(resource.getContentType());
        metadata.setContentLength(content.length);
        metadata.setContentDisposition(Servlets.formatContentDispositionHeader(filename, false));
        metadata.setLastModified(new Date(lastModified));

        client.putObject(bucket, path, new ByteArrayInputStream(content), metadata);
    }

}

Then the CDNResource marker class will automatically force the CombinedResourceHandler to generate the following <script> and <link rel="stylesheet"> markup:

<script type="text/javascript" src="https://cdn.example.com/omnifaces.combined/XYZ.js?v=123"
    onerror="document.write('&lt;script src=&quot;/javax.faces.resource/XYZ.js.xhtml?ln=omnifaces.combined&v=123&quot;&gt;&lt;/script&gt;')"></script>

<link rel="stylesheet" type="text/css" href="https://cdn.example.com/omnifaces.combined/XYZ.css?v=123"
    onerror="this.onerror=null;this.href='/javax.faces.resource/XYZ.css.xhtml?ln=omnifaces.combined&v=123'" />

You see, the CombinedResourceHandler will automatically include the onerror attribute which points to the local URL as a fallback.

UUID and IP in exception logs

A very common requirement in real world projects is that any logging of the exception stack trace should also include an unique identifier (UUID) which in turn is also included in the error page and/or an automatic exception email to the administrator. This will make it easier to find back the stack trace in the server logs.

The OmniFaces FullAjaxExceptionHandler and FacesExceptionFilter will from now on also include the UUID and client IP address in the exception logs. Besides, the FacesExceptionFilter will now also start logging exception stack traces wheres it didn't do it. The UUID is in turn available as a request attribute with the name org.omnifaces.exception_uuid which you can if necessary easily include in your custom error page.

<li>Error ID: #{requestScope['org.omnifaces.exception_uuid']}</li>

These improvements will make it unnecessary to customize the FullAjaxExceptionHandler and/or FacesExceptionFilter to include the UUID.

How about OmniFaces 2.x and 1.1x?

The 2.x got on special request also the CDNResource and hence steps from 2.6.9 to 2.7 instead of to 2.6.10. For the remainder only bugfixes are done and no other new things. As long as you're still on JSF 2.2 with CDI, you can continue using latest 2.x.

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.

Maven download stats

Here are the 2018's Maven download stats so far:

  • January 2018: 14646
  • February 2018: 14786
  • March 2018: 18059
  • April 2018: 16642
  • May 2018: 17876
  • June 2018: 17432
  • July 2018: TBD

The Definitive Guide to JSF in Java EE 8

Just in case if you have missed it .. I have finally written a book!

The Definitive Guide to JSF in Java EE 8 is since July 11, 2018 available at Amazon.com. This book is definitely a must read for anyone working with JSF or interested in JSF. It uncovers the history, inner workings, best practices and hidden gems of JSF. The source code of the book's examples can be found at GitHub.

Wednesday, January 3, 2018

OmniFaces 3.0 released!

OmniFaces 3.0 has been released!

The minimum requirements have been upgraded from Java 1.7, JSF 2.2, EL 2.2, Servlet 3.0, CDI 1.1 and (optional) BV 1.1 to Java 1.8, JSF 2.3, EL 3.0, Servlet 3.1, CDI 2.0 and (optional) BV 2.0. WebSocket 1.1 hasn't been upgraded since, so it's still the same. As of now, OmniFaces 3.0 is technically still backwards compatible with EL 2.2, CDI 1.1 and BV 1.1, as features in newer versions are still unutilized, but that may change in a future 3.x version. OmniFaces 3.0 will explicitly prevent deployment when Java 1.8, JSF 2.3 or CDI 1.1 are absent in the environment.

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

As shown in what's new in OmniFaces 3.0 at the showcase site, below is a summary of breaking changes, new things and deprecated things in OmniFaces 3.0 as compared to OmniFaces 2.6.8, which is also released today.

Breaking changes:

  • <o:form useRequestURI="true"> is now the default behavior of <o:form> as it was basically the main reason to use <o:form>. I.e. it will always submit to exactly the same URL as in browser's address bar whereas the <h:form> only submits to the forwarded URL without query string, causing among others view parameters and path parameters to get lost every time. You can if necessary disable this by <o:form useRequestURI="false"> or switching back to <h:form>.
  • <o:form> will now by default perform a partial submit on any JSF ajax request. In other words, only the parameters actually covered by execute attribute of <f:ajax> will be sent, hereby reducing the request payload to not contain unnecessary parameters. This is similar to PrimeFaces partialSubmit feature. Even though this has proven to work flawlessly in PrimeFaces for ages, you can if necessary disable this by <o:form partialSubmit="false">.
  • <o:validateBean showMessageFor="@violating"> will not anymore show the "remaining" messages (coming from other bean properties which are not covered by the JSF form) as a global message but just suppress them.
  • ValidateMultipleFields (all multi-field validators basically) have previously skipped all disabled/readonly/nonrendered inputs. Now they won't anymore be skipped, and the values argument (3rd argument of validate method) will contain their current model value (and thus not the submitted value).
  • Package org.omnifaces.component.output.cache has been migrated to org.omnifaces.util.cache and several previously private/hidden artifacts have been made public, so that it's now more useful for non-component related caches.
  • All Faces/FacesLocal/Components/Servlets methods which previously threw IOException will now throw Java8's java.io.UncheckedIOException instead, hereby reducing down unnecessary throws IOException boilerplate in methods because those should always be bubbled up into the container.
  • org.omnifaces.facesviews.FacesServletDispatchMethod and ViewHandlerMode which was deprecated since 2.6 have now been removed without replacement (as they have become superfluous since Servlet 3.0).
  • org.omnifaces.renderkit.Html5RenderKit which was deprecated since 2.2 has now been removed without replacement (as this has become superfluous since JSF 2.2 with new passthrough attribtue feature).
  • org.omnifaces.config.BeanManager which was deprecated since 2.5 has now been removed with org.omnifaces.util.Beans as replacement.
  • RichFaces targeted hacks have been removed. OmniFaces 3.0 is therefore not anymore compatible with RichFaces. Note that RichFaces itself was declared "End of Life" June 2016 and is already not compatible with JSF 2.3.

New things:

Deprecated things:

  • <o:commandScript> has been deprecated as it's now moved into JSF 2.3 as <h:commandScript> with exactly the same functionality.
  • fixviewstate.js has been deprecated as it's now finally solved in JSF 2.3 (by yours truly).
  • <o:form includeViewParams="true"> as well as <o:form includeRequestParams="true"> have been deprecated as those have never proven to be more useful than useRequestURI="true".

Noted should be that the <o:socket> is also moved into JSF 2.3 as <f:websocket> with here and there a few small API generifications. But this won't be deprecated anywhere in OmniFaces 3.x as it still has room for new ideas and improvements.

As to new things, the benefits of <o:selectItemGroups>, omnifaces.ImplicitNumberConverter and Faces#getXxxAttribute() overloads are detailed below.

Not anymore explicitly creating new SelectItemGroup()

When grouping select item options in a <optgroup>, you had to manually bake SelectItemGroup instances yourself. Assuming that you have List<Category> as wherein Category in turn has a List<Product>:

private Product selectedProduct;
private List<SelectItem> categories;

@Inject
private ProductService productService;

@PostConstruct
public void init() {
    categories = productService.listCategories().stream().map(category -> {
        SelectItemGroup group = new SelectItemGroup(category.getName());
        group.setSelectItems(category.getProducts().stream()
            .map(product -> new SelectItem(product, product.getName()))
            .toArray(SelectItem[]::new));
        return group;
    }).collect(Collectors.toList());
}
<h:selectOneMenu value="#{bean.selectedProduct}" converter="omnifaces.SelectItemsConverter">
    <f:selectItem itemValue="#{null}" />
    <f:selectItems value="#{bean.categories}" />
</h:selectOneMenu>

This is too much JSF 1.0. Even in JSF 2.3, there's still nothing like <f:selectItemGroups>. OmniFaces 3.0 therefore brings a <o:selectItemGroups> into the game.

private Product selectedProduct;
private List<Category> categories;

@Inject
private ProductService productService;

@PostConstruct
public void init() {
    categories = productService.listCategories();
}
<h:selectOneMenu value="#{bean.selectedProduct}" converter="omnifaces.SelectItemsConverter">
    <f:selectItem itemValue="#{null}" />
    <o:selectItemGroups value="#{bean.categories}" var="category" itemLabel="#{category.name}">
        <f:selectItems value="#{category.products}" var="product" itemLabel="#{product.name}" />
    </o:selectItemGroups>
</h:selectOneMenu>

Not anymore explicitly entering $ or %

Sometimes you need to have the enduser to input currencies. For that, you'd intuitively use <f:convertNumber type="currency">.

<h:outputLabel for="price" value="Price $" />
<h:inputText id="price" value="#{bean.price}">
    <f:convertNumber type="currency" currencySymbol="$" />
</h:inputText>
<h:message for="price" />

However, this would surprisingly fail with a conversion error when the enduser doesn't explicitly specify the currency symbol in the input field.

Price $
'12.34' could not be understood as a currency value. Example: $99.99

Of course you could work around it by replacing type="currency" with pattern="#,##0.00". But this is embarrassingly harder to remember than just type="currency" and even prone to locale-specific differences. In some locales the fractions are optional or even omitted. And what if the enduser thought to enter the currency symbol anyway?

With the new omnifaces.ImplicitNumberConverter you don't need to worry about this all.

<h:outputLabel for="price" value="Price $" />
<h:inputText id="price" value="#{bean.price}">
    <o:converter converterId="omnifaces.ImplicitNumberConverter" type="currency" currencySymbol="$" />
</h:inputText>
<h:message for="price" />

Entering the currency symbol has now become optional and it will be inferred when absent. On outputting, it will be hidden from the output and it will be assumed that the user interface already covers this. The same facility is also available for type="percent".

Not anymore explicitly checking if scoped attribute exists

You might have repeatedly stumbled into the below use case in some non-managed JSF artifact (where you'd otherwise of course just have used CDI):

public SomeObject getSomeObject() {
    SomeObject someObject = Faces.getRequestAttribute(SomeObject.class.getName());

    if (someObject == null) {
        someObject = new SomeObject();
        Faces.setRequestAttribute(SomeObject.class.getName(), someObject);
    }

    return someObject;
}

The getRequest/Flash/View/Session/ApplicationAttribute() methods have now all an overload which takes a Supplier<T>. The usage is now much like Map#computeIfAbsent().

public SomeObject getSomeObject() {
    return Faces.getRequestAttribute(SomeObject.class.getName(), SomeObject::new);
}

Not anymore explicitly rethrowing IOException

Some utility methods in Faces throw IOException when something fails during handling the HTTP response. Generally this is unavoidable, such as end user facing a local network error. Those exceptions are documented to be rethrown. For example,

@PostConstruct
public void init() throws IOException {
    if (someCondition()) {
        Faces.redirect("other.xhtml");
    }
}

However, redeclaring this everytime is tiresome and moreover, in this specific example it goes against the @PostConstruct contract as it may actually not throw a checked exception. Weld will even warn about this during webapp startup.

With OmniFaces 3.0, all methods which previously threw IOException will now throw it as Java 8's new UncheckedIOException instead. So you can safely strip out any unnecessary throws IOException coming from Faces/FacesLocal/Servlets utility methods over all place.

@PostConstruct
public void init() {
    if (someCondition()) {
        Faces.redirect("other.xhtml");
    }
}

Installation

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

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

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

But my server doesn't support JSF 2.3!

Don't panic. I wrote a clean set of installation instructions in Mojarra README. In a nutshell, for Tomcat 8.x you just need to upgrade the JARs and add JSONP API as compared to Mojarra 2.2 (with CDI + BV). For Java EE 7 (e.g. WildFly 8-11.x, Payara 4.x, TomEE 7.x, etc) you need to manually swap out server-provided JARs as instructed in the README.

How about OmniFaces 2.x and 1.1x?

2.x is basically already since 2.6 in maintenance mode. I.e. only bugfix versions will be released. It's currently already at 2.6.8, also released today. As long as you're still on JSF 2.2 with CDI, you can continue using latest 2.6.x, but it won't contain new things introduced in 3.x.

1.1x is basically already since 2.5 in maintenance mode. I.e. only 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.

Maven download stats

Here are the 2017's Maven download stats

  • January 2017: 10889
  • February 2017: 12060
  • March 2017: 14669
  • April 2017: 11999
  • May 2017: 12521
  • June 2017: 11535
  • July 2017: 12197
  • August 2017: 13925
  • September 2017: 13502
  • October 2017: 13080
  • November 2017: 14372
  • December 2017: 11735

Thursday, December 14, 2017

OmniFaces 3.0-RC2 ready for testing!

You can grab it here.

This is the first second (27 december 2017) release candidate of OmniFaces 3.0, which is the first version developed specifically for JSF 2.3 and Java 1.8. As of now, it won't run when you still have JSF 2.2 or Java 1.7 installed, but it will run when you still have CDI 1.1 installed, in spite of a CDI 2.0 dependency marked provided. It may change later when CDI 1.2 or 2.0 specific features are utilized.

I will later go in more detail about the changes when 3.0 final is released, for now below is a quick overview of all changes so far as compared to 2.6.7:

Breaking changes:

  • <o:form useRequestURI="true"> is now the default behavior of <o:form> as it was basically the main reason to use <o:form>. I.e. it will always submit to exactly the same URL as in browser's address bar whereas the <h:form> only submits to the forwarded URL without query string, causing among others view parameters and path parameters to get lost every time. You can if necessary disable this by <o:form useRequestURI="false"> or switching back to <h:form>.
  • <o:form> will now by default perform a partial submit on any JSF ajax request. In other words, only the parameters actually covered by execute attribute of <f:ajax> will be sent, hereby reducing the request payload to not contain unnecessary parameters. This is similar to PrimeFaces partialSubmit feature. You can if necessary disable this by <o:form partialSubmit="false">.
  • <o:validateBean showMessageFor="@violating"> will not anymore show the "remaining" messages (coming from other bean properties which are not covered by the JSF form) as a global message but just suppress them.
  • ValidateMultipleFields (all multi-field validators basically) have previously skipped all disabled/readonly/nonrendered inputs. Now they won't anymore be skipped, and the values argument (3rd argument of validate method) will contain their current model value (and thus not the submitted value).
  • Package org.omnifaces.component.output.cache has been migrated to org.omnifaces.util.cache and several previously private/hidden artifacts have been made public, so that it's now more useful for non-component related caches.
  • All Faces/FacesLocal/Components/Servlets methods which previously threw IOException will now throw Java8's java.io.UncheckedIOException instead, hereby reducing down unnecessary throws IOException boilerplate in methods because those should always be bubbled up into the container.
  • org.omnifaces.facesviews.FacesServletDispatchMethod and ViewHandlerMode which was deprecated since 2.6 have now been removed without replacement (as they have become superfluous since Servlet 3.0).
  • org.omnifaces.renderkit.Html5RenderKit which was deprecated since 2.2 has now been removed without replacement (as this has become superfluous since JSF 2.2 with new passthrough attribtue feature).
  • org.omnifaces.config.BeanManager which was deprecated since 2.5 has now been removed with org.omnifaces.util.Beans as replacement.
  • RichFaces targeted hacks have been removed. OmniFaces 3.0 is therefore not anymore compatible with RichFaces. Note that RichFaces itself was declared "End of Life" June 2016 and is already not compatible with JSF 2.3.

New things:

Deprecated things:

  • <o:commandScript> has been deprecated as it's now moved into JSF 2.3 as <h:commandScript> with exactly the same functionality.
  • fixviewstate.js has been deprecated as it's now finally solved in JSF 2.3 (by yours truly).
  • <o:form includeViewParams="true"> as well as <o:form includeRequestParams="true"> have been deprecated as those have never proven to be more useful than useRequestURI="true".

Noted should be that the <o:socket> is also moved into JSF 2.3 as <f:websocket> with here and there a few small API generifications. But this won't be deprecated anywhere in OmniFaces 3.x as it still has room for new ideas and improvements.

Installation

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

Maven users: use <version>3.0-RC2</version>.

<dependency>
    <groupId>org.omnifaces</groupId>
    <artifactId>omnifaces</artifactId>
    <version>3.0-RC2</version>
</dependency>

If you have found any issues or comments related to above listed changes, please by all means report an issue.

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.

Update 12 April 2018: OmniFaces 2.6.9 has been released with the main bugfixes being: 1) Removed accidentally introduced Java 1.8 specific methods causing OmniFaces 2.6 it to not run on Java 1.7 anymore. 2) Fixed several MyFaces incompatibilities with @ViewScoped unload. 3) Fixed the problem of standard JSF converters not being useable anymore when a @FacesConverter is created which extends from a standard JSF converter while having beans.xml with bean-discovery-mode set to all.

Installation

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

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

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

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

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.x will be the latest OmniFaces release for JSF 2.2. The focus will now 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.

Maven download stats

Here are the Maven download stats after previous release:

  • October 2016: 11663
  • November 2016: 12911
  • December 2016: 13439
  • January 2017: 10889
  • February 2017: 12060
  • March 2017: 14669

Thursday, September 15, 2016

OmniFaces 2.5.1 released with o:inputFile, @GraphicImageBean and MultiViews

Update 21 sep 2016: Oops, I made a mistake in <o:validateBean> class level validation. OmniFaces 2.5.1 has therefore been released!

OmniFaces 2.5 2.5.1 has been released!

The major visible changes are the <o:inputFile> component which extends and improves <h:inputFile>, the @GraphicImageBean annotation which can be used to mark a bean as a public and dedicated image service, and adoption of "MultiViews" in FacesViews.

The major invisible changes are removal of CDI 1.0 targeted workarounds (including the deprecation of org.omnifaces.config.BeanManager enum), intergration of Travis CI and addition of several integration tests based on JUnit/Arquillian/Selenium/Graphene. As of now, @ViewScoped, @Eager, FullAjaxExceptionHandler, FacesViews and CombinedResourceHandler have IT cases which are run against WildFly 10.0.0, TomEE 7.0.1 and Payara 4.1.1.163.

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

Installation

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

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

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

For CDI-less users, there will be no version 1.15. There are no major 2.x bugfixes anymore which should also end up in 1.1x. Therefore it isn't worth the effort to merge 2.x back to 1.1x again. And, due to major changes in 2.x project structure, merging 2.x branch back to 1.1x branch has become a tedious task which isn't worth the effort anymore. OmniFaces 1.1x is now in maintenance mode. The latest 1.1x version is still 1.14. Of course there may be bugfix releases in the future, but so far there are no major 2.4-2.5 bugs which also affect 1.14, so there's no 1.14.1 yet as of now.

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.

Multi file upload with builtin file type and size validation

The relatively new <h:inputFile> component, which was introduced only in JSF 2.2, has been extended and enhanced into new <o:inputFile> component with support for multiple file selection and folder selection via new HTML5 multiple and directory attributes.

<h:form enctype="multipart/form-data">
    <o:inputFile value="#{bean.files}" multiple="true" />
    <h:commandButton value="Upload" action="#{bean.upload}" />
</h:form>
private List<Part> files; // +getter+setter

public void upload() {
    if (files != null) {
        for (Part file : files) {
            String name = Servlets.getSubmittedFileName(file);
            String type = file.getContentType();
            long size = file.getSize();
            InputStream content = file.getInputStream();
            // ...
        }
    }
}

Also, media type filtering via new HTML5 accept attribute has been added, along with built-in server side validation based on file extension. The below example will in modern browsers only show image files in file browse dialog.

<o:inputFile value="#{bean.file}" accept="image/*" />

And, file size validation via a custom maxsize attribute has been added which runs in both client and server side. The below example sets the file size limit to 10MiB.

<o:inputFile value="#{bean.file}" maxsize="#{10 * 1024 * 1024}" />

In client side JavaScript, the new HTML5 File API will be used to check the file size and a special ajax request will be sent to trigger a JSF faces message. This all will take place without that the whole file needs to be sent to the server side. Instant feedback thus. As fallback for older non-HTML5 clients and as safeguard against spoofed requests, the server side will validate the file size once again after the file is arrived over there.

Independent image service

The <o:graphicImage> component, which was added in OmniFaces 2.0, had a builtin security restriction to prevent users from being able to invoke arbitrary bean methods by manipulating the GET request URL path. One of the consequences of this security restriction is that images served by <o:graphicImage> component aren't consistently hotlinkable. It works only if the page referencing the <o:graphicImage> has been invoked at least once in application's lifetime.

Therefore a new managed bean annotation has been introduced which should mark a bean as a dedicated and public graphic image service. When putting @GraphicImageBean annotation on the bean, then all of bean's public methods which return either InputStream or byte[] will become accessible by a direct GET request URL.

import org.omnifaces.cdi.GraphicImageBean;

@GraphicImageBean
public class Images {

    @Inject
    private ImageService service;

    public byte[] get(Long id) {
        return service.getContent(id);
    }

}

This also allowed the creation of a bunch of new EL functions such as #{of:graphicImageURL(...)} which merely print the graphic image URL without the need for a whole <o:graphicImage> component, which finally makes the below case possible:

<a href="#{of:graphicImageURL('images.full(product.imageId)')}">
    <o:graphicImage value="#{images.thumb(product.imageId)}" />
</a>
import org.omnifaces.cdi.GraphicImageBean;

@GraphicImageBean
public class Images {

    @Inject
    private ImageService service;

    public byte[] full(Long id) {
        return service.getFullContent(id);
    }

    public byte[] thumb(Long id) {
        return service.getThumbContent(id);
    }

}

MultiViews

Ones who are familiar with Apache HTTPD+PHP world are probably aware of the age-old but very useful MultiViews feature of Apache HTTPD which allowed usage of clean URLs such as http://example.com/foo/bar/baz which searches for respectively /foo/bar/baz.php, /foo/bar.php and /foo.php until the first one is found, and then passes the rest of the URL as PATH_INFO variable.

The OmniFaces-builtin extensionless URL feature known as FacesViews has been enhanced to support exactly this MultiViews feature too. It's a matter of adding /* suffix to the org.omnifaces.FACES_VIEWS_SCAN_PATHS context parameter value. The below context parameter makes all files in the current webapp available via extensionless URLs with MultiViews support.

<context-param>
    <param-name>org.omnifaces.FACES_VIEWS_SCAN_PATHS</param-name>
    <param-value>/*.xhtml/*</param-value>
</context-param>

With above configuration, an URL such as http://example.com/contextpath/foo/bar/baz will search for /foo/bar/baz.xhtml, /foo/bar.xhtml and /foo.xhtml in this order until the first one is found and then invoke it. The existing @Param annotation has been enhanced to inject path parameters by their index. Assuming that you've a /foo.xhtml, then the path parameters bar and baz can be injected in the managed bean associated with /foo.xhtml as below:

@Inject @Param(pathIndex=0)
private String bar;

@Inject @Param(pathIndex=1)
private String baz;

Maven download stats

Here are the Maven download stats after previous release:

  • July 2016: 8560
  • August 2016: 8660
  • September 2016: 9341

Friday, July 1, 2016

OmniFaces 2.4 revives c:url, improves @Param and simplifies logging

OmniFaces 2.4 has been released!

This version has relatively few new additions, only one brand new tag <o:url> which revives good 'ol JSTL <c:url> for Facelets, and a few utility methods/functions. There are several enhancements and bugfixes in existing stuff, such as the @Param finally supporting injecting multi-valued parameters such as foo=bar1&foo=bar2 into a String[] or List<String> and injecting into primitive type fields. Those few utility methods and enhancements must further simplify logging of JSF (ajax) actions, exceptions and JavaScript errors.

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

Installation

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

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

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

For users who don't want to use CDI, there's the CDI-less 1.14 with all 2.4 enhancements and fixes (but no brand new 2.x additions nor the o:url!).

Good ol' JSTL <c:url> revived

Sometimes there's need to access and/or print the current request URL "plain vanilla" in a JSF page. Common use cases are to prepopulate a ?from= request parameter for the login form so it can figure out where to redirect back after a successful login, and filling the HTML5 link relations referring the current or another page on the same site. Previously, this could to some extent (no control over domains) be done with JSTL ><c:url> which was removed in Facelets.

This has now been revived in OmniFaces 2.4 as <o:url> and even further enhanced. It supports setting the (relative) domain via domain attribute and it supports the same includeViewParams, includeRequestParams and useRequestURI attributes as <o:form>. The <o:url> also supports the var attribute allowing to set the value as an EL variable which can in turn be used in an EL expression elsewhere in the page.

Here's an example:

<o:url var="currentURL" />
...
<h:head>
    ...
    <link rel="canonical" href="#{currentURL}" />
    ...
</h:head>
<h:body>
    ...
    <h:link value="Login" outcome="login">
        <f:param name="from" value="#{currentURL}" />
    </h:link>
    ...
</h:body>

@Param now supports multiple values and primitives

The CDI @Param annotation has in OmniFaces 2.4 been further improved to also support injecting multi-valued request parameters such as foo=value1&foo=value2&foo=value3 into an array or List.

@Inject @Param(name="foo")
private List<String> foos;

@Inject @Param(name="bar")
private String[] bars;

Conversion and validation is also supported on multi-valued parameters like as already supported on single-valued parameters. On multi-valued parameters, JSF native conversion and validation will run on each submitted value. Bean Validation, if any, will however be performed on the entire List or array property and not on each individual item.

Furthermore, the @Param now also supports injecting into a primitive field. Previously this wasn't possible because CDI would attempt to inject an empty value as null into the field, which would only throw an exception (no autoboxing magic there in reflection). The @Param will now automatically take care about injecting the right default value of the primitive field in case the value is absent or empty.

Logging made easier

OmniFaces 2.4 comes with several utilities and enhancements which should further simplify logging of (ajax) actions, exceptions and JavaScript errors.

First, the Components got two new utility methods, one to find out the source component of the (ajax) form submit and another to extract all actions and listeners from it as a list of EL expression strings such as #{bean.action}, #{bean.actionListener}, #{bean.ajaxListener}.

UIComponent actionSource = Components.getCurrentActionSource();
List<String> actionExpressions = Components.getActionExpressionsAndListeners(actionSource);
// ...

This also works for e.g. <h:inputText><f:ajax listener="#{bean.listener}">. You can find a concrete example as PhaseListener in my answer to How can I log method expressions of JSF ajax requests.

Second, the FullAjaxExceptionHandler got a new logException overloaded method with a LogReason enum argument which should help in determining when exactly the exception has been caught and whether it was successfully handled or not. For example, if handling has failed because the response was already committed, or the error page itself contained an error, then you could take additional action by sending an email to site admin.

@Override
protected void logException(FacesContext context, Throwable exception, String location, LogReason reason) {
    switch(reason) {
        case RENDER_EXCEPTION_UNHANDLED:
        case ERROR_PAGE_ERROR:
            yourMethodToSendAnEmailToAdmin(exception, reason);
            break;
        default:
            super.logException(context, exception, location, reason);
    }
}

Third, the CombinedResourceHandler and <o:deferredScript> have been altered to always render crossorigin="anonymous" attribute to the generated <script> element. This will basically enable JavaScript error logging via window.onerror on scripts possibly served via a CDN. Such a logger can look like below, with little help of jQuery and a servlet:

$(window).on("error", function(message, source, line, column, error) {
    try {
        $.post("/script-error", $.param({
            url: window.location.href,
            client: navigator.userAgent,
            message: message,
            source: source,
            line: line,
            column: column,
            error: error ? error.stack : null
        }));
    }
    catch(e) {
        // Ignore.
    }
});
@WebServlet("/script-error")
public class ScriptErrorServlet extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String url = request.getParameter("url");
        String client = request.getParameter("client");
        String message = request.getParameter("message");
        String source = request.getParameter("source");
        String line = request.getParameter("line");
        String column = request.getParameter("column");
        String error = request.getParameter("error");
        // ...
    }

}

Noted should be that the detail of the information being sent depends on the client used. Chrome for example is very kind to include the full stack trace

More OmniFaces in upcoming JSF 2.3

Besides the new JSF 2.3 <f:websocket>, which is largely based on <o:socket> introduced in previous OmniFaces version, more OmniFaces solutions have been integrated into upcoming JSF 2.3 the last months. The FixViewState is now in standard jsf.js as per spec issue 790, the <o:commandScript> is integrated as new JSF 2.3 <h:commandScript> as per spec issue 613, and the <o:importConstants> is integrated as new JSF 2.3 <f:importConstants> as per spec issue 1424 (which should be declared inside <f:metadata>). Those OmniFaces artifacts will be deprecated as per upcoming OmniFaces 3.0 for JSF 2.3.

Maven download stats

Here are the Maven download stats after previous release:

  • April 2016: 8050
  • May 2016: 8590
  • June 2016: 8560

Friday, April 1, 2016

OmniFaces 2.3 brings JSF and WebSockets together!

OmniFaces 2.3 has been released!

This version brings a fairly important new tag to the world: <o:socket> for WebSocket based server-side push. Not only because it integrates JSR-356 WebSocket API seamlessly into JSF API with help of the CDI API, but also because it is the base for JSF 2.3's upcoming new <f:websocket> tag. Basically, OmniFaces offers you the opportunity to benefit of it long before JSF 2.3 itself is released (not before 2017). Afterwards, any migration should be a piece of cake.

Next to the <o:socket> and a lot of utility methods and functions, there's another new tag <o:skipValidators> and a further improved @ViewScoped which now also really physically destroys the associated JSF view state during unload (and thus not only the view scoped bean itself as it did before).

Note: whilst perhaps confusing, OmniFaces version 2.3 is still targeted at JSF 2.2. All OmniFaces 2.x versions are targeted at JSF 2.2. Only the future OmniFaces 3.x will be targeted at JSF 2.3.

Installation

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

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

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

For users who don't want to use CDI, there's the CDI-less 1.13 with all 2.3 enhancements and fixes (but no brand new 2.x additions nor the o:socket!).

Easy scoped and user-targeted push

Besides "easy to use", an important design decision of <o:socket> was being able to send push messages from the server side to a specific scope (application, session or view) and/or to a specific user.

Example 1

The below application scoped socket example will receive the same push message from the application in all pages having the same socket channel.

<o:socket channel="top10" onmessage="top10Listener" />
function top10Listener(top10) {
    console.log(top10); // Do your thing with HTML DOM.
}
@Startup @Singleton
@ConcurrencyManagement(BEAN)
public class SomeTop10Manager {

    private List<Some> top10;

    @PersistenceContext
    private EntityManager entityManager;

    @Inject
    private BeanManager beanManager;

    @PostConstruct
    @Schedule(hour="*", minute="*/1", second="0", persistent=false)
    public void load() {
        List<Some> top10 = entityManager.createNamedQuery("Some.top10", Some.class).getResultList();

        if (!top10.equals(this.top10)) {
            this.top10 = top10;
            beanManager.fireEvent(new SomeTop10Event(top10));
        }
    }

    public List<Some> list() {
        return top10;
    }

}
@ApplicationScoped
public class SomeTop10Observer {

    @Inject @Push
    private PushContext top10;

    public void onTop10Change(@Observes SomeTop10Event event) {
        top10.send(event.getTop10());
    }

}

Example 2

The below user-targeted (implicitly session scoped) socket example will receive the same push message in all pages having the same socket channel and user. This example assumes that the logged-in user is available via an EL variable as #{user} which has an id property representing its identifier.

<o:socket channel="chat" user="#{user.id}" onmessage="chatListener" />
function chatListener(message) {
    console.log(message); // Do your thing with HTML DOM.
}
<h:form>
    <p:inputText value="#{someChatBacking.message}" required="true" />
    <p:selectOneMenu value="#{someChatBacking.recipientUser}" required="true">
        <f:selectItems value="#{someSocketObserver.onlineUsers}" var="user" itemLabel="#{user.name}" />
    </p:selectOneMenu>
    <p:commandButton value="send" action="#{someChatBacking.send}" />
</h:form>
@Named
@RequestScoped
public class SomeChatBacking {

    @Inject @Push
    private PushContext chat;

    private String message;
    private User recipientUser;

    public void send() {
        Set<Future<Void>> sent = chat.send(message, recipientUser.getId());

        // TIPS:
        // sent.size() represents amount of open web sockets at send time.
        // future.get() will return null if message is successfully delivered.
        // future.get() will throw ExecutionException if message delivery failed.
        // You can observe @Opened/@Closed websockets with CDI. See o:socket showcase/javadoc.
    }

    // ...
}

Example 3

The below conditionally connected view scoped socket example will receive the push message as result of a user-initiated asynchronous task only in the current view.

<h:form>
    <p:commandButton widgetVar="asyncCommand" value="submit"
        action="#{someTaskBacking.start}" onclick="startAsync()" />
</h:form>
<o:socket channel="async" scope="view" onmessage="asyncListener" connected="false" />
function startAsync() {
    PF("asyncCommand").disable();
    OmniFaces.Push.open("async");
}
function asyncListener(result) {
    OmniFaces.Push.close("async");
    console.log(result); // Do your thing with HTML DOM.
    PF("asyncCommand").enable();
}
@Named
@RequestScoped
public class SomeTaskBacking {

    @Inject
    private SomeTaskService service;

    @Inject @Push
    private PushContext async;

    public void start() {
        service.someLongRunningAsyncTask(result -> async.send(result));
    }

}
@Stateless
public class SomeTaskService {

    @Asynchronous
    public void someLongRunningAsyncTask(Consumer<Object> callback) {
        // ...
        // ... (some long process)
        // ...

        callback.accept(result);
    }

}

Complex UI updates

The only disadvantage from the JSF perspective is that the target JSF view state isn't anywhere available at the moment the push message is being sent. So, no partial rendering based on JSF view could be performed. That's why it isn't possible to perform complex UI updates as easily as with JSF ajax. If you're not that fluent with e.g. jQuery, then you could combine <o:socket> with <o:commandScript> which in turn uses ajax to obtain the desired rendering based on the message.

<o:socket ... onmessage="commandScriptName" />
<o:commandScript name="commandScriptName" action="#{bean.pushed}" render="result" />

If you pass a Map<String, V> or a JavaBean as push message object, then all entries/properties will transparently be available as request parameters in the command script action method.

Session/view expired

Oh, if you ever wanted to immediately warn the user about an expired session, then you could now use onclose of a session scoped <o:socket> for that. If it returns code 1000 while you didn't manually close the socket, then it means that session has been expired on the server side. How useful! This also works on view scoped sockets (but indicates "View expired" instead and should actually be a very rare case if it isn't caused by an expired session).

<o:socket ... scope="session" onclose="sessionCloseListener" />
function sessionCloseListener(code) {
    if (code == 1000) {
        alert("Session has expired! Page will be reloaded.");
        window.location.reload(true); // Or just go to some login/home page.
    }
}

For an elaborate documentation and several live demos, checkout the <o:socket> showcase.

Skip validators

Contributed by Michele Mariotti, <o:skipValidators> allows developers to entirely skip JSF validation (required, <f:validateXxx>, custom validators, etc) when executing a specific UICommand or ClientBehaviorHolder action.

For example, when adding a new row to the data table, you'd like to not immediately validate all empty rows.

<h:form>
    <h:dataTable value="#{bean.items}" var="item">
        <h:column>
            <h:inputText value="#{item.value}" required="true" />
        </h:column>
    </h:dataTable>
    <h:commandButton value="add new row" action="#{bean.add}">
        <o:skipValidators />
    </h:commandButton>
    <h:commandButton value="save all data" action="#{bean.save}" />
    <h:messages />
</h:form>

Under the covers, it will basically during PreValidateEvent remove and remember all validators and re-attach them during the PostValidateEvent.

Only converters will still run. Conversion failures can still be suppressed with help of the existing <o:ignoreValidationFailed>.

Further improved @ViewScoped

The OmniFaces CDI @ViewScoped annotation has been further improved to also physically destroy the associated JSF view state in session during an unload event. I.e. during an unload it does not only destroy view scoped beans in order to eagerly free memory, but it will now also immediately push out unused JSF view states from the session. This is important in order to prevent "older" JSF view states from expiring too soon as they are basically held in a LRU map.

A good use case is described in OmniFaces issue 208:

  1. Set number of physical views to 3 (in Mojarra, use com.sun.faces.numberOfLogicalViews context param and in MyFaces use org.apache.myfaces.NUMBER_OF_VIEWS_IN_SESSION context param).
  2. Create a page which immediately initializes an OmniFaces @ViewScoped bean during load, and has a form which submits to it. For clarity, add @PostConstruct and @PreDestroy methods which log a debug line.
  3. Open this page in a tab and keep it open all time.
  4. Open the same page in another tab and then immediately close this tab.
  5. Open the same page in another tab and then immediately close this tab.
  6. Open the same page in another tab and then immediately close this tab.
  7. Submit the form in the first tab.

Previously, this would have failed with ViewExpiredException, but since OmniFaces 2.3 not anymore. Currently, a fairly convoluted hack has to be used, as the way to explicitly destroy a physical view is implementation specific. Both MyFaces and Mojarra required a different way. Currently, it only works when partial state saving is kept turned on (as by default). Those who still use full state saving should better ask themselves if it's still fruitful to keep using full state saving in JSF 2.2. During JSF 2.0/2.1 there have been reasons to keep using full state saving, but all of them are workarounds for partial state saving bugs which have long been fixed in JSF 2.2.

While at it I noticed in MyFaces org.apache.myfaces.spi.ViewScopeProvider source code a TODO comment saying that something should be provided to cleanup the session when a view is discarded. This is exactly what we want! It would be even nicer if there was a standard API which explicitly destroys the view state. Perhaps a new removeState or destroyState method in ResponseStateManager. That would make things simpler, also for the JSF implementation itself.

Maven download stats

Here are the Maven download stats after previous release:

  • November 2015: 5906
  • December 2015: 6068
  • January 2016: 6135
  • February 2016: 6635
  • March 2016: 7265

Below is the version pie of March 2016: