Skip to main content

skip to main content

developerWorks  >  Sample IT projects  >

Consumer self-service, Part 4: Develop and deploy rich clients on the portal

Using IBM FacesClient Components technology to enhance your Web site

developerWorks
Document options

Document options requiring JavaScript are not displayed


Rate this page

Help us improve this content


Level: Introductory

Rod Henderson (rodhende@us.ibm.com), Advisory Software Engineer, IBM
Yongcheng Li (ycli@us.ibm.com), Senior Software Engineer, IBM
Thomas McElroy (tmcelroy@us.ibm.com), Advisory Software Engineer, IBM

24 Nov 2004

In this series, you learn about the high-level business requirements that lead to the adoption of a consumer self-service solution. In this article, discover the advantages of using FacesClient Components (Odyssey Browser Framework), which ships with IBM® WebSphere® Studio Application Developer Version 5.1.2, to develop a rich-client, consumer self-service solution in a portal environment. You are introduced to a prototype portlet application that highlights the FacesClient Components programming features.

Introduction

The introduction to this series describes a consumer self-service solution that addresses both customer loyalty and operational costs. Customer loyalty is a customer's commitment to a specific company. A company can create customer loyalty by providing superior customer service. The main goal of the self-service solution is to give consumers the ability to provide service for themselves through a single point of access to frequently used services on the Web. The consumer self-service solution can also be extended to let customer service representatives (CSRs) take advantage of a single Web-based interface to handle services on the consumer's behalf.

Portal technology can provide a cost-effective means to deploy and administer rich communication channels tailored to the consumer and CSR. To meet today's demanding business needs, portal applications must be designed to ensure a high-quality user experience. One way you can improve the quality of a user's experience with a portal application is to integrate rich-client features.

FacesClient Components (formerly Odyssey Browser Framework) is a set of tooling that ships with IBM WebSphere Studio Application Developer Version 5.1.2. The FacesClient Components set is an on demand JavaScript framework that lets you create portal applications with rich-client features.

You can use FacesClient Components to develop a rich-client, consumer self-service solution in a portal environment. In this article, you are introduced to a prototype portlet application that highlights the FacesClient Components programming features. Constructed end to end to provide an environment for Web applications that dramatically improve the quality of end-user applications, FacesClient Components can make the Web more relevant and useful to businesses and consumers. With FacesClient Components, you can create portal applications that incorporate rich user experiences such as:

  • Interactivity
  • Drag and drop
  • Editing
  • High-end graphics
  • Multitasking
  • Load balancing
  • Improved client responsiveness (avoiding excessive server round trips)
FacesClient Components provides the infrastructure you need to create and support Web applications that require considerable data manipulation, resulting in an application that vastly improves a user's efficiency. And because FacesClient Components is a JavaScript framework, applications developed with it do not require a browser plug-in for rendering on the browser.

In Part 1, you are introduced to consumer banking as the sample business for the consumer self-service solution. Figure 1 shows the business context of the roles in our scenario and how they are defined:

Consumer
Interacts with the Web-based interface provided by the banking institution to conduct financial transactions, manage customer information, or to chat with a financial advisor
Customer service representative or bank teller
Interacts with the solution to update customer information or performs financial transactions on behalf of the customer
Financial advisor
Is a specialist, a skilled professional who advises consumers on financial matters (The financial advisor uses the solution to chat online with consumers and to provide financial advice.)


Figure 1. Business context
Figure 1. Business context

Our implementation of the rich-client portlet application for the consumer portion of the consumer self-service solution is described in the developerWorks article, FacesClient Components, Part 1: Portlet programming with FacesClient Components.

This article describes our portal FacesClient Components rich-client implementation for the CSR portion of the consumer self-service solution. The prototype was built using the beta 1 version of FacesClient Components. You'll get:



Back to top


Understanding the legacy of the thin client

Current browsers (including Internet Explorer 5.5 and later; Netscape 6 and later) and Mozilla 1.x support rich-client features, such as advanced use of JavaScript for programming client-side interactions across objects in an HTML document. However, the majority of existing Web applications were developed using the thin-client computing model, where application servers are programmed to dynamically generate and send HTML pages to the user through the Web browser.

Developers successfully used the thin-client computing model to lower the cost of creating and distributing applications. However, this model has major shortcomings: Most of the resources exist at the server, and the user interaction with the application is at the page level for accessing resources. The system responds to the user in a request-response page rendering approach (page by page) rather than at the HTML component level, which usually means several interactions (roundtrips) between the Web browser and application server are needed to access resources. This large-scale system reply is inefficient for many classes of applications where a user's interaction may warrant an immediate response. Other limitations of the model include a lack of richness in application content and user interfaces. The quality of a user's experience with some Web applications suffers because of the limited sophistication of the solutions built and delivered using the thin-client computing model.



Back to top


Discovering the advantages to the rich-client model

