Oracle ADF Data Binding Primer and ADF/Struts Overview

Author: Steve Muench, JDeveloper/ADF Development Team
Date: January 19, 2006
Revision 1.5 (Revision History)

Abstract

This article explains the basics of the new Oracle Application Development Framework's data binding layer (based on JSR-227), gives an overview of the ADF runtime and design time data binding facilities, explains how ADF integrates with and supports easily building Struts-based web applications, and puts the concepts into practice with three simple example applications.


NOTE:

This whitepaper explains the new data binding concepts and facilities in the Oracle Application Development Framework (ADF) by explaining the interesting technical details of three simple, example applications that put the features into practice. If you plan to print the whitepaper and follow along offline, you should download the three example applications before unplugging from the network: ADFBindingInfo.zip [1], DataActionEventsExample.zip [2], and MethodBindingExample.zip [3].

This document goes into considerable technical depth about the main features of ADF data binding and Struts integration. If you are looking for a higher-level overview of the whole Oracle ADF framework, please see the Oracle Application Development Framework Overview [4] whitepaper (PDF format).


Contents

        Introduction
        Overview of ADF Data Binding Concepts
        Support for Existing and Emerging Standards
        ADF Binding Design Time Experience
                Creating Data Controls
                Designing UI's with the Data Control Palette
                Creating Binding Containers and Bindings
                Seeing the Contents of Your Binding Context
        ADF Information in the Online Help
        Understanding a Working Example Application
                Overview of the Sample Application
                Basic Details of ADF/Struts Integration
                Examining Our ADF / Struts Example Actions
                Using a Custom Method to Setup the Model Data
                 Using Action Bindings to Decouple Controller and Service
                Using the DataAction for Full Lifecycle Support
                Using "Data Pages" to Simplify Your Page Flow Diagrams
                Using DataAction Features to Go Totally Declarative
                Using EL Expressions to Refer to Data in Web Pages
        Understanding ADF DataAction Event Handling
                The DataAction Events Sample Application
                What Are Events in a Web Application?
                Handling Events with the Struts DispatchAction
                Studying the DispatchAction Example
                Exploring the Equivalent DataAction Implementation
                Paying Attention to Internationalization
                Using ADF Bindings and Declarative Events
                Exploiting Declarative Event-Based Page Navigation
        Understanding Method Results Data Binding
                Binding to Results of Service Methods
                Understanding Iterators Over Collection Valued Properties
                Binding to Method Passing String Arguments
                Binding to Method Passing String and int Arguments
                Using Bean Data Control as Argument
                Why We Use Separate DataActions to Invoke Custom Methods
        ADF Data Binding Q & A
        Conclusion
        Related Documents

Introduction

The new Oracle Application Development Framework [5] (ADF) in Oracle JDeveloper 10g evolves our road-tested BC4J [6] framework to a new level of flexibility and openness. By introducing a new "data control" abstraction for back-end business services and generalizing our existing data binding objects to support it, we've added a consistent and pluggable model layer to our J2EE application architecture.

Figure 1 illustrates where the ADF Data Control and ADF Bindings fit into the overall ADF Model, View, Controller, and Business Services architecture.

Oracle ADF Architecture for J2EE Applications
Figure 1: Oracle ADF Architecture for J2EE Applications

Oracle ADF provides out-of-the-box support for data controls based on Java classes, EJB Session Beans, Web Services and ADF Application Modules. Like other ADF components you may already be familiar with, both ADF Bindings and ADF Data Controls are configured using XML metadata so a small set of framework base classes can handle most of your application development needs without coding. When needs dictate, you can either customize the base framework classes or provide completely new data control and binding implementations.

As Figure 1 suggests, we've updated all of our existing view-layer technologies to support the ADF Bindings layer, so they now share both runtime and design time data binding functionality. This common data binding architecture, and our improved visual design time facilities that accompany it, are two cornerstones of JDeveloper 10g's "productivity with choice" value proposition.

In this article, we'll gain a high-level understanding of the Oracle ADF data binding concepts, see how we work with them at design time using Oracle JDeveloper 10g, and observe how they work in practice by studying a simple, working example of a Struts-based JSP application using Oracle ADF that complies with the best-practice Model, View, Controller design approach.


NOTE:

While not covered in-depth this article, it's also possible to use the ADF Binding facilities in JSP applications that do not use a formal controller layer like Struts. See the ADF Data Binding Q & A section below for more information on our support for this so-called "Model 1" JSP approach.


Overview of ADF Data Binding Concepts

The key data binding concepts in Oracle ADF are the following:

Figure 2 shows what these concepts would look like in simple Discussion Forum application. The ReviewThreads.jsp page allows web users to review the open threads in a discussion forum, and assign one or more threads to a given team to answer them. The page gets the data about the discussion forums and the discussion threads they contain from a service named ForumService, implemented using an ADF Application Module. The data for the poplist on this page, containing the list of valid team names for thread assignment, is pulled from a backend Web Service named LookupCodes.

A data control corresponding to each business service adapts it in a consistent way into the ADF Binding model layer. The application's binding context contains two binding containers, one named ReviewThreads used by the ReviewThreads.jsp page, and another named UnansweredPostings, shared by a uiXML page (Page2.uix) and a JClient Swing panel (Panel3.java). The ReviewThreads binding container contains:

Note that the control value bindings reference the iterator bindings that provide their data. In the case of the list binding, it's related to two different iterators: one provides the values for the list, the other provides the target that will be updated if a new value is picked from the list.

Example of ADF Data Binding Concepts in Action
Figure 2: Example of ADF Data Binding Concepts in Action

Figure 3 illustrates how the concepts hang together at runtime. The application's binding context contains one or more data controls being used, as well as one or more binding containers that use them. Each binding container has one or more iterator bindings identifying the data collections used by a given page. Each iterator binding works with data from one particular data control. Binding containers also have one or more control bindings that support the UI controls in the page. Control bindings are related to an iterator when they are bound to data, or may be related straight to a data control if they are control action bindings, used to invoke custom data control methods.

UML Diagram of ADF Binding Runtime Classes
Figure 3: UML Diagram of ADF Binding Runtime Classes

Support for Existing and Emerging Standards

