As with any framework, JSF is not a silver bullet for solving all your Web application development problems, but it does provide a good foundation where the Java community, and visual component and GUI tool vendors can come together to standardize Java Web application development. I recommend considering JSF for your next Web development project.
Once you decide to use JSF as your Web framework, the next question you might have is which JSF implementation to choose. Indeed, many JSF component sets and libraries are available, both as open source and commercial offerings. Among them, the MyFaces implementation meets most needs well in terms of building an enterprise-grade real-world application without requiring you to make your own components. MyFaces not only provides a rich set of pre-built components, it also addresses many shortcomings and issues found in JSF 1.1. Recently, Oracle ADF (Application Development Framework) components have joined the MyFaces family. Let's see how MyFaces solves a classic Web application use-case.
The header/detail use-case is common in Web applications: A list of search results are presented in a table view. A user can traverse the results in the table and select a row to work on. Figure 1 shows an employee module, which demonstrates the header/detail use-case.
Figure 1. Employee module. Click on thumbnail to view full-sized image.
In this employee-header example, we use two MyFaces components: <t:dataTable>
to create the table view for presenting header information and <t:dataScroller>
to provide easy pagination capability. In addition, we also add the cell-level employee information update. When a user clicks the Edit icon, the employee's details display at the bottom of the screen, as shown in Figure 2.
Figure 2. Employee information displays when user clicks Edit icon. Click on thumbnail to view full-sized image.
The user can also click on the Contact tab to view other employee detail information (see Figure 3).
Figure 3. Employee contact information displays when user clicks on Contact tab. Click on thumbnail to view full-sized image.
In the employee-detail-view page, we use <t:panelTabbedPane>
to nicely group related employee information for the best GUI presentation. The MyFaces <t:panelTabbedPane>
component provides both the DHTML client-side and server-side tab-toggling. In this use-case, we use the default DHTML client-side tab-toggling.
With JSF, the coding of the header/detail is simple and straightforward. Let's examine a few lines of employee.jsp
. In Listing 1, we use MyFaces component <t:panelGrid>
to lay out a table of the employee information on the screen and the MyFaces component <t:subview>
to compose the header (employerHeader.jsp
) and detail view (employeeDetailTab.jsp
). Notice that the EmployeeTable
managed bean's showDetailView
attribute controls whether the employee detail displays:
Listing 1
<f:view><h:form><t:div style="width:70%"><s:fieldset legend="Employee Information" ><%-- employee header information list--%><t:panelGrid columns="1" cellpadding="2" cellspacing="2" ><t:column><f:subview id="eh"><jsp:include page="employeeHeader.jsp" flush="false"/></f:subview>
</t:column><t:column></t:column><%-- employee detail information list--%><t:column><f:subview id="ed" rendered="#{employeeTable.showDetailView}"><jsp:include page="employeeDetailTab.jsp" flush="false"/></f:subview></t:column><t:column><t:commandButton action="#{employeeTable.save}" value="Save" /><t:commandButton action="#{employeeTable.cancel}" value="Cancel" /></t:column></t:panelGrid></s:fieldset></t:div></h:form></f:view>
In employeeHeader.jsp
(see Listing 2), we use <t:dataTable>
to display the employee header information. The employee data binds to the data table value attribute: <t:dataTable value="#{employeeTable.employees}" ...>
.
In the MyFaces implementation, <t:dataTable>
also provides many additional features like row index, row style class, column sorting, and row highlighting capabilities (see Figure 2). Another interesting MyFaces component is <t:saveState>
, illustrated in Listing 2, which preserves employee list information in a so-called page context. <t:saveState>
is one component widely used in MyFaces JSF Web applications. It literally can save your managed bean state anywhere you want.
Listing 2
<t:saveState value="#{employeeTable}" ></t:saveState>
<!-- core list table usage and tag reference --><t:dataTable value="#{employeeTable.employees}" var="employee"id="employeeData"cellpadding="0" cellspacing="0" width="100%" border="0"preserveDataModel="false"forceId="true"rows="6"rowClasses ="odd,odd,odd,even,even,even"rowIndexVar ="rowIndex"sortColumn ="#{employeeTable.sort}"sortAscending ="#{employeeTable.ascending}" >
<!-- core column table cells --><t:column><t:graphicImage value="/resources/images/icons/checkmark.gif"alt="Current Selection"rendered="#{rowIndex==employeeTable.selectedRowIndex&& employeeTable.employee!=null}"></t:graphicImage></t:column><t:column>
<f:facet name="header" ><h:outputText value="Select One" id="h1"/></f:facet><t:commandLink action="#{employeeTable.viewEmployee}" ><t:graphicImage value="/resources/images/icons/edit_icon.gif"border="0" alt="Click here to edit the record" /><f:param name="selectedRowIndex" value="#{rowIndex}"/></t:commandLink></t:column><t:column id="c1"><f:facet name="header" ><t:commandSortHeader columnName="name" arrow="true" id="s1"><h:outputText value="Name" id="h2"/></t:commandSortHeader></f:facet><t:inputText value="#{employee.firstName}" id="p1"/><f:verbatim> </f:verbatim><t:inputText value="#{employee.lastName}" id="p2"/></t:column><t:column id="c3"><f:facet name="header"><h:outputText value="Address" id="h3"/></f:facet><t:inputText value="#{employee.address.addressOne}"id="a1" size="35" maxlength="100" /><f:verbatim> </f:verbatim><t:inputText value="#{employee.address.city}"size="10" maxlength="20" id="a2"/><f:verbatim> </f:verbatim><t:inputText value="#{employee.address.state}"size="2" maxlength="2" id="a3"/><f:verbatim> </f:verbatim><t:inputText value="#{employee.address.zipCode}"size="5" maxlength="10" id="a4"/></t:column>
<t:commandLink>
and <f:param>
MyFaces components to pass the selected row index for the employee information lookup: Listing 3
<t:commandLink action="#{employeeTable.viewEmployee}"><t:graphicImage value="/resources/images/icons/edit_icon.gif"border="0" alt="Click here to edit the record" /><f:param name="selectedRowIndex" value="#{rowIndex}"/></t:commandLink>
The <t:commandLink>
action attribute links the HTTP action to the EmployeeTable.viewEmployee
method; we use the selected row index from the request parameter to retrieve the employee information from the employee list collection. Listing 4 is a code snippet from the viewEmployee()
method. You can achieve the same result using different approaches available in the MyFaces wiki.
Listing 4
public String viewEmployee(){setSelectedRowIndex(Integer.parseInt((String)getParameter("selectedRowIndex")));setEmployee((Employee)(getEmployees().get(this.getSelectedRowIndex())));
setShowDetailView(true);return "OK";}
In EmployeeDetailTab.jsp
, shown in the code below, employee detail information is divided into three sections presented by the <t:panelTabbedPane>
component. Each nested <t:panelTab>
includes a corresponding section of employee information. The content of the tabbed panel is simple and clean, but provides powerful GUI presentation.
<t:panelTabbedPaneactiveTabStyleClass="googleHouse_panelTabbedPane_activeHeaderCell"tabContentStyleClass="googleHouse_panelTabbedPane"activeSubStyleClass="googleHouse_panelTabbedPane_subHeaderCell_active" ><t:panelTab label="General Information" style="width:35%" ><f:subview id="ltinc1"><jsp:include page="employeeInfo.jsp" flush="false"/></f:subview></t:panelTab><t:panelTab label="Contact" style="width:30%" ><f:subview id="ltinc2"><jsp:include page="employeeContact.jsp" flush="false"/></f:subview></t:panelTab><t:panelTab label="Work Information" style="width:35%" ><f:subview id="ltinc3"><jsp:include page="employeeWork.jsp" flush="false"/></f:subview></t:panelTab></t:panelTabbedPane>
Like any other Web frameworks, JSF also has the configuration file to deal with; it contains your managed bean information, navigation rules, and your converter and validation classes. You can define your entire managed bean in one or more configuration files to better organize the content of JSF configuration files. In this use-case, we have two MyFaces configuration files; faces-config.xml and data-config.xml. The first one contains one EmployeeTable
managed bean and one navigation rule, as shown in Listing 5; the second file has all the employee data information (see Listing 6):
Listing 5
<managed-bean><managed-bean-name>employeeTable</managed-bean-name><managed-bean-class>com.googlehouse.usecases.listtable.EmployeeTable</managed-bean-class><managed-bean-scope>request</managed-bean-scope><managed-property><property-name>employees</property-name><property-class>java.util.List</property-class><list-entries><value-class>com.googlehouse.usecases.listtable.data.Employee</value-class><value>#{employee1}</value><value>#{employee2}</value><value>#{employee3}</value><value>#{employee4}</value><value>#{employee5}</value><value>#{employee6}</value><value>#{employee7}</value><value>#{employee8}</value><value>#{employee9}</value><value>#{employee10}</value></list-entries></managed-property></managed-bean><navigation-rule><navigation-case><from-outcome>home</from-outcome>
<to-view-id>/employee.jsp</to-view-id></navigation-case></navigation-rule>
Listing 6
<faces-config><managed-bean><managed-bean-name>employee1</managed-bean-name><managed-bean-class>com.googlehouse.usecases.listtable.data.Employee</managed-bean-class><managed-bean-scope>session</managed-bean-scope><managed-property><property-name>address</property-name><property-class>com.googlehouse.usecases.listtable.data.Address</property-class><value>#{address1}</value></managed-property><managed-property><property-name>firstName</property-name><property-class>java.lang.String</property-class><value>Peter</value></managed-property><managed-property><property-name>lastName</property-name><property-class>java.lang.String</property-class><value>Wang</value></managed-property><managed-property><property-name>middleName</property-name><property-class>java.lang.String</property-class><value/></managed-property><managed-property><property-name>cellPhone</property-name><property-class>java.lang.String</property-class><value>678-923-2719</value></managed-property><managed-property><property-name>workPhone</property-name><property-class>java.lang.String</property-class><value>678-923-2719</value></managed-property><managed-property><property-name>title</property-name><property-class>java.lang.String</property-class><value>IT Manager</value></managed-property><managed-property><property-name>SSN</property-name><property-class>java.lang.String</property-class><value>678-93-2719</value></managed-property><managed-property><property-name>emailAddress</property-name><property-class>java.lang.String</property-class><value>pwang33@yahoo.com</value></managed-property><managed-property><property-name>workEmailAddress</property-name><property-class>java.lang.String</property-class><value>pwang@googlehouse.com</value></managed-property></managed-bean><managed-bean><managed-bean-name>address1</managed-bean-name><managed-bean-class>com.googlehouse.usecases.listtable.data.Address</managed-bean-class><managed-bean-scope>session</managed-bean-scope><managed-property><property-name>addressOne</property-name><property-class>java.lang.String</property-class><value>1234 Google House St</value></managed-property><managed-property><property-name>addressTwo</property-name><property-class>java.lang.String</property-class><value/></managed-property><managed-property><property-name>city</property-name><property-class>java.lang.String</property-class><value>Atlanta</value></managed-property><managed-property><property-name>state</property-name><property-class>java.lang.String</property-class><value>GA</value></managed-property><managed-property><property-name>zipCode</property-name><property-class>java.lang.String</property-class><value>30394</value></managed-property></managed-bean>
Thanks to <t:saveState>
, we don't have to declare the EmployeeTable
managed bean in the session scope, but in the request scope. The JSF framework injects the employee list content into EmployeeTable
's employees
attribute for you. This kind of injection is often called IoC, or Inversion of Control. In our case, JSF constructs a list instance based on the <list-entries>
value in the configuration file and calls EmployeeTable
's setEmployees()
method. We only need a navigation rule. When a user clicks on the Cancel button, JSF routes the return page to employee.jsp
since the cancel()
method returns home
as the value:
Listing 7
public String cancel(){setSelectedRowIndex(-1);setEmployee(null);setShowDetailView(false);return "home";}
Now that we have done the code review, let’s see how we can run the application. With the help of a free version of the Exadel Studio Eclipse plug-in, you can create an empty JSF project easily. The following quick steps help you get there:
Figure 4. Exadel New Project's select-wizard window. Click on thumbnail to view full-sized image.
Figure 5. New JSF Project window. Click on thumbnail to view full-sized image.
To run our use-case in your Exadel Studio Eclipse environment, you can follow the steps below.
Figure 6. Import window. Click on thumbnail to view full-sized image.
Figure 7. Import JSF Project window. Click on thumbnail to view full-sized image.
Figure 8. Exadel Tomcat server console window. Click on thumbnail to view full-sized image.
Figure 9. View employee list. Click on thumbnail to view full-sized image.
With just a few MyFaces JSF components like <t:dataTable>
, <t:dataScroller>
, <t:panelTabbedPane>
, and <t:saveState>
, we have seen what JSF has promised: a simple and powerful component-based Web application framework. The developer now can leave the details and complexity to the component builder and focus on what's important for the business.
In this article, I only demonstrate a small fraction of MyFaces components that are part of MyFaces Tomahawk. As of Tomahawk version 1.1.1, about 27 components are ready to use. These Tomahawk components are 100 percent compatible with other JSF 1.1 reference implementations like the Sun RI or MyFaces RI.
Another great thing about MyFaces components is that MyFaces is an open source project; developers can dive into the source code to see how the JSF framework works and even make contributions to the MyFaces open source community.
Sun has added JSF as part of Java EE 5 (Java EE is Sun's new name for J2EE), and the effort to make a standard and mature Web framework has given JSF a promising future. As testament of JSF's potential, AJAX (Asynchronous JavaScript and XML) is now part of JSF, so developers can now make the best use of both technologies. To explore AJAX in JSF, check out MyFaces sandbox component or Ajax4Jsf.
Peter Wang is CIO of Google House, a consulting firm specializing in enterprise integration and IT outsourcing. Wang has been in the IT industry since 1994. He holds a master's degree in computer science and lives with his wife and two boys in Atlanta. He recently attended JBossWorld 2006 and was a break-out session speaker for VoIP application success using JBPM and JSF (see http://www.jbossworld.com/jbwv_2006/jems_verticles.htm).
聯(lián)系客服