Modern browsers have implemented the W3C Document Object Model (DOM) using JavaScript client-side scripting. As a result, Web application designers can now build powerful user interfaces (UIs) that let users instantaneously manipulate data on the client browser. Abstractly, when a user accesses a rich-client Web application, they download an application that contains an interactive UI, business logic, and a data set to their browser for immediate user interaction. Common operations for HTML tables containing information from the data set, such as sorting, paging, and filtering, can be executed on the client browser. Having a well-defined DOM implemented in JavaScript within the browser, Web application designers can build JavaScript code to access and control elements of a Web page and the attributes of the elements. Prior to the development of the modern browser, Web application UI designs were largely restricted to page-level interactions. Today, designers can create UIs that support element-level interactivity.



Back to top


Using FacesClient Components

FacesClient Components is a JavaScript technology-based software development structure you can use to create rich-client Web applications. FacesClient Components provides an exceptional infrastructure in terms rich data structures, application interface elements, and communications. With FacesClient Components, you can create rich-client designs that significantly enhance a user's efficiency when working with Web applications that involve significant data manipulation. FacesClient Components includes an object model for interactive client applications, and provides support for creating events and event handlers.

FacesClient Components contains a set of rich UI controls and components, including TreeView, DataGrid, TabbedPanel, DatePicker, and GraphDraw -- letting you quickly create applications that offer a richer user experience. The controls incorporate features that make applications aware of the overall page run time. The controls are designed to dynamically bind to data and have the capability to read and write to a shared data model. The data model can be shared with other controls on the page, and it can be used entirely on the client computer to store complex object data for reuse across or within an application. Shared data models improve application performance by reducing the number of requests made to servers and the amount of data needed to transmit before a user can begin working.

FacesClient Components encompasses a rich programming model that includes a core JavaScript Library (JSL), JSL emitters, and a JSF layer, as shown in Figure 2. Our series prototype was developed using the JSL emitter layer of the programming model. This layer represents a Java™ language-based library of helper classes that emit JSL code onto a page. Emitters wrap two important concepts from the JSL -- controls and adapters. Controls are responsible for the visual representation of data. Controls construct page elements (DOM elements) to visually display the control and manage control events. During the development phase of the prototype, the tooling and JSF layers of the programming model were not mature enough to support development of the prototype. Stay tuned for an upcoming follow-on article describing a JSF and tooling approach.


Figure 2. FacesClient Components programming model
Figure 2. FacesClient Components programming model


Back to top


Implementing the CSR FacesClient Components portlet

The consumer self-service CSR prototype is a portlet application used by a CSR or teller to handle banking-related services on a consumer's behalf. The rich-client platform, constructed using FacesClient Components, provides the tools needed by a CSR to execute the following business scenarios.

Scenario 1
John, a banking consumer, enters a branch bank and requests that funds be deposited into one of his banking accounts. The CSR searches for the customer and retrieves his customer and banking information. The consumer gives the CSR a check or cash for deposit. The CSR takes the funds and completes the deposit transaction on the consumer's behalf.
Scenario 2
Another consumer, Diane, enters the branch bank and requests that funds be withdrawn from her banking account. The CSR does a search and retrieves the customer and banking information. Next, the CSR enters the amount of cash to be withdrawn into the UI display and clicks Submit to execute the withdrawal transaction on the consumer's behalf. After receiving the withdrawal transaction results, the CSR gives the consumer the requested cash.

The CSR initial screen is the Customer Search portlet shown in the portlet maximized state in Figure 3. CSRs see the initial screen when they log into the portlet application. A customer's banking identification is used to search for banking account information. When the CSR helps a customer perform a banking task like that discussed in Scenario 1, she first sees the screen shown in Figure 4. An application containing an interactive UI, business logic, and a data set is downloaded to the CSR's browser for immediate use.

The five FacesClient Components-enabled portlets (Customer Search, Customer Sessions, Personal Information, Global Position, and Teller) shown in Figure 4 share a common data model and are wired to an event model that supports portlet-to-portlet interaction. DataGrid, TabbedPanel, and GraphDraw controls were used to construct the Global Position and Customer Sessions portlets. The controls are used to display a consumer's banking and financial information in both table and graphical form.


Figure 3. Customer Search portlet
Figure 3. Customer Search portlet

Figure 4. Scenario 1
Figure 4. Scenario 1: search john

To complete the deposit transaction, the CSR selects the Deposit link in the Teller portlet and populates the account number input field by selecting one of the account choice options listed in the combo box or by clicking an account in the Global Position portlet banking tab. Through the interaction between the controls, a CSR can click a bank account number in the Global Position portlet banking tab, which then fills in the account number input field of the Teller portlet.

To execute the deposit transaction, the CSR then clicks Submit on the Teller portlet. The component business logic used to perform the deposit is on the back-end server. After completion of the back-end transaction, the CSR receives notification of the deposit results, as shown in Figure 5, where a $100 deposit was transacted. The data model on the client side was updated to reflect the new banking account balance. Updating the data model automatically triggers an update of all the controls bound to the modified data elements. A data exchange between the FacesClient Components technology-enabled browser and the back-end server is accomplished without a page refresh. FacesClient Components contains the communications and run-time infrastructure to support element-level interactivity.

After completing the deposit transaction, the application is programmed to merge the incremental data change into the data model using FacesClient Components APIs. This programming feature improves the client's responsiveness and is one of the major benefits of using FacesClient Components technology.


Figure 5. Deposit results
Figure 5. Deposit results

