FacilitatIMe form processing with the Form Processing API 2.0
Explore the new 2.0 features with JSP- and XSLT-based examples
The Form Processing API (FPAPI) represents HTML or other forms using a class named Form
. A form consists of one or more form fields, and consequently, class Form
has a number of form fields, each represented by class FormElement
. As form fields consist of different types, class FormElement
resides at the top of the hierarchy of other classes that represent specific form fields.
(I suggest you read the first part of the original “Facilitate Form Processing with the Form Processing API” to fully understand this article; the example of that article no longer applies to the new version.)
With 2.0, that hierarchy is somewhat changed and looks like Figure 1.
At the center of this structure is the validate(HttpServletRequest request)
method. This method validates all form fields based on criteria that FieldValidator
and/or GroupValidator
classes attach to each field. (Note that FieldValidator
is a new name for 1.0’s FieldController
; GroupValidator
is addressed below.) Thanks to the above class structure, a Form
object can fully validate itself, so you can separate that validation from further form processing (in fact, I might have called this API Form Validation API).
In this article, I’ll follow a how-to-use approach rather than show how the API works. Using FPAPI 2.0 in a Web application differs from the 1.0 version in three ways:
- Designing forms
- Presenting forms to the client
- Validating fields in a group
Design forms and their fields in XML format
The final task in the form design operation is to create Form
objects. In version 1.0, you had to create Form
objects by writing Java code (extending class Form
). With 2.0, you design forms by writing an XML form designer file called forms.xml
; FPAPI then reads that file and creates all Form
objects for you.
To create a Form
object, the form designer file must define the following:
- Form name
- Path to the form’s presentation file
- Path to the form’s
action
- Each field (
FormElement
) the form contains
To create a FormElement
object, the form designer file defines the following:
- Name of class the field belongs to
- Field name
- In the case of
RadioButtons
,CheckBoxes
, andMenuBoxes
, all possible values those fields can take - Field initial value, if any
- Whether the field is required or not
- Initial error message, if any
- Repeated error message, if any
FieldValidator
object(s), if anyGroupValidator
object, if any (2.0 only)
To create a FieldValidator
or a GroupValidator
object, the form designer file defines the following:
- Full class name
- Name of a reference variable to the object
A very simple forms.xml
file might look like this:
<?xml version='1.0' encoding='utf-8'?>
<forms>
<forms-field-validator name="postalCodeValidator "
type="mypackage.PostalCodeValidator"/>
<form name="myform"
page="/jsp/myforms/myform.jsp"
action="/actionPath">
<field name="firstName"
type="com.codepassion.form.TextBox">
</field>
<field name="lastName"
type="com.codepassion.form.TextBox"
required="true">
</field>
<field name="postalCode"
type="com.codepassion.form.TextBox"
required="true">
<field-required-message>
(please fill)
</field-required-message>
<field-validator>
postalCodeValidator
</field-validator>
</field>
<field name="age"
type="com.codepassion.form.RadioButton">
<field-value state="selected">
20-30
</field-value>
<field-value>
30-45
</field-value>
<field-value>
45-60
</field-value>
</field>
</form>
</forms>
You can easily see that each file element defines one item listed above. See the example application’s forms.xml
in the FPAPI 2.0 download for detailed instructions on using possible elements (tags) and their meaning. FPAPI 2.0 Javadocs also show how to express a class or a method through the form design file.
Present forms to the client
The forms.xml
file informs FPAPI how to define forms and their fields, and how to define each field’s validation logic. FPAPI uses that information to create the form object model shown in Figure 1.
Class Form
is the form object model’s central point. All presentation techniques can use Form
object as the unique reference for accessing the dynamic pieces of the form’s presentation page. The Form
class provides this information to the presentation layer in two ways. One way uses the method getFormElements()
to return an array of FormElement
objects representing all the form’s fields. The other way uses the generateXML()
or generateXmlAsString()
method to return an XML presentation of that information.
The following illustration shows how the generated XML presentation looks:
<form name="formName" >
<field type="com.codepassion.form.TextBox" >
<field-name>aFieldName</field-name>
<field-value>aValueOrNothing</field-value>
<field-error>anErrorMessageOrNothing</field-error>
</field>
<field type="com.codepassion.form.CheckBox" >
<field-name>aFieldName</field-name>
<field-value state="checked">aCheckedValue</field-value>
<field-value state="">anUncheckedValue</field-value>
<field-value state="checked">aCheckedValue</field-value>
<field-error>anErrorMessageOrNothing</field-error>
</field>
<field type="com.codepassion.form.RadioButton" >
<field-name>aFieldName</field-name>
<field-value state="checked">aCheckedValue</field-value>
<field-value state="">anUncheckedValue</field-value>
<field-error>anErrorMessageOrNothing</field-error>
</field>
<field type="com.codepassion.form.MenuBox" >
<field-name>aFieldName</field-name>
<field-value state="">anUnselectedValue</field-value>
<field-value state="selected">aSelectedValue</field-value>
<field-value state="">anUnselectedValue</field-value>
<field-error>anErrorMessageOrNothing</field-error>
</field>
</form>
Now how do you use this (or the FormElement
array) for building the form’s presentation? FPAPI’s presentation layer materializes through the com.codepassion.form.view.ContentGenerator
interface, which has two methods:
void sendFormContent(HttpServletRequest req,
HttpServletResponse resp,
Form form);
and
void forwardToFormAction(HttpServletRequest req,
HttpServletResponse resp,
Form form);
FPAPI automatically calls the sendFormContent()
method the first time the form is requested and every time the validation doesn’t succeed. The sendFormContent()
method should read all form data from the Form
object (as FormElement
objects or as XML format), make them available to the presentation page, and then forward the request to the presentation page.
FPAPI automatically calls the forwardToFormAction()
method but only when all field validation succeeds. This method should clean the session from any attributes no longer used and forward the request to the form’s action
. The form’s action
here normally maps to your application’s controller servlet, which manages further form processing.
You should only have one ContentGenerator
class per Web module. The ContentGenerator
class’s full name is entered through forms.xml
‘s <forms-content-generator>
tag.
FPAPI provides one ContentGenerator
implementation class, com.codepassion.form.utils.SimpleContentGenerator
, which you can use for JavaServer Pages (JSP) presentations. If no <forms-content-generator>
element is declared in forms.xml
, SimpleContentGenerator
is used. See FPAPI 2.0 Javadocs for more information on this class and on the ContentGenerator
interface.
Validate fields with GroupValidator interface
Validating fields in a group is a new 2.0 feature. In some situations the validity of one field’s value depends on the value(s) of one or more other fields of the same form. The simplest example is a form that has two password fields, one for entering the password, and the other for reentering the same password. These two password fields constitute a validation group, because the validity of an entry in one field depends on the entry to the other field and vice versa. The dependency criteria is values on those two fields must be the same.
Let’s take another example. Suppose you have a form that, in addition to other fields, has a “street name” field, a “postal code” field, and a “city” field. Obviously the user enters an address in those fields. Let’s also suppose that the user is not required to fill the address information. On the other hand, if he decides to supply the address information, this information should be complete; he should fill in all three fields. These three fields form a validation group; the validation criteria is even if only one field is filled, then all must be filled.
Value dependencies between fields can have infinite variations. Analogous to FieldValidator
, FPAPI handles infinite field group variations through the GroupValidator
interface. This interface has the method:
Hashtable getErrorMessages(Hashtable nameValuePairs);
Hashtable nameValuePairs
contains as keys, names of fields in the group; and as values, respective field value(s). The returned Hashtable
contains as keys, names of fields resulting in an error message; and contains as values, the respective error messages. When validation succeeds (no error messages generated) this method must return null
.
The two field group examples are commonly used, which is why FPAPI provides the implementation classes com.codepassion.form.utils.PasswordGroup
and com.codepassion.form.utils.SimpleGroup
.
Please see the FPAPI 2.0 Javadocs for more information on GroupValidator
methods before implementing them yourself.
Finally, don’t forget that the group’s individual fields might as well have one or even more FieldValidator
s registered with them, as explained in “Facilitate Form Processing with the Form Processing API.”
Form examples
Now it’s time for real examples; suppose you have the form in Figure 2.
And you have the form in Figure 3.
I’ve built two Web applications that simply validate these form entries. One application uses JSP and JavaBeans as a presentation technique, while the other uses XML-XSLT-HTML transformations.
Throughout the Web application development process, you should integrate FPAPI by following four steps:
- Design forms with
forms.xml
file - Write the necessary validation classes
- Edit the deployment descriptor (
web.xml
) - Build the form presentation
I’ll describe these steps in both application cases.
JSP and JavaBeans application
This application is called jspBeanExample; its files are in the FPAPI 2.0 download.
1. Design forms with forms.xml
Each form field is designed in forms.xml
through one field
element plus zero or more subelements, depending on that field’s requirements. To easily distinguish the different validation groups, the same (nonwhite) background color goes to all fields belonging to a certain group. Fields with no particular (white) background color belong to no group.
The registration form’s first group involves two password fields, and as mentioned above, FPAPI provides class com.codepassion.form.utils.PasswordGroup
. In forms.xml
, you first declare the following class:
<forms-field-groupValidator name="pass"
type="com.codepassion.form.utils.PasswordGroup"/>
Then apply it to both password fields:
<field name="password" type="com.codepassion.form.PasswordBox">
other elements
<field-groupValidator>pass</field-groupValidator>
</field>
<field name="passwordAgain" type="com.codepassion.form.PasswordBox">
other elements
<field-groupValidator>pass</field-groupValidator>
</field>
The second group involves three fields. This group’s validation criteria is:
- If the user selects “yes” to the “Can we contact you?” field, then he must fill the other two fields
- If the user selects “no” or makes no selection, then he’s not required to fill the group’s other two fields
I’ve implemented this criteria with a class called mypackage.validators.ContactGroup
.
The third group involves five fields. Their validation criteria is:
- All fields can be empty
- Even if only one field is filled, then all must be filled
FPAPI itself provides this criteria’s implementation through class com.codepassion.form.utils.SimpleGroup
.
See jspBeanExample’s forms.xml
in FPAPI 2.0 download.
2. Write the necessary validation classes
Simply check the validation classes’ source code. Note that I’ve created a utility class called mypackage.validators.TextRestrictions
and have used it in all FieldValidator
classes.
3. Edit the deployment descriptor
For this example, you configure in web.xml
the com.codepassion.form.control.FormControllerServlet
, which initially handles all form requests:
<servlet>
<servlet-name>
myforms
</servlet-name>
<servlet-class>
com.codepassion.form.control.FormControllerServlet
</servlet-class>
<init-param>
<param-name>
XMLReaderName
</param-name>
<param-value>
org.apache.xerces.parsers.SAXParser
</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>myforms</servlet-name>
<url-pattern>/forms/*</url-pattern>
</servlet-mapping>
First, you give this servlet the name myforms
. Then you give a value to this servlet’s XMLReaderName
init parameter. This value represents the class name of the XMLReader
used to parse forms.xml
. (This example uses Apache’s Xerces2 parser.)
The last four lines map the FPAPI controller servlet to the /forms/*
url-pattern
. Please note that FPAPI requires you use exactly the same url-pattern
for any application.
4. Build form presentation
Normally, you must do two things here: write the ContentGenerator
class and write the form presentation pages. This example uses JSP/JavaBeans for presentation, and for this case, you can use FPAPI’s SimpleContentGenerator
. See Resources for complete source code.
When called for the first time, SimpleContentGenerator
‘s SendFormContent()
method gets all FormElement
objects from the current Form
object, places them into the session as beans, and then forwards the request to the presentation page. When validation fails, SendFormContent()
is called again and just forwards the request to the presentation page. The other method forwardToFormAction()
(only called when validation succeeds) cleans the session from Form
and FormElement
objects (if required to do so) and then forwards the request to the form’s action
.
The form presentation pages in jspBeanExample are JSP files registrationForm.jsp
and questionnaireForm.jsp
. See these files from the jspBeanExample application in FPAPI 2.0 download.
For testing jspBeanExample in Tomcat 3.2.3, you add these lines to Server.xml
:
<Context path="/jspBeanExample "
docBase="C:mypathfpapi2.0examplesjspBeanExample " >
</Context>
Then add xerces.jar
into jspBeanExample’s lib
directory (to keep the size small, I did not include xerces.jar
with FPAPI 2.0 download, but you can download it from Resources). After starting the server, call:
XML/XSLT application
The second application, xsltExample, uses XML/XSL Transformations (XSLT) as a presentation technique. As explained earlier, when parsing forms.xml
, FPAPI creates one Form
object for each form. This object holds all the data needed to build the form’s presentation, and the generateXML()
or generateXmlAsString()
method returns that data in XML format. Then you can apply an XSLT to the XML format data and get whatever presentation format you want.
This example application processes XSLT transformations in a Servlet 2.3 filter. This requires you use Servlet API 2.3 for compiling, and Tomcat 4.0 for deploying the application.
Here are the four steps again, applied to this application.
1. Design forms with forms.xml
This example differs in two ways when compared to jspBeanExample’s forms.xml
. First, this file declares the ContentGenerator
as:
<forms-content-generator type="mypackage.contentGenerator.FilterContentGenerator"/>
Second, the page
attribute in each form
element is a path to registrationForm.xsl
and questionnaireForm.xsl
respectively:
<form name="registration"
page="web-inf/registrationForm.xsl"
action="/servlet/applicationController?action=registration">
and
<form name="questionnaire"
page="web-inf/questionnaireForm.xsl"
action="/servlet/applicationController?action=questionnaire">
2. Write the necessary validation classes
Validation classes are exactly the same as in the jspBeanExample application.
3. Edit the deployment descriptor
In addition to what I described in jspBeanExample’s web.xml
, you must declare and map the filter:
<filter>
<filter-name>xslFilter</filter-name>
<filter-class>mypackage.filters.FormXslFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>xslFilter</filter-name>
<url-pattern>/forms/*</url-pattern>
</filter-mapping>
Note that web.xml
requires that you place <filter>
tags before any <servlet>
tag. Also note that document type definition (DTD) declaration here is different from that used in web.xml
of jspBeanExample (version 2.2):
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"
4. Build form presentation
I have written two classes — mypackage.contentGenerator.FilterContentGenerator
and mypackage.filters.FormXslFilter
, as well as two XSL files — registrationForm.xsl
and questionnaireForm.xsl
. Class FilterContentGenerator
is this application’s ContentGenerator
. This class’s sendFormContent()
method does nothing, allowing the filter to do the presentation, while the forwardToFormAction()
method clears the session and forwards to the form’s action
.
Class FormXslFilter
extends javax.servlet.Filter
. This class’s doFilter()
method gets XML form presentations, applies XSL files to them, and generates the HTML presentations.
Note that FormXslFilter
uses Apache’s Xalan-Java 2 XSLT processor, which is why you have to place xalan.jar
(as well as xerces.jar
) in xsltExample’s lib
directory. You can download xalan.jar
from Resources.
That’s about it. After you register this application with Tomcat 4.0, test it by typing:
Final tips
Note that “form design” and “form presentation” are two different concepts and are implemented in two different ways: form design by writing forms.xml
file, and form presentation through your preferred presentation technique. Keep in mind that you must fully synchronize form presentation with form design; in simpler words, don’t forget to design what you will present first.
When using FPAPI, the URL to any form in your application is not the real path to the form’s presentation page, but is /contextName/forms/formName
, where formName
is the name
attribute’s value of the form
element designing the form.
You should use the same URL as action
in the form’s presentation files:
<form action "/contextName/forms/formName" method="post">
............
</form>
And place the classic form’s action
path, as value of form
element’s action
attribute in forms.xml
.
While one field can have unlimited FieldValidator
s, it cannot be part of more than one GroupValidator
(at least FPAPI does not support that).
Please note that the substantial changes in FPAPI 2.0 make it incompatible with the 1.0 version.
This version is released under the open Apache-style license.
Always room for improvement
Processing forms with FPAPI 2.0 removes the need for writing most code, and makes the rest easy and reusable for similar situations. Developers can use FPAPI 2.0 “as is” in projects of all sizes, but I believe we can find a way to also integrate it (or at least its approach) with more complex frameworks like Apache’s Struts or Turbine. Sure, FPAPI can be improved, extended, and optimized. Reader comments and contributions are welcome.