Level: Intermediate Richard Hightower (rhightower@arc-mind.com), CTO, ArcMind Paul Tabor (ptabor@rizonsoftware.com), CTO, Rizon Software
19 Apr 2005 Java™Server Faces (JSF) provides a standard conversion, validation, and messaging framework that accommodates most form-processing needs while ensuring data-model integrity. In this third article in the JSF for nonbelievers series, Paul Tabor and Rick Hightower show you how simple it can be to plug-in your own custom flavor of conversion or validation, even for complicated applications.
Editor's notes: Since publication, Sun has open sourced JSF 1.2 under their CDDL license. See Resources for a link to the new project page.
For details on getting started with JSF 1.2, now integrated in JEE 5, see Richard Hightower's tutorial series.
This month, Rizon Software's CTO Paul Tabor joins me on my quest to dispel the FUD about JSF. In this article, we'll work through
the concepts of JSF's conversion and validation framework, which is
much simpler to use and far more flexible than you may think.
We'll start with an introduction to conversion and validation
procedures as they apply to the JSF lifecycle, then demonstrate a
default conversion and validation procedure in a simple JSF
application. We'll also show you how to create and plug-in your own
custom implementations to deal with more demanding scenarios. As Rick
has in previous articles, we'll focus on both theory and practice,
explaining the concepts and then walking through them with a working
example. The example application will cover most conversion and
validation use cases, albeit at a basic level.
 |