Diane from Scenario 2 now requests services from the same CSR that completed Scenario 1. Diane initiates a funds-withdrawal transaction from her banking account. In this case, the CSR uses the Customer Search portlet explained in Figure 4 to search for the customer and retrieve her information. The search produces the results shown in Figure 6, where the customer's banking and personal information are simply added to the data model on the client-side browser.

For the new customer search, the flow process starts with the Customer Search portlet, which is programmed to verify that the new customer data object is not presently stored in the client-side data model. After determining that the new customer data object is not an existing client-side object, the portlet is programmed to send a request to the back-end server to supply the new customer data object. The back-end server sends only the new data object, which is added to the client-side data model. Without a page refresh, data is exchanged between the browser and the back-end server.

In Scenario 2 (shown in Figure 6), you see how FacesClient Components can be used to cleanly separate presentation and business logic from the data set. This feature is very beneficial for low-bandwidth users since the UI and business logic for the application are downloaded once with an initial data model. Incremental changes to the data model created by the user's interaction patterns with the application produce follow-on network traffic. And, the FacesClient Components request-response approach reduces the server workload when compared with the thin-client request-response page rendering approach.


Figure 6. Scenario 2
Figure 6. Scenario 2


Back to top


Building the FacesClient Components portlet application

The main building blocks of a FacesClient Components portlet application are the data model, UI controls, and event model. This section outlines the steps for creating each building block.

Data model

To create the application data model, determine the data set needed to support the application and model the data. The core model for the CSR portlet application is shown in Figure 7. Each model must have a root node or starting point. This node is not a special class. The root object in our model contains zero or more customer objects. As described in Scenario 2, additional customer objects can be added on demand to the root object as needed. Each customer can contain zero or more positions (position objects), and it has a CustomerInfo object. The position objects hold zero or more banking accounts, credit card, fund, and loan objects.


Figure 7. CSR data model
Figure 7. CSR data model

A FacesClient Components mechanism called Service Data Objects for JavaScript (SDO4JS) is used to transform the Java objects shown in Figure 7 to JavaScript objects. The mechanism uses the Eclipse Modeling Framework (EMF) to provide the base infrastructure needed to model Java classes. The procedure and tools used to create the JavaScript objects are described in the IBM FacesClient Components Developer's Guide.

UI controls

Customer Search, Customer Sessions, Personal Information, Global Position, and Teller are the five FacesClient Components technology-enabled portlets in Figure 4. They share a common data model and are wired to an event model that supports portlet-to-portlet interaction.

How do you construct and share data and event models for a portal page that contains several portlets when the order in which portlets are processed is not guaranteed? Our solution was to design each portlet to extend a base class called OBFTellerBasePortlet, shown in Listing 1. Using the beginPage and endPage methods of the base class implementation of PortletPageListener, a portlet can be constructed to insert code or markup at the beginning and end of a page. The initialization and postProcessing methods in the class OBFTellerUtility (shown in Listing 2) are used as safeguards to ensure the event model-related logic in OBF_Teller_Init.jsp and OBF_Teller_End.jsp is inserted only once per page. The initialization and postProcessing methods are used to simply set attributes on the servlet request in accordance with a set of arguments.


Listing 1. Base class
 
...
public class OBFTellerBasePortlet extends PortletAdapter
                             implements PortletPageListener {
   public void beginPage(PortletRequest request, 
      PortletResponse response)throws 
   PortletException,
                         java.io.IOException {
   PortletSession portletSession = (PortletSession) request.getSession();
   String customerID = (String)portletSession.getAttribute("CUSTOMER_ID");
     if ((customerID != null) && (!customerID.equals(""))) {
        OBFTellerUtility du = new OBFTellerUtility();
        du.initialization(request, response,
                          getPortletConfig().getContext(),
                          customerID, "resource/OBF_Teller_Init.jsp")}
   }
     public void endPage(PortletRequest request, PortletResponse
                response) throws PortletException, java.io.IOException {
     PortletSession portletSession = (PortletSession) request.getSession();
     String customerID = (String) portletSession.getAttribute
        ("CUSTOMER_ID");
     if ((customerID != null) && (!customerID.equals(""))) {
       OBFTellerUtility du = new OBFTellerUtility();
       du.postProcessing(request, response,getPortletConfig().getContext(),
                       customerID, "resource/OBF_Teller_End.jsp");}
   }
} 


