May 3, 2015

JSF: Validation Localization (I18N) by example (part 1 of 3)



JSF provides a powerful yet complex tech stack to back any web application. This is also due to the fact that JSF closely interacts with accompanying frameworks from the umbrella Java EE stack. In a JSF overview post I published earlier, I listed some essential resources for learning common JSF techniques; however, I didn’t find any single source of information which thoroughly explains best practices and pitfalls of the vital topic of validation and the closely related topic of internationalization (I18N), or localization. I will therefore try to cover this topic exhaustively in this article by providing hands-on tips and background information.

Validation and Internationalization (18N) basics

In JSF, validation and I18N are closely interrelated. This even kind of makes sense as a validation message written in a language the user doesn’t understand is basically useless.

Resource bundles

The localized texts are placed in two separate .properties files:
  • a “resource bundle” file (by default at /src/main/resources/messages.properties) contains all output texts you can freely define and which can be displayed in your application.
  • a “message bundle” file (by default at /src/main/resources/ValidationMessages.properties) contains all the messages provided by one of the following sources. Message keys you do not explicitly define / overwrite will yield the default message, as defined in the respective message source. You can however add your own validators with their respective default messages. Potential sources of messages are:
    • JSR 303 / JSR 349 Bean Validation framework messages
    • JSF validator messages
    • JSF converter messages
For more details and information about the mandatory configuration of faces-config.xml, please refer to this excellent stackoverflow answer by BalusC.

Concerning the file paths of the .properties files: note that these are not placed in the /src/main/webapp/resources/ folder (where static web site resources (e.g. CSS files) are stored) – their path is actually quite different. You might have to manually create that folder in your file explorer if your IDE doesn’t create it for you per default. E.g. NetBeans 8 will create that folder only after you went into Maven project properties > Frameworks and add “JavaServer Faces”.

Common best practices

In this post I will try to not only show how to do validation and I18N, but also to show how to do it in accordance with common best practices. In general, I will use the following strategies:
  • Use Bean Validation annotations for validation wherever applicable. This framework provides a cross-cutting, DRY compliant validation mechanism for all layers of an application. Use JSF validators as a technical fallback only.
  • Use JSF default mechanisms for tasks like including a component’s label in a message or styling mandatory input fields.
  • Where appropriate, use input component key filters and / or input masks alongside validation to provide assistance or instant feedback to the user and actually prevent him from even submitting invalid input.
  • Where appropriate, use accessibility enhancements for a smoother user experience and better mobile devices support.
Note that I’ve made the design decision to not use any client-side validation. (I will thus disable implicit JavaScript validation applied with HTML5 input type definitions.) JSF is a server-side framework, and using a mixture of client-side / server-side validation would typically disrupt the user experience. If you whish to, you can of course use client-side validation as an additional validation layer. From an architectural point of view, this would however mark a DRY violation when combined with Bean Validation.

By example!



I built this simple example application to illustrate some of the most common options / combinations of validators and their configuration to show them in action. I will start with the most basic configuration and build the more complex use cases on top of each other.

TL;DR? Check out the complete source code of the example application instead!

Resource bundle I18N

This really is all explained in BalusC’s advice I mentioned earlier.

Note that the default var name for referring the message-bundle is typically msg.

In order to see I18N in action, the user of course needs to be able to change the application language in their session. BalusC also explains this on stackoverflow. I implemented it in the example application according to that.

Basic I18N is of course integrated in the example code which accompanies this article.

With this faces-config.xml configuration:

    messages
    msg

and this messages.properties entry:
model.book.title=Title
We then see
<p:outputLabel value="#{msg['model.book.title']}" for="title"/>
displayed as
Title
(In the demo application, you can use the locale switch to change the view’s locale.)

Note that you explicitly need to provide a messages_xy.properties (where xy is a valid locale) file, not a common messages.properties file, even for the default locale. The latter really is only used if for the current user’s locale, no message with any requested key is found (a fallback).

In order to denote the default locale, use the faces-config.xml setting instead, as shown in BalusC’s advice.

Validation and conversion with I18N

First of all, the validation message bundle must be configured in faces.config.xml as well:
<message-bundle>ValidationMessages</message-bundle>
As with messages.properties files, you explicitly need to provide a ValidationMessages_xy.properties file for each supported locale, even for the default locale.

Bean Validation basics