JEE WARTAC blog
Author Rick Hightower takes his discussion of JSF one step further in JEE WARTAC, a blog that focuses on topics related to the tools he uses in his daily development, including JSF, Facelets, Hibernate, Spring, Tomahawk, MyFaces, RDBMS systems, and iBatis.
|
|
Note that the default build
environment for the example application is Maven, though an Ant script
is also provided. You can download the example source by clicking the
Code icon at the top or bottom of this page. For simplicity,
you'll find the example setup the same as it was in the previous
article. See Resources for further directions
on the build environment setup, including directions for building and
running the example application in Ant rather than Maven.
Conversion and validation
While it isn't necessary to understand the underpinnings of the JavaServer
Faces lifecycle to use conversion and validation in a JSF Web application, it
probably is a good idea to review some of the basics before jumping into
conversion and validation specifics. Besides, a little dose of JSF lifecycle
know-how can go a long way toward simplifying your Web application development
efforts. It will also help you better understand JSF's pluggable capabilities.
Figure 1 outlines what we call the "basic JSF lifecycle."
Basic simply implies a typical faces request-and-response
scenario dealing with submitted form values.
Figure 1. Basic JSF lifecycle
Obviously, different scenarios could differently impact the
lifecycle descriptions highlighted here. We'll describe some of them a
little later in the article. For now, you should simply note that conversion and
validation processes occur at the phases of apply request
values, process validations, and render response.
We'll explain in a minute why conversion and validation happen at
these phases, but first let's clear up a more basic question: What
is conversion? Simply put, it's the process of ensuring data is
of the right object or type. Here are two typical conversions:
- string value is convertible to a
java.util.Date
- string value is convertible to a Float
As for validation, it ensures data contains the expected
content. Here are two typical validations:
- java.util.Date is MM/yyyy format
- Float is between 1.0 and 100.0
Lifecycle phases of interest
The main purpose of conversion and validation is to ensure values have been
properly sanitized before updating model data. Subsequently, when the time
comes to invoke application methods to actually do something with that data,
you can safely make certain assumptions about the state of your model.
Conversion and validation allow you to focus on business logic rather than the
tedious qualifications of input data such as null checks, length qualifiers,
range boundaries, etc.
It makes sense, then, that conversion and validation processes happen
before component data is bound to your backing bean model in the
update model data lifecycle phase. As you saw in Figure 1,
conversion occurs in the apply request values phase and
validation in the process validations phase. These phases are
highlighted in Figure 2.
Figure 2. Conversion and validation
phases of interest
About the immediate attribute
Note that the conversion and validation processes outlined in
Figure 2 represent the application flow when the UIInput component's
immediate attribute is set to false. Were the
attribute set to true, conversion and validation would occur
earlier in the lifecycle, during the apply request values phase (see
Figure 3). A detailed discussion about using the immediate attribute is
beyond the scope of this article, but there are instances where it is
useful -- such as managing dynamic lists (which you may recall from
the previous article in this series) and even bypassing validation
altogether (when used with a UICommand component). Can
you think of an application where it would be useful to bypass
validation completely?
Figure 3 shows where conversion and validation would occur in the JSF
application lifecycle were the immediate attribute set to true.
Figure 3. When the immediate attribute is true
A working example
From here on we'll use an example application to demonstrate
the concepts discussed. This month's example application
will demonstrate JSF's conversion and validation capabilities. Bear in
mind that the application is very simple and not necessarily comprehensive:
Our goal isn't to build an application for use in the real world, after all!
The example app will demonstrate the following:
- Usage of standard JSF converters for converting form field data
- Usage of standard JSF validation components for validating form field data
- How to write custom converters and validator
- How to register custom converters and validators in the faces-config.xml file
- How to customize default error messages
The example application is a simple user registration form. Our
objective is to gather user data such as name, age, e-mail address, and
phone number. Then, we'll show you how to utilize JSF conversion and
validation to ensure that the collected data is appropriate for the model.
The application utilizes three JSP pages:
- index.jsp redirects the user to UserRegistration.jsp
- UserRegistration.jsp contains the application's form fields
- results.jsp notifies the application that the user was registered
We'll start start with a look at the options for coding JSF conversion
processes.
JSF conversion
As previously mentioned, conversion is the process of ensuring data
is of the right object or type; hence we convert strings values into
other types like Date objects, primitive float, or
Float objects. You can use built-in converters or write
custom converters.
JSF supplies many standard data converters. You can also plug in your own
custom converter by implementing the Converter interface, but more on that later.
The following table shows converter ids and the corresponding implementation classes
used by JSF for a simple data conversion. Most data conversions happen automatically.
javax.faces.BigDecimal
|
javax.faces.convert.BigDecimalConverter
|
javax.faces.BigInteger
|
javax.faces.convert.BigIntegerConverter
|
javax.faces.Boolean
|
javax.faces.convert.BooleanConverter
|
javax.faces.Byte
|
javax.faces.convert.ByteConverter
|
javax.faces.Character
|
javax.faces.convert.CharacterConverter
|
javax.faces.DateTime
|
javax.faces.convert.DateTimeConverter
|
javax.faces.Double
|
javax.faces.convert.DoubleConverter
|
javax.faces.Float
|
javax.faces.convert.FloatConverter
|
Figure 4 demonstrates a default conversion on the user's age. The JSF tag is
configured as such:
<!-- UserRegistration.jsp -->
<h:inputText id="age" value="#{UserRegistration.user.age}"/>
|
Figure 4. User registration: default
conversion on age
A converter for every occasion
UserRegistration.user.age represents a value-binding property that is of type
int. For bindings of either primitive types, or BigInteger/
BigDecimal, JSF chooses a standard converter. However, you may
also use a specific converter for increased granularity using the
<f:converter/> tag, as shown here.
<!-- UserRegistration.jsp -->
<h:inputText id="age" value="#{UserRegistration.user.age}">
<f:converter id="javax.faces.Short"/>
</h:inputText>
|
In Figure 5 you see one scenario where JSF uses a standard
converter. In this case, while age is actually a valid integer,
conversion still fails because the value is not a short integer.
Figure 5. Using the f:converter tag
Choosing a date format pattern
Although JSF handles primitives and such quite nicely by default,
when dealing with date data you must specify the conversion
tag <f:convertDateTime/>. This tag is based on the
java.text package and utilizes short, long, and custom
patterns. Here's an example:
<!-- UserRegistration.jsp -->
<h:inputText id="birthDate" value="#{UserRegistration.user.birthDate}">
<f:convertDateTime pattern="MM/yyyy"/>
</h:inputText>
|
This example demonstrates how to use
<f:convertDateTime/> to ensure the user's birth
date is convertible into a date object formatted as MM/yyyy
(month/year). See JSF's java.text.SimpleDataFormat (in
Resources) for a list of patterns.
Additional patterns
In addition to allowing you to convert date and time formats, JSF
provides a special converter for dealing with numbers such as
percentages or currency. This converter deals with grouping (such
as commas), number of decimal digits, currency symbols, and such. For
example, the following usage of <f:convertNumber/>
is one technique for dealing with currency:
<!-- UserRegistration.jsp -->
<h:inputText id="salary" value="#{UserRegistration.user.salary}">
<f:convertNumber maxFractionDigits="2"
groupingUsed="true"
currencySymbol="$"
maxIntegerDigits="7"
type="currency"/>
</h:inputText>
|
In Figure 6, you see some incorrectly formatted currency data and the
resulting conversion error.
Figure 6. Using the f:convertNumber tag
Custom converters
Custom data conversion is necessary if you need to convert field data into an
application-specific value object, as in the following examples:
- String to PhoneNumber object (PhoneNumber.areaCode, PhoneNumber.prefix, ...)
- String to Name object (Name.first, Name.last)
- String to ProductCode object (ProductCode.partNum, ProductCode.rev, ...)
To create a custom converter, you must do the following:
- Implement the
Converter interface (a.k.a.
javax.faces.convert.Converter).
- Implement the
getAsObject method, which converts a
field (string) into an object (for example, PhoneNumber).
- Implement the
getAsString method, which converts an
object (for example, PhoneNumber) into a string.
- Register your custom converter in the
Faces
context.
- Insert the converter into your JSPs with the
<f:converter/> tag.
You can see for yourself how these steps fit into the JSF application lifecycle.
In Figure 7, JSF calls the getAsObject method of the custom
converter during the apply request values phase. This is where the converter
must convert the request string value into the desired object type, and then
return the object for storage in the corresponding JSF component. When the value is rendered
back to the view, JSF will call the getAsString method in the render response
phase. This means the converter is also responsible for transforming the
object data back in to a string representation.
Figure 7. Custom converter
getAsObject and getAsString methods
Creating a custom converter
We'll use a case study to demonstrate the implementation of the Converter
interface, getAsObject and getAsString methods,
and how to register the converter with the Faces context.
The goal of this case study is to convert a single field string value into a
PhoneNumber object. We'll go through it step by step.
Step 1: Implement the Converter interface
This step implements the Converter interface.
import javax.faces.convert.Converter;
import org.apache.commons.lang.StringUtils;
...
public class PhoneConverter implements Converter {
...
}
|
Step 2: Implement the getAsObject method
This step converts a field value into a PhoneNumber object.
public class PhoneConverter implements Converter {
...
public Object getAsObject(FacesContext context, UIComponent component, String value) {
if (StringUtils.isEmpty(value)){ return null;}
PhoneNumber phone = new PhoneNumber();
String [] phoneComps = StringUtils.split(value," ,()-");
String countryCode = phoneComps[0];
phone.setCountryCode(countryCode);
if ("1".equals(countryCode)){
String areaCode = phoneComps[1];
String prefix = phoneComps[2];
String number = phoneComps[3];
phone.setAreaCode(areaCode);
phone.setPrefix(prefix);
phone.setNumber(number);
}else {
phone.setNumber(value);
}
return phone;
}
}
|
Step 3: Implement the getAsString method
This step converts a PhoneNumber object into a string.
public class PhoneConverter implements Converter {
...
public String getAsString(FacesContext context,
UIComponent component, Object value) {
return value.toString();
}
}
public class PhoneNumber implements Serializable {
...
public String toString(){
if (countryCode.equals("1")){
return countryCode + " " + areaCode
+ " " + prefix + " " + number;
}else{
return number;
}
}
}
|
Step 4: Register custom
converter with faces context
Step 4 can be executed in one of two ways. The first option is to register the
PhoneConverter class with the id of (for example)
arcmind.PhoneConverter. This id
will then be used by the <f:converter/> tag in your
JSP pages. Here's the code for Step 4, option 1:
<converter>
<converter-id>arcmind.PhoneConverter</converter-id>
<converter-class>com.arcmind.converters.PhoneConverter</converter-class>
</converter>
|
Alternately, you could register the PhoneConverter class to
handle all PhoneNumber objects automatically, as shown here.
<converter>
<converter-for-class>com.arcmind.value.PhoneNumber</converter-for-class>
<converter-class>com.arcmind.converters.PhoneConverter</converter-class>
</converter>
|
Step 5: Use the converter tag in your JSPs?
The execution of the next step will, of course, depend on the
registration option you've chosen. If you chose to register the
PhoneConverter class with the id of
arcmind.PhoneConverter, then you'll use the
<f:converter/> tag as shown here.
<h:inputText id="phone" value="#{UserRegistration.user.phone}">
<f:converter converterId="arcmind.PhoneConverter" />
</h:inputText>
|
If you chose to register the PhoneConverter class to
handle all PhoneNumber objects automatically then you
won't need to use the <f:converter/> tag in your JSPs.
Here's the code for Step 5 without the converter tag.
<h:inputText id="phone" value="#{UserRegistration.user.phone}">
[Look mom no converter!]
</h:inputText>
|
And with that we're done with the conversion processing code for
the example application! Here's what the app looks like so far.
Figure 8. Example app with
conversion processing
JSF validation
As you'll recall, JSF validation ensures the application data contains
the expected content, such as:
- java.util.Date is MM/yyyy format
- Float is between 1.0 and 100.0
There are four forms of validation within JSF:
- Built-in validation components
- Application-level validation
- Custom validation components (which implement the
Validator interface)
- Validation methods in backing beans (inline)
We'll explain and demonstrate each of these forms in the discussion that follows.
The JSF validation lifecycle and components
Figure 9 shows a lifecycle case study for the first name field of
our user registration form. The code references are intended to be interpreted as
pseudo-code.
Figure 9. Validation in the JSF lifecycle
Here's a list of the standard validation components supplied by JSF:
-
DoubleRangeValidator: Component's local
value must be numeric type; must be in range
specified by minimum and/or maximum values.
-
LongRangeValidator: Component's local value
must be numeric type and convertible to long; must be in range specified by minimum and/or
maximum values.
-
LengthValidator: Type
must be string; length must be in range
specified by minimum and/or maximum values.
Standard validation
In our example app the user's age can be any valid integer (byte, short, int).
Because it doesn't make sense to allow an age of, say, -2, you'll probably
want to add some validation to the field. Here's some simple validation code for
ensuring the model integrity of data in an age field:
<h:inputText id="age" value="#{UserRegistration.user.age}">
<f:validateLongRange maximum="150"
minimum="0"/>
</h:inputText>
|
Once you have the age field sorted out, you might want to specify length
restrictions on the first-name field. You could code this validation as follows:
<h:inputText id="firstName"
value="#{UserRegistration.user.firstName}">
<f:validateLength minimum="2"
maximum="25" />
</h:inputText>
|
Figure 10 shows the default verbose validation messages generated by the
above standard validation examples.
Figure 10. Standard validation error
messages
Although it works for many scenarios, JSF's built-in validation
is somewhat limited. When dealing with e-mail validation, phone
numbers, URLs, dates, etc., it's sometimes better to write your
own validator; but we'll get to that in just a bit.
Application-level validation
In concept, application-level validation is really business logic validation.
JSF separates form- and/or field-level validation from business-logic validation.
Basically, application-level validation entails adding additional code to
the backing bean methods that use the model to qualify the data already bound to your model.
In the case of a shopping cart, form-level validation may validate whether a quantity
entered is valid, but you would need business-logic validation to check whether
the user had exceeded his or her credit limit. This is another example of the separation
of concerns in JSF.
For example, let's say the user clicks a button that is bound to an action
method, which gets invoked during the invoke application phase (refer
back to Figure 1 for details). Prior to performing any
manipulation of model data, which was
presumably updated during the update model phase, you could add code
that checks to see if the data entered is valid based on the application's business
rules.
For instance, in the example application, the user clicks the
Register button, which is bound to the register() method of
the application controller. We could add validation code to the register() method
to determine whether the first-name field is blank or null. In cases where the
field was null, we could also add a message to the FacesContext
directing the associated component to return navigation to the
current page.
Now, granted this is not a good example of business-rule logic. A
better example would be checking to see if the user had exceeded his
or her credit limit. In this case, instead of checking to see if the
field was blank we could invoke a method on a model object to ensure
that the current user wasn't already in the system.
You can see an outline of this process in Figure 11.
Figure 11. Application-level validation
Notice how in the register() method, the message is added to the
FacesContext as ${formId}:${fieldId}. Figure 12
shows the relationship between messages and component ids.
Figure 12. A validation message
Pros and cons of application-level validation
Application-level validation is clearly straightforward and easy to do.
However, this form of validation occurs after other forms of validation
(standard, custom, component).
The advantages of application-level validation are as follows:
- Easy to implement
- No need for a separate class (custom validator)
- No need for page author to specify validator
The disadvantages of application-level validation are as follows:
- Occurs after other forms of validation (standard, custom)
- Validation logic limited to backing bean method, resulting in limited re-use
- Can be difficult to manage in large applications and/or team environment
Ultimately, application-level validation should be used only for
circumstances requiring business-logic validation.
Custom validation components
You will need to build your own custom validation components for data types
that aren't supported by the standard JSF validators, including e-mail
addresses and zip codes. You'll also need to build your own validators in
cases where you want explicit control over the validation messages displayed
to the end user. With JSF, you can create pluggable validation components that
are re-usable throughout your Web applications.
 | |