Listing 2. Teller utility class
...
 public class OBFTellerUtility {
          ...
    public OBFTellerUtility() { }

    public static HttpServletRequest getHttpServletRequest(PortletRequest
        request) {
	if (request == null) {
	    return null;
	}
	com.ibm.wps.pe.pc.legacy.core.InternalPortletRequest _request = 
	   com.ibm.wps.pe. 
        pc.legacy.core.PortletUtils.getInternalRequest(request);
	return _request.getHttpServletRequest();
    }

    public static HttpSession getHttpSession(PortletSession session) {
	if (session == null) {
	    return null;
	}
	com.ibm.wps.pe.pc.legacy.core.InternalPortletSession _session = 
	   com.ibm.wps.pe. 
        pc.legacy.core.PortletUtils.getInternalSession(session);
	return _session.getHttpSession();
    }

    public void initialization(PortletRequest request, PortletResponse 
       response,
	PortletContext context, String customerID,
	String jspPage) throws PortletException, java.io.IOException {

	HttpServletRequest hsr = this.getHttpServletRequest(request);
	String wdo4js_prefix = (String) hsr.getAttribute
	   ("OBF_TELLER_INIT");

	// do this only once across all the portlets
	if ((wdo4js_prefix == null) || (!wdo4js_prefix.equals("true"))) {
	   hsr.setAttribute("OBF_TELLER_INIT", "true");
	   context.include(jspPage, request, response);
	}
    }

    public void postProcessing(PortletRequest request, PortletResponse 
       response,
		 PortletContext context, String customerID,
           String jspPage) throws PortletException, java.io.IOException {
	HttpServletRequest hsr = this.getHttpServletRequest(request);
	String wdo4js_end = (String) hsr.getAttribute("OBF_TELLER_END");

	// do this only once across all the portlets
	if ((wdo4js_end == null) || (!wdo4js_end.equals("true"))) {
	   hsr.setAttribute("OBF_TELLER_END", "true");
	   context.include(jspPage, request, response);
	}
    }

}

OBF_Teller_Init in Listing 3 and OBF_Teller_End contain JavaScript and FacesClient Components emitter code used to support the event model. With FacesClient Components, you can create a variety of event handlers for control events to support portlet-to-portlet interaction. Get more details about FacesClient Components emitter classes shown in Listing 3 in the IBM FacesClient Components Developer's Guide.


Listing 3. OBF_Teller_Init.jsp
...
   >%
	HttpServletRequest hsr = OBFTellerUtility.getHttpServletRequest
	   (portletRequest);

   	EventSelectAndActivateEmitter bankingAccountData = new 
        EventSelectAndActivateEmitter();
	bankingAccountData.setEClassName("Position");
   	hsr.setAttribute("BankingAccountData", bankingAccountData);

   	EventSelectAndActivateEmitter creditCardData = 
                       new EventSelectAndActivateEmitter();
	creditCardData.setEClassName("Position");
   	hsr.setAttribute("CreditCardData", creditCardData);

   	EventSelectAndActivateEmitter fundData = 
                       new EventSelectAndActivateEmitter();
	fundData.setEClassName("Position");
   	hsr.setAttribute("FundData", fundData);

   	EventSelectAndActivateEmitter loanData = new 
   	   EventSelectAndActivateEmitter();
	loanData.setEClassName("Position");
   	hsr.setAttribute("LoanData", loanData);

   	EventSelectAndActivateEmitter newCustomer = 
                       new EventSelectAndActivateEmitter();
	newCustomer.setEClassName("Root");
   	hsr.setAttribute("NewCustomer", newCustomer);

   	EventSelectAndActivateEmitter selectCustomer = 
                       new EventSelectAndActivateEmitter();
   	hsr.setAttribute("SelectCustomer", selectCustomer);

   	EventSelectAndActivateEmitter selectAccount = 
                       new EventSelectAndActivateEmitter();
   	hsr.setAttribute("SelectAccount", selectAccount);
%<
...
       

Portlet messaging is used to share the data model with the other portlets. Action events are processed before message and window events, so the portlet actionPerformed method is the appropriate place to send a message. A message is created in the CustomerSearch's actionPerformed method shown in Listing 4A. CustomerSearch is shown in both Listings 4A and 4B. The message is an OBFTellerMessage object broadcast by the PortletContext object's send method to the other four portlets on the page. To receive the message, each of the other portlets must implement the MessageListener interface and messageReceived method, as shown by the GlobalPosition portlet in Listing 5. In GlobalPosition, the OBFTellerMessage object is retrieved from the message event.