As I mentioned in the introduction, the Bean Validation library will serve as the base for any input validation. This library, as specified in JSR 303 (v. 1.0, 2009) and JSR 349 (v. 1.1, 2013) offers a number of advantages:
  • based on an open standard
  • validation constraints are specified in the model, which supports domain-driven / object oriented and DRY design
  • validation constraints are implemented through annotations, thus in a declarative language
  • the library is widely integrated in the Java EE ecosystem, namely in JSF (GUI incl. localization) and JPA (persistence incl. DB schema generation)
However, the Bean Validation standard still suffers from some technical limitations and code design flaws. I thus advocate a very pragmatic way of using the library in order to stay clean code compliant.

We’ll see some examples of this throughout the next sections.

Basically, Bean Validation constraints are expressed with annotations from the javax.validation.constraints.* package. You can either annotate the field in question (recommended for clarity) or its respective getter. Annotating the setter will not work!

So if the Book entity specifies
@Min(1) private int pages;
which is used by this component:
<p:inputtext id="pages" value="#{bookController.item.pages}"/>
and in the GUI, the user sets “pages” to 0 (by this I mean: he does this and submits the form), he gets the validation error
must be greater than or equal to 1
The accompanying message however is clearly not perfect yet.

Bean Validation: Messages with component reference

Note: If you use the Apache MyFaces JSF implementation, you can skip this section as these settings are implemented as the framework’s default.

A <messages> component (either from the JSF core components or from an extension library such as PrimeFaces) is typically used to display all validation messages from validation errors during the last form submit. In order to being able to identify which component actually threw the error, you will typically want to include a reference to the component in question.

To that end, you must overwrite the Bean Validation’s default message template by adding to your ValidationMessages.properties file:
javax.faces.validator.BeanValidator.MESSAGE={1}: {0}
Here, {1} is the component’s label (or its clientId, if no label is set), and {0} is the localized validation error message.

The validation error discussed above would now be rendered as
form:pages: must be greater than or equal to 1

This is slightly better, but still not ideal. We didn’t specify a label for the pages component, thus it displayed its clientId, which is not localizable.

Validation messages with component label

In order to display the component label in validation error messages, you could either explicitly specify the label attribute for every input component. Or you can use the existing label component which typically accompanies any input component, but instead of using the JSF core implementation, you use the PrimeFaces equivalent <p:outputLabel> (or the OmniFaces equivalent <o:outputLabel>, if you don’t build upon PrimeFaces). This will implicitly set the label attribute of its accompanying input component to its own value. This is much more DRY and thus best practice.

The validation error discussed above would now be rendered as
Pages: must be greater than or equal to 1

Invalid components markup

Another typical requirement is to make invalid components stand out graphically. Here you basically have two options:
  • If you have PrimeFaces, styling of invalid components is built-in.
  • Otherwise, use OmniFaces’ <o:highlight> component. However, beware of the focus attribute (discussed below).
If you have PrimeFaces, you can adjust styling by some simple CSS rules, as does the example application.

Resetting invalid components markup

By default, when submitting a form, validation is re-executed and thus the “invalid” state of input components is reset. However, this mechanism only works for components which are actually submitted as part of the request. In a plain GET / POST request, this is always the case. However, in an AJAX request, components which are not implicitly or explicitly part of execute (for JSF standard components) or process (for PrimeFaces components) will not be submitted and will thus keep their “invalid” state.

This is most typically the case for “reset” buttons which do not actually submit any form input.

With PrimeFaces, the solution is simple: You’ll have to apply <p:resetInput> to that button. This is actually implemented in the example application’s „clear“ button:
<p:commandButton id="clearAction" action="#{bookController.clear}" value="#{msg['general.action.clear']}"
                 immediate="true" process="@this" update="@form">
    <p:resetInput target="panel" />
</p:commandButton>

Bean Validation: Validating empty fields

How do you validate empty fields? There are actually two possibilities.

You could simply use the Bean Validation constraint
@Size(min = 1) private String title;
This works because by default, JSF submits empty input fields as empty Strings.

However, this is not a best practice. JSF’s default behavior of interpreting empty input Strings as empty Strings is actually flawed. From the model’s point of view, it’s much more reasonable to interpret these values as null, thus as actually empty. This also makes testing the value much cleaner and simpler (it avoids “Stringly typed” anti-pattern).

