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