Listing 4a. Customer Search
...
public class CustomerSearch extends OBFTellerBasePortlet implements 
ActionListener {
    
  public void actionPerformed(ActionEvent event)
	throws PortletException, AccessDeniedException {
String actionEvent = event.getActionString();
	
PortletRequest request = event.getRequest();
PortletSession portletSession = (PortletSession) request.getSession();
	...

if (actionEvent.equals("EndCustomerSearch")) {
 String customerID = "";
 portletSession.setAttribute("CUSTOMER_ID", customerID);
 portletSession.setAttribute("SearchPortletState", "Maximized");
   ...
OBFTellerMessage msg = new OBFTellerMessage();
msg.put("CUSTOMER_ID", customerID);
getPortletConfig().getContext().send(null, msg);

} else if (actionEvent.equals("StartCustomerSearch")) {

String customerID = request.getParameterValues("CustomerID")[0];

  ....
PageContext ctx = new PageContext();

portletSession.setAttribute("CUSTOMER_ID", customerID);

 OBFTellerMessage msg = new OBFTellerMessage();
 msg.put("CUSTOMER_ID", customerID);
 msg.put("Root", root);
 msg.put("GlobalPosition", position);
 msg.put("CustomerInfo", customerInfo);
 msg.put("ODCPCTX", ctx);

 request.setAttribute("ODCPCTX", ctx);
 request.setAttribute("Root", root);

   getPortletConfig().getContext().send(null, msg);
	}
 }
...


Listing 4b. Customer Search continued
...
  public void doView(PortletRequest request, PortletResponse response)
		throws PortletException, java.io.IOException {
	
PortletSession portletSession = (PortletSession) request.getSession();
HttpSession hSession = OBFTellerUtility.getHttpSession(portletSession);
PortletContext context = getPortletConfig().getContext();

String action = request.getParameter("ActionType");

if (action == null) {
      ...
  String customerID = (String) portletSession.getAttribute("CUSTOMER_ID");

   if ((customerID != null) && (!customerID.equals(""))) {
   portletSession.setAttribute("SearchPortletState", "Maximized");
   context.include("resource/CustomerSearch.jsp", request, response);
  } else {
	    String searchPortletState = 
         (String)portletSession.getAttribute("SearchPortletState");
	    if ((searchPortletState == null) || 
	       (!searchPortletState.equals("Maximized"))) {
	   portletSession.setAttribute("SearchPortletState", "Maximized");
	   context.include("resource/CustomerSearchEmptyRedirect.jsp", 
	       request, response);
	  } else {
         portletSession.setAttribute("SearchPortletState", "Normal");
	    context.include("resource/CustomerSearchEmpty.jsp", 
	       request, response);
         }
	   }
	} else if (action.equals("EndCustomerSession")) {
	     portletSession.setAttribute("CUSTOMER_ID", "");
         hSession.setAttribute("Root", null);
	     context.include("resource/CustomerSearchEmpty.jsp", request,
	        response); }
    }

}
   


Listing 5. Global Position
...
 public class GlobalPosition extends OBFTellerBasePortlet implements 
 MessageListener {

    public void messageReceived(MessageEvent event) {
       ...
     OBFTellerMessage msg = (OBFTellerMessage) event.getMessage();

	 if (msg instanceof OBFTellerMessage) {
	   String customerID = (String) msg.get("CUSTOMER_ID");
       PortletRequest request = event.getRequest();
	   PortletSession session = request.getPortletSession();
	   session.setAttribute("CUSTOMER_ID", customerID);

	   if (!customerID.equals("")) {
	      PageContext ctx = (PageContext) msg.get("ODCPCTX");
	      Root root = (Root) msg.get("Root");
	      Position position = (Position) msg.get("GlobalPosition");
          request.setAttribute("ODCPCTX", ctx);
	      request.setAttribute("Root", root);
	      request.setAttribute("GlobalPosition", position);}
	}
   }
   public void doView(PortletRequest request, PortletResponse response)
		throws PortletException, java.io.IOException {
	
	 PortletContext context = getPortletConfig().getContext();
     PortletSession session = (PortletSession) request.getSession();
     String customerID = (String) session.getAttribute("CUSTOMER_ID");
	 if ((customerID != null) && (!customerID.equals(""))) {
	   context.include("resource/GlobalPosition.jsp", request, 
	      response);
	 } else {
	   context.include("resource/GlobalPositionEmpty.jsp", request, 
	      response);}
   }
}
       

Take a look at how a portlet UI is constructed using FacesClient Components technology. Using the GlobalPosition portlet shown in Figure 8, you can see how to create a FacesClient Components technology-enabled portlet that shares a common data model with other portlets on a page and is wired to an event model that supports portlet-to-portlet interaction. The portlet contains the TabbedPanel control, which has four panels:

  • Banking
  • Credit Card
  • Fund
  • Loan
Each panel uses a DataGrid to display the banking, credit card, fund, and loan data stored in the page data model. A GraphDraw control is used to graphically display the customer's bank account data in the Banking panel. The DataGrid is a rich-client control that lets a user sort, page, and filter the banking, credit card, fund, and loan data on the client side (in the browser window). To sort the content of the DataGrid, the user simply clicks on one of the column headers. With GraphDraw, a user can toggle between a pie and bar graphical representation of the data without page refreshes.


Figure 8. Global position portlet
Figure 8. Global position portlet

FacesClient Components technology satisfies two important development requirements. First, the relationships between the controls and data model used in the GlobalPosition portlet provide a rich interaction experience for the user. Second, the FacesClient Components programming model is straightforward and relatively easy to set up, so you can focus on each control, one by one.

For example, the GlobalPosition JavaServer Pages (JSP) component contains the FacesClient Components emitter Java code used to construct the TabbedPanel, DataGrid, and GraphDraw UIs in the GlobalPosition portlet. Listing 6 and Listing 7 show fragments of the GlobalPosition JSP. To construct the FacesClient Components technology-enabled JSP component, use the jsp: useBean action in Listing 6 to generate the root and position data model objects as defined in Figure 7. Then use the jsp: useBean action to build the BankingAccountData, CreditCardData, FundData, LoanData, and SelectAccount event model objects for the JSP, as seen in Listing 3.

The PageContext object, in Listing 6 and created by jsp: useBean, is essential to the framework. FacesClient Components uses the PageContext object to maintain state information about the framework. The state information is employed to:

  • Prevent cycles in the data model graphs. (For example, in the data model a customer object has position objects, and a position object points back to the customer object.)
  • Ensure certain objects or types are not sent to the browser more than once.
  • Recover exported object IDs.