What you should thus do instead is changing the JSF default behavior by adding to web.xml:

    javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL
    true

and then using the Bean Validation constraint
@NotNull private String title;
If in the GUI, the user then leaves “title” empty, he gets the validation error
Title: may not be null

Bean Validation: Overwrite I18N messages globally

Bean Validation’s default messages are quite… technical, to say the least. It’s thus a common requirement to overwrite the default message of a given validation constraint globally.

For example, the typical user would at least be irritated with a message such as
Title: may not be null
So let’s change that. To that end, you must again overwrite Bean Validation’s default message by adding the appropriate message key to your ValidationMessages.properties file. What is the appropriate message key? The easiest way to find out typically is to search for the unaltered validation error message in the source code of the Bean Validation implementation your project uses. This is most typically Hibernate Validator, which is the reference implementation.
You’ll find that you thus have to define in your own ValidationMessages.properties:
javax.validation.constraints.NotNull.message = must not be empty
If in the GUI, the user then leaves “title” empty, he gets the validation error
Title: must not be empty

Empty fields / “required” best practices

Note: If you use PrimeFaces 5.0+, you can skip this section and move to the next one as PrimeFaces changes default rendering of empty / „required“ components.

Typically, you want to style required components differently in the GUI in order to make them stand out. From a usability perspective, this is what the user expects from the web. Unfortunately, adding a Bean Validation constraint such as @NotNull does not have any influence at JSF component styling. Instead, you’ll be forced to use JSF’s own mechanisms to mark required components: the required attribute.

Thus, for the “title” input component, set:
<p:inputText id="title" value="#{bookController.item.title}" required="true" />
By default, this will trigger rendering an asterisk (*) next to the component’s label.

Now however, if in the GUI, the user leaves “title” empty, he gets the validation error
Title: Validation Error: Value is required.
This is because JSF’s own required validator gets precedence over the Bean Validation @NotNull annotation. You’ll thus need to overwrite JSF’s default "required" message by adding the appropriate message key to your ValidationMessages.properties file. Again, the easiest way to find out the appropriate message key is to search for the unaltered validation error message in the source code; in this case, you want to search the source code of the JSF implementation your project uses. This is typically either Oracle Mojarra, which is the reference implementation, or Apache MyFaces.
(You can ignore the fact that these files are named “Messages.properties” in above projects. You do have to place the message key in question into your ValidationMessages.properties file!)

Note that there are discrepancies between those two implementations. Most notable is the different default for javax.faces.validator.BeanValidator.MESSAGE.

You’ll find that you thus have to define in your own ValidationMessages.properties:
javax.faces.component.UIInput.REQUIRED={0}: must not be empty
Note that we must provide the “insert component reference” pattern explicitly in every JSF validation message (this is {0}). In contrast to Bean Validation, JSF does not denote any “default message template”. This indeed is quite an annoying discrepancy.

Of course, we still leave the @NotNull validation annotation in place. It will assure proper, consistent validation throughout all application layers, which the JSF “required” attribute naturally cannot do. Having two separate mechanisms for the same thing is not DRY and error-prone. As a best practice advice, I would thus change Bean Validation @NotNull's message again to something like:
javax.validation.constraints.NotNull.message=ERROR!! Must specify required="true" attribute on the JSF component !!ERROR
If the developer forgets to put required="true" on the component in question, the approprate validation error message will remember him to do so:
Title: ERROR!! Must specify required="true" attribute on the JSF component !!ERROR

Empty fields / “required” best practices: PrimeFaces 5.0+

As of V. 5.0, PrimeFaces recognizes Bean Validation constraints which make user input mandatory and automatically renders the respective label as “required” (with asterisk). It’s thus not required anymore to use the “required” attribute.

Actually, we can now invert the former best practice and set the empty component error message with
javax.validation.constraints.NotNull.message=must not be empty
whilst triggering an error in case a developer accidentally adds a required=”true” attribute by setting
javax.faces.component.UIInput.REQUIRED={0}: ERROR!! Must not use required attribute on the PrimeFaces component \
because Bean validation constraints are implicitly recognized !!ERROR
If the developer then accidentally puts required="true" on any component in question, the appropriate validation error message will remember him to not use this mechanism any more:
Title: ERROR!! Must not use required attribute on the PrimeFaces component because Bean validation constraints are implicitly recognized !!ERROR

Pages: 1 2 3