Sunday, April 5, 2009

Validators in JSF

In JSF, validators are components that provide a mechanism for clean and easy validation of user input and, if necessary, generation of messages that can be displayed back to the client.

JSF comes with some basic built-in validators, but it allows for new, custom ones, to be added by the developer.

Built-in validators



The following validators are available out of the box:

f:validateDoubleRange: Checks whether a numeric input (convertible to double) is within a specified range.
f:validateLength: Checks if the string input length is within a given range.
f:validateLongRange: Checks whether a numeric input (convertible to long) is within a given range.


In order to be used, a validator needs to be nested into an h:input component that requires it.
To dictate the boundaries of the acceptable range, you need to specify the values for the minimum and/or maximum attributes to each of these validators.

Let's exemplify this. Assume we were creating an application that calculates monthly rates for the chosen loan. The client is expected to input the number of months they require the loan for, but our application does not allows this to be less than 6 or greater than 120 (or 10 years).

If we had a backing bean called loansManager and, within it, a HtmlInputText field, the component we would use would look like this:
<h:inputText id="account" required="true"
binding="#{loansManager.client.accountNumber}"
requiredMessage="Value is required!">
<f:validator validatorId="AccountNumberValidator"/>
</h:inputText>

Custom validators



If you need validation beyond the scope of the afore mentioned built-in components, you will have to write a custom validator to take care of your requirements.

You have two options here, to create a validator class or to create a validator method in one of your backing beans.

The first approach means implementing the Validator interface and it's validate method.

This time assume our application expects the client to input his account number in form ***.*******.**, where each * represents one character (probably should be a digit, but let's keep it simple).

The implementing class would then look like this:

package loans.validators;

import javax.faces.application.FacesMessage;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

public class AccountNumberValidator implements Validator {

public AccountNumberValidator() {
}

public void validate(FacesContext ctxt, UIComponent cmp, Object val) {

String message = null;

try {
String[] parts = ((String)val).split("-");
if (parts.length < 3)
message = "Wrong account number!";
else {
if (parts[0].length() != 3 || parts[1].length() != 7 || parts[2].length() != 2)
message = "Wrong account number!";
}
} catch (Exception e){}

if (message != null) {
FacesMessage msg = new FacesMessage(
FacesMessage.SEVERITY_ERROR, message, message);
throw new ValidatorException(msg);
}
}
}

To register this validator, you would have to add the following snippet to your faces-config.xml file:
<validator>
<validator-id>AccountNumberValidator</validator-id>
<validator-class>loans.validators.AccountNumberValidator</validator-class>
</validator>

Now, you can add your validator to any h:input component that requires it.

For example:
<h:inputText id="account" required="true"
binding="#{loansManager.client.accountNumber}"
requiredMessage="Value is required!">
<f:validator validatorId="AccountNumberValidator"/>
</h:inputText>

The second option, mentioned earlier, is writing a validator method instead. For this, we would make a validateAccountNumber method in our loansManager backing bean. The method should look like this:

public void validateAccountNumber(FacesContext ctxt, UIComponent cmp, Object val) {

String message = null;

try {
String[] parts = ((String)val).split("-");
if (parts.length < 3)
message = "Wrong account number!";
else {
if (parts[0].length() != 3 || parts[1].length() != 7 || parts[2].length() != 2)
message = "Wrong account number!";
}
} catch (Exception e){}

if (message != null) {
FacesMessage msg = new FacesMessage(
FacesMessage.SEVERITY_ERROR, message, message);
ctxt.addMessage(cmp.getClientId(ctxt), mess);
}
}

Note that this time, instead of throwing a validator exception, we're manually adding a message the FacesContext's queue. In the previous example this was done automatically.

To use this new validator, you would add it to your h:input component like this:
<h:inputText id="account" required="true"
binding="#{loansManager.client.accountNumber}"
validator="#{loansManager.validateAccountNumber}"
requiredMessage="Value is required!"/>

Note:
You might as well do validation anywhere inside your application and just add the message to the FacesContext in the same manner you've seen earlier, but this has one important constraint. In place of cmp.getClientId(ctxt) inside
ctxt.addMessage(cmp.getClientId(ctxt), msg);
you would have to hard code the component id:
ctxt.addMessage("account", msg);

since you no longer have the reference to the component itself.
Also, you might put a global message (not bound to any component) into a FacesContext message queue with
ctxt.addMessage(null, msg);

but these messages can only be displayed with <h:messages/> element.

Saturday, April 4, 2009

Extended Persistence Context

Extended persistence context is an interesting feature of the JPA specification. It allows one to declare that the injected EntityManager lives beyond the scope of the JTA transaction, that is, to extend it to the lifetime of the stateful session bean itself. This is achieved with the following annotation:
@PersistenceContext(type=PersistenceContextType.EXTENDED) EntityManager em;

Doing this has a couple of implications.
First of all, within the extended persistence context, managed entities remain managed even after the transaction commits (or is rolled back). This behavior is very different than that of a transaction scoped context, were entities get detached at the end of the transaction.
Second implication important to note is that changes done to the managed entities from outside of a transaction will not be propagated to the database immediately, and will be buffered instead. In order to get the changes reflected in the database, the EntitiyManager needs to be accessed from a transaction context and flushed (by invoking it's flush() method).

For obvious reasons, extended persistence context is only available within stateful Session Beans.

The most frequently quoted example of use is a multi-step conversation with a client, where the data that is to be persisted in a single transaction is acquired form a client in a series of steps with arbitrary periods of time in between. In this scenario, changes are sequentially applied to the managed entity in question, and the EntityManager is flushed at the very end of the conversation.

To illustrate this, image you were dealing with a multi-page client registration form. On each page, the client is expected to input some information about themselves, but each time the information pertains to a different aspect. For example, the first page could ask for general details, like name and title, the second would deal with the contact info, and the third could ask for some optional data, like an average income or a favorite hobby.
Now, to achieve this, you could create a stateful bean similar to the following:
@Stateful
public class RegistrationBean implements Registration
{
@PersistenceContext(type=PersistenceContextType.EXTENDED) EntityManager em;

private Customer customer;

public long createCustomer(String name, String lastName)
{
customer = new Customer();
customer.setName(name);
customer.setLastName(lastName);
em.persist(customer);
return customer.getId();
}

public void setContacts(String address, String phoneNumber, String email)
{
customer.setAddress(address);
customer.setPhoneNumber(phoneNumber);
customer.setEmail(email);
}

public void setOptionals(...)
{
...
}

@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void confirmRegistration() {
em.flush();
}
}

In conclusion, it's worth to note that the same effect could have been achieved by wrapping the whole conversation in a single user-managed transaction, but that it still makes more sense to exploit the power of an extended persistence context as it provides both a simpler and cleaner solution.