MyFaces is an open source implementation of JSF that provides many additional
validators, including some that are not included in JSF. See Resources
to learn about MyFaces.
|
|
The steps to create a custom validator are as follows; we'll go over them
one by one:
- Create a class that implements the
Validator interface
(javax.faces.validator.Validator).
- Implement the
validate method.
- Register your custom validator in the faces-confix.xml file.
- Use the
<f:validator/> tag in your JSPs.
Here's the example code to create a custom validator, step by step.
Step 1: Implement the Validator interface
The first step is to implement the Validator interface.
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;
...
public class ZipCodeValidator implements Validator{
private boolean plus4Required;
private boolean plus4Optional;
/** Accepts zip codes like 85710 */
private static final String ZIP_REGEX = "[0-9]{5}";
/** Accepts zip code plus 4 extensions like "-1119" or " 1119" */
private static final String PLUS4_REQUIRED_REGEX = "[ |-]{1}[0-9]{4}";
/** Optionally accepts a plus 4 */
private static final String PLUS4_OPTIONAL_REGEX = "([ |-]{1}[0-9]{4})?";
...
}
|
Step 2: Implement the validate method
Next, you need to implement the validate method.
public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException {
/* Create the correct mask */
Pattern mask = null;
/* more on this method later */
initProps(component);
if (plus4Required){
mask = Pattern.compile(ZIP_REGEX + PLUS4_REQUIRED_REGEX);
} else if (plus4Optional){
mask = Pattern.compile(ZIP_REGEX + PLUS4_OPTIONAL_REGEX);
} else if (plus4Required && plus4Optional){
throw new IllegalStateException("Plus 4 is either optional or required");
}
else {
mask = Pattern.compile(ZIP_REGEX);
}
/* Get the string value of the current field */
String zipField = (String)value;
/* Check to see if the value is a zip code */
Matcher matcher = mask.matcher(zipField);
if (!matcher.matches()){
FacesMessage message = new FacesMessage();
message.setDetail("Zip code not valid");
message.setSummary("Zip code not valid");
message.setSeverity(FacesMessage.SEVERITY_ERROR);
throw new ValidatorException(message);
}
}
|
Step 3: Register your custom validator
with the FacesContext
The code to register the custom validator with the FacesContext should look
familiar to you by now.
<validator>
<validator-id>arcmind.zipCodeValidator</validator-id>
<validator-class>com.arcmind.jsfquickstart.validation.ZipCodeValidator</validator-class>
</validator>
|
Step 4: Use the <f:validator/> tag in your JSPs
The <f:validator/> tag declares use of the
zipCodeValidator. The <f:attribute/>
tag sets the plus4Optional attribute to true.
Note that this defines an attribute of the inputText
component, not the validator!
<h:inputText id="zipCode" value="#{UserRegistration.user.zipCode}">
<f:validator validatorId="armind.zipCodeValidator"/>
<f:attribute name="plus4Optional" value="true"/>
</h:inputText>
|
To read the plus4Optional attribute for the zipCode
inputText component, do the following:
private void initProps(UIComponent component) {
Boolean optional = Boolean.valueOf((String) component.getAttributes().
get("plus4Optional"));
Boolean required = Boolean.valueOf((String) component.getAttributes().
get("plus4Required"));
plus4Optional = optional==null ? plus4Optional :
optional.booleanValue();
plus4Required = required==null ? plus4Optional :
required.booleanValue();
}
|
Overall, creating custom validators is fairly straightforward and makes
validation reusable across many applications. The downside is that you do
have to create another class and manage validator registration within the
faces context. However, you can take the implementation of your custom
validator one step further and make it look like built-in validation by
creating a custom tag that utilizes the validator. For common validation
concerns, such as e-mail validation, this approach can support design
philosophies where code re-use and consistent application behavior are
of utmost importance.
Validation methods in backing beans
As an alternative to creating a separate validator class, you can simply
implement custom validation in a backing bean method, as long as the method
adheres to the same argument signature as the Validator interface's
validate method. For instance, you might write the following method:
[SomeBackingBean.java]
public void validateEmail(FacesContext context,
UIComponent toValidate,
Object value) {
String email = (String) value;
if (email.indexOf('@') == -1) {
((UIInput)toValidate).setValid(false);
FacesMessage message = new FacesMessage("Invalid Email");
context.addMessage(toValidate.getClientId(context), message);
}
}
|
The method would then be used in the JSF tag via the validator attribute as
shown here:
<h:inputText id="email"
value="#{UserRegistration.user.email}"
validator="#{UserRegistration.validateEmail}"
required="true">
</h:inputText>
|
The validateEmail method is used by JSF to perform
custom validation on an inputText component value bound to
a user.email model
property. If the e-mail format is invalid, then a message is added to the faces
context for the associated component. Now, considering that this validation
method is actually part of the backing bean, why does the value have to be
generically evaluated with a component association in lieu of directly
inspecting the local bean properties? For a hint, look at the prior lifecycle
figures. Don't worry if you can't quite figure it out right now; we'll
explain it all at the end of the article.
Default validation
Notice the required attribute of the
email tag above. Utilizing the required
attribute is a form of default validation. If the attribute is
true then the corresponding component must have a value. One
important note: if the required attribute is false,
and no validation has been assigned to the tag/component, then JSF
will skip validation for this component and leave the value and
component state unchanged.
Figure 13 is an overview of the forms of validation we've discussed.
Figure 13. Validation overview
Custom messages
As you might of noticed, the default conversion and validation messages
provided by JSF are verbose and could generate confusion and
irritability for end users who insist on entering invalid form data.
Fortunately, it's possible to change the default messages supplied by JSF by
creating your own message resource bundle. Contained in the jsf-impl.jar
(or similar) is a message.properties file that contains the default messages
shown in Figure 14.
Figure 14. Default JSF conversion and
validation messages
You can change the default messages by creating your own message.properties
file and switching out the message resource bundle in the faces context for
the specified locale, as shown in Figure 15.
Figure 15. Switching out the
message resource bundles
See Resources to learn more about
creating custom conversion and validation messages in JSF.
Working the JSF lifecycle
We left a few loose ends for you to ponder earlier in the article,
and the time has come to clean them up! One thing we mentioned was
using immediate attributes for
UICommand buttons such as commandLink or
commandButtons. We asked you to consider in what types of
application scenarios you might want to skip validation.
Basically, in any scenario where a user must enter data, that data
must be validated. If the overall data entry is optional, however,
then validation need not occur. One way of working around the
validation phase of the JSF lifecycle is to utilize the
UICommand component's immediate attribute,
which can force the action to be invoked during the apply request values phase
before the process validations phase (rather than during the invoke
application phase, which occurs after the process validations phase).
The immediate attribute allows you to control page
flow through standard navigation rules while bypassing validation
altogether. You could implement this technique for specific
scenarios such as online wizards with optional steps and/or forms
(such as when the user clicks the Skip button to advance to the next
view) or in cases where the user cancels out a form for some
reason.
The second loose end we left in the article is the question of why,
if a validation method is actually part of a backing bean, its value must be
generically evaluated with a component association. We told you to
refer back to the JSF application lifecycle and see
if you could figure it out.
The trick here is that, although the validateEmail inline
validation method is part of the actual backing bean, the method must reference
the value via component association rather than accessing the local properties
directly. Because validation occurs before the component values are bound to the
model (in the update model values phase), the model is in an
unknown state. Therefore, you must write the inline custom validation logic as
if it were dealing with validation in a custom Validator object.
This is also explains the requirement of maintaining the same method signature.
What's interesting about both of these loose ends, of course, is that they
ultimately lead back to the JSF application lifecycle. Together they illustrate
the importance of understanding the lifecycle -- backwards, forwards, and
inside out -- so that you can manipulate it when you need to.
Conclusion
We've covered quite a bit of ground in this article when it comes to JSF
conversion and validation. In fact, we've covered most of what you'll ever need
to know about these processes to make them work in your
applications (at least for this version of JSF)!
Of course, we couldn't cover everything. For example, you
might want to check out MyFaces (see Resources) for validator components not offered
in JSF or discussed here. Additionally, while we discussed the most
common conversion and validation techniques, there are some we
couldn't include here. For instance, when writing custom components you
can deal with conversion and/or validation directly within the
component's decode/encode process (depending on the type of component
and its functionality); but we'll have to save a more in-depth
discussion of custom component development for a later time.
Something else to keep in mind is that conversion and validation
don't necessarily work well together. Conversion converts strings
into objects, whereas most of the standard validators work on strings.
Therefore, you must execute caution when using custom convertors and
validators together. For instance, our PhoneNumber
object would not work with a length validator. In this case, you would
either have to write a custom validator, as well, or simply include
any special validation logic in the custom converter. We prefer the
latter option because it allows us to simply associate a custom
converter (with built-in validation logic) with a specific object type
and have JSF handle that object type. JSF does this for us
automatically without having to specify any converter ids in
the JSP. (Of course, some might call this lazy programming, and it
isn't necessarily the best solution for all use cases.)
We think the discussion in this month's article shows, once again,
that JSF provides a flexible, powerful, and pluggable framework for
Web application development. In addition to standard converters and
validators, JSF facilitates custom implementations to accommodate both
application and framework developers alike. Ultimately, the
conversion and validation strategy you choose is up to you. JSF
allows you to easily and quickly get started (standard converters,
validators, inline validation) during prototyping, and migrate to more
sophisticated production solutions (custom objects, custom messages)
during later development phases. And throughout it all, the JSF
application lifecycle provides a reliable infrastructure for
consistently ensuring data-model integrity.
We'll wrap up this series next month, with an in-depth look at writing your
own custom components in JSF.
Downloads | Description | Name | Size | Download method |
|---|
| validation zip file without JAR files | jsf-validation-no-jars.zip | 32 KB | HTTP |
|---|
| validation zip file with JAR files | jsf-validation.zip | 2170 KB | HTTP |
|---|
Resources
- Click the Code icon at the top or bottom of this page to
download the article source.
- Don't believe the FUD about JSF -- and don't miss a single installment in the
JSF for nonbelievers
series.
- Visit the JSF project page to download the JavaServer Faces APIs, custom tag library,
and related documentation.
- You can download Maven from the Apache Maven Project
page.
- For detailed install and build instructions for Ant and Maven, see
the JSF
resources for this series.
- See the MyFaces home page
for a listing of validator components not currently offered by JSF.
- Jackwind Li Guojie's "UI
development with JavaServer Faces" (developerWorks, September
2003) is an early-bird's look at the technology.
- Roland Barcia's five-part "Developing JSF Applications using WebSphere
Studio V5.1.1" (developerWorks, January 2004) tutorial is a
hands-on introduction to programming with JSF.
- Srikanth Shenoy and Nithin Mallya show you how to integrate the
features of Struts, Tiles, and JavaServer Faces in the advanced article,
"Integrating Struts, Tiles, and JavaServer Faces"
(developerWorks, September 2003).
-
Browse for books on these and other technical topics.
- You might also want to check out the detailed
JSF tutorial from Sun Microsystems.
- You'll find articles about every aspect of Java programming in
the developerWorks Java technology
zone.
- Also see the Java technology zone tutorials page for a complete Listing of
free Java-focused tutorials from developerWorks.
About the authors  | |  | Rick Hightower serves as chief technology officer for ArcMind Inc.
He is coauthor of the popular book Java Tools for Extreme Programming, about
applying extreme programming to J2EE development, as well as co-author of
Professional Struts. Rick worked on
JSF QuickStart
with Warner Onstine and some of the material in this series is based on
examples in that course. Contact Rick at rhightower@arc-mind.com. |
 | |  | Paul Tabor serves as chief technology officer for Rizon Software.
He has collaborated with Rick Hightower on several projects over the past year.
Paul and Rick have utilized JSF along with Spring and Hibernate in
several high-technology applications. Contact Paul at ptabor@rizonsoftware.com or at
ptabor@arc-mind.com. |
Rate this page
|