Monday, November 24, 2014

OmniFaces 2.0 released!

OmniFaces 2.0 has been released!

The minimum requirements have been upgraded from Java 1.6, JSF 2.0, EL 2.1 and Servlet 2.5 to Java 1.7, JSF 2.2, EL 2.2, Servlet 3.0 and CDI 1.1. An important change is thus that the optional CDI dependency is now required. OmniFaces 2.0 won't deploy on an environment without CDI. This way we can keep things simple and move forward.

As of now, OmniFaces 2.0 is still fully backwards compatible with JSF 2.1, but not with JSF 2.0 anymore. OmniFaces 2.0 is also backwards compatible with CDI 1.0, as long as you don't use the Beans#destroy() utility method which works only in CDI 1.1. Noted should be that future 2.x versions may not be backwards compatible with either JSF 2.1 or CDI 1.0 anymore, so don't take the backwards compatibility of 2.0 as a guarantee for the same in future 2.x versions.

As usual, in the What's new page of the showcase site you can find an overview of all what's been added/changed/fixed for 2.0. The top three additions which will likely become popular are the <o:viewParamValidationFailed> taghandler, <o:graphicImage> component and @Param annotation. Do however check out the What's new page for more, with among others the NoAutoGeneratedIdViewHandler and the <o:validateBean> which Arjan Tijms blogged about.

Installation

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

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

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

Handle f:viewParam validation fail with a redirect or error status

You've perhaps stumbled upon at least one of the following functional requirements:

  • Send a redirect when a <f:viewParam> has failed validation (e.g. absent, invalid format, unknown value, etc)
  • Send a HTTP error when a <f:viewParam> has failed validation (e.g. absent, invalid format, unknown value, etc)

Generally, the answer was to fiddle around in @PostConstruct, <f:viewAction>, or preRenderView and explicitly check for FacesContext#isPostback() and/or #isValidationFailed() and then deal with it using ExternalContext#redirect() or #sendError(). And then you've perhaps also take messaging into account and/or ignore the "Faces message has been enqueued but is not displayed" warnings over all place in logs.

OmniFaces now comes with a <o:viewParamValidationFailed> taghandler for this very purpose which can be applied per <f|o:viewParam>, or for them all. Here's an example which sends a HTTP 400 error when the foo parameter is absent.

<o:viewParam name="foo" required="true" >
    <o:viewParamValidationFailed sendError="400" />
</o:viewParam>

Note that the <o:viewParam> is being used instead of <f:viewParam> to bypass a design mistake in <f:viewParam required="true">. Namely, it works only exactly like as <h:inputText required="true"> when an empty string is supplied as request parameter instead of null. So, when the request parameter is totally absent, i.e. it is null, then the <f:viewParam required="true"> behaves internally differently, causing among others postValidateEvent listeners and even JSR303 bean validation annotations such as @NotNull to be entirely skipped.

Stream images directly from backing bean, optionally as data URI

The <h:graphicImage> is relatively limited in usage. In JSF 1.x it only generates a HTML <img> element without any true JSF advantages. It didn't support e.g. directly referencing a byte[] or InputStream property representing the raw image content. You could just as well use plain HTML <img> directly. In JSF 2.x, it got additional support for referencing JSF resources via new name and library attributes. However, it still didn't support directly referencing the image's content as bean property.

PrimeFaces came with <p:graphicImage> which supported referencing a special wrapper class StreamedContent which in turn should contain among others the image via an InputStream. However, this caused much trouble and confusion among starters because the majority created and assigned it as a property of a request or view scoped bean during some postback bean action. This won't work because the webbrowser requests the image in a different and independent HTTP GET request, during which the initial request scoped bean won't exist anymore, and the JSF view state of the initial view (and thus also the view scoped bean) isn't available anywhere. Basically, you'd need to create the StreamedContent only in the getter method. This is fleshed out in the answer of among others the following questions:

