Validating and Converting User Input With the JSF Framework
This document is the second installment in the jAstrologer series of JSF tutorials.
In the first installment of the series,
we created a simple web application called jAstrologer. You can download
the project here if you have not run through the first installment. Make
sure you undeploy any other applications with the /jAstrologer context
root that you may have already deployed to the server.
So far the jAstrologer web application does the following:
Presents greeting.jsp, which uses two inputText components
to ask for the user's name and birthday.
Saves the values entered by the user in the properties of the UserBean
backing bean.
Reads the values of the properties from UserBean and displays them
in success.jsp.
But was it really a success? In the present state, we don't know. We just take
any input (or no input at all) and call it a success. That's why we need to
build in a little validation to our web application. In this section we're going
to do the following:
Use a converter to convert the birthday inputText field into a
java.util.Date object, which in itself checks that the data is a
valid date.
Make both textInput components required fields, which will automatically
validate that something was input and show error messages if it isn't.
Write some custom validation in the backing bean, along with nicer display
of the error messages.
Prerequisites
This document assumes you have some basic knowledge of, or programming experience
with, the following technologies:
The first thing we need to do is make sure that the user enters something for
the name field. We can easily do this by using the required attribute
of the inputText component.
Open greeting.jsp and change the name inputText component
as follows (in bold):
<p>Enter your name: <h:inputText value="#{UserBean.name}"
id="name" required="true"/>
<h:message for="name" /></p>
What we have done is to give an ID to the name text field, so we can then
specify for which component the message shows. We have then specified that
the field is required, so the web application will show the error message
if the user does not enter anything.
Run the project and click Submit without entering a name. You get the following
error:
Using a Converter
Now we need to start treating our birthday field as a date and not just a random
string. The JSF framework provides a number of converters that you can use
to convert text input into object types, like booleans and so forth. In the
process of converting the data, it also checks that the data is valid for the
type it's converting it into. This is especially handy for our birthday input
field because we can specify the date format, validate the input, and get a
nice Date object all at once.
Open greeting.jsp and change the birthday inputText component
as follows:
What we have done is to give an id to the birthday text field, so we can
then specify for which component the message is displayed. We have then set the
converter to the pattern dd/MM/yyyy. Anything the user enters that does
not match this format will cause the web application to redisplay greeting.jsp
with an error message. We have also specified that the field is required,
like we did with the name field.
Now we need to change the type of the birthday property in UserBean.java
to a Date object. Open UserBean.java and make the following
changes in bold and add an import statement to import java.util.Date:
private String name;
private Date birthday;
...
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
Run the project. When you try to click Submit without entering a date, you
get the following error message:
If you enter an invalid date, you get the following error:
Changing the Default Error Messages
The error messages that are shown for each type of validation error are controlled
by the Message.properties file, which is located in the javax.faces
package of jsf-impl.jar. You can view this file by expanding Libraries
> Sun Java System Application Server > jsf-impl.jar > javax.faces
and double-clicking Messages.properties.
You can create custom messages for these errors by replacing
the properties file used by the application.
Right-click the jAstrologer project and choose New > File/Folder. Under
the Other category, select Properties File and click Next. Name the file MyMessages,
type src/java/astrologer/ui for the Folder, and click Finish. MyMessages.properties
opens in the Source Editor. In the Files window, expand the src/java/astrologer/ui folder to see the file.
Copy over the following properties from Messages.properties to MyMessages.properties:
javax.faces.component.UIInput.REQUIRED={0}: Validation Error: Value is required.
javax.faces.converter.DateTimeConverter.DATE={2}: ''{0}'' could not be understood as a date.
javax.faces.converter.DateTimeConverter.DATE_detail={2}: ''{0}'' could not be understood as a date. Example: {1}
Change the values of the properties:
javax.faces.component.UIInput.REQUIRED=Please enter a value for this field.
javax.faces.converter.DateTimeConverter.DATE=Please enter a valid date.
javax.faces.converter.DateTimeConverter.DATE_detail=Please enter a valid date. Example: {1}
Open faces-config.xml (under the Configuration Files node in the Projects window) and
enter the following inside the main faces-config element:
Right-click the application and choose Run Project. When you do not enter
anything for a required field or enter a bad date format for the birthday
field, the application now shows the following errors:
Note that any messages that you haven't specified in your custom properties
file will be taken from the default Messages.properties in jsf-impl.jar.
Also, you can define a CSS style for error messages and then specify that
style in the message tag by doing the following:
You can code your own validators if the standard JSF validators do not work
for you. In our example, we will code a validator that checks a string to see
if it is a valid email. To create a custom validator, you create a class that
implements the javax.faces.validator.Validator interface and register
the class in faces-config.xml. You can then use the validator using
the <f:validator> tag.
Right-click the project node and choose New > Java Class. Name the class
EmailValidator, place it in the astrologer.validate package,
and click Finish.
In the class declaration, implement Validator as follows:
public class EmailValidator implements Validator {
Use the hint to implement the validate method.
Modify the method signature and add the following code to the validate method:
public void validate(FacesContext facesContext,
UIComponent uIComponent, Object value) throws ValidatorException {
//Get the component's contents and cast it to a String
String enteredEmail = (String)value;
//Set the email pattern string
Pattern p = Pattern.compile(".+@.+\\.[a-z]+");
//Match the given string with the pattern
Matcher m = p.matcher(enteredEmail);
//Check whether match is found
boolean matchFound = m.matches();
if (!matchFound) {
FacesMessage message = new FacesMessage();
message.setDetail("Email not valid - The email must be in the format ");
message.setSummary("Email not valid - The email must be in the format ");
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
}
Press Alt+Shift+F to add any necessary import statements.
(You should choose to import java.util.regex.Matcher, java.util.regex.Pattern
and javax.faces.application.FacesMessage.)
Run the project. When you enter a non-valid email in the field, you get
the following error:
Creating a Custom Converter
Although performing validation using required fields and converters is easy,
it is also very limited.
For example, the converter checks that the birthday field is a valid date,
but it doesn't check that the date was in the past. To fine-tune how the date is
checked, we will create a custom converter.
Our custom converter will check that the date is in the correct format and that it is in the past.
If an error is encountered, the converter will display the appropriate message.
To create a custom converter, you create a class that implements the javax.faces.converter.Converter
interface and then register the class in faces-config.xml.
You can then use the converter using the <f:converter> tag.
Right-click the project node and choose New > Java Class.
Name the class MyDateConverter, place it in the astrologer.convert package, and click Finish.
In the class declaration, implement Converter as follows (changes in bold):
public class MyDateConverter implements Converter {
Use the IDE hint to add the appropriate import statement and to implement the abstract methods.
(In the previous section, you used the IDE hint to implement the validate method).
The IDE generates two methods: getAsObject and getAsString.
Add the following code to the getAsObject method (Be sure to change the String parameter in the method signature to value):
public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException {
String pattern = "dd/MM/yyyy";
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
Date nDate;
try {
nDate = sdf.parse(value);
} catch (ParseException ex) {
FacesMessage message = new FacesMessage();
message.setDetail("Date is missing or not valid");
message.setSummary("Date is missing or not valid");
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ConverterException(message);
}
if(nDate.getTime() > new Date().getTime()){
FacesMessage message = new FacesMessage();
message.setDetail("Date is bigger than current date");
message.setSummary("Date is bigger than current date");
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ConverterException(message);
}
return nDate;
}
Modify the method signature and add the following to the getAsString method
(In the method signature, be sure to change the name of the Object parameter to value):
Run the project.
When you enter a non-valid or future birthday date in the field, you see the following error:
And that's it. There are, of course, a lot of other ways you can handle validation.
See the Java EE 5
Tutorial for a full description of custom validators, custom error messages,
and more robust validation.