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:
- Display image from database with p:graphicImage
- Display database blob images in <p:graphicImage> inside <ui:repeat>
- ManagedBean return null to graphicImage in Primefaces
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 levelServlets
utility class got a bunch of new methods related to cookies and JSF- New
BeansLocal
utility class next toBeans
class with the same philosophy asFaces
/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
andPre/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 ofrequired="true"
CDNResourceHandler
andCombinedResourceHandler
can now be disabled via aweb.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
whenweb.xml
context param says so Components#hasInvokedSubmit()
failed in iterating components such asUIData
andUIRepeat
- 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.
4 comments:
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?
I am using spring 4.1.2
+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
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.
Post a Comment