Whilst the getter method approach worked, this ultimately ended up in ugly and confusing backing bean code, because the getter method is called twice and you only needed to populate the StreamedContent with the image's content in the second (stateless) call. You'd be tempted to just switch to a plain vanilla @WebServlet for the sole purpose of serving images, or to use the data URI scheme to immediately render the image content directly embedded in the HTML output.

OmniFaces now comes with a <o:graphicImage> component which solves all of those problems. It supports directly referencing a byte[] or InputStream property representing the raw image content. It also immediately validates if the managed bean holding the property is @ApplicationScoped, so that the developer is automatically forced to "do things the right way" (i.e. don't accidentally deal with request/view scoped state by declaring the byte[] or InputStream as an instance variable). And, it supports a new dataURI="true" attribute which will immediately embed the image in the HTML output, and hereby thus supporting the image as property of a request or view scoped bean which is set during some postback bean action (hereby providing a perfect solution for a "preview uploaded image" case without polluting the data store).

Here's an example which shows images from a DB in a loop. Note that the getter method basically immediately invokes the service call and that it's also really only invoked when the browser actually requests the image, and thus not during rendering the HTML output. The application scoped bean is basically acting like an image servlet/service, but then with less boilerplate code than e.g. the @WebServlet approach.

@Named
@RequestScoped
public class Bean {

    private List<Image> images; // Image class should NOT have "content" property, or at least it be lazy loaded.

    @Inject
    private ImageService service;

    @PostConstruct
    public void init() {
        images = service.list();
    }

    public List<Image> getImages() {
        return images;
    }

}
@Named
@ApplicationScoped
public class ImageStreamer {

    @Inject
    private ImageService service;

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

}
<ui:repeat value="#{bean.images}" var="image">
    <o:graphicImage value="#{imageStreamer.getById(image.id)}" />
</ui:repeat>

See the showcase for live examples, with among others an upload-preview example.

Inject, convert and validate HTTP request parameters via CDI and have them ready in @PostConstruct

The @Param has been improved to support directly injecting the desired target type. So instead of

@Inject @Param private ParamValue<String> paramName;

you can now finally use

@Inject @Param private String paramName;

, lowering the burden to actually use it. It even supports injecting (auto)converted values like so

@Inject @Param private Product product;

with a @FacesConverter(forClass=Product.class).

The major advantage as compared to <f:viewParam> is that the injected value is readily available in @PostConstruct and you don't need an additional <f:viewAction> (or the JSF 2.0/2.1 <f:event type="preRenderView"> workaround) to perform the desired bean initialization based on those params. The disadvantage is that it's fully decoupled from the JSF lifecycle. I.e. conversion and validation is performed immediately during bean's construction, which is not necessarily during validations phase. Also, you would still need an additional Faces#isValidationFailed() check inside the @PostConstruct if you'd like to conditionally perform bean initialization depending on the validation outcome.

It doesn't make <f:viewParam> entirely superfluous, but depending on the business requirements it's a pretty good alternative as it makes the model (the backing bean) more declarative and the view (the XHTML) more clean. For example, one of the disadvantages of <f:viewParam> is that you can't declare it in a common template file and thus you'd have to copypaste it over all template clients. This can be solved by replacing it by using @Param on the common backing bean.

An overview of all additions/changes/bugfixes in OmniFaces 2.0

Taken over from the What's new? page on showcase:

Added in OmniFaces 2.0 (does NOT apply to 1.10)

  • NoAutoGeneratedIdViewHandler which throws a runtime exception when an autogenerated JSF client ID is being rendered
  • <o:viewParamValidationFailed> which enables sending either a redirect or error status on validation failure of view parameters
  • <o:graphicImage> which is capable of referencing a byte[] or InputStream property with optional support for data URI format
  • @Param now supports directly injecting the (auto-converted) value
  • <o:moveComponent> via which components, facets and behaviors can be moved at runtime to a target component in various ways
  • <o:resolveComponent> via which a component can be looked up by its ID and a reference to it put in various scopes
  • <o:validateBean> now supports validating beans at the class level
  • Servlets utility class got a bunch of new methods related to cookies and JSF
  • New BeansLocal utility class next to Beans class with the same philosophy as Faces/FacesLocal

Changed in OmniFaces 2.0 (also applies to 1.10)

  • Default validation message of ValidateMultipleFields components can now be supplied via <message-bundle>
  • <o:viewParam> enables support for @NotNull and Pre/PostValidateEvent even when parameter is not specified, on contrary to <f:viewParam>
  • Html5RenderKit is now not forgiving anymore on broken renderers that don't pass the current component to the write methods (fixes "plain HTML" messup)
  • Skip null/empty values in <o:validateOrder> as that's actually the responsibility of required="true"
  • CDNResourceHandler and CombinedResourceHandler can now be disabled via a web.xml context param supporting a request based EL expression

Fixed in OmniFaces 2.0 (also applies to 1.10)

  • Set system default timezone in CDI @FacesConverter when web.xml context param says so
  • Components#hasInvokedSubmit() failed in iterating components such as UIData and UIRepeat
  • Fixed <o:massAttribute> to only consider own children instead of those of parent
  • GzipResponseFilter is made better compatible with Servlet 3.1 containers

Maven download stats

Here are the Maven download stats:

  • June 2014: 4429
  • July 2014: 4243
  • August 2014: 4169
  • September 2014: 4531
  • October 2014: 3651

Below is the version pie of October 2014:

But ... I don't want / can't use CDI at all!

Don't panic! OmniFaces 1.10 is the new release from the 1.x branch and is the same as 1.8.1, but with the bugfixes from 2.0 (but no additions!) and without any CDI dependency. This still depends on Java 1.6, JSF 2.0, EL 2.1 and Servlet 2.5 like the previous 1.x releases. The CDI part, which was optional since 1.6, has completely been removed (except for the org.omnifaces.config.BeanManager singleton enum, which uses reflection anyway). The reason for this is that some 1.x users were unable to upgrade to OmniFaces 1.6 or newer in an outdated environment due to bugs or conflicts in the ancient CDI library used.

I can't say if this will be the last OmniFaces 1.x release. If there's a bug report which applies to both 1.x and 2.x, then it will be fixed for both versions. The 1.x release won't contain any new features. This is not only to push users towards CDI, but also because any newer features will most likely also depend on the existence of new APIs. For example, the <o:viewParamValidationFailed> depends on JSF 2.1 and <o:graphicImage> depends on EL 2.2.

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

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

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

Vdldoc 2.0

Vdldoc, our Facelets .taglib.xml documentation generator, has also had its 2.0 release. The Java 7 javadoc look'n'feel has been upgraded to Java 8 javadoc look'n'feel. It now also supports Java EE 7's http://xmlns.jcp.org/* XML namespace. There are no other technical/functional changes.

5 comments:

Kamil said...

I use JSF 2.2, latest web server etc. but don't want to use CDI because I have Spring 4.1 in my app. I believe there is about 50% of OmniFaces users in the same situation (guessing from Spring market share).

Spring essentially does the same thing as CDI so I am definitely not going to duplicate my libraries and knowledge.
OmniFaces is a great library. Do you think there could be a branch of OmniFaces 2 for Spring that would replace the CDI dependency with Spring dependency AND perhaps also fill in the gaps that are missing (like support for @javax.faces.view.ViewScoped). I can actually help with that. What do you think?

jack jin said...

I am using spring 4.1.2

Anton Ustyuzhanin said...

+1 for omnifaces for spring

Qlubbing said...

+1 for omnifaces for spring also. Would love to use the CombinedResourceHandler with the newer caching option. Unfortunately, adding CDI to larger Spring projects is, at least for me, not an option

Adam Steffen said...

It would be nice to couple the convenience of the @Param annotation and the viewParamValidationFailed tag. Redirecting on validation error would allow me to inject view params without needing to deal with the possibility that they are null or invalid in my bean code.