Developing interesting and effective Java Web applications requires simple, robust, and manageable frameworks and the tools that complement them. If you design and develop Java applications for a living, it could be quite a challenge to stay abreast of all the software developments and frameworks both from commercial software vendors and the vast open source community. In this article, we will survey the various Java Web development frameworks that are popular today and then take an in-depth look into the JavaServer Faces (JSF) technology.
Ever since the creation of servlets and JSPs, Web application architects and developers have contemplated on how to standardize the application development process, particularly for large enterprise projects. They often include questions such as which frameworks and design patterns to use; how to perform data validation; and how to handle exceptions, errors, and logging.
As architects, we've pursued these challenges and from time to time have adopted and used the frameworks that were in vogue and extracted their best practices. From among these many frameworks, we would predict the emergence of what could potentially become a standard for Web development only to see it slip away because it lacked some features, or it was too proprietary, and so on.
We revisited these questions recently in an enterprise portal project. It started out as a debate on what would be the critical size of a Web application to warrant using a robust framework. Once we crossed that point, the next challenge we faced was whether to advocate one from a plethora of open source frameworks or recommend our own internally developed framework. As much as Java and open source have in common, this question is an interesting one in view of enterprises that are concerned about support for the tools and frameworks used in their applications.
Once a framework is chosen, you have to justify the overheads such as the ramp-up cost and the learning curve against its return on ease-of-use, maintainability, and standardization of the developmental process. The last but not the least of those challenges is about successfully executing the project's requirements.
All of this in today's ever-challenging Web application development projects with their dynamic requirements and deadlines. Wouldn't it be desirable to have a standard-based solution that extracts the best ideas from the innumerous proprietary frameworks and frameworks from the open source communities? Could JavaServer Faces be such a standard? In this article we survey the landscape of Web development frameworks and then look under the hood of JSF and see what it has to offer.
For an Enterprise Web development framework to be successful and popular, it has to cater to two types of audiences. One type is those who are attached to the tools and IDEs whose selection may have long been made. These developers derive high productivity gains from these tools, much of which may come from familiarity and ease of development with their existing applications.
The second type is those who are not particularly locked into any tools or IDEs. These developers crave the best-of-breed solutions and often don't hesitate to create their own frameworks if that's what they think will best meet their architectural requirements. The first type has let the IDE industry thrive and prosper, although lately the IDE vendors have shrunk tremendously in number due to considerable mergers and acquisitions. The latter type has fueled the imagination and creativity of those who have made their Web development frameworks accessible to the public in varying licensing terms similar to the terms of open source software.
Survey of Open Source Web Frameworks
The open source community has contributed quite an amazing list of quality software and it's not surprising to find the same quality in some of the Web development frameworks. Some of the leading ones that come to mind are shown in Table 1.
Expresso is offered by a private company. It uses Struts as its tag library but adds capabilities of its own for security, robust object-relational mapping, background job handling and scheduling, self-tests, logging integration, automated table manipulation, and more.
JPublish is a powerful Web publishing system that uses the Velocity template engine in combination with a content management framework to build dynamic Web sites. JPublish was designed to ensure a clean separation of content, programming logic, and presentation logic. JPublish is loosely based on the FreeEnergy methodology, which was essentially designed to handle page development in an object-oriented fashion so that common objects could be reused.
The JStateMachine project is a free library designed to allow you, as an application or Web site designer, to produce and enforce a well-defined design for your application's user interface using the UML state chart notation.
SOFIA (Salmon Open Framework for Internet Applications) is a RAD tool set for J2EE. It integrates best-of-breed tools with a Java framework to provide an end-to-end solution for developing high quality database-driven Web applications.
Struts framework is a flexible control layer based on standard technologies like Java servlets, JavaBeans, ResourceBundles, and Extensible Markup Language (XML). Struts encourages application architectures based on the Model 2 approach, a variation of the classic Model-View-Controller (MVC) design paradigm. Struts provides its own controller component and integrates with other technologies to provide the model and the view. For the model, Struts can interact with any standard data access technology, including Enterprise JavaBeans, JDBC, and Object-Relational Bridge. For the view, Struts works well with JavaServer Pages, Velocity templates, XSLT, and other presentation systems.
Tapestry is a powerful, open source, all Java framework for creating leading-edge Web applications in Java. Tapestry's approach, using a component object model similar to a traditional GUI, provides a high level of reuse within and between projects, allows applications' complexity to scale well, and offers easy internationalization/localization and easy team integration.
Turbine is a servlet-based framework that allows experienced Java developers to quickly build secure Web applications. Parts of Turbine can also be used independent of the Web portion. In other words, portions of Turbine are easily available for use in other applications.
An excellent summary of their features in terms of strengths and weaknesses can be found at the Wafer project, www.waferproject.org/index.html, put together by Anthony Eden and Thomas Wheeler.
When you develop a Web application framework, what are the pressing requirements and constraints? We start by designing a controller as in Model-View-Controller. This controller distinguishes between secure and nonsecure actions of the application. It then coordinates and routes the request to the various action classes. Almost all these action classes process the request and its associated parameters, something that begs to be generalized into some kind of base class method. The action classes then proceed to do some specialized processing on their end and then start constructing the view, all the while saving state in the session.
JavaServer Faces (JSF) is a user interface framework for Java Web applications. It standardizes the life-cycle management and the internal component management that together constitute a Web application. It aids and promotes reuse at the UI component level and, at the same time, provides for a framework to assemble newer components in a standardized fashion. It provides an easier way to pass application data back and forth with UI components. It has a simple model to map client-generated events to server-side application logic. It also provides transparency in managing and reusing session data. Above all, JSF attempts to define and delineate the tasks of the various roles in a Web development project. All of the above enable easy integration with an IDE.
Right off the bat, a quick perusal of JSF will make clear that it heavily and easily integrates with JSPs; JSF even relies on JSP-related technologies. One of the promises of JSF is to abstract the eventual format of the presentation layer, and basing it on JSP-related technologies does not contradict that. An example of this reliance is the requirement of JSTL, or custom tag libraries, to aid the JSF engine in reconstituting the request tree and helping the presentation-tier designer to easily code components. The very nature of JSF's request/response architecture will lend to the natural usage of JSP as the presentation technology of choice for Web applications. JSPs and JSTL can now find a home within a standardized framework instead of being a standalone technology.
Request Processing Life Cycle
All requests within JSF go through a request processing life cycle before they get their response. This life cycle is made up of seven phases (see Figure 1). The solid lines indicate the normal flow of control. The dotted lines indicate error conditions.
Reconstitute Request Tree
As the name implies, in this phase JSF acquires any state information required, either from the request or from the session, to construct a request tree that's made up of components, irrespective of whether you were involved in rendering the previous page. If this is the first request, a new component tree gets instantiated and associated with the context. Once you have the component tree, all the associated event handlers, validators, and the RenderKit are instantiated. The appropriate locale is associated with the FacesContext and then the instance of the tree is saved.
Apply Request Values
In this phase the JSF implementation walks down the components in the request tree and delegates the task of decoding the relevant information from the request to obtain their current values. At the end of this phase, all components in the request component tree will have been updated with the new values included in this request.
Handle Request Events
Request events typically indicate the need for a change in visual representation. JSF calls getRequestEventsCount() on the associated FacesContext, walks down the components in the request tree, and delegates the processing of the events to those components so they update their state. It subsequently creates a response component tree and stores the handle in the FacesContext to allow for modification of the same. It also switches the RenderKit that will be utilized during the render response phase for the current response. Once all events for all components have been processed, the JSF implementation proceeds to the process validations phase or to the render response phase, depending on the return value of the event handlers.
In this phase, the JSF implementation must walk down the components in the request tree and call the processValidators() method on each component, which in turn calls the validate() method on the component along with any registered validators associated with this component that are to be called. All the correctness checks are performed and the results, both success and failure, are enqueued on the FacesContext of the request. A new tree instance is created as a response component tree and the RenderKit gets set to render the response. The control transfers to the Render Response phase if there are any error messages, otherwise it proceeds toward the Update Model Values phase.
Update Model Values
Control transfers to this phase only if the incoming request is syntactically and semantically correct. The local values for all the components are updated and the JSF implementation must walk down the components in the Request tree and update the application's model data by calling the updateModel() on every component. If errors occur, it's handled the same way as in the Process Validations phase, wherein control transfers to the Render Response phase. Otherwise, it resets all the nonnull values to null in the request component tree.
In this phase, the registered application handler is fetched and all the application events that have been queued up are dispatched to the processEvent() on the application event handler. Application event handlers can appropriately deal with the dispatched events. It can change the response component tree, add or modify components, change the RenderKit, add messages, or even delegate control to the Render Response phase.
Just as the name suggests, the response text is created for the contents of the response component tree. Upon completion of the rendering, the completed state of the response component tree must be saved to be made accessible to a subsequent request.
User Interface Component Model
A JSF user interface is assembled with building blocks otherwise known as user interface components. Request(s) and response(s) are made out of a component tree. The base class for all user interface components is an abstract class that defines the state information and its associated behavioral contracts. Every component has a type, an identifier, local values, and some generic attributes. It has the capability to update the model and the means to associate RequestEventHandlers, Validators, and Renderers with the components so it can add, modify, and delete them as appropriate.
As components can aggregate other components and make a tree, provisions are made to construct and walk the tree as necessary. The behavior of user interface components is described by events, more specifically FacesEvents. FacesEvents inherit from java.util.EventObject and conform to the design patterns of the JavaBeans specification.
Two special kinds of events exist, a CommandEvent and a FormEvent. CommandEvent indicates an application command that should be processed by the application. FormEvent indicates a form submission. Validators are objects that can be registered with a component to perform certain validations. Every data type has well-defined syntax and semantics. It's therefore very desirable to extract the common operations that we perform on them, thus making these operations reusable instead of delegating these operations to the application development phase.
Some of these validation operations can also be defined in configuration files. JSF comes up with many standard validators such as DoubleRangeValidator, LengthValidator, LongRangeValidator, RequiredValidator, and StringRangeValidator. DoubleRangeValidator checks a value, optionally convertible from a string, against the minimum and maximum value specified in an attribute. LengthValidator checks the length of a string. RequiredValidator checks for a nonnull value, and StringRangeValidator checks for the range of a string object.
Standard User Interface Components
JSF provides a number of concrete user interface components that cover the most basic requirements. These components will grow in number and can be extended by component developers and vendors. The components have render-independent characteristics similar to the JavaBean properties. UICommand, UIForm, UIGraphic, UIInput, UIOutput, UISelectButton, SelectItem, UISelectItem, and UITextEntry are some of the components in the current version of the spec. UICommand class is one such UIComponent that when activated by the user triggers an application-specific command. The default behavior of these components include decode() to process and encodeBegin() and encodeEnd() to render the HTML. The other components behave similarly and abstract the common behavior of widgets seen in the UI world from the yesteryears.
Per-Request State Information
Now, let's look at two key objects in a JSF implementation. The first is javax.faces.context.FacesContext. FacesContext is defined as an abstract class that leaves room for the API to have default behavior, although none exists in the reference implementation as of yet. The purpose of FacesContext is to provide server-side developers with a window into and access to the JSF implementation. More precisely, it provides in-context information for the current incoming request and eventual response. This includes the request and response UI component object graphs (Tree), servlet request, servlet response, session variables, and a host of other information (see Figure 2).
In addition, FacesContext is the repository that events are registered against. FacesEvents are generated when action is taken on the UI, such as clicking on a link or submitting a form. Descendants of FacesEvents include CommandEvent and FormEvent, where the CommandEvent handles invocation, such as URL clicks and FormEvents handle form submissions.
If you've worked with ServletContext, then FacesContext is familiar. It provides necessary information about the current request, which implies that the life of a FacesContext object is delineated by a single request/response cycle. Since FacesServlet.service() is the entry point into a JSF application, you'll find that this is where the FacesContext is created and, after pacing through all the phases, will get a release() call.
If you are a component developer or building a JSF app, you can get a reference to the FacesContext primarily via callback methods. For example, in the Invoke Application phase, your application handler will have a callback executed. This callback has a signature of processEvent (FacesContext facescontext, FacesEvent facesevent). In this method you're responsible for entertaining each event fired by the user. By the way, if you're thinking that this will lead to a branching nightmare (remember WinMain() ?) to properly delegate event handling, then you're right. We'd like to see some type of XML configuration file where events can declaratively invoke event handler classes. This is something akin to what Struts has done à la Action classes by implementing a Command pattern. However, we'll leave that for the JCP group to figure out.
Life Cycle Management
The second object worthy of mention is javax.faces.lifecycle.Lifecyle. Understanding this object will provide a stronger understanding of JSF's life-cycle management requirements and capabilities. Like FacesContext, Lifecycle is an abstract class and needs to be implemented by JSF providers. Take a quick glance at the class diagram for Lifecycle shown in Figure 3. As you'll see, static members are defined for each phase in the request processing cycle. Lifecycle's job is to act like a state machine and pace the request through the phases, and these static members work as state condition markers. As mentioned earlier, the entry point into a JSF app is FacesServlet. In addition to creating the FacesContext object (or pulling it from a pool), you'll find that in this servlet is where Lifecycle gets kicked into action. Lifecycle's execute() starts the request life-cycle process. An interesting feature that would be nice to have is the ability to add custom phases (i.e., by subclassing phase and registering it with Lifecycle).
Once Lifecycle is geared up, it adheres to its responsibility of cycling the request through the phases or short-circuiting directly to the Render Response phase. Once the request has done its time in the life-cycle process, it's time to push the response that's prepared. Lifecycle delegates the response navigation to a ViewHandler. According to the specification, the ViewHandler will be renamed, possibly as Navigation-
Handler. This is more descriptive of its behavior, as the ViewHandler is responsible for determining where the client should forward to. In the case of a JSP client, ViewHandler would forward to a JSP.
This leads us to the question of how JSF renders the content of a page. Remember, the page may be different from the page the request was initiated from or it may be the same page. A scenario in which the same page is displayed is when a change in a UI component results in a visual change of another UI component. For example, clicking on a radio button may populate an adjacent list box. But how does JSF take a UI component and render it to something the client can display?
The answer is to use RenderKits. When it comes to creating client device-specific UIs, RenderKits are to JSF what XSLT is to XML. They allow you to abstract how the UI is rendered. The reference implementation comes with a default HTML RenderKit. RenderKits can be swapped during design- or runtime and you can expect a plethora of kits to be available on the market shortly. The RenderKit will take a UI component such as UITextEntry and push a text input box to the browser. A configuration file is used to map UI components to Renderer objects. As seen in Listing 1, a UISelectBoolean is mapped to an HTMLCheckboxRenderer in a custom-created RenderKit. HTMLCheckboxRenderer has encodeBegin(), encodeChildren(), and encodeEnd() callbacks (see Listing 1) (Listings 1-3 can be downloaded from below).
However, you have the option of not using a RenderKit. The responsibility of rendering a UI component then falls on the component. The encoding process is traversed across the response component tree by calling encodeBegin(), encodeChildren(), and encodeEnd(). In either case (with or without a RenderKit), an encode() will have code that creates an HTML response for an <input type=text>. Example: output. write ("<INPUT TYPE=.....")
Once the HTML is rendered and the ViewHandler forwards to a JSP, JSF relinquishes control and the user can now initiate another request from the UI. The cycle has been completed.
Using JSF in Web Applications
Let's look at a simple JSF application and see how the pieces fit the puzzle. For those of you like me who hate "Hello World" applications, here's a simple weather-service application that takes input from a form with fields such as zip code, city, and state in the U.S., and looks up a weather service application and displays the weather for that location. As in most servlet applications, the WAR files contain the following files:
The web.xml file (see Listing 2) enlists a context listener and the controller servlet. It provides a standard URL mapping and specifies a welcome file. This file is a JSP that serves a form that solicits input into three fields, namely zip code, city, and state.
The weatherQuery.jsp (see Listing 3) displays a form with three text input fields. Two of these have validation constraints, namely a zip code has to have at least five characters (although it can be any character, something that will not quite work in the U.S.). The city has no constraints but the state has to be at least two characters in size. This is not an error-proof application, but you get the idea of how to use the JSF built-in validators to enforce these constraints as opposed to writing custom code.
JavaServer Faces has certainly addressed the woes of many developers. It has the right representation from a diverse developer community that's part of the Java Community Process. Though a bit late to arrive compared to the several popular frameworks already in vogue, it has sufficient merit to get developers' buy-in irrespective of their development preferences. Like all things within the Java technologies, separating the interfaces from their implementation is certainly going to allow the software vendors to innovate and make it available within IDEs and other tools.
1. The JavaServer Faces Specification:
2. Anthony Eden's and Thomas Wheeler's contributions to the Wafer project:
Murali Kaundinya is a senior enterprise architect with Sun Software Services. He leads large projects for Sun's customers, delivering solutions with architecture using Java and Sun ONE technologies.
Jamiel Sheikh is a senior Java architect with Sun Software Services. He has 10 years of industry experience in architecture, mentoring and development of large-scale enterprise-wide client/server and Internet-based systems, frameworks, and applications.
Vol. 8, Issue 5, p. 16