 | Developing Unwired JSF Components by Marcel Urbanek 20 Sep 2005 02:45 EDT |
Learn how to develop JSF components that render for WAP and desktop browsers.
The
proliferation in recent years of web-enabled devices has brought
tremendous challenges to application developers, with the need to
develop components that can work across various platforms. For
example, extending an application built for a standard web browser to
work on a WAP device may require components of your web application
to be rendered in more than one way.
It is
tempting to think that if you use JSF it will be a simple matter to
extend an application built for a standard web browser to work on a
WAP 2.0 device, requiring adjustment only for screen size. However,
some components (such as a table) are not viewable on mobiles, while
other components don’t work because of the bugs in WAP browsers
(which are more common than is usually recognised).
Thankfully
there is a solution. We can use JSF to extend WAP with
self-programmed components supported by render kits. This article will
show you how to implement JSF components that use different renderers
depending on the device used. This is achieved by separating the
rendering from the rest of the component.
Renderers defined
A
renderer is a type of interacting class that creates the client-side
representation of a component, typically the generation of HTML. A
renderer can also take input from the client and transform it into
values the component can understand. In normal applications,
components render themselves. This can create difficulties because if
the component is to be used on different devices, it may need to be
programmed differently for each one. Alternatively, if you implement
encoding method that renders in any language, the source code becomes
unreadable. So, the solution is to develop separate renderers for the
component. Any JSF component can make use of renderers, with each
different one rendering the component in a special language or style.
In the case of WAP devices, we would probably use renderers to create
HTML for desktop web browsers and HTML-MP for the WAP devices.
Let the Renderer do the heavy lifting
Developing
a component that uses a renderer is different than developing a
component that renders itself. Figure 1 shows a JSF component without
an external renderer.
 Figure 1. UML diagram of a component that renders itself (this is called the direct implementation rendering model).
This
illustration shows that with a normal JSF component you have a tag
that is accessible from the JSP. The component class which should be
used for this tag is determined by the outcome from the
getComponentType() method, and will be instantiated and given as a
parameter to the setProperties() method of the tag class. The
setProperties() method will then hand over all tag properties to the
component class. JSF can then render the component by invoking the
encodeBegin(), encodeEnd(), and decode() methods (there is also an
encodeChildren() method).
Render
kits extend this model, as shown in the following illustration.
 Figure 2. UML diagram of component development with a renderer (this is called the delegated implementation rendering model).
It can be seen that the model is extended by one or more rendering
classes, and instead of calling the encoding method of the component,
JSF invokes the encoding methods of the renderer. The outcome
of the component’s getRenderer() method determines which
renderer (defined in the XML file faces-config.xml) will be
used.
Implementation
In
this section we will illustrate how to implement a JSF component that
uses a renderer. We will use the example of a menu bar. Web
applications often have menu bars with different items at the top of
the page. They consist of buttons, each with text or an icon, as
shown in figure 3.
 Figure 3. Menu bar rendered for an HTML browser. This example has been generated with the button generator this discussed in this article.
Our
goal is to implement a JSF component that dynamically renders such
buttons not only for the standard web browsers but also for WAP
applications. The latter would be similar to the one above, but a lot
smaller with a font of 4*3 pixels.
 Figure 4. Menu bar rendered for WAP.
We
will first examine how this component is usually implemented, then
describe how the button generation works.
A
number of attributes will be required, some of which are pretty
obvious, such as:
-
text (which text should be rendered into the button)
-
action (which action to take when the button is clicked)
These
two attributes would probably be enough to program a working button
component, although we might also want to consider whether the menu
items are activated or deactivated. This may be necessary if, for
example, you are using form validators to test whether form data
entered by the user is correct. If the user doesn’t enter any
data and tries to submit the form, an error may be generated because
there is no data to validate. In this situation, an attribute may be
required to set the validators to skip, with some JavaScript
attributes (e.g. “onclick”) to toggle the validators on
and off.
-
active (specifies if the button should be rendered active or not)
-
description (renders a tooltip for the button)
-
immediate (specifies if form validators should be processed or not)
-
onClick (renders JavaScript "onclick")
-
onMouseOver (renders JavaScript "onMouseOver")
The next step is to generate a tag class. A tag class extends
javax.faces.webapp.UIComponentTag, and has set/get methods to set the
parameters necessary to render the component. Further, we will need
to implement a setProperties() method, which passes the attributes to
the component. The following excerpt from the class ButtonTag
illustrates this point:
[...]
public void setProperties(UIComponent component)
{
ButtonComponent buttonComponent=(ButtonComponent)component;
//If the attribute isn't set we simply replace it with an empty
//string.
if (description==null)
{
description="";
}
//If the attribute value is a valuebinding, a valuebinding will
//be instantiated and stored.
if (isValueReference(description))
{
ValueBinding vb = getFacesContext()
.getApplication().
createValueBinding(description);
buttonComponent.setValueBinding("description", vb);
}
//If the attribute value is a normal value (no valuebinding),
//it is simply set as-is.
else
{
buttonComponent.getAttributes().put("description",
description);
}
[...]
public String getComponentType()
{
return ButtonComponent.COMPONENT_TYPE;
}
[...]
As
can be seen in the setProperties() method, the attribute values are
handed over to the component. If the attribute value (in this case
"description") is null, it is set to an empty string ("").
If the attribute value is a value binding (that is, it starts with
"#{", ends with "}") a new ValueBinding will be
instantiated and stored in the component. If the attribute is not a
valu ebinding, the component will store the attribute value as-is.
(If you're wondering why this code doesn't set component properties,
remember that setting a component's attribute automatically sets the
corresponding property, if one exists. So, if there is a description
property for this component, the setting the description attribute
will automatically set the description property).
Page:
1
2
3
Download: source code (ZIP; 2MB) Marcel Urbanek graduated in 2005 from the Gelsenkirchen University of Applied Sciences with a degree in applied informatics. During his tenure, he developed web applications for Shotam, Volkswohlbund, and GUP GmbH. Before graduating, he wrote a thesis about developing a WAP email client using JSF. Marcel now works for RSC Commercial Services OHG, and can be reached at MUrbanek@gmx.net. |