Implement a Graphical JSF Component
See how simple it is to build a graphical Web application component that cannot be built easily using pure HTML
by Marc Durocher
April 7, 2005
Developers agree that having the right tools for creating interactive Web interfaces allow them to focus their time on core requirements and customization, and at the end of the day deliver applications on time. JavaServer Faces (JSF) technology brings many advantages to creating interactive Web applications versus other technologies such as JavaServer Pages or Apache Struts. JSF provides a clear separation between application logic and GUI presentation, improved maintenance capability for Web applications, and a framework for the development and reuse of Web UI components.
Many Web application developers are now migrating to use JSF, but they are finding the set of predefined JSF UI components limited to basic DHTML widgets. Advanced applications such as supervision or business-process monitoring need advanced visualization components that are compatible with the JSF framework. The JSF framework's standardization makes it easy to develop custom Web GUI components that can be reused. In addition, Web component vendors are now able to provide more sophisticated components, with the assurance that Web application developers will be able to easily take advantage of these components. Such JSF UI components must integrate and deploy cleanly into the JSF run-time framework and must integrate well at design time into IDEs that provide JSF support.
Despite the standard UI framework brought by JSF, there are several pitfalls and traps for the person developing their first custom JSF component. Let's look at how to build a graphical JSF component that cannot be built easily using pure HTML. The characteristics of a graphical JSF component require not only the generation of DHTML, but also some extra support for image generation and client-side interactions. We'll illustrate these characteristics with an example of a charting component that is designed to provide graphical charts, as well as various client-side navigation and interaction facilities. We'll also look at the steps required to integrate the chart component into a JSF-enabled IDE. By understanding the design of the charting component, you will have a better understanding of how a graphical JSF component can be implemented, which should allow you to develop your own custom JSF graphical components.
What Is JSF?
JSF is a standard server-side framework that simplifies the construction of the presentation layer of Web applications. JSR 127 (see Resources), which defines the JSF framework, comes with a reference implementation that provides basic UI components, such as input fields and buttons. You can assemble reusable UI components to create Web pages, bind these components to the application data source, and process client-side events with server-side event handlers. By following the specification, component vendors can write components that integrate cleanly into the JSF run-time framework and can integrate into IDEs that are JSF compliant at design time.
For the most part, JSF components correspond directly to the HTML components and tags available in the HTML 2.0 specification. This set of relatively simple components is sufficient for many Web applications. However, many applications such as supervision or monitoring require more complex data display and interaction such as charting, diagramming, and mapping. The design of these advanced components is not obvious because of the limited capability to render complex graphics widgets directly in HTML. The solution requires that the server-side components deliver images to the client, but it brings its own problems because interactions on basic HTML images are limited. Finally, JavaScript must be used to enable the client-side interactions that allow the user to navigate and interact with the data.
Let's take a look at the steps for developing a very simple JSF component, one that imports CSS into an HTML page. The description and code samples for this simple component will then serve as background as we develop our advanced JSF charting component. Figure 1 shows how to use the component that we are going to develop and the result that we will get. The benefit of using such a component is to be able to change the complete look of the page by changing the component value from a JSF action.
Develop a Component
A JSF component consists of several Java classes and configuration files. To create a custom JSF component, you'll need to develop a Java class that extends one of the JSF base component classes; develop a renderer for the default render kit; develop a Java class that describes the tag that will be used in the JSP page; write a tag library definition (TLD) file; and write the JSF configuration file. Let's take a closer look at these five steps.
Develop the component Java class. The component class is responsible for managing the properties that represent the component's state. Therefore, we must choose an appropriate base class for the component, based on its behavior, such as an input or output component (see Listing 1). The component described here extends javax.faces.component.UIOutput to display a URL pointing to a style sheet file or the contents of an inline style sheet. The component can be used to switch from one style sheet to another in a JSF action. The link property specifies the type of the value: either a URL or the inline style. The component must also be able to store and restore its state between requests to the server using an object processed by the JSF framework. The state of a component consists of the significant property values that are needed to rebuild the object. The JSF framework automatically calls the saveState() and restoreState() methods, which we have implemented in our component to achieve this goal.
Develop the renderer. The renderer has two roles. First, the renderer is responsible for emitting an appropriate HTML fragment that will render the component in the client. Usually, this HTML fragment will consist of some HTML tags that are suitable for rendering in general Web browsers. This phase of the JSF life cycle is called the encoding or render-response phase. This rendering phase can also be used to emit JavaScript code that can be used to enhance client-side interaction.
The second role of a renderer is to decode the data that comes from the client to update the server-side component state (for example, the text that the user enters in a text field). The standard renderer kit is mandatory, but other renderer kits can be provided to offer an alternate client-side representation or language such as SVG (see Resources). The renderer implemented (see Listing 2) chooses the type of CSS to be emitted in the HTML page by checking the link property of the component.
Develop the tag class. Again, the JSF framework provides base classes you may want to extend to write the tag associated with the component. The tag class is responsible for defining the component type and the rendering type of the component that will be used in the faces-config.xml file, which will be described shortly. It's also responsible for creating the JSF component (handled by the JSF framework) and passing attributes that are contained in the JSF tag to initialize the component (see Listing 3).
The tag provides setters and getters to manage the link and value attributes. When the component is created, the setProperties() method is called to initialize its properties from the tag attributes. Each tag attribute can be either a literal value or a binding to a bean property.
Write the tag library definition (TLD). The TLD is an XML file that describes the tag by associating the name of the tag with the corresponding Java class. The TLD also describes the allowed attributes of the tag (see Listing 4). This TLD defines a tag named css that is bound to the CSSTag class. It also declares the link and value tag attributes.
Write the JSF configuration file. To integrate a JSF component into the framework, you must provide a configuration file called faces-config.xml. This file associates component types and renderer types, which are used in the JSP custom tag handler, to their corresponding Java class, and it also describes the renderer that should be used with each component (see Listing 5). This file defines the faces.CSSFamily component family. In our example, the family is made of a single component of type faces.CSSComponent, which is bound to the CSSComponent class. Finally, the renderer of type HTML.LinkOrInlineRenderer, implemented by the CSSRenderer class, is associated with the faces.CSSFamily family.
Go Graphical
You can also provide additional information if you want your component to integrate into a JSF-enabled IDE. For example, you can provide an XML configuration file named sun-faces-config.xml that describes the properties of the component that should be exposed in the IDE, as well as other design-time information.
Now that we've seen how to create a simple JSF component, let's look at how to create a graphical JSF component. We'll follow the same basic steps to design an advanced JSF graphical component. Let's use the example of a charting component, such as the ILOG JSF charting component, that provides a visual representation of the distribution of data values across a set of categories. The chart is able to display a dataset with various representations such as bars, pies, and bubbles. The JSF charting component has two initial design constraints:
- We already have a Java charting bean component that has all of the graphical presentation capabilities we want. This component can display a wide range of charts and is highly customizable. Ideally, we want to leverage this bean component and use its capabilities to form the basis of our JSF component.
- Common JSF applications need to reload the entire page to refresh the view. This behavior can be appropriate for form-based applications, but it is not appropriate in many cases for highly graphical user interfaces. Therefore, our JSF charting component needs to be able to handle some simple navigation without refreshing the entire page to provide a better user experience.
Here is a solution that satisfies these requirements: The JSF charting component will manage the chart bean component, which includes creating the chart bean, customizing the bean, and making the bean available for server-side actions. Rendering the JSF component will be done in two phases. The JSF renderer generates an <img> tag and a set of JavaScript objects (see Figure 2). The client will request an image from the server. This request is done by a servlet that retrieves the chart bean and generates an image using methods provided by the chart (see Figure 3). Any further user interactions (zooming, panning, changing a style sheet, and so on) that change the chart image only will result in an incremental refresh of the chart image only. If the client-side action requires more than just an update of the chart image, then the page will be submitted (see Figure 4).
The JSF charting component is also accompanied by a set of additional JSF components. An overview displays a global view of the chart, displays a rectangle representing the chart view, and also allows the user to pan the visible area. A legend component shows information about the displayed dataset and can also be displayed in the chart itself, depending on the style of the displayed data. Client-side interactors, such as pan and zoom, are also provided that can be seen as client-side interactions, meaning that interacting with the chart will not reload the entire page as is the case for a regular JSF interaction.
To render the chart component, you simply have to use the chartView tag:
<jvcf:chartView id="c" style=
"width:500px;height:300px" … />
The data is displayed as an image on the HTML page. The image is built by a servlet in response to an HTTP request that includes various parameters specifying the resulting image, the generation of an image map, the generation of an inline legend, and so on. The resulting image is then inserted in the client-side DOM, and the only part of the page that is refreshed is the image itself.
Application Centerpiece
Let's look at some differences between a simple custom JSF component and the advanced charting component. The JSF chart component class is very much like a standard component, with the addition of a chart property that gives access to the chart bean responsible for generating the image displayed on the HTML page. The JSF component can retrieve this chart bean locally through a value binding or in the current session. When the JSF charting component is the centerpiece of an application, optional JSF components such as an overview or a legend can be connected to the main chart to display additional information (see Listing 6).
The renderer is the major complexity of this JSF implementation. As mentioned previously, the renderer is not generating simple HTML, but rather DHTML that consists of HTML (the <IMG> tag) and JavaScript proxies.
A proxy is an instance of a JavaScript class responsible for managing the display of the component image on the client. This object is the client representation of the server-side Java component class; it has the same properties. Each component on the page, the chart and its companions, has a proxy instance. A good practice when rendering JavaScript is to use the facesContext.getExternalContext().encodeNamespace(name) method on every JavaScript variable. It will make future integration of the component into a JSR 168-compliant portlet environment easier.
To instantiate a proxy on the client, JavaScript support libraries must be imported on the page. To keep the client as thin as possible, the JavaScript libraries are modularized based on the proxy classes they support. Therefore, each proxy class needs a different, and possibly overlapping, set of libraries to be imported. One of the tricky parts of the chart rendering is the phase that emits these script libraries. The renderer of each component declares which library it requires, and, when emitting the library references, it needs to be aware of the previously emitted libraries to avoid duplication. A script manager that only exists during page rendering does this filtering. Each time a renderer wants to emit the set of library imports, it gives the list to the script manager, which filters out the libraries that have already been emitted.
The purpose of the client-side proxies is to allow scripting and to avoid unnecessary page refresh. Once the chart is rendered, the proxies are available on the client side to dynamically install interactors and to show or hide the image map. The proxy objects are also available for regular JSF components that support JavaScript mouse event handling:
<jvcf:chartView id=
"chartView" .. />
<h:selectBooleanCheckbox id=
"genImageMap" onclick=
"chartView.setGenerateImageMap(
this.checked ? true : false,
true);" />
The problem with locally modifying the component's client-side proxy is that its state will no longer be synchronized with that of the Java component on the server. To solve this problem, the proxies use a hidden input tag (<INPUT TYPE="HIDDEN">) to save the new state on the client. When a standard JSF action is performed and the page is submitted, this hidden state will be decoded by the renderer so that the client and server are synchronized. This behavior requires special decoding behavior in the renderer class. The standard decode method is enhanced to decode the state that comes from the client and update the server-side component state.
Test Cases
The connection between the chart and its associated component is made by id references or binding. To allow flexible page design, a component can be referenced before it is rendered. Therefore, at rendering time, if a component property references another component that is not already rendered, the emission of the JavaScript code that resolves this dependency on the client is delayed until the referenced component is rendered. A dependency manager does this work.
For illustration, let's have a look at a typical case that involves an overview that references a chart:
<jvcf:overview viewId=
"chart" [...] />
<jvcf:chartView id=
"chart" [....] />
There are two cases. The referenced chart component is already rendered, so there is no problem:
JSP:
<jvcf:chartView id=
"chart" [....] />
<jvcf:overview viewId=
"chart" id="overview" [...] />
render:
[...]
var chart =
new IlvChartViewProxy ( .. );
[...]
var overview=
new IlvFacesOverviewProxy (
.. );
overview.setView(chart);
[...]
The referenced chart component is not rendered before the dependent overview component. In this case, a component-creation listener is registered on the dependency manager. When the referenced chart component is eventually rendered, its renderer notifies the dependency manager of its creation. At this point, the code needed to resolve the dependency will be emitted:
JSP:
<jvf:overview viewId=
"chart" id="overview" [...] />
<jvdf:chartView id=
"chart" [....] />
render:
[...]
var overview =
new IlvFacesOverviewProxy (
.. );
[...]
var chart =
new IlvChartViewProxy ( .. );
overview.setView(chart);
[...]
One of the goals of developing JSF components is the ability to use them in any JSF-compliant IDE. Nevertheless, JSF compliance is not always sufficient to guarantee that such design-time integration will work. Here are some simple ideas to keep in mind during the development of your JSF component to facilitate its future integration into an IDE.
First, your custom JSF component should provide a basic HTML rendering. During design time, JSF IDEs cannot render dynamic graphical components that require live data or app server connections. Therefore, components that have a complex or nonconventional (for example, not HTML) rendering should use Beans.isDesignTime() to determine whether they should provide a basic HTML representation or the true component rendering.
Another design-time issue is the positioning and dimensioning of the component. Different IDEs use different attributes or properties. A component that can be resized, such as an image, should be able to handle the different ways of defining the size.
Finally, to integrate into IDEs, the component must provide extra information that is not defined by the JSF specification yet. Unfortunately, each IDE currently requires special handling to integrate your component: an XML file in one case, an eclipse plug-in in another, and so on. A main goal of the next JSF JSR (version 2.0) will be to specify the additional metadata formats.
As you can see, writing a simple JSF component is easy because the framework does most of the work. The JSF framework manages the component state, manages the renderers, and so on. Here, we have extended these basic concepts to design an advanced graphical JSF component that is able to display complex datasets, offer incremental refresh, support rich client-side interactions, and coordinate with companion components. Supporting these features required many enhancements to the basic JSF component architecture. Certainly, the concept of incremental refresh would be a good future enhancement to the JSF framework, as it would allow rendering only the parts of the page that have changed and avoid a complete page refresh.
Following the JSF specification is not always sufficient to ensure that a component will be fully integrated into a JSF IDE; a new JSR should soon solve these issues. Despite these pitfalls, the JSF framework greatly speeds up Web component development and facilitates mixing components from various sources to build complete and complex Web applications.
About the Author
Marc Durocher is a software architect with ILOG, a leading provider of enterprise-class software components and services, where he is responsible for the JSF component offering in the ILOG JViews product line. Contact Marc at .
|