The context object is used to manage information across invocations.


Listing 6. GlobalPosition.jsp: Setting up the data model
...
<%@ page import="com.ibm.fs.obf.portlet.*" %>
<%@ page import="com.ibm.odcb.jrender.emitters.*" %>

<jsp:useBean id="ODCPCTX" class="com.ibm.odcb.jrender.mediators.
   PageContext" 
scope="request" />
<jsp:useBean id="Root" class="com.ibm.fs.obf.businessobjects.Root" scope=" 
request" />
<jsp:useBean id="GlobalPosition" class="com.ibm.fs.obf.businessobjects. 
Position" scope="request" />

<jsp:useBean id="BankingAccountData" class="EventSelectAndActivateEmitter" 
scope="request" />
<jsp:useBean id="CreditCardData" class="EventSelectAndActivateEmitter" 
  scope="request" />
<jsp:useBean id="FundData" class="EventSelectAndActivateEmitter" scope=" 
request" />
<jsp:useBean id="LoanData" class="EventSelectAndActivateEmitter" scope=" 
request" />

<jsp:useBean id="SelectAccount" class="EventSelectAndActivateEmitter" 
scope="request" />

<portletAPI:init/>


<%
  PortletURLRewriter portletURLRewriter = new PortletURLRewriter
     (portletResponse);
  InitializationEmitter Init = new InitializationEmitter(false, -1, 
     "en", "", 
  portletURLRewriter, 2, request.getServerName(), request.getServerPort());
  Init.Export(out, ODCPCTX);

  WDO4JSEmitter E = new WDO4JSEmitter();
  E.Init(Root, "Customer");
  E.Export(out, ODCPCTX);

  String RootId = E.getExportIDByObject(Root);
  String GlobalPositionId = E.getExportIDByObject(GlobalPosition);
%>
... 


Listing 7. GlobalPosition.jsp -- Creating the banking panel
...
<%
PanelEmitter BankingPanel = new PanelEmitter("Banking", "Banking", false);
%>

<div id="<%=BankingPanel.getPanelID()%>">
...
<%
  DataGridEmitter  DGBanking = new DataGridEmitter();
  DGBanking.Init("Customer", GlobalPositionId, "BankingAccounts", true, 3);
  DGBanking.addColumn(new DataGridEmitterHelper("AccountNumber", "Acct #",
     true,
    "center", 10, false, false, false, null, null, 0, null, null));
  DGBanking.addColumn(new DataGridEmitterHelper("Balance", "Balance", true, 
    "right", 15, false, false, false, null, null, 2, null, null));
  DGBanking.addColumn(new DataGridEmitterHelper("AvailableBalance", 
     "Avail Bal.", 
     true, "right", 30, false, false, false, null, null, 2,null,null));
  DGBanking.addColumn(new DataGridEmitterHelper("Currency", "Currency",
      true,
      "center", 30, false, false, false, null, null, 0, null, null));
  DGBanking.setCssPrefix("ad");
   DGBanking.Export(out, ODCPCTX);

 BankingAccountData.addTargetEmitter(DGBanking, null);
 SelectAccount.Init(DGBanking, "OnHighlight", "BankingAccount");

 // create a graphdraw control to display the account chart
  GraphDrawEmitter GD = new GraphDrawEmitter();
  GD.Init("Customer", GlobalPositionId, "BankingAccounts", 
     "DollarBalance", " 
  AccountNumber", "Banking Accounts (USD)", true, 378, 180, true, true, 
     false, 
  GraphDrawEmitter.BAR_CHART);
  GD.Export(out, ODCPCTX);

BankingAccountData.addTargetEmitter(GD, null);
%>
 ...
</div>
...
      

There are three classes of FacesClient Component emitters. Use an emitter:

  • To export data models
  • To control the overall page run time
  • For each UI control listed in the framework
The data model, page context, and event model objects serve as attributes for the emitters, as shown Listing 6, where the InitializationEmitter and WDO4JSEmitter are created and initialized. The InitializationEmitter creates several lines of JavaScript, such as the proper library files, logger framework setup, locale, and URL rewriter. The WDO4JSEmitter was designed to emit the data model and instance data to a page. In the following code from Listing 6, the instance data Root is generated by the jsp: useBean tag for the page.
WDO4JSEmitter E = new WDO4JSEmitter();
E.Init(Root, "Customer");
E.Export(out, ODCPCTX);

TabbedPanel is a control that manages a set of panels and transitions between each. Individual panels are needed to create a TabbedPanel. We created this section of code from Listing 7 to construct the banking panel.

PanelEmitter BankingPanel = new PanelEmitter("Banking", "Banking",
      false)

To generate the DataGrid located in the GlobalPostion portlet, the DataGrid emitter must be created and initialized with five parameters: ModelName, ObjectId, EReferenceId, ReadOnly, and PageSize. One of the DataGrid emitter parameters, ObjectId, is an export ID for the bean used as the root of the DataGrid. The export ID represents the data element the emitter needs for binding. In this case, the emitter binds to the export ID of the GlobalPosition bean, which has an ID created from the following code from Listing 6.

String GlobalPositionId = E.getExportIDByObject(GlobalPosition)
 