ADF Bindings have been designed with Java standards in mind, so they are easy to use with any client technology that can interact with JavaBean's and the Java Collections Framework. For example, popular tag libraries like the JSP Standard Tag Library [7] (JSTL) and those from the Jakarta Struts project now work seamlessly with Oracle ADF. You can use the standard Expression Language [8], common to both JSTL and JSP 2.0 [9], to easily work with data from your business services in your Web pages. For rich client applications with Swing UI's, we've refactored our existing JClient bindings to extend the base ADF Bindings functionality with additional bindings needed by more sophisticated Swing controls like trees, spinners, sliders, and others.

Oracle and other vendors [10] believe a common data binding facility should be a standard part of the Java platform, and as the specification lead on JSR 227 [11] Oracle is working with others as part of the Java Community Process [12] to make it happen. The ADF Bindings and ADF Data Control in JDeveloper 10g are an early implementation of the proposed JSR 227 data-binding facility and future ADF releases will evolve to support JSR 227's final API's and metadata.

ADF Binding Design Time Experience

JDeveloper 10g provides complete declarative design-time support for working with the JSR 227-style data binding that Oracle ADF offers. We'll focus our attention in this section specifically on how you work with the Oracle ADF Binding concepts we learned about above.


NOTE:

The screen shots in this section are based on the ADFBindingIntro sample that we explore further in later sections of this paper. To keep the example simple, it's based on data from the familiar EMP table in the SCOTT schema, as well as some data coming from a simple Java Bean for illustration purposes.


Creating Data Controls

By default, any ADF Business Components application modules that you create are accessible as data controls. You need to explicitly create other kinds of data controls by selecting the Java Class, EJB Session Bean, or Web Service in the Application Navigator and either:

The DataControls.dcx file appears automatically in any project where you've created some data controls for business services that are not implemented using ADF application modules. The file stores a few pieces of basic metadata needed by the runtime framework about your data controls. Figure 4 shows what you'll see if you click on the DataControls.dcx file in the Application Navigator. The Structure Pane shows the list of data controls you've defined. If you're curious you can double-click on the file itself to see its XML content in the source editor, but the file is read-only since its contents are automatically managed by the JDeveloper 10g design time facilities.

DataControls.dcx File Stores Data Control Metadata
Figure 4: DataControls.dcx File Stores Data Control Metadata

In the sample application that we'll be working with later in this article, I created a JavaBean named test.model.beans.CommissionPlan and another bean named test.model.services.CommissionService with source code that looks like this:

package test.model.services;
import java.util.*;
import test.model.beans.CommissionPlan;
public class CommissionService  {
  private static final ArrayList plans = new ArrayList(4);
  public CommissionService() {}
  public Collection getAvailableCommissionPlans() {
    return plans;
  }
  static {
    plans.add(new CommissionPlan("High",2000));
    plans.add(new CommissionPlan("Medium",1000));
    plans.add(new CommissionPlan("Low",500));    
  }
}

When I selected Create Data Control for CommissionService.java file in the Application Navigator, the ADF design time created a companion CommissionService.xml metadata file to record some extra information required by the ADF runtime about this service. Figure 5 shows what selecting CommissionService.xml in the Application Navigator looks like. The Structure Pane shows the properties and methods that this service exposes, and the Property Inspector shows me the additional metadata that ADF can record about the availableCommissionPlans property.

Structure and Properties of a Data Control
Figure 5: Structure and Properties of a Data Control

Since introspection of the CommissionService only tells us that the property is of type Collection, this additional "Bean class" metadata property lets us record the specific kind of bean that will be in the collection at runtime, test.model.beans.CommissionPlan. I had to set this property manually, using the Property Inspector. Once you tie the CommissionPlan bean into the CommissionService by setting this BeanClass property, JDeveloper 10g creates the CommissionPlan.xml metadata file as well to record metadata about that bean as well.


NOTE:

Since ADF application modules already have their own XML metadata file, and already contain enough metadata to be exposed as a data control, no additional metadata files are needed for those.


Figure 6 shows the kinds of data control implementations that are provided by default with Oracle ADF. Metadata recorded about your data controls provides the name of a factory class that will create instances of the appropriate data control implementation.

UML Diagram of Supplied Data Control Implementations
Figure 6: UML Diagram of Supplied Data Control Implementations

Designing UI's with the Data Control Palette

After you've created any necessary data controls, you can begin using them in your user interface projects. The new Data Control Palette shows you all of the available data controls in the current workspace, and presents their data and operations (methods) in a uniform way.

Data for iterator and control bindings can come from properties exposed by the service, or from the return values of invoking service methods. Operations for control action bindings can be built-in ones supplied by the binding objects (like Create, Delete, Next, Previous, Save, Commit, etc.) or be custom methods from your data control's implementation. Figure 7 shows our JavaBean-based data control named CommissionServiceDataControl with the availableCommissionPlans property we saw above. Its value is a collection of (CommissionPlan) beans with amount and name properties.

The ADF Data Control Palette Allows Drag & Drop Data Binding
Figure 7: The ADF Data Control Palette Allows Drag & Drop Data Binding

You can also see a data control named EmpServiceDataControl for our test.model.services.EmpService application module. Its Employees property, a view object instance, is a set of rows with attributes like Empno, Ename, Hiredate, etc. Regardless of how the data is exposed, the data binding experience is uniform for all kinds of back-end data sources.

Notice the Drag and Drop As: poplist at the bottom of the Data Control Palette. When a web page or Swing panel is active, this area of the palette presents a list of commonly used UI elements that are appropriate for the combination of the selected data source and the active UI page or panel. Figure 8 shows the Drag and Drop As: choices available for a data collection with a JSP page active.

Use Data Control Palette to Drop Databound UI Controls
Figure 8: Use Data Control Palette to Drop Databound UI Controls

Of course, when working on a Swing panel or a uiXML page, a different set of appropriate UI elements is presented. Also, the choices are different if you select a single attribute like Ename or amount, or an operation in the palette, instead of a collection.

Creating Binding Containers and Bindings

There are two ways to create binding containers and bindings: implicit and explicit.

Creating ADF Bindings the Implicit Way

When you drag and drop data-bound UI elements from the Data Control Palette to either the Visual Editor, Structure Pane, or the Code Editor, JDeveloper 10g will implicitly create bindings and a binding container to hold them if necessary. That is, it will:


NOTE:

The binding container definition file is an XML file named YourPageNameUIModel.xml in the directory indicated by your project's default package project-level setting. Before creating your binding container either via implicit or explicit approaches, you can see the project's default package name by clicking on the project node in the Application Navigator, and looking at the defaultPackage property in the Property Inspector. Should you want to change the name of the default package for the project, you can do it right there in the Property Inspector.


Figure 9 shows how you can use the UI Model tab of the Structure Pane to view the bindings in current page's binding container. With the showEmployees.jsp page active in the visual editor, the UI Model tab of the Structure pane shows the associated showEmployeesUIModel binding container and the various bindings it contains.

An obvious question is, "What's the difference between the Data Control Palette and the UI Model tab of the Structure Pane?" It's easy. The Data Control Palette shows all data controls in the workspace and represents all data that is available for binding. The UI Model tab shows the contents of the current page's binding container, representing the subset of data that the current page cares about displaying or modifying.

UI Model Tab of Structure Pane Shows Current Page's Bindings
Figure 9: UI Model Tab of Structure Pane Shows Current Page's Bindings

Bindings are described by metadata that lives in the binding container XML file, however you never need to edit that XML file yourself. To set properties on a binding, for example the iterator binding named EmployeesIterator used by the showEmployees.jsp page above, simply click on the binding in the Structure Pane, and use the Property Inspector as shown in Figure 10 to see or edit its settings.


NOTE:

Some more complex binding properties can only be set from the binding editor dialog. To bring up the binding editor dialog, click on the Edit... task link in the Property Inspector, or select Edit... from the context menu on the binding object in the Structure Pane.


Property Inspector with "Edit..." Task Link for Current Binding
Figure 10: Property Inspector with "Edit..." Task Link for Current Binding

Notice the value of the Model Reference property of the EmployeesIteratoriterator binding in the Property Inspector above. It is the standard "EL" (expression language [8]) expression, relative to the binding context, that refers to the data collection named Employees in the EmpServiceDataControl data control.

If you click on the control binding named Employees, a Range binding used to display a table of rows, you will notice in the Property Inspector that it has an IteratorName property equal to EmployeesIterator. It is through these property values that control bindings are related to iterator bindings and, in turn, iterator bindings tied to the data in the data control that supplies it.

Creating ADF Bindings the Explicit Way

In addition to the automatic drag and drop approach, there is also a manual way to create binding containers and the bindings within them. With a page or panel active in the editor, if you click on the UI Model tab of the Structure Pane and see "<no bindings>", then that tells you that the current page has no associated binding container yet. If dragging and dropping from the Data Control Palette is not your style, you can right-mouse on the "<no bindings>" as shown in Figure 11 and select the self-explanatory Create UI Model menu choice to create the UI Model for the page. This results in creating the YourPageNameUIModel.xml file in the directory indicated by your project's default package project-level setting.

Manually Creating a Binding Container (UI Model)
Figure 11: Manually Creating a Binding Container (UI Model)

You can use the context menu in the UI Model tab of the Structure Pane to create new bindings in any binding container. Just select the root node in the tree that represents the binding container itself, and pick from the choices in the Create Binding context menu as shown in Figure 12.

Manually Creating a Binding in a Binding Container
Figure 12: Manually Creating a Binding in a Binding Container

The choices available in the Create Binding context menu allow you to create all of the principal kinds of binding objects shown in Figure 13. These framework binding classes are engaged at runtime based on the metadata recorded about your bindings in the binding container XML file. While not typically necessary, you can also provide custom binding implementations as well, or customized versions that extend the supplied binding classes.

UML Diagram of Principal Runtime Binding Objects
Figure 13: UML Diagram of Principal Runtime Binding Objects

When you create a binding manually, the binding editor appears in order to capture the key information needed to define the binding. If you don't like the default name of your bindings, you can use the Property Inspector to change the value of the id property of that binding. of the manually-created binding to something more pleasing. If you need to delete a binding, you can just select it and choose the Delete item from the context menu. A warning will appear reminding you that you may need to revisit any places in your page where you might be referencing the deleted binding.


NOTE:

If you rename a binding, make sure to update places in your UI page or panel that might refer to the old name. If you rename an iterator binding, we try to cascade that name change to any control bindings in the binding container that might have referred to the old name.


Seeing the Contents of Your Binding Context

As you use data controls and create binding containers, JDeveloper 10g records the necessary metadata describing them in the DataBindings.cpx file. Clicking on the DataBindings.cpx file in the Application Navigator shows the contents of your application's binding context in the Structure Pane. Figure 14 shows that my ADFBindingIntro application has two data controls, and uses one binding container (UI Model).

DataBindings.cpx File Defines Binding Context
Figure 14: DataBindings.cpx File Defines Binding Context

The DataBindings.cpx file acts as the table of contents to the data controls in use in this application. At runtime, the metadata definitions for the data controls and binding containers are loaded lazily.

ADF Information in the Online Help

Before moving on to study a simple, working example, it's worth pointing out that the JDeveloper 10g online help provides additional details on ADF Data Controls and ADF Bindings. We also provide a link below to a number of helpful tutorials on using them with various clients and back-end business service implementations.

Select Help | Table of Contents from the main menu and the Help Navigator will appear as shown in Figure 15.

JDeveloper 10g Help Navigator Window
Figure 15: JDeveloper 10g Help Navigator Window

Start with the About Oracle ADF Data Controls topic shown in Figure 16.

Online Help Topic About Oracle ADF Data Controls
Figure 16: Online Help Topic About Oracle ADF Data Controls

After selecting the help topic and double-clicking on it, a new tab will appear in the editor area allowing you to read the help material.

If you want to keep the help system around without occupying screen real estate, you can click on the "Auto Hide" window icon as shown in Figure 17.

Clicking on the Auto Hide Button for Help Window
Figure 17: Clicking on the Auto Hide Button for Help Window

The Help Navigator window will auto-hide itself into the left margin of your screen as shown in Figure 18.

Help Window Icon in Margin When Auto-Hidden
Figure 18: Help Window Icon in Margin When Auto-Hidden

To refer at any time to the help, just hover your mouse over the Help "tile" in the margin for a second or two, and the Help Navigator will spring into view to allow you pick another topic. Once you've chosen a topic, the Help content window will auto-hide itself again. You can independently size auto-hidden windows so that you can make the auto-hidden Help contents window as wide as you need to. If after trying out the auto-hidden Help window you decide that you prefer to keep the window open, just click the Keep Open icon on the window title bar that looks like a little pushpin.

For more details on ADF Bindings, see the About the Oracle ADF Bindings topic shown in Figure 19.

Online Help Topic About Oracle ADF Bindings
Figure 19: Online Help Topic About Oracle ADF Bindings

For specific information about each ADF binding and the properties that it exposes to clients at runtime, see the About the Properties of the ADF Bindings topic shown in Figure 20.

Online Help Topic About Oracle ADF Binding Properties
Figure 20: Online Help Topic About Oracle ADF Binding Properties

NOTE:

When you have selected any binding in the UI Model tab of the Structure pane, you can press the F1 key to quickly jump to context-senstive online help for that binding type. This can be especially handy as a quick reference to the property names that are available from the binding using EL expressions. Similarly, if you right-mouse on a binding in the Structure Pane and select Goto JavaDoc..., you'll be brought to the JavaDoc for the underlying framework binding object supporting that particular binding.


Finally, to see the available ADF-related tutorials, click on the Tutorials [13] link on the JDeveloper Welcome page. You can show the Welcome Page by selecting Welcome Page item in the Help main menu.

Understanding a Working Example Application

In this section we're going to study a simple Struts-based web application that makes use of an Oracle ADF binding container. You can download the ADFBindingIntro.zip [1] file containing the files needed to try the sample yourself.


NOTE:

You'll need to create a connection named scott in the Connection Navigator under the "Databases" heading. It should point to the familiar SCOTT/TIGER schema on some database you have access to.


Overview of the Sample Application

The ADFBindingIntro sample application consists of one main JSP page named showEmployees.jsp which shows data from two different data controls, one based on an ADF application module and the other based on a simple Java Bean.

Figure 21 shows what the main showEmployees.jsp page looks like in the new JDeveloper 10g JSP Visual Editor. This page implements the view layer of our application, and is cleanly separated from the model layer and the controller layer as we'll see below.

The showEmployees.jsp Page
Figure 21: The showEmployees.jsp Page

We've used the new visual Struts Page Flow diagram to build six different Struts actions, all of which eventually forward to the same showEmployees.jsp page for rendering the data. We have six different actions in order to illustrate some different options you have for using ADF binding containers from your Struts application. The approaches for using ADF with Struts fall into two main categories:

For both of these approaches, we illustrate three different implementation variations to help you understand what is possible, and in doing so, help you identify which approach you might prefer for your ADF/Struts projects.

Figure 22 shows the page flow diagram with these six actions, one JSP page, and some three on-diagram notes.

Struts Page Flow for the Sample Application
Figure 22: Struts Page Flow for the Sample Application

The index.jsp page shown in Figure 23 gives us a starting page with links to exercise all six of our different actions. The first three examples show different approaches for implementing the typical ADF page handling lifecycle without using any ADF-supplied Struts action classes. The second three examples show different approaches for accomplishing the same functionality without having to write the page-lifecycle handling code yourself. They take advantage of the ADF-supplied DataAction Struts action which implements the page lifecycle for us. We'll study some interesting details of these actions in sections that follow.

The index.jsp Home Page for the Sample
Figure 23: The index.jsp Home Page for the Sample

Basic Details of ADF/Struts Integration

The Oracle ADF runtime controller support for Struts in JDeveloper 10g is implemented a little differently than the JDeveloper9i BC4J/Struts support (described in detail in the BC4J Toy Store Demo [14] whitepaper), so I'll try to point out the salient differences.

In JDeveloper 9i, developers building BC4J/Struts applications used the customized Struts request processor named BC4JRequestProcessor to handle initializing the data binding context and acquiring an instance of an appropriate application module. The use of the custom request processor caused issues when developers tried to integrate BC4J/Struts with Tiles or other technologies.

In JDeveloper 10g, our Struts integration comprises the following key elements:

The six actions in our ADFBindingIntro sample application let us see how to use some or all of these elements as we'll see in the next section.

Examining Our ADF / Struts Example Actions

Next let's study the sample actions and how they work, highlighting which aspects of the ADF/Struts runtime integration they are making use of.

Setting Up the ADFBindingFilter

Before any of our Struts actions can access the ADF binding context, we need to make sure the ADFBindingFilter is properly configured in our web.xml file. This is done for you automatically by the JDeveloper 10g design time tools the first time it's needed, but if you want to peek into the web.xml file in the ViewController.jpr project, you'll see the necessary entries:

     :
  <!-- ADF Binding Filter Class Setup -->
  <filter>
    <filter-name>ADFBindingFilter</filter-name>
    <filter-class>oracle.adf.model.servlet.ADFBindingFilter</filter-class>
  </filter>
  <!-- 
   | ADF Binding Filter Setup to engage the filter when
   | the Struts "action" servlet is used.
   +-->
  <filter-mapping>
    <filter-name>ADFBindingFilter</filter-name>
    <servlet-name>action</servlet-name>
  </filter-mapping>
  <filter-mapping>
    <filter-name>ADFBindingFilter</filter-name>
    <url-pattern>*.jsp</url-pattern>
  </filter-mapping>
  <filter-mapping>
    <filter-name>ADFBindingFilter</filter-name>
    <servlet-name>action</servlet-name>
  </filter-mapping>
     :
  <!-- Servlet setup for the Struts action servlet -->
  <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
      :
  </servlet>
  <!-- Servlet setup to map *.do URL's to the Struts action servlet -->
  <servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
       :

The ADFBindingFilter looks at the value of the servlet context parameter named CpxFileName to know which CPX file will be used to define this application's binding context. In our sample application, you can see the following in the web.xml file:

<context-param>
  <param-name>CpxFileName</param-name>
  <param-value>DataBindings</param-value>
</context-param>

This means that it will read the DataBindings.cpx file to discover the contents of the binding context.

Indicating the Binding Container for an Action

To declaratively set the name of the ADF binding container to be used by a given action, you can tailor the action to use the DataActionMapping implementation class (className), and add a nested <set-property> tag to assign a value to the modelReference property. The value of modelReference should be the short name of one of the binding containers listed in your DataBindings.cpx file. Only the short name is needed. The fully-packaged-qualified name of the file will be looked up from the binding context the first time the binding container is used.

This is the snippet from our struts-config.xml file that sets up our /showEmpsActionCentric action, implemented by the ShowEmpsActionCentricAction action class.

<action path="/showEmpsActionCentric"
        className="oracle.adf.controller.struts.actions.DataActionMapping"
        type="sample.controller.ShowEmpsActionCentricAction"
        name="DataForm">
      <set-property property="modelReference" value="showEmployeesUIModel"/>
      <forward name="success" path="/showEmployees.jsp"/>
    </action>

Accessing the Binding Container

We can use code like the following to access the binding container for an action based on the declarative modelReference property setting in the Struts action mapping:

  /**
   * Setup the binding container for use. Name of the binding container
   * to use is gotten from the modelReference property on the Struts
   * action mapping (through the extended DataActionMapping in the
   * oracle.adf.controller.struts.actions package).
   */
  private void initializeBindingContainer(ActionMapping mapping,
                                          HttpServletRequest request) {
    if (mapping instanceof DataActionMapping) {
      DataActionMapping daMapping = (DataActionMapping)mapping;
      String bindingContainerName = daMapping.getModelReference();
      DCBindingContainer bc = getBindingContainer(request);
      if(bc == null) {
        BindingContext ctx = BindingContext.getContext(request);
        bc = DCUtil.findBindingContainer(ctx, bindingContainerName);
        if( bc != null) {
          /*
           * Put a reference to the binding container under the
           * request-scoped attribute named "bindings" so JSP pages
           * can easily refer to bindings in the binding container
           * using EL expressions like ${bindings.Empno}
           */
          request.setAttribute(BINDINGS, bc);
        }
      }
    }
  }

The code references the declarative modelReference property from the action and uses the getBindingContainer() helper method to see if the binding container has already been setup during this request. If not, it calls DCUtil.findBindingContainer() and then sets a "bindings" attribute in the request scope so the view layer page can access the binding container with EL expressions like ${bindings.BindingName}.

Accessing a Data Control

Once we have the binding container, we can find any bindings it contains. The data controls in use by the binding container are accessible via the iterator bindings that point to them. They can also be looked up explicitly by name from the binding context. The following sample code illustrates how we can lookup a data control by name. It also shows how to test for a particular data control implementation type -- like the ADF Business Components data control -- and how to return the service interface of its data provider:

  protected ApplicationModule getApplicationModule(String dataControlName,
                                                   HttpServletRequest request) {
    DCDataControl dc = getBindingContext(request).findDataControl(dataControlName);
    /*
     * Test to see if our data control is an instance of the ADF Business Components
     * Data Control before trying to cast the data provider to a specific
     * service interface
     */
    if (dc instanceof DCJboDataControl) {
      return (ApplicationModule) dc.getDataProvider();
    }
    return null;
  }

The helper method used above to get hold of the binding context, looks like this:

  protected BindingContext getBindingContext(HttpServletRequest request) {
    return HttpBindingContext.getContext(request);
  }

Setting Up the Basic ADF Action Lifecycle

If we are not leveraging the built-in features of the ADF DataAction to handle the page lifecycle for use, we need to write code ourselves to do that. The basic steps in the lifecycle of handing the HTTP request involve:

  1. Initializing the binding container
  2. Doing custom controller processing and determining the ActionForward
  3. Releasing the binding container

Since all of our hand-written actions will need to follow this approach, I've written a BasicADFAction class in the sample that overrides the default Struts action execute method to define this basic lifecycle:

  public final ActionForward execute(ActionMapping mapping, ActionForm form,
    HttpServletRequest request, HttpServletResponse response)
    throws Exception {
    return handleLifecycle(mapping, form, request, response);
  }

I've made the execute() method final to force subclasses to override specific lifecycle methods in order to accomplish action-specific needs instead of trying to override execute as might be their first inclination if they are already familiar with Struts.

My handleLifecycle method, called by the implementation of execute above, looks like this:

  protected ActionForward handleLifecycle(ActionMapping mapping,
                                          ActionForm form,
                                          HttpServletRequest request,
                                          HttpServletResponse response)
                                          throws Exception {
    initializeBindingContainer(mapping, request);
    ActionForward fwd = performActionLogic(mapping, form, request, response);
    refreshBindingContainer(request);
    return fwd;
  }

My three custom Struts actions in the sample application:

extend the BasicADFAction and override the performActionLogic method to supply the custom controller processing needed for that action.

Example of Writing Controller Code in the Action

The basic functionality that we are implementing in this demonstration is to show an ordered list of employees. If the value of the HTTP parameter named sort is missing or if it equals the letter "a", then we want to perform an ascending sort. If the value of the sort parameter is equal to the letter "d", then we want to perform a descending sort.

The ShowEmpsActionCentricAction class illustrates how to accomplish this functionality by writing all the model setup code directly in the Struts action class. This is an approach that will be familiar to developers using JDeveloper9i release 9.0.3 or 9.0.4:

public class ShowEmpsActionCentricAction extends BasicADFAction {
  protected ActionForward performActionLogic(ActionMapping mapping,
    ActionForm form, HttpServletRequest request, 
    HttpServletResponse response) throws Exception {
    ApplicationModule am = getApplicationModule("EmpServiceDataControl", request);
    ViewObject vo = am.findViewObject("Employees");
    String sortDirection = request.getParameter("sort");
    if ((sortDirection == null) || sortDirection.equals("a")) {
      vo.setOrderByClause("sal asc");
    }
    else {
      vo.setOrderByClause("sal desc");
    }
    vo.executeQuery();
    return mapping.findForward("success");
  }
}

This action code finds the application module (using an appropriate helper method that it inherits from BasicADFAction) and proceeds to set the ORDER BY clause of the view object whose instance name is "Employees".

Using a Custom Method to Setup the Model Data

If your service is deployed on a remote server, it's especially important that your client action code not make a lot of "chatty" API invocations to the back-end business service across the wire. This is a good reason why ADF application modules support exporting custom service methods to clients. All model setup can be performed in a single round-trip to the server. ShowEmpsTypesafeAction is a variant of the ShowEmpsActionCentricAction that invokes the custom prepareModelForShowEmployeesPage method on the application module's custom service interface.

public class ShowEmpsTypesafeAction extends BasicADFAction {
  protected ActionForward performActionLogic(ActionMapping mapping,
                                             ActionForm form,
                                             HttpServletRequest request,
                                             HttpServletResponse response)
                                            throws Exception {
    /*
     * "EmpService" is the custom business service interface created by
     * the ADF Business Components wizard when you mark a custom method
     * like "prepareModelForShowEmployeesPage" as exported to the client.
     */
    EmpService svc = (EmpService)getApplicationModule("EmpServiceDataControl",request);
    svc.prepareModelForShowEmployeesPage(request.getParameter("sort"));
    return mapping.findForward("success");
  }
}

Using Action Bindings to Decouple Controller and Service

One of the ADF control bindings that we talked about in the first section of this paper was the "action binding". This binding object acts as an insulation layer between the client layer and the service methods on the data control. As an alternative to calling data control service methods directly in your code, you can use action bindings to further decouple the client layer from the business service. The action binding captures the metadata needed at runtime to invoke the service method, and can be used to invoke the underlying method -- passing any parameters as needed -- and retrieve its result (if any).

The code for the ShowEmpsActionBindingAction variant that uses an action binding to invoke the service method looks like this:

public class ShowEmpsActionBindingAction extends BasicADFAction {
  protected ActionForward performActionLogic(ActionMapping mapping,
                                             ActionForm form,
                                             HttpServletRequest request,
                                             HttpServletResponse response)
                                            throws Exception {
    invokeActionBinding("prepareModelForShowEmployeesPage",
      request.getParameter("sort"), request);
    return mapping.findForward("success");
  }
}

The BasicADFAction base class contains the helper method invokeActionBinding that illustrates how you can provide an ArrayList of method parameters to be passed to the service method being invoked. In this case, our service method is void so we're not expecting a return value, but if the method we're invoking returns something, we can retrieve the return value by invoking the getResult() method on the action binding object.

Using the DataAction for Full Lifecycle Support

The fourth Struts action in the sample named "/showEmployeesTypesafe" is implemented using the generic ADF-supplied DataAction class instead of a custom-coded page lifecycle handling approach. The DataAction is a Struts action that implements a much more complete request-handling lifecycle than the trivial one we setup in our BasicADFAction above. Keep in mind that what we did above was for the simple read-only scenario in the demo. The DataAction handles all of the typical situations you will need while building Struts-based business applications.

Using an approach structurally similar to the BasicADFAction, the DataAction base class (in the oracle.adf.controller.struts.actions package) overides the base Struts action's execute() method and invokes a handleLifecycle method that a number of protected lifecycle methods to orchestrate the common steps that every request should follow.

The DataAction lifecycle handles:

In the demo we're using a CustomizedDataAction class that extends the base ADF DataAction to add a helper method named invokeActionBinding() like we used in our hand-coded BasicADFAction.

The showEmployeesActionBindingAction class extends CustomizedDataAction and overrides one of the lifecycle methods that it inherits from DataAction named prepareModel like this:

public class ShowEmployeesActionBindingAction extends CustomizedDataAction {
  protected void prepareModel(DataActionContext ctx) throws Exception {
    invokeActionBinding("prepareModelForShowEmployeesPage",
      ctx.getHttpServletRequest().getParameter("sort"), ctx);
    super.prepareModel(ctx);
  }
}

Notice that rather than passing many parameters to each method in the lifecycle, the ADF DataAction is designed to pass the single DataActionContext object. This single object, whose lifetime is only the current HTTP request being processed, contains setter and getter methods to access any of the things your data action subclasses might need. In the code above, we've called the getHttpServletRequest() method to access the HTTP request object to retrieve a parameter value for the sort parameter. Other handy methods on the DataActionContext object like getBindingContext() and getBindingContainer(). Other methods on the DataActionContext allow you to set the ActionForward that will be used for determining where the page flow will go next, as well as aspects related to Struts error reporting and other functionality.

The key difference between the hand-coded examples and the DataAction is that with the DataAction, we don't have to write the page lifecycle handling code ourselves. Oracle has provided a rich, base implementation as a standard Struts action for you to extend and override where necessary with just application-specific code rather than request-handling "plumbing" code.

Using "Data Pages" to Simplify Your Page Flow Diagrams

It's often the case that a Struts action will be used both to:

This web design pattern, sometimes called the "postback pattern", centralizes both the page initialization logic and page event handling into the same action class. Oracle ADF provides the DataForwardAction (which extends DataAction) that caters to this web design pattern and allows you to simplify your page flow diagrams by using it.

The DataForwardAction combines the lifecycle features of the ADF DataAction with the implicit page forwarding behavior of the basic Struts ForwardAction [15]. Since by design the DataForwardAction always forwards to the same page for rendering, which in turn contains forms or hyperlinks that post back to that same action to handle any of the page's events, JDeveloper shows the pair of action and page as a single "Data Page" icon in the diagram.

As shown in Figure 24 you can hover your mouse of the icon for a data page and see both the name of the action class and the associated page. We can see that this /showEmployeesTypesafe action also forwards to the same "/showEmployees.jsp" page, although it does so implicitly instead of via an explicit forward.

Tooltip Shows Action and Page For a "Data Page"
Figure 24: Tooltip Shows Action and Page For a "Data Page"

NOTE:

Similar to the Struts ForwardAction [15], the page path for a "data page" is recorded in the Struts action mapping property named parameter as shown in this snippet of struts-config.xml syntax from our demo.

<action path="/showEmployeesTypesafe"
        className="oracle.adf.controller.struts.actions.DataActionMapping"
        type="sample.controller.ShowEmployeesTypesafeAction"
        name="DataForm"
        parameter="/showEmployees.jsp">
  <set-property property="modelReference" value="showEmployeesUIModel"/>
</action>

The ShowEmployeesTypesafeAction class shown below illustrates that using the DataForwardAction, we can use the same typesafe service interface approach shown earlier to invoke a method on the data control's data provider service.

public class ShowEmployeesTypesafeAction extends DataForwardAction  {
  protected void prepareModel(DataActionContext ctx) throws Exception {
    /*
     * "EmpService" is the custom business service interface created by
     * the ADF Business Components wizard when you mark a custom method
     * like "prepareModelForShowEmployeesPage" as exported to the client.
     */
    EmpService svc = (EmpService)ctx.getBindingContext()
                                     .findDataControl("EmpServiceDataControl")
                                     .getDataProvider();
    svc.prepareModelForShowEmployeesPage(
       ctx.getHttpServletRequest().getParameter("sort"));  
    super.prepareModel(ctx);
  }

Using DataAction Features to Go Totally Declarative

Our final example is the /showEmployeesDeclarative action. This "data page" accomplishes the same functionality as the ones we've seen before, but it does so without any custom code whatsoever. The DataAction and DataForwardAction (used by "data pages") support the ability to declaratively invoke a service method from the data control.

We saw in the /showEmpsActionBinding and /showEmployeesActionBinding examples that ADF action bindings can be used to encapsulate the interaction with a data control's service methods. To support fully-declarative method invocation, the DataAction integrates the optional feature of:

Here's how I setup the declarative method invocation being performed by the /showEmployeesDeclarative data action. Using the Data Control Palette, by dragging the prepareModelForShowEmployeesPage operation for the the EmpServiceDataControl data control and dropping it onto the "/showEmployeesDeclarative" data action in the page flow diagram, the JDeveloper 10g design time automatically set up the necessary properties in the struts-config.xml file to allow that DataAction to perform the invocation of the prepareModelForShowEmployeesPage method at runtime.

In addition to the modelReference property we saw above, you can see in the <action> entry below the additional properties like methodName, resultLocation, numParams, and paramNames[0]. The value of numParams indicates the our prepareModelForShowEmployeesPage method accepts one argument, and the paramNames[0] property supplies the EL expression to evaluate to provide the value of that property when the method is invoked by the framework. The expression ${param.sort} is the standard EL syntax to refer to the HTTP request parameter named sort.

<action path="/showEmployeesDeclarative"
        className="oracle.adf.controller.struts.actions.DataActionMapping"
        name="DataForm"
        parameter="/showEmployees.jsp"
        type="oracle.adf.controller.struts.actions.DataForwardAction">
  <set-property property="modelReference"
                   value="showEmployeesUIModel"/>
  <set-property property="methodName" 
                   value="showEmployeesUIModel.prepareModelForShowEmployeesPage"/>
  <set-property property="resultLocation"
                   value="${requestScope.methodResult}"/>
  <set-property property="numParams"
                   value="1"/>
  <set-property property="paramNames[0]"
                   value="${param.sort}"/>
</action>

The result is that without having to write any controller code for /showEmployeesDeclarative, the data control method is invoked to prepare the model, and the default "success" forward is returned to forward control to the showEmployees.jsp page for rendering.

To understand the different methods in the DataAction lifecycle, you can refer to the JavaDoc for the handleLifecycle method in the DataAction class, or have a look at its source code. To jump quickly to any ADF Binding or ADF Struts controller source code or JavaDoc, just use the new Navigate | Go to Java Class main menu selection or press the accelerator key Ctrl+Minus. You'll see the dialog shown in Figure 25 where you can just start typing the name of the class (without having to recall what package it lives in!) into the Name field. Hitting Enter opens the source code (or JavaDoc) for the desired class.

Quickly Find the Source for Any Class
Figure 25: Quickly Find the Source for Any Class

NOTE:

Source code for ADF Bindings, ADF Data Controls and ADF Struts controller support are provided with the release. For convenience, the Struts controller source code is also provided.


Using EL Expressions to Refer to Data in Web Pages

The sample showEmployees.jsp page uses tags in the "core" JSTL [7] tag library which support the use of a standard expression language [8] for referencing beans and collections. For example, to keep the expressions shorter, at the top of the page you'll find:

<%-- 
 | Capture Employees and CommissionPlans bindings
 | into variables with shorter names
 +--%>
<c:set var="e" value="${bindings.Employees}"/>
<c:set var="c" value="${bindings.availableCommissionPlans}"/>

This creates variables named "e" and "c" respectively to hold the Employees range binding object, and the availableCommissionPlans range binding object. The expression ${bindings.Employees} refers to the member property named Employees in the object named bindings.


NOTE:

Click here [16] or here [17] for handy quick references to JSTL and its Expression Language. (PDF format)


Recall that in our BasicADFAction, after finding the binding container, we set a request-scope attribute named bindings to have the value of the binding container. The DataAction performs this same task for us, as well, using the same attribute name bindings. This allows our JSP pages to refer to the binding container with the expression ${bindings} and by using dot notation, to any binding in the binding container using ${bindings.BindingName}. By assigning a shorter variable name "e" to the value of the expression ${bindings.Employees} inside the page using <c:set>, we can subsequently write an expression like ${e.labels.Ename} instead of the longer ${bindings.Employees.labels.Ename}.

This simple example of refering to the labels property of the range binding in our table headers shows just one of the helpful pieces of information the binding object makes available to your page. If you click on the index.html file in the ViewController.jpr file and run it, then click on one of the links in that home page to exercise your favorite action, you should see the page shown in Figure 26.

Notice that the column titles show up as "Id", "Employee Name", and "Salary", instead of the raw attribute names Empno, Ename, and Sal. This happens because we defined UI control hints for the human-readable labels of the attributes on the Emp entity object. The labels property on the binding gives the client easy access to these locale-sensitive UI hints.


NOTE:

To see all of the properties available via EL expressions for any binding, just click on the binding in the UI Model tab of the Structure Pane and press F1 for help.


The showEmployees.jsp Page Output at Runtime
Figure 26: The showEmployees.jsp Page Output at Runtime

Rendering the table is accomplished by using the JSTL <c:forEach> tag. For example, the rows of the employee table are rendered with this fragment of tags:

<!--
 | REMEMBER that "e" is a local variable assigned to ${bindings.Employees}
 | range control binding at the top of this JSP page
 +--> 
<c:forEach var="x" items="${e.rangeSet}" varStatus="idx">
  <tr class="r<c:out value='${idx.index % 2}'/>">
    <td><c:out value="${x.Empno}"/></td>
    <td><c:out value="${x.Ename}"/></td>
    <td><c:out value="${x.Sal}"/></td>
  </tr>
</c:forEach>

The rangeSet property of the Employees range binding exposes the rows in the current range of the view object as a collection. The var="x" attribute of the <c:forEach> tag assigns a looping variable named "x" and then the tags inside the loop refer to the values of the attributes in each row bean by using EL dot notation. The varStatus="idx" attribute of the <c:forEach> tag assigns a looping status variable named "idx". The JSTL specification tells us that this object has a property named index that tells us which row of the iteration we are on. Notice the line above that looks like:

<tr class="r<c:out value='${idx.index % 2}'/>">

Here we're using the JSTL <c:out> tag as an embedded part of the <tr> tag's class attribute value. Since the idx.index value, modulo 2, will alternate between 1 and 0 for odd and even row numbers, the result will be CSS class names that alternate between the values "r1" and "r0". This makes it easy to color odd and even rows differently in the demo using CSS classes defined in the ./css/blaf.css stylesheet that is part of the demo.

To experience the full extent of the application's (admittedly meager!) functionality, click on the tiny arrow next to the "Salary" header to toggle between ascending and descending order for the rows. "Why do we only see five EMP rows?" you ask. Easy. Just click on the EmployeesIterator iterator binding in the UI Model Structure Pane tab for the showEmployees.jsp page, and you'll see in the Property Inspector (as we saw above in Figure 10) that the "Range Size" property is set to 5. With ADF Bindings, all of the mechanics of data binding are cleanly factored out into binding metadata in your binding container. The UI client pages and panels only need to refer to the bindings they want to work with.


NOTE:

For web applications, the ADF framework automatically "prepares" the correct binding container for the current page request during the "prepareModel" phase of the lifecycle, and releases any state held by the bindings in that current binding container at the end of the request. This improves scalability by avoiding unnecessary session-level state. It is best practice that each page's binding container contain all the bindings that it needs to reference. In other words, instead of referencing a binding from another binding container using an expression like:

${data.SomeOtherBindingContainer.someBinding.inputValue}

create a binding in the current page's binding container that refers to the same iterator(s) and attributes and use the expression:

${bindings.someBinding.inputValue}

This way each page is self-contained with respect to its bindings.


Understanding ADF DataAction Event Handling

In order to build more sophisticated and dynamic applications, the ADF DataAction provides a simple-to-use event-handling mechanism that allows you to control what happens when your end-user clicks on certain buttons or links in your pages. This section explains the basics by walking through the small example application that you can download from here: DataActionEventsExample.zip [2].


NOTE:

To run the example, you will need to have first defined a database connection in the JDeveloper 10g Connection Navigator with the name "scott" that connects to the familiar SCOTT/TIGER demo account with the EMP table in it.


The DataAction Events Sample Application

Figure 27 shows the page flow for the DataAction Events Example application. After extracting the example zip file and opening the DataActionEventsExample.jws workspace in JDeveloper 10g, you can see the diagram in your own environment by clicking on the ViewController project and selecting Open Struts Page Flow Diagram... from the right-mouse context menu.

Page Flow for DataAction Events Example
Figure 27: Page Flow for DataAction Events Example

NOTE:

Notice that the Page Flow diagram shows Struts forwards as solid lines with the forward name next to the line (e.g. "winner", "success", "SaveAll", etc. in Figure 27), while the dotted lines represent normal web page links based on:

  • <a href=""> tags for HTML hyperlinks, or
  • <form> or <html:form> tags for HTML form submissions

When using ADF DataPages (shown in the page flow diagram as "page-with-database-can" icons) , we consciously omit the dotted line from the page back to itself that represents the "postback pattern". In usability testing, most users commented that showing those postback form submissions just cluttered up the diagram.


Running the ViewController project will bring up the demo home page (index.jsp) that you see in Figure 28.

Data Action Event Examples Home Page
Figure 28: Data Action Event Examples Home Page

NOTE:

JDeveloper knows which page to run when you run the ViewController project because that page is setup as the Default Run Target on the "Runner" panel of the ViewController project properties.


The Struts DispatchAction Example (DispatchExample), ADF DataPage Example (DataPageExample) and Example2 (DataPageExample2), and ADF Declarative Example (DataPageDeclarative) all implement the same, simple functionality of allowing the user to increment, decrement, or update the value of an integer. When the user performs an action that results in the integer's value changing to the "magic number" 10, we shown them a page that says "You Win!". We will study them to understand the different approaches available for handling page events while using Oracle ADF and Struts.

The Events, Actions, and Forwards Example (UpdateEmp1 and UpdateEmp2) uses a simple employee editing form, implemented across two different pages, to show off the remaining interesting aspects about the ADF event-handling mechanism.

What Are Events in a Web Application?

Java developers who work with JavaBeans or who build user interfaces with Swing [18] are familiar with the notion that Java components can expose a set of well-defined events. For example, a Swing JButton object has an event named actionPerformed that will "fire" when the end-user clicks on the button. To handle the events, developers write code in event-handling methods that get invoked when a specific event occurs on a specific component at runtime. For instance, they might write a method like the following to respond to a click on the (Update) button in a dialog:

/**
 * Event handler for the actionPerformed event on JButton instance "updateButton".
 */
private void updateButton_actionPerformed(ActionEvent e) {
  // Do something here when the [Update] button is pressed
}

Prior to beginning to experiment with JavaServer Faces 1.0 [19] which debuted in March 2004, web developers on the J2EE platform have not had any server-side, web-based analog to the Swing visual controls that support well-defined events. So nearly all J2EE web developers to date have invented their own approaches to identify what actions the user has performed in the browser so that their middle-tier code can react accordingly.

One common approach is to use a parameter named event whose value indicates which event should fire. For example, given the following two buttons and a hyperlink in an HTML page:


web developers might code these to each use the event parameter name in their JSP page like this:

<%-- Excerpt of code in SomePage.jsp --%>
:
<form action="SomePage.jsp" method="post">
     :
    <input type="submit" name="event" value="Update"/>
    <input type="submit" name="event" value="Increment"/>
    <a href="SomePage.jsp?event=Decrement">Remove One</a>
</form>

Then, they would include code either directly in the JSP page, or in a bean used by the page, to test for the presence of an event parameter, and conditionally call event handling code based on the value like this:

<%-- Excerpt of code in SomePage.jsp --%>
<%
   String event = request.getParameter("event");
 &nb