Abstract
This article gives an approach on how JSF, DWR, DOJO can be integrated to create rich web applications which use Portlets and Facelets. It is assumed that the readers have a basic understanding of these frameworks and the features they provide.
Sample Application
The sample application that is discussed in this article is a product management application. The application provides its users the following features:
- The user can search for a product type based on name
- When the user selects a product then a new screen is shown to the user with a split pane. The left side of the split pane shows all the product subtypes that belong to that product type, in the form a Tree (as you see in Windows Explorer). It is possible that each product subtype may have its own subtypes. The right side of the pane shows a tabbed pane with tabs labeled:
- Products - this tab shows the list of products that belong to the selected subtype
- Add Product Subtype - this tab shows a form that accepts values to add a new product subtype to the selected product subtype
- At the bottom of the split pane there is a back button provided to go back to the search product type page.
- When a list of products associated with a subtype are shown on the right side of the split pane, then it should show only 10 products on a single page. This means it should be possible to do pagination and sorting of results on the screen.
- The Tree shown on the left side of the split pane should be expandable/collapsable and it shouldn't result in page refresh and thus enhancing the user experience.
- Right clicking any node (representing a product subtype) in the Tree should show the user with a popup menu with options to add a new product subtype as a child of the selected subtype, remove the selected product subtype, refresh the list of child product subtypes of the selected product subtype with latest data from the database, etc.
- When a new product subtype is added to a selected product subtype then the new product subtype should immediately show up in the Tree without page refresh.
Figure 1 shows how the screen is expected to look when the user selects a product type/subtype.
Fig 1. The product subtype details screen
DOJO
Creating cross-browser compatible tree structures, tabbed panes, split panes, popup menus, etc is time consuming and at the same time can be best done by an experienced Javascript/DHTML programmer. There are many toolkits that provide these UI widgets and are cross-browser compatible but not many provide a rich event handling model. The following lists some of the user actions that the sample application needs to repond to meet the user requirements:
User Action | Application Response |
Select a tree node | Show the list of product subtypes that belong to this node |
Right click a tree node | Show a popup menu with options to add a child product subtype, remove the selected product subtype, etc |
Select 'Add Product Subtype' tab in the tabbed pane | Show form to enter new product subtype information |
When user clicks the [+] sign to expand the tree and view the child nodes | Loads child nodes information from the database and displays in the tree |
The DOJO toolkit is a Javascript/DHTML toolkit which provides a rich set of UI widgets (including but not limited to tree, tabbed pane, popup menu) with a rich event handling model which makes it suitable for use in the sample application.
DWR
DWR ( Direct Web Remoting ) is an AJAX framework that simplifies building AJAX applications in Java. DWR provides many features which includes, but not limited to:
- Creating Javscript from a Java class (that the developer creates to handle AJAX requests)
- Allows a JSF managed bean to act as Java class that handles the AJAX requests
- Conversion of Javascript associative arrays into Java beans and vice-versa using converters
- Conversion of Javascript arrays to Java Collections and vice-versa using converters
The converters in DWR play a very important role and provide a cleaner programming model. For example, if the user enters the new product subtype information and requests the application to save it then there are two ways to retrieve this information in web layer:
- Use HttpServletRequest's getParameter method to retrieve all the information about the new product subtype.
- Create a DTO ProductSubtype which has getters and setters for all the attributes of a product subtype. Configure a converter in dwr.xml file which specifies ProductSubtype as a bean converter. In Javascript, simply create an associative array and pass it to the Java class (which handles the AJAX request) method which accepts ProductSubtype as a parameter. In this case DWR will do the conversion from the Javascript associative array to ProductSubtype because ProductSubtype was declared to use the bean converter in DWR.
The later option provides a cleaner programming model and you don't have to deal with retrieving request parameters and creating a DTO to be used in Java program.
When an AJAX request is processed, most of the times its required to receive a status code, message or some data in the Javascript callback method, to decide on what to show or what not to show to the user. The bean converter comes very handy in such scenarios.
DOJO's rich event model coupled with DWR's clean approach towards handling AJAX requests in Java application provides a way to create highly interactive web applications (like the sample application) in which the events generated by the DOJO components are passed to DWR for processing.
Problem Description
- In a Portal environment, the developer is not responsible for generating the HTML for the user interface, its generated by parsing the XHTML files that correspond to the Facelets. Even if a JSF managed bean's property contains HTML string as its value, it is not parsed by the Portal and is shown as is to the user on the user interface. How to generate DOJO specific HTML that can be executed by the web browser to create the UI widgets ?
- DOJO provides rich event model and it is possible to intercept these events in Javascript. The DWR framework can be used to receive these events on the server side but where the conversational state needs to be maintained on the server side ?
- The Javascript takes time to create DOJO tree nodes in IE or Mozilla when the number of nodes in the Tree is more than a few hundreds. So does it mean that applications can't use DOJO tree widgets in browsers if the number of nodes is more than a few hundreds ?
- When using AJAX in a web application then how to generate the HTML or HTML fragment when a user event occurs on the screen ? This should be coded in the Java code or picked from an external file ? Can the application show a complex user interface in response to a AJAX request ?
- When using AJAX the code may get clutterred with getParameter("fieldName") method of HttpServletRequest and thus it will not be easily maintainable
Solution Approach
Custom JSF component
Custom JSF components were used to generate the necessary HTML for the Tree and Split Container components of DOJO. The HTML generated by JSF components is always parsed by the browser and is not output as a text by the Portal. The following portion of XHTML file shows how the custom JSF compoenent was used to generate the DOJO's tree and tabbed pane widgets.
<div xmlns="http://www.w3.org/1999/xhtml"
...
...
xmlns:dojo="http://dojotoolkit.org/"
xmlns:mytree="http://mytree.com/tree"
xmlns:mytab="http://mypane.com/tabPane">
<ui:composition>
<ui:define name="body">
<f:view>
<h:form styleClass="form" id="formId">
<div dojoType="SplitContainer" orientation="horizontal" sizerWidth="5" activeSizing="false" style="overflow:
auto; whitespace: nowrap; height: 550px; background: transparent; padding: 5px;" >
<div dojoType="ContentPane" sizeShare="20"
style="overflow: auto; whitespace: nowrap;">
<mytree:treeComponent backingBeanName="treeBackingBean"></mytree:treeComponent>
</div>
<div dojoType="ContentPane" sizeShare="80" style="overflow: auto; white-space nowrap;">
<mytab:tabPaneComponent/>
</div>
</div>
...
...
If the number of tree nodes is large ( more than 200 ) then the custom JSF component must not generate code for more than 200 nodes. If the number of nodes is more than 200 then its seen that IE takes substantial time to create these widgets when loading the page. The sample application's custom JSF component created only 100 tree nodes ( at the root level ) and showed the option 'Show more...' in the end. When the user selected the 'Show More..' option then it becomes the responsibility of DWR to fetch the information about remaining nodes from the database. This information was then passed to the Javascript callback method to programmatically create the TreeNode widgets.
MyFaces also provides components that generate DOJO Tree but the MyFaces component creates all the nodes in one go, which is not a good approach because the component becomes useless in web applications when the number of tree nodes runs into thousands.
DWR and JSF
DWR requires that you create a Java class and configure that class in the dwr.xml configuration file. DWR creates a Javascript file (.js file extension) with a name that is configured in dwr.xml.
The following configuration information from dwr.xml shows how a Java class is configured:
<create creator="jsf" javascript="AjaxBean" scope="request">
<param name="managedBeanName" value="ajaxBean" />
<param name="class"
value="com.somebean.AjaxBean" />
</create>
creator="jsf"
This means that the Java class is configured as a JSF managed bean. This Java class will contain all the AJAX methods that will be invoked by the Javascript.
<param name="managedBeanName" value="ajaxBean" />
This means that the name of the managed bean in faces-config.xml configuration file is ajaxBean.
<param name="class"
value="com. somebean.AjaxBean" />
This refers to the actual Java class.
javascript="AjaxBean"
This is the name with which the Java class is referred by the Javascript code.
To make use of the AjaxBean in the Javascript code, it is required to import this Javascript using the <script> tag.
DWR Java classes (DWR Java classes are nothing but plain Java classes. These classes don't implement any DWR specific interfaces or classes.) are instantiated evertime an AJAX request is received. This means that if the application has to maintain state in AjaxBean class then it will be erased when a new request comes. The methods in the AjaxBean are expected to be stateless in nature and the conversational state must be maintained somewhere else.
The AJAX request is not a PortletRequest, which means that the DWR Java classes don't have access to PortletSession object. AJAX requests are simple HTTP requests, therefore, DWR Java classes can access HttpSession object. According to the Portal Specification, it is required that the HttpSession and PortletSession objects should be in sync, i.e., if an attribute is added to a PortletSession object then it should also be added to HttpSession object (not necessarily with the same name).
For example, in case of JBoss AS, when a managed bean is added to the PortalSession with the name someManagedBean then the same object is available through HttpSession with the attribute name javax.portlet.p.<portletInstanceName>?someManagedBean.
In the sample application, the conversational state is maintained in the someManagedBean. The DWR Java classes obtain the instance of someManagedBean from the HttpSession object and set its properties to reflect the current conversational state of the user.
To access the business services ( lets say in the Spring layer ), the AjaxBean can make use of Service Locator pattern to interact with the business services.
NOTE: A JSF managed bean is instantiated only after it receives a faces request. So make sure that the JSF managed bean that you are going to use for storing the conversational state has already received a faces request. The scope of this managed bean should be 'session'.
HTML templates
One of the problems that is faced in working with AJAX is creation of complex HTML fragments based on user actions. This normally results in high-maintenance applications and sometimes its very difficult to even change the user interface as the application requirements are refined over a period of time. HTML doesn't have a concept of tiles, but something similar to tiles can be done in the sample application.
<table style="height: 80%; width: 100%; padding-bottom: 100px; visibility: {0};">
<tr valign="top">
<td>
<table align="left" valign="top">
<tr>
<td class="formLabel">Product Category:</td>
<td class="formField">{1}</td>
</tr>
<tr>
<td class="formLabel"><span class="required">*</span>Description:</td>
<td class="formField"><textarea rows="2" id="desc_field" cols="80"
name="desc_field"></textarea></td>
</tr>
<table>
<tr>
<td>{dataTable}</td>
</tr>
<tr>
<td>{dataScroller}</td>
</tr>
</table>
<tr valign="bottom">
<td align="right" style="padding-right: 50px;">
<table>
<tr>
<td><input type="button" class="inputButton" onclick="saveDetails('{2}');" value="Save"/></td>
</tr>
</table>
</td>
</tr>
...
...
The above HTML template file shows the two types of placeholders:
- Data placeholders
- HTML placeholders
The placholders {0}, {1} and {2} represent data placeholders, and are easily filled by having a static method like:
public static String getStringWithValues(String template, Object[] values) throws IncorrectNumberOfValues {
for(int i = 0; i < values.length; i++) {
int index = template.indexOf("{" + i + "}");
if(index == -1) {
throw new IncorrectNumberOfValues("The number of values passed is : " + values.length + "
which doens't match the number of placeholders in : " + template);
} else {
if(values[i] != null) {
template = StringUtils.replace(template, "{" + i + "}", values[i].toString());
} else {
template = StringUtils.replace(template, "{" + i + "}", "");
}
}
}
return template;
}
where, template is the html template that needs to be parsed and placeholders removed with the value in the values array. So, the data placeholder {0} is removed by the 1st element in the values array, the data placeholder {1} is removed by the 2nd element in the values array, and so on.
The placholders {dataTable} and {dataScroller} represent HTML placeholders and are expected to be replaced by an HTML fragment. The {dataTable} placholder is supposed to be replaced by a data table which shows records corresponding to each product that is part of the product category. The HTML corresponding to {dataTable} is in another HTML template. There are a couple of approaches in which the HTML placeholders can be replaced by the corresponding HTML templates:
- Programmatically: Inside the ajax beans the placeholders can be replaced by the HTMLs. This approach doesn't require creating any kind of a framework.
- Creating a mapping between a placeholder and HTML template in a properties file or an XML document. For example, a properties file may be created to contain the following mapping: {dataTable} = /WEB-INF/classes/templates/dataTableTemplate.html {dataScroller} = /WEB-INF/classes/templates/dataScroller.html
At runtime these placeholders can be replaced by the corresponding templates. This approach requires creating a small framework which reads in the properties file and parses the HTML templates to replace HTML placeholders by corresponding HTML files.
DWR bean converter
Web applications normally use DTOs to pass information from Web layer to the Service layer. The sample application uses another level of DTOs which pass information from Javascript to the Web layer. For example, when searching for product a user can enter the following information: product name, product code, product id and also the corresponding type of search (LIKE or EQUALS). These parameters are passed to the Web layer of sample application using the DTOs. The concept of associative arrays (i.e., named arrays) in Javascript is used to set these parameters and then DWR does the conversion from these associative arrays to corresponding DTO. The names in the associative array must match with the properties in the DTO.
The following configuration in dwr.xml shows how to configure a DTO:
<convert match="com.search.product.SearchCriteria" converter="bean"/>
Use of this approach makes the Java code cleaner on the server side because no longer you have to explicitly obtain values corresponding to each request parameter that came with the AJAX request.
JSF's rendered attribute
All JSF HTML tags have a rendered attribute, the boolean value of which decides whether the HTML widget will be shown to the user or not. This same functionality can be achieved when working with HTML templates. This will require creating an expression framework which parses the expressions in HTML and show/hide an HTML fragment.
<exp:if value="someManagedBean.permissions.save">
<td><input type="button" class="inputButton" onclick="removeProduct('{0}');" value="Remove" /></td>
</exp:if>
FacesRequest or HttpServletRequest for navigation
When the user clicks the back button on the product details page then the product search page needs to be shown. If clicking the back button is handled using an AJAX request then the application isn't taking the advantage of the navigation rules defined in the faces-config.xml file. So, it's a good idea to use the HTML button component of JSF on the product details page rather than using an HTML button which sends an HttpServletRequest (or AJAX request) to the server to navigate to another page.
Alternatives considered
Ajax4jsf
Ajax4jsf provides way for JSF components to use AJAX, but the Portlet support came with version 1.1.1. At the time of creation of sample application the ajax4jsf didn't support Portlets.
HTML and CSS based trees
It's possible to use a combination of <li> HTML element and CSS to create a tree structure. But these tree structures lack any kind of event handling model and therefore can't meet the requirements of a highly interactive tree widget.
RichFaces
RichFaces provides rich JSF components with AJAX functionality. RichFaces use Ajax4jsf for achieving AJAX functionality.
About the Author
Ashish Sarin has over 8 years of experience in developing / designing Java based web applications.
References
Dojo
Dojo is an open source DHTML toolkit written in Javascript. For more info please refer to http://www.dojotoolkit.org/about.
DWR
DWR or Direct Web Reporting allows easy use of Ajax in Java applications. For more info please refer to http://getahead.org/dwr/documentation.
RichFaces
http://labs.jboss.com/jbossrichfaces/.
Ajax4jsf
http://labs.jboss.com/jbossajax4jsf/.
Facelets
https://facelets.dev.java.net/.