Before the TabbedPanel emitter is created, a Div element is required. The ID of BankingPanel is needed to define Div, as follows.

<div id="<%=BankingPanel.getPanelID()%>">
         

The DataGrid emitter is created and initialized as follows, in this code from Listing 7.

DataGridEmitter  DGBanking = new DataGridEmitter()
DGBanking.Init("Customer", GlobalPositionId, "BankingAccounts", true, 3)

The four other Init method parameters are:

ModelName
Name of the model used for this DataGrid
EReferenceId
Name of the EReference that lists the rows to be shown in the DataGrid. For instance, in our example, the object mapped to the DataGrid is a global position. The rows come from its BankingAccounts EReference, which is generated by the FacesClient Components WDO4JS tool.
ReadOnly
Defines whether the table allows deletion or creation or rows
PageSize
Defines the page size for the DataGrid control or how many rows are to be shown at the same time

The next step is to add metadata to enable the DataGrid to navigate the data and display the appropriate data items. The following code is from Listing 7.

DGBanking.addColumn(new DataGridEmitterHelper("AccountNumber", "Acct
      #", true, "center", 10, false, false, false, null, null, 0, null, 
         null));
DGBanking.addColumn(new DataGridEmitterHelper("Balance", "Balance", true,
    "right
", 15, false, false, false, null, null, 2, null, null));
DGBanking.addColumn(new DataGridEmitterHelper("AvailableBalance", "Avail 
   Bal.",
true, "right", 30, false, false, false, null, null, 2, null, null));
DGBanking.addColumn(new DataGridEmitterHelper("Currency", "Currency", 
   true, "
center", 30, false, false, false, null, null, 0, null, null));
DGBanking.setCssPrefix("ad");
DGBanking.Export(out, ODCPCTX);

Each column of the DataGrid is defined by the addColumn method. A DataGridEmitterHelper class is used to characterize the columns. The class constructor has 13 parameters, described in the IBM FacesClient Components Developer's Guide.

The Credit Card, Fund, and Loan panels are created in a manner similar to the Banking panel. Each panel uses a DataGrid to display credit card, fund, and loan data stored in the page data model. The emitter code used to generate the panels and data grids is shown in Listings 8, 9, and 10.


Listing 8. GlobalPosition.jsp: Creating the credit card panel
...
<%
PanelEmitter CreditCardPanel = new PanelEmitter("Credit Card", 
   "CreditCard", false);
%>

<div id="<%=CreditCardPanel.getPanelID()%>">
...
<%
DataGridEmitter  DGCreditCard = new DataGridEmitter();
DGCreditCard.Init("Customer", GlobalPositionId, "CreditCards", true, 4);
DGCreditCard.addColumn(new DataGridEmitterHelper("CardNumber", "Card #", 
   true,
  "center", 10, false, false, false, null, null, 0, null, null));
DGCreditCard.addColumn(new DataGridEmitterHelper("CardType", "Card Type", 
  true, 
  "center", 10, false, false, false, null, null, 0, null, null));
DGCreditCard.addColumn(new DataGridEmitterHelper("Balance", "Balance", 
   true,
  "right", 15, false, false, false, null, null, 2, null, null));
DGCreditCard.addColumn(new DataGridEmitterHelper("Currency", "Currency", 
   true,
  "center", 30, false, false, false, null, null, 0, null, null));
DGCreditCard.setCssPrefix("ad");
DGCreditCard.Export(out, ODCPCTX);

CreditCardData.addTargetEmitter(DGCreditCard, null);
%>

...
</div>

 ...
      


Listing 9. GlobalPosition.jsp: Creating the fund panel
...
<%
	PanelEmitter FundPanel = new PanelEmitter("Fund", "Fund", false);
%>

<div id="<%=FundPanel.getPanelID()%>">
...

<%
DataGridEmitter  DGFund = new DataGridEmitter();
DGFund.Init("Customer", GlobalPositionId, "Funds", true, 4);
DGFund.addColumn(new DataGridEmitterHelper("FundName", "Fund Name",true, 
 " center", 10, false, false, false, null, null, 0, null, null));
DGFund.addColumn(new DataGridEmitterHelper("Balance", "Balance", true, 
   "right", 
15, false, false, false, null, null, 2, null, null));
DGFund.addColumn(new DataGridEmitterHelper("Currency", "Currency", true, 
 "center", 30, false, false, false, null, null, 0, null, null));
DGFund.setCssPrefix("ad");
DGFund.Export(out, ODCPCTX);

FundData.addTargetEmitter(DGFund, null);
%>
 ...
</div>
 ...
      


Listing 10. GlobalPosition.jsp: Creating the loan panel
...
<%
PanelEmitter LoanPanel = new PanelEmitter("Loan", "Loan", false);
%>

<div id="<%=LoanPanel.getPanelID()%>">
...
<%
 DataGridEmitter  DGLoan = new DataGridEmitter();
 DGLoan.Init("Customer", GlobalPositionId, "Loans", true, 4);
 DGLoan.addColumn(new DataGridEmitterHelper("AccountNumber", "Acct #", 
    true,
  "center", 10, false, false, false, null, null, 0, null, null));
 DGLoan.addColumn(new DataGridEmitterHelper("LoanType", "Loan Type", true,
 "center", 10, false, false, false, null, null, 0, null, null));
 DGLoan.addColumn(new DataGridEmitterHelper("Balance", "Balance", true, 
    "right", 
 15, false, false, false, null, null, 2, null, null));
 DGLoan.addColumn(new DataGridEmitterHelper("Currency", "Currency", true,
  "center", 30, false, false, false, null, null, 0, null, null));
 DGLoan.setCssPrefix("ad");
 DGLoan.Export(out, ODCPCTX);

LoanData.addTargetEmitter(DGLoan, null);
%>
...
</div>
...
      

After the set of panels are constructed, they are used to create the TabbedPanel. The code in Listing 11 is used to construct the TabbedPanel. The finished tabbed panel is a rich control designed to support dynamic behavior, all in the client-side browser, without having to go back to the server after each click.


Listing 11. GlobalPosition.jsp: Creating the TabbedPanel
...
 <%
	TabbedPanelEmitter TabbedPanel = new TabbedPanelEmitter();
	TabbedPanel.Init(370, 410, 4, false, true, null, false);
	TabbedPanel.addPanel(BankingPanel);
	TabbedPanel.addPanel(CreditCardPanel);
	TabbedPanel.addPanel(FundPanel);
	TabbedPanel.addPanel(LoanPanel);
	TabbedPanel.Export(out, ODCPCTX);
%>

 ...
 

The GraphDraw is a Flash-based control that behaves like a standard JSL control through the emitter wrapper code and the adapter. The GraphDraw control is used to graphically display the customer's bank account data in the Banking panel. The interfaces in Listing 12 are similar to the DataGrid control.


Listing 12. GlobalPosition.jsp: Creating the graph
...
 GraphDrawEmitter GD = new GraphDrawEmitter();
    GD.Init("Customer", GlobalPositionId, "BankingAccounts", 
       "DollarBalance", " 
    AccountNumber", "Banking Accounts (USD)", true, 378, 180, true, true, 
       false, 
    GraphDrawEmitter.BAR_CHART);
    GD.Export(out, ODCPCTX);
 ...
      

Event model

The final piece of the FacesClient Components puzzle is the event-handling setup. The relationships between the elements and the model provide a rich interaction model for the user. In the GlobalPosition portlet, the DataGrid and GraphDraw controls are bound to the GlobalPosition of the selected customer. Each control is independently mapped to the model. As a result, the programming model for FacesClient Components emitters can be seen as a set of control-to-model relations. The control-to-data relationships make possible implied control-to-control relationships if the controls are bound to the same data, where changes in one control are reflected in the other control. From Listing 3, the following code shows the relatively easy setup required for the event handlers used in the GlobalPosition portlet. One of the constructors for the select and activate emitter is shown, and the setEClassName method is used to establish the EClassName. The EClassName is the type of EObject that activates the handler.

EventSelectAndActivateEmitter bankingAccountData = new
EventSelectAndActivateEmitter();
bankingAccountData.setEClassName("Position");
      

Additional code from Listing 7 shows the setup for the emitter, which represents the target control. Using the addTargetEmitter method, the DGBanking DataGrid is pinpointed to be the target control. The second property of the method represents the property string. The Init method of the SelectAccount class is used to set up the name of the event on the source control, such as OnHighlight or OnSelec.

BankingAccountData.addTargetEmitter(DGBanking, null);
	SelectAccount.Init(DGBanking, "OnHighlight", "BankingAccount");
 



Back to top


Summary

FacesClient Components and portal technology can be used to create rich-client CSR applications that can be deployed and administered in a cost-effective manner. With a FacesClient Components technology-enabled application, the CSR can interact with the working dataset using easily programmable controls that minimize round trips back to the server until there is a need to submit data or complete a transaction. The CSR benefits from the improved response times and increased interactivity with the portal pages when compared with non-FacesClient Components Web pages.



Resources



About the authors

Rod Henderson is an Advisory Software Engineer in the IBM Software Group System House, Advanced Technology department in Research Triangle Park, NC. He received a Ph.D. in electrical engineering from North Carolina A&T State University in 2002. Rod is involved in advanced technology projects in the areas of rich client technology, solution integration, and scenario-based software development. You can contact Rod at rodhende@us.ibm.com.


Yongcheng Li is a Senior Software Engineer for the IBM Software Group System House, Advanced Technology group in Research Triangle Park, NC. He received a Ph.D. in Computer Science from Tsinghua University in 1993. Yongcheng is currently involved in advanced technology projects in the areas of rich client technology, data caching and replication, solution integration, and scenario-based software development. You can contact him at ycli@us.ibm.com.


Thomas McElroy is an Advisory Software Engineer in the IBM Software Group System House organization in Research Triangle Park, NC. He has been working on Web-application technology projects for the last three years, including projects with on demand clients, edge-side includes, Integrated Solutions Console, and a novel approach to using a Java Database Connectivity (JDBC) caching proxy. You can reach Thomas at tmcelroy@us.ibm.com.




Rate this page


Please take a moment to complete this form to help us better serve you.



YesNoDon't know
 


 


12345
Not
useful
Extremely
useful
 


Back to top