You may be wary of deploying XForms because it currently doesn't have much browser support. Most browsers will support XForms down the road, but for now, the only reliable and scalable way to implement it is through a server-side mechanism. For the purposes of this article, I'll walk you through using XForms via the Java Framework.
What is XForms?
XForms is an XML application that represents the next generation of forms for the Web. It splits traditional XHTML forms into three parts:
- · XForms model
- · Instance data
- · User interface
This division allows you to implement strong typing, reusability, separation of presentation and content, and device independence. XForms almost eliminates the need for scripting. It acts as an interface for online interaction between a person and another agent (usually remote). This interaction is achieved through XForms Processor, a program that implements the XForms specification. One such implementation is Chiba. Let’s look at the steps involved in working with it. Note that there's currently no industry standard for XForms' Java-like APIs for XML processing (JAXP, JAXM, etc.), and therefore, the Chiba internal API is subject to change in the future.
The form model
First of all, we need to build the form model that will describe the logic of our form (Listing A). This model will specify which fields are required, their format, and what XML elements of instance data they're bound to. In the example, the form is simple. It contains a list box with several choices and a text box. In a Web browser, the most convenient way to render such controls is by using a select box, <html:select>, and a text input form element, <html:input type="text">. The rendered form will look like the one in Figure A.
In the model, you can see optional default instance data contained in the <xforms:instance> element. With this instance, the XForms Processor will be initialized. In a moment, we will see the other way of passing the instance to the processor—directly from Java as a DOM node. If the instance is uninitialized, the form should render empty.
Rendering the form
Next, we can render the form and work with data the user will input (Listing B). We'll write a small servlet, drawing the form with the doGet() method, and collect the user data in a DOM document when the user submits the form through POST—that is, in the doPost() method.
One of the main goals of the Chiba XForms framework is flexibility, which relies on Java interfaces. You can just implement the interfaces that best meet your needs. For example, one user may want to store an XForms model in a separate file, while another may want to store forms in a database. This flexibility is the reason we use connectors in Chiba. We'll leverage a BasicFileSystemConnector—a connector that implements the XFormsConnector interface. One of the drawbacks of Chiba’s flexibility is that constructing an instance can be tricky because increased flexibility means increased complexity. Specifically in this example, we constructed a Chiba instance using several additional wrapper classes instead of just creating an object.
Following the instance construction
After the ChibaProcessor instance is constructed, we use the setStylesheetDir() method to set the default style sheet directory, which Chiba will use in rendering our form. Chiba heavily relies on XSLT for form rendering and handling instance data. For now, only server-side transformations are supported. Moreover, Chiba depends on a specific XSLT processor—Xalan. But the use of server-side XSLT allows the application wide presentation capabilities. For example, by changing style sheets, our forms become usable in wireless (because the format will be WML instead of XHTML) and voice browsers (the format will be VoiceXML instead of XHTML).
Create a session variable
Now, we need to save the processor instance in a session variable. It will be used again in a doPost() method when the user submits the form. Saving created objects in a session between requests is a scalable technique as long as your servlet is threadsafe. But note that by saving objects in sessions, your application will consume heavy amounts of memory.
Currently, Chiba doesn't have any built-in features for supporting multithreading, so that becomes the problem of the servlet developer. Several solutions are possible, such as pooling and/or caching Chiba processors or synchronizing the code.
We load the XForms model into the processor by calling the init() method. Here, we can save instance data in a DOM document that can be constructed in various ways: selected from a database, received from a remote Web service by SOAP, or constructed on the fly.
The magic of XSLT
Now, we can make the last magic call—getResponse()—that transparently transforms the XForms model with instance data to the presentation layer via XSLT. Here, the HTML form will be rendered in the browser, as shown in Figure A.
Handling XML data
Here's the most interesting part—handling XML data sent by the processor to the application in doPost(). In this case, the application is our servlet. We load the currently working processor from a session variable and receive a completed form as plain request parameters that were specifically encoded by Chiba.
First, we convert the instance data into XML. To accomplish this, you need to implement a special request parameters parser, XmlRequest, that returns a DOM document by calling the doTransform() method. In addition, Chiba encodes other submitted information, such as the called method, update, delete, add, and submit. After updating the instance data, we can retrieve the resulting XML document by calling a chain of methods for retrieving the instance document.
Effective for data-driven solutions
My experience has shown that XForms offers an effective solution for data-driven frameworks, such as a content management system (CMS) or customer relationship management (CRM) application. Although more and more browser platforms are starting to support this standard, there will still be a demand for server-side processing of XForms as long as thin clients are running data-driven applications.