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");
   if ("Update".equals(event)) {
     // code that handles the 'Update' event goes here
   }
   else if ("Increment".equals(event)) {
    // code that handles the 'Increment' event goes here
   }
   else if ("Decrement".equals(event)) {
    // code that handles the 'Decrement' event goes here
   }
%>
<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>

While this technique is not as elegant as having native components that publish events, in one form or another this is the standard approach in use today in most JSP applications.

Handling Events with the Struts DispatchAction

Struts developers, who consciously opt for a strict separation of web-tier controller logic from their view layer's JSP pages, typically extend the Struts DispatchAction [20] when they want to handle events. DispatchAction provides the machinery to conditionally dispatch to an appropriate event handler method based on the value of an event parameter in the request, providing similar runtime behavior to the "Model 1" style JSP page from the previous section.

For example, an action mapping named "/SomePage" that handles the events "fired" when a user interacts with buttons or links on a related SomePage.jsp page might be configured in struts-config.xml like this:

<action-mappings>
  :
  <action path="/SomePage"
          type="somepackage.SomePageAction"
          parameter="event"
          name="SomeFormBean">
    <forward name="success" path="SomePage.jsp"/>
  </action>
  :

Notice that the parameter attribute in the <action> element has the value "event". This indicates the name of the HTTP request parameter that will be used to signal events. The somepackage.SomePageAction class is a Struts action that extends DispatchAction as in the following example, providing methods whose names match the expected event-name values that the event parameter might take on at runtime:

package somepackage;
public class SomePageAction extends DispatchAction {
  /**
   * When the "event" parameter has the value "DoSomething",
   * the DispatchAction will delegate to this method to handle it.
   */
  public ActionForward DoSomething(ActionMapping mapping,
                                   ActionForm form,
                                   HttpServletRequest request,
                                   HttpServletResponse response) throws Exception {

    /* Do Something Here */
    return mapping.findForward("success");
  }
  /**
   * When the "event" parameter has the value "DoSomethingElse",
   * the DispatchAction will delegate to this method to handle it.
   */
  public ActionForward DoSomethingElse(ActionMapping mapping,
                                   ActionForm form,
                                   HttpServletRequest request,
                                   HttpServletResponse response) throws Exception {

    /* Do Something Else Here */
    return mapping.findForward("success");
  }
  /**
   * We have to implement this method to handle the case when
   * no "event" parameter is supplied as part of the request.
   * By default, the DispatchAction would throw an Exception.
   */
  public ActionForward unspecified(ActionMapping mapping,
                                   ActionForm form,
                                   HttpServletRequest request,
                                   HttpServletResponse response) throws Exception {
    /* Handle the no-event-happening case here */
    return mapping.findForward("success");
  }
}

The SomePage.jsp page that goes with the "SomePage" action above might then look like this:

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

Observe that the page implements the "postback pattern" by posting its HTML form (as well as targeting its Remove One hyperlink) back to the "SomePage.do" Struts action mapping. In this way, the combination of the action mapping and the JSP page divide the work to implement the controller layer and view layer functionality, respectively, for this particular page.

Studying the DispatchAction Example

The DispatchExample action and DispatchExample.jsp page in the DataActionEventsExample workspace look like Figure 29 at runtime. They implement this same "postback pattern" using the Struts DispatchAction.

The Struts DispatchAction Example
Figure 29: The Struts DispatchAction Example

This example uses a simple Struts form bean called ExampleFormBean that exposes a single bean property named value of type int. The code for this simple form bean is, as expected, quite simple:

package controller;
import org.apache.struts.action.ActionForm;
public class ExampleFormBean extends ActionForm {
  private int _value = 0;
  public ExampleFormBean() {}
  public void setValue(int value) { _value = value; }
  public int getValue() { return _value; }
}

Using the JDeveloper Struts Page Flow visual editor, in combination with the Property Inspector, I configured the DispatchExample action mapping to use the ExampleFormBean. If you toggle the Struts Page Flow design view to the Source tab, and search for "DispatchExample", you'll see that it's configured as follows in the struts-config.xml file:

  <action-mappings>
    :
    <action path="/DispatchExample"
            type="controller.DispatchExampleAction"
            parameter="event"
            name="ExampleFormBean">
      <forward name="success" path="/DispatchExample.jsp"/>
      <forward name="winner" path="/YouWin.jsp"/>
    </action>
    :

The DispatchExample.jsp page contains the HTML form, using the Struts HTML Form tag library, that looks like this:

    :
<html:form action="DispatchExample.do">
  <DIV align="center">
    <table cellspacing="0" cellpadding="3" border="1">
      <tr>
        <td>Value</td>
        <td><html:text property="value"/></td>
      <tr>
      <tr valign="middle">
        <td colspan="2">
          <input type="submit" value="Update"/>
          <input type="submit" name="event" value="Increment"/>
          <a href="DispatchExample.do?event=Decrement">Remove One</a> 
        </td>
      </tr>
    </table>
  </DIV>
</html:form>
    :

Notice the use of the event parameter name for the name of the "Increment" and "Decrement" events on the button and hyperlink, respectively.

Since the Struts layer will automatically handle applying the value of the submitted form field named "value" to our ExampleFormBean's property named value, there is no need to write custom event-handling code for the (Update) button. So, accordingly, notice that in the JSP page the (Update) button has no name property. The final step is implementing the controller-layer logic to make our simple application work. Here are the three basic behaviors it needs to have, and the code I wrote to achieve each:

  1. When the "Increment" event occurs, increment the value property of the form bean.

    To implement this, I added the following method to the DispatchExampleAction class:

      /**
       * When the "event" parameter has the value "Increment",
       * the DispatchAction will delegate to this method to handle it.
       */
      public ActionForward Increment(ActionMapping mapping,
                                     ActionForm form,
                                     HttpServletRequest request,
                                     HttpServletResponse response) throws Exception {
        ExampleFormBean formBean = (ExampleFormBean) form;
        formBean.setValue(formBean.getValue() + 1);
        return mapping.findForward("success");
      }
  2. When the "Decrement" event occurs, decrement the value property of the form bean.

    To implement this, I added the method:

     /**
      * When the "event" parameter has the value "Decrement",
      * the DispatchAction will delegate to this method to handle it.
      */
      public ActionForward Decrement(ActionMapping mapping,
                                     ActionForm form,
                                     HttpServletRequest request,
                                     HttpServletResponse response) throws Exception {
        ExampleFormBean formBean = (ExampleFormBean) form;
        formBean.setValue(formBean.getValue() - 1);
        return mapping.findForward("success");
      }
  3. After performing any event handling, if the value property of the form bean equals the magic number 10, then reset the value to 0 and forward to the "winner" forward (mapped to the YouWin.jsp page).

    To implement this I overrode the standard Struts Action's execute method, and performed my conditional page routing logic after calling the super.execute() method, like this:

      /**
       * If the "value" property equals 10, then reset the value to 10
       * and forward to the "winner" forward
       */
      public ActionForward execute(ActionMapping mapping,
                                   ActionForm form,
                                   HttpServletRequest request,
                                   HttpServletResponse response) throws Exception {
        ActionForward fwd = super.execute(mapping, form, request, response);
        ExampleFormBean formBean = (ExampleFormBean) form;
        if (formBean.getValue() == 10) {
          formBean.setValue(0);
          return mapping.findForward("winner");
        }
        else {
          return fwd;
        }
      }

Recall from above that the Struts DispatchAction requires the names of the event-handling methods in the action class to exactly match the values of the event parameter that represent the named events we want this action to handle. So as expected, we have Increment() and Decrement() methods to handle the Increment and Decrement named events.

One additional step we need to take is to override the unspecified() method in DispatchExampleAction to avoid the default behavior of throwing an exception when no event parameter is present or when it has a value that does not correspond to one of our event-handler methods. In this case we write a method like the following which just returns the "success" forward.

  /**
   * We have to override this method to handle the case when
   * no "event" parameter is supplied as part of the request.
   * By default, the DispatchAction would throw an Exception.
   */
  public ActionForward unspecified(ActionMapping mapping,
                                   ActionForm form,
                                   HttpServletRequest request,
                                   HttpServletResponse response) throws Exception {
    return mapping.findForward("success");
  }

Running the ViewController project, and clicking on the Struts DispatchAction Example link, you can experiment with the running application to verify that it implements the basic functionality we outlined above:

The "You Win" Page
Figure 30: The "You Win" Page

Exploring the Equivalent DataAction Implementation

Now that you're familiar with the basic functionality of this simple web event-handling example using Struts DispatchAction, let's study how we can implement the same functionality using the Oracle ADF DataAction. You'll see that conceptually and functionally the two approaches are nearly identical. Since we're building pages that follow the "postback pattern", we'll use the ADF DataPages that we learned about in the Using "Data Pages" to Simplify Your Page Flow Diagrams section so that our DataAction and JSP page combination shows as a single icon in the page flow diagram.

Except for the page title and postback URL, our DataPageExample.jsp page is identical to the DispatchExample.jsp page that we just saw in the previous example. So, the only interesting thing to study is how the controller layer functionality is handled when using the ADF DataAction instead of the Struts DispatchAction. The "DataPageExample" action mapping is configured in the struts-config.xml file like this:

<action-mappings>
  :
  <action path="/DataPageExample"
          className="oracle.adf.controller.struts.actions.DataActionMapping"
          type="controller.DataPageExampleAction"
          name="ExampleFormBean"
          parameter="/DataPageExample.jsp">
    <set-property property="modelReference" value="none"/>
    <forward name="winner" path="/YouWin.jsp"/>
  </action>
  :

We see that we're reusing the same ExampleFormBean, so that aspect is exactly the same as before. The things that are different, when compared with the previous DispatchExample, are the following:

  1. Struts Action classes based on the ADF DataAction require the use of a customized Struts action mapping class.

    This DataActionMapping class (in the oracle.adf.controller.struts.actions package) extends the base Struts ActionMapping to add a few extra properties needed by the ADF binding layer and DataAction to automate aspects of the page request lifecycle and data binding for you. Accordingly, the action mapping's className attribute value reflects the name of this customized action mapping class.

  2. The value of the parameter property, instead of being the name "event", is the name of the JSP page to which the DataAction will forward by default.

    The ADF DataAction combines the functionality of the Struts DispatchAction [20] and the Struts ForwardAction [15] into a single, handy class that also implements the coordination with the ADF binding layer for us. The Struts ForwardAction uses the parameter property to indicate the path of the page to forward to. Since we obviously can't use the parameter property to represent two different things, the ADF DataAction fixes the event parameter name to be "event", and follows the ForwardAction's use of the parameter property to reflect the "built-in" page forward path.


    NOTE: If necessary, you can programmatically override the name of the event parameter in a custom DataAction class by calling the setEventPrefix() method on DataActionContext from an overridden handleLifecycle() method, before calling super.handleLifecycle().

  3. Our controller.DataPageExampleAction action extends the ADF DataForwardAction class instead of extending the Struts DispatchAction class.

To create the controller.DataPageExampleAction where our custom controller code needed to go, I just clicked on the DataPageExample icon in the Struts Page Flow diagram, and selected Go to Code from the right-mouse context method. This brought up the dialog shown in Figure 31, allowing me to adjust the class or package name if desired. Clicking (OK) created the skeleton DataPageExampleAction class for me.

Create Struts Data Action Dialog
Figure 31: Create Struts Data Action Dialog

Taking a closer look at the methods that we've added to the controller.DataPageExampleAction class, we see that it also implements the same controller-layer behavior as the above DispatchAction example, albeit with a slight simplification to the method names and method signatures:

  1. When the "Increment" event occurs, increment the value property of the form bean:

     /**
      * When the "event" parameter has the value "Increment" (or "increment"),
      * the DataAction will delegate to this method to handle it.
      */
      public void onIncrement(DataActionContext ctx) {
        ExampleFormBean formBean = (ExampleFormBean) ctx.getActionForm();
        formBean.setValue(formBean.getValue() + 1);
      }
  2. When the "Decrement" event occurs, decrement the value property of the form bean:

     /**
      * When the "event" parameter has the value "Decrement" (or "decrement"),
      * the DataAction will delegate to this method to handle it.
      */
      public void onDecrement(DataActionContext ctx) {
        ExampleFormBean formBean = (ExampleFormBean) ctx.getActionForm();
        formBean.setValue(formBean.getValue() - 1);
      }
  3. After performing any event handling, if the value property of the form bean equals the magic number 10, then reset the value to 0 and forward to the "winner" forward (mapped to the YouWin.jsp page):

      /**
       * If the "value" property equals 10, then reset the value to 10
       * and forward to the "winner" forward
       */
      protected void findForward(DataActionContext ctx) throws Exception   {
        super.findForward(ctx);      
        ExampleFormBean formBean = (ExampleFormBean) ctx.getActionForm();
        if (formBean.getValue() == 10) {
          formBean.setValue(0);
          ctx.setActionForward("winner");
        }
      }

The salient differences to observe between the DispatchAction implementation and this DataAction-based implementation are the following:

  1. The event-handling methods have the name onEventname, instead of Eventname.
  2. The signature of the event-handling methods is a void method with the single DataActionContext parameter, instead of the four-parameter signature returning ActionForward that the DispatchAction-based event handler methods take.


    NOTE: With the DataAction, you can still access the data action mapping, the form bean, as well as the HTTP request and response objects by calling appropriate getter methods on the DataActionContext object that you are passed by the DataAction base code.

  3. We override the findForward method of the ADF DataAction's page handling lifecycle to conditionalize the "Find Forward" behavior, instead of overriding the default Struts Action's execute() method.

    Note that we're calling setActionForward() method on the DataActionContext, instead of returning a new ActionForward object as the method result.

  4. There is no need to write an unspecified method, as with the DispatchAction.

    By default, the ADF DataAction does not throw an exception when no event parameter is part of the request, rather than throwing an exception by default as the DispatchAction does.

Running the ViewController project and clicking on the ADF DataPage Example link, you can verify that this ADF DataAction-based example mirrors the functionality of the Struts DispatchAction example from before. It should look like what you see in Figure 32.

The Struts ADF Data Page Example
Figure 32: The Struts ADF Data Page Example

Paying Attention to Internationalization

The ADF DataPage Example2, accessed by clicking the Example2 link from the home page, is identical to the DataPage Example we just ran through, except that it illustrates an alternative approach to "firing" named events from within a web page.

While both the DispatchExample and the ADF DataPage Example coded the button that fired the "Increment" event like this:

<input type="submit" name="event" value="Increment"/>

using "event" as the name of the button, and "Increment" as the value of the button, this has some limitations when it comes to both internationalization and naming flexibility.

Building Internationalizable JSP Pages

The HTML button's value attribute is used both as its user-visible label, as well as its machine-readable value. However, this means that if we need to provide our user interface in multiple languages, to translate the (Increment) button, we would need to change the value of the value attribute from "Increment" to its translation in other languages. This, in turn, would cause the event name to change and our middle-tier controller logic would stop working as expected.

Best practice for Struts-based applications that (even which eventually might) need to support multiple languages is to isolate the translatable user interface strings into the Struts ApplicationResources.properties file, like this:

# From ApplicationResources.properties
button.add.one=Add One
button.update=Update
link.remove.one=Remove One

With the translatable strings in a resource, we then use the Struts Bean tag library to refer to the messages by their string "key" value so that the appropriate translated version of the message can be emitted at runtime, based on the preferred language of the current browser user. So, for example instead of having:

<input type="submit" value="Update"/>
<input type="submit" name="event" value="Increment"/>
<a href="DataPageExample.do?event=Decrement">Remove One</a>

we would end up with this more I18N-savvy version instead:

<input type="submit" value="<bean:message key='button.update'/>"/>
<input type="submit" name="event" value="<bean:message key='button.add.one'/>"/>
<a href="DataPageExample2.do?event=Decrement"><bean:message key='link.remove.one'/></a>

NOTE:

This allows developers to produce any number of alternative translations for the user interface strings which Struts will use as appropriate at runtime. For example, an Italian version of the ApplicationResources.properties file, by convention, would be named ApplicationResources_it.properties and might look like this:

# From ApplicationResources_it.properties
button.add.one=Aggiungere Uno
button.update=Aggiornare
link.remove.one=Togliere Uno

Keeping Button Labels and Event Names Separate

Since the event-handler method names need to correspond to the names of the events, the second issue is that even with properly internationalized display strings, we run into trouble if we want our end user to see a button named (Add One), instead of (Increment). The problem is the space in the name, since the corresponding event handler method name would need to contain a space to match the value "Add One" and Java method names cannot contain spaces.

While, Struts provides the LookupDispatchAction to help with this translatability issue, the ADF Data Action provides an even simpler approach; instead of using the value property of the HTML control to represent the event name to fire, you can alternatively use the "event_" prefix in the name of the button instead. That is, rather than writing:

<input type="submit"
       name="event"
       value="<bean:message key='button.add.one'/>"/>

to fire an event whose name is given in the value of the button, we can write instead:

<input type="submit"
       name="event_Increment"
       value="<bean:message key='button.add.one'/>"/>

changing the name="event" to name="event_Increment". This way, regardless of the value of the button or whether that value contains spaces in the name, we have a reliable way to fire the "Increment" event without running into internationalization issues.

By studying the source code of the DataPageExample2.jsp, you'll see that in addition to the use of the <bean:message> tags for internationalizability, this is the only real difference between it and DataPageExample.jsp we looked at previously. The DataPageExample2 data action is configured to use the exact same ExampleFormBean as well as the exact same DataPageExampleAction class as previously, and at runtime clicking the Example2 link on the home page shows that it has identical functionality to its predecessor.

Using ADF Bindings and Declarative Events

Continuing our adventure to explore the ADF binding and event-handling mechanism further, we move next to examine the ADF Declarative Example. Again, the application functionality is identical to the above "magic number" example implementations, yet the implementation takes advantage of even more features of the ADF framework and its DataAction's integration with Struts.

In our previous "magic number" examples, our application did not really have any proper "business service" or "model" layer as we explored in earlier sections of this paper. The value of the integer being changed simply lived as a property of the Struts ExampleFormBean. Struts form beans play the role of data transfer objects that shuttle information between the submitted form data and the view layer. In other words, in these previous implementations we "cheated" by ignoring the model layer completely.

The ADF Declarative Example implements a proper model layer, implemented as a simple JavaBean in the Model project named model.ExampleBean:

package model;
public class ExampleBean {
  private int _value;
  public ExampleBean() {}
  public void setValue(int value) { _value = value; }
  public int getValue() { return _value; }
  public void increment() { setValue(getValue() + 1); }
  public void decrement() { setValue(getValue() - 1); }
}

Except for the obvious addition of the encapsulated increment() and decrement() methods, at first glance, ExampleBean looks identical to the ExampleFormBean that we used before. However, a key difference is that it does not extend from any Struts-dependent class as the ExampleFormBean did (recall that it extended the base Struts ActionForm class). This means that our model layer is just a pure, simple bean, in this case with one bean property named value of type int.

By clicking on the ExampleBean.java and selecting Create Data Control from the right-mouse context menu, I created my ExampleBeanDataControl, making it ready for ADF data binding. Figure 33 reflects this property and these operations in the Data Control Palette.

ExampleBeanDataControl in the Data Control Palette
Figure 33: ExampleBeanDataControl in the Data Control Palette

The DataPageDeclarative.jsp page implements the view layer of the DataPageDeclarative data page. To do data binding for this page, I needed to create an ADF binding container for it, with three types of ADF bindings that we'll use in this example:


NOTE:

These two void methods don't return any data themselves. We'll learn a lot more about control action bindings and binding to the data returned by their related business service methods in the Understanding Method Results Data Binding section below.


Recall from the Creating Binding Containers and Bindings section that there are two ways to create bindings at design time: explicit and implicit. I used a combination of both approaches when building DataPageDeclarative.jsp:

  1. I first clicked on the value attribute under ExampleBeanDataControl in the Data Control Palette. Then I selected "Input Field" from the Drag and Drop As combobox at the bottom of the palette. Finally, I dragged the value property and dropped it onto the empty page. This prompted me, as shown in Figure 34 to create form element to "wrap" this HTML text input field being created, and I answered "Yes".

    Add Form Element Confirmation
    Figure 34: Add Form Element Confirmation

    This drag/drop operation resulted in the implicit creation of the:

  2. Next, I clicked on the increment method, and dragged and dropped a "Button" to the form, making sure to drop it inside the dotted line the represents the wrapping <html:form> elements as shown in Figure 35.

    After Dropping a Button for the "increment" Method
    Figure 35: After Dropping a Button for the "increment" Method

    This drag/drop operation resulted in the creation of a new control action binding named increment in the binding container.

  3. Finally, I used the explicit approach to create a second action binding for invoking the decrement method by doing the following:

    This explicit binding creation resulted in the new decrement in the binding container.

By clicking on the DataPageDeclarative.jsp page in the project and clicking on the "UI Model" tab of the Structure Pane, you can see the result of my actions above that will like what you see in Figure 36.

UI Model Tab Showing Bindings for DataPageDeclarative.jsp
Figure 36: UI Model Tab Showing Bindings for DataPageDeclarative.jsp

With the bindings created, I went back and fixed up the look of the page to match the other "magic number" examples, so that my <html:form> section of the DataPageDeclarative.jsp ended up looking very similar to the one with the internationalized user interface strings from DataPageExample2.jsp:

<html:form action="DataPageDeclarative.do">
  <input type="hidden"
         name="<c:out value='${bindings.statetokenid}'/>"
         value="<c:out value='${bindings.statetoken}'/>"/>
  <DIV align="center">
    <table cellspacing="0" cellpadding="3" border="1">
      <tr>
        <td>Value</td>
        <td>
          <html:text property="value" />
        </td>
      </tr>
      <tr valign="middle">
        <td colspan="2">
          <input type="submit" value="<bean:message key='button.update'/>"/>
          <input type="submit" name="event_increment"
                               value="<bean:message key='button.add.one'/>"/>
          <a href="DataPageDeclarative.do?event=decrement"
           ><bean:message key='link.remove.one'/></a> 
        </td>
      </tr>
    </table>
  </DIV>
</html:form>

With the view layer explained, let's shift our attention to the controller layer configuration and implementation. The DataPageDeclarative action is configured like this in the struts-config.xml file:

<action-mappings>
  :
  <action path="/DataPageDeclarative"
          className="oracle.adf.controller.struts.actions.DataActionMapping"
          type="controller.DataPageDeclarativeAction"
          name="DataForm"
          parameter="/DataPageDeclarative.jsp">
    <set-property property="modelReference" value="DataPageDeclarativeUIModel"/>
    <forward name="winner" path="/YouWin.jsp"/>
  </action>
  :

Notice that just like the previous DataPageExample, it is using the extended ADF DataActionMapping class to represent the Struts action mapping. This gives the DataAction runtime access to properties like the modelReference that you see in the nested <set-property> element, so that it can automatically setup your binding container and expose it through the DataActionContext. The only other interesting difference this time is that rather than using the ExampleFormBean that we used previously, this time we are using the form bean named DataForm.

From the <form-beans> section of the struts-config.xml file, we can see that the DataForm form bean is implemented by the BindingContainerActionForm. This is the all-purpose form bean that ADF supplies, which exposes the bindings in your binding container as a Struts DynaActionForm.

  :
<form-beans>
  <form-bean name="ExampleFormBean" type="controller.ExampleFormBean"/>
  <form-bean name="DataForm" 
     type="oracle.adf.controller.struts.forms.BindingContainerActionForm"/>
</form-beans>
  :

Having an all-purpose form bean like this makes your life a lot easier as an application developer because you don't need to worry about designing different Struts form beans for each web page that you create. It is a clever Struts DynaActionForm implementation that reflects the binding objects for the current page's binding container directly to the Struts layer, with no additional fuss. When Struts goes to set a property on this form bean, it automatically translates into setting the value on the appropriate binding.


NOTE:

This single BindingContainerActionForm form bean can be used for all of your pages that combine ADF data binding and rely on working with Struts form beans. For example, any pages that use the Struts HTML tag library are in this category, although pages that render display-only data would not require using any form bean.

It's worth noting that due to Bug# 3607830 ("Data Actions Without Form Bean Reset To Use 'DataForm' On Opening Diagram"), if you remove the "DataForm" form bean from a DataAction or DataPage by blanking out the value of its name property, the next time you open the diagram JDeveloper will incorrectly reset it to use the 'DataForm' again. The workaround is to define a DynaActionForm with no properties named "None" as shown here, and then use this None form instead of leaving the form bean name property blank on any DataAction or DataPage that doesn't require the use of the BindingContainerActionForm.

   :
  <form-beans>
     :
    <form-bean name="None" type="org.apache.struts.action.DynaActionForm"/>
     :
  </form-beans>
   :

Running the ViewController project and clicking on the ADF Declarative Example link, the page looks like Figure 37 and you can verify that the functionality implemented is the same the previous "magic number" examples we've explored:

The ADF DataAction Declarative Example
Figure 37: The ADF DataAction Declarative Example

Let's take a closer look at how this particular example is working and what code was required to get it to go. Looking at the source code for the DataPageDeclarativeAction we see that it only contains a single, overridden findForward method:

/**
 * If the "value" property equals 10, then reset the value to 10
 * and forward to the "winner" forward
 */
protected void findForward(DataActionContext ctx) throws Exception {
  super.findForward(ctx);
  DCBindingContainer bc = ctx.getBindingContainer();
  JUCtrlValueBinding binding = (JUCtrlValueBinding) bc.findCtrlBinding("value");
  int value = ((Integer) binding.getInputValue()).intValue();
  if (value == 10) {
    binding.setInputValue(new Integer(0));
    ctx.setActionForward("winner");
  }
}

Notice that the code above uses the findControlBinding() method on the binding container and the getInputValue() and setInputValue() methods on the control binding to read the integer value from the model layer (via the "value" binding) and conditionally set it back to zero if necessary.

But wait! There are no onIncrement() or onDecrement() methods in the action class to handle the increment and decrement events generated by the (Add One) button and the Remove One link in the page. What's going on?

There is, of course, a simple explanation. One of the many declarative features of the ADF DataAction is the automatic invocation of an ADF control action binding from the current binding container if its name matches the name of the current event being handled. So, recalling our bindings from Figure 36 for this page, when the "increment" event is handled by the ADF DataAction, it notices that we have a matching increment action binding in the binding container, and it performs equivalent of the following code to invoke the matching action binding for the event being handled (if there is one!):

/* NOTE: ctx is the current DataActionContext */
if (ctx.getEventActionBinding() != null) {
  PageLifecycle p = (PageLifecycle)getPageLifecycle(ctx);
  /* Invoke the action binding corresponding to the name of the event */
  p.invokeActionBinding(ctx,ctx.getEventActionBinding().getName());
}  

Since the increment action binding is bound to the increment() method of our ExampleBeanDataControl, calling doIt() on this action binding results in the ADF binding layer's invocation of the ExampleBean's increment() method, which increments the value of our integer bean property by one. This same declarative mechanism explains how the decrement() method of ExampleBean gets invoked by clicking on the Remove One link in the page. The DataAction finds the action binding named decrement corresponding to the name of the current event being handled and invokes it.

While not required to implement the functionality of our very simple example application, you can combine the onEventname event handling approach with the declarative action binding invocation when needed. For example, you could write a handler for your SomeEventName event like this:

  public void onSomeEventName(DataActionContext ctx) {
    // Code before executing the default action
    if (ctx.getEventActionBinding() != null) {
      PageLifecycle p = (PageLifecycle)getPageLifecycle(ctx);
      /* Invoke the action binding corresponding to the name of the event */
      p.invokeActionBinding(ctx,ctx.getEventActionBinding().getName());
    }
    // Code after executing the default action
  }

Notice that the event handler includes the code to invoke the default action binding (if it's present). If your onSomeEventName method fails to include this code, then you will short-circuit the default declarative invocation of the matching action binding.

If you find yourself doing this in several places, you could write the helper method invokeEventAction() like this, and use it wherever you want your event handlers to invoke the default action binding matching their event name:

  protected void invokeEventAction(DataActionContext ctx) {
    if (ctx.getEventActionBinding() != null) {
      PageLifecycle p = (PageLifecycle)getPageLifecycle(ctx);
      p.invokeActionBinding(ctx,ctx.getEventActionBinding().getName());
    }    
  }

Using a helper method like this, our example event handler above would look like this:

  public void onSomeEventName(DataActionContext ctx) {
    // Code before executing the default action
    invokeEventAction(ctx);
    // Code after executing the default action
  }

In this way, you just need to write your custom controller code before and/or after that invocation to achieve the result you need.


NOTE:

The careful reader may have noticed a subtle thing that changed between the DataPageExample and the DataPageDeclarative. The name of our events changed from having an initial capital letter (Increment,Decrement) to being in lower case (increment, decrement). This was a result of the fact that the ExampleBean class has method names increment() and decrement() with initial lower-case letters, and of the way that the ADF design time defaults the names of action bindings to match the method names to which they are bound.

Had it been important to keep the initial-capital event names of Increment and Decrement, then to keep the declarative action binding invocation working, we would have needed to rename the increment and decrement action bindings accordingly to Increment and Decrement respectively. You can rename action bindings (or any bindings for that matter), by clicking on them in the UI Model tab and editing the value of their id property in the Property Inspector.


Exploiting Declarative Event-Based Page Navigation

To understand the last major feature of the ADF DataAction's event-handling mechanism, we shift our attention to Events, Actions, and Forwards Example, implemented by the two pages at the top of our example project's Struts page flow diagram shown in Figure 27.

The UpdateEmp1 and UpdateEmp2 data pages show information about employees from the familiar EMP table in the SCOTT schema, divided across two pages. As shown in Figure 38, the UpdateEmp1.jsp page shows the employee's name and role information about the employee, while the UpdateEmp2.jsp page shows the employee's compensation information

Name and Role Information of the Employee on UpdateEmp1.jsp
Figure 38: Name and Role Information of the Employee on UpdateEmp1.jsp

Two buttons at the top of the page act like a poor man's tab control, allowing the user to navigate between the Name and Role Info page and the Compensation Info page by clicking on the button of the same name. The four buttons at the bottom of the page allow the user to save or cancel all changes as well as move to the next and previous employees while editing. The user sees and changes the Deptno information based on a data-driven combox.


NOTE:

If your curious to see how the declarative data-binding for the data-driven combobox works, click on the LOV binding named Deptno in the binding container for UpdateEmp1.jsp and pick Edit... from the right-mouse context menu to examine all of the binding properties that make the combobox tick.


The EmpServiceDataControl shown in Figure 39 supplies the Employees data source that is being shown and edited, as well as the DepartmentLOV datasource, which drives the choices the user sees in the "Deptno" combobox. This data control is implemented by the model.EmpService ADF Application Module component in our Model project. It contains instances named Employees and DepartmentLOV of appropriate view object components model.views.Employees and model.views.DepartmentLOV, respectively. Figure 39 Shows what this data control looks like in the Data Control Palette.

The EmpServiceDataControl
Figure 39: The EmpServiceDataControl

This two-page employee editing example supports numerous functionalities:

Exploiting features of the ADF DataAction event-handling mechanism, we've been able to implement all of these features declaratively without having to write any custom controller code. We'll explore each functionality in turn to understand how the ADF DataAction is supporting this declarative approach.

Since there is no custom code required for this Events, Actions, and Forwards Example application, it makes sense that the struts-config.xml file configuration entry for the UpdateEmp1 and UpdateEmp2 actions reflects that we're using the base class DataForwardAction, instead of a custom subclass of it as we've used in previous DataAction-based examples:

<action-mappings>
  :
  <action path="/UpdateEmp1"
          className="oracle.adf.controller.struts.actions.DataActionMapping"
          type="oracle.adf.controller.struts.actions.DataForwardAction"
          name="DataForm"
          parameter="/UpdateEmp1.jsp">
    <set-property property="modelReference" value="UpdateEmp1UIModel"/>
    <forward name="ShowCompensationInfo" path="/UpdateEmp2.do"/>
  </action>
  <action path="/UpdateEmp2"
          className="oracle.adf.controller.struts.actions.DataActionMapping"
          type="oracle.adf.controller.struts.actions.DataForwardAction"
          name="DataForm"
          parameter="/UpdateEmp2.jsp">
    <set-property property="modelReference" value="UpdateEmp2UIModel"/>
    <forward name="ShowNameInfo" path="/UpdateEmp1.do"/>
    <forward name="SaveAll" path="/UpdateEmp1.do"/>
  </action>
  :

We see that as in the DataPageDeclarative example we looked at before, they are also both using the "DataForm" form bean, and the DataActionMapping as expected.

Looking at the UpdateEmp1.jsp page, we see that the various buttons are named with the event_ prefix so that they fire the following named events: Commit, Rollback, Next, Previous, and ShowCompensationInfo. Flipping to view the "UI Model" tab in the Structure Pane for the UpdateEmp1.jsp page shown in Figure 40, we see action bindings with names matching those of four of those five events for this page: Commit, Rollback, Next, Previous.

UI Model Tab Showing Bindings for UpdateEmp1.jsp Page
Figure 40: UI Model Tab Showing Bindings for UpdateEmp1.jsp Page

The Commit and Rollback action bindings are bound to the built-in Commit and Rollback methods of the Data Control, so we didn't need to write any custom data control methods to enable that functionality. The Next and Previous action bindings are bound to the built-in iterator methods for Next and Previous, so again no custom code was required to implement them. These bindings were created implicitly when I dragged the respective built-in operations from the Data Control Palette, and dropped them onto my UpdateEmp1.jsp page in the JSP Visual Editor. Again, I made sure to drop each button inside the HTML form container as we stressed above in Figure 35. We'll explain how the remaining ShowCompensationInfo event is handled shortly.

Similarly, peeking into the UpdateEmp2.jsp page, we see buttons named to fire the SaveAll, CancelAll, Next, Previous, and ShowNameInfo events. Figure 41 shows the UI Model for this page, with its matching SaveAll, CancelAll, Next, Previous action bindings.

UI Model Tab Showing Bindings for UpdateEmp2.jsp Page
Figure 41: UI Model Tab Showing Bindings for UpdateEmp2.jsp Page

I setup the bindings for these buttons in the same way I did for UpdateEmp1.jsp, with one educational twist. In case you prefer to use event names other than the default ones that the ADF design time creates when you drop actions onto your page, here I wanted to show how you can do it. By default, dropping the Commit and Rollback operations onto your page will create (Commit) and (Rollback) buttons with:

It will also implicitly create the matching Commit and Rollback action bindings in your binding container. To change these default (Commit) and (Rollback) buttons, which firie the Commit and Rollback events, to be (Save All Changes) and (Cancel Changes) buttons firing the SaveAll and CancelAll events instead, I selected each button in turn and used the Property Inspector to change:

We now understand how the buttons at the bottom of the UpdateEmp1 and UpdateEmp2 pages are being handled at runtime. The buttons are named to raise events that are handled automatically by the ADF DataAction by invoking the action bindings in the current binding container whose names exactly match those event names. In contrast to the increment and decrement action bindings in our earlier example, which invoked custom methods that we wrote, these action bindings are invoking built-in operations provided by ADF on the data control and iterator that we're using. The mechanism works the same way for both built-in and custom method invocation based on action binding names matching the name of the event being handled.

The last two events to understand are the ShowCompensationInfo event for the UpdateEmp1.jsp page and the ShowNameInfo event for the UpdateEmp2.jsp page. The answer lies in the names of the Struts forwards between the UpdateEmp1 and UpdateEmp2 pages that we can see in Figure 42.

Update Emp Pages in DataActionEventsExample Page Flow
Figure 42: Update Emp Pages in DataActionEventsExample Page Flow

Notice that there is a "forward" named ShowCompensationInfo that goes from the UpdateEmp1 data page to UpdateEmp2. By default, in the findForward() phase of the page handling lifecyle, the ADF DataAction will check to see if the developer has previously set the name of the ActionForward to use by calling the getActionForward() method on the DataActionContext. If this check returns null, meaning that the developer has not previously programmatically set the ActionForward to use, then as a useful fallback behavior, it will try to find a forward by the same name as the current event being handled. If such a forward with a matching name exists, the default findForward() implementation will set that ActionForward to be used.

This explains why, without writing any custom controller code, the user can click on the (Show Compensation Info) button and see the UpdateEmp2.jsp page. The ShowCompensationInfo event fires, and the ADF DataAction uses the matching ShowCompensationInfo forward to perform declarative navigation.

We're not making use of it in the example, but you of course can combine this declarative page navigation behavior with the event handling we learned before. That is, if I happened to have an onShowCompensationInfo() method in a custom data action class, my event-handling code would be called before the findForward() phase of the lifecycle. If my event-handling code were to programmatically call setActionForward() on the DataActionContext to any value, then the declarative page navigation behavior would get short-circuited. The programmatically set ActionForward takes precedence.

The same declarative page navigation mechanism explains how the ShowNameInfo event is handled for UpdatePage2.jsp, causing declarative navigation back to the UpdatePage1.jsp via the matching ShowNameInfo forward. By running the ViewController project and clicking on the Events, Actions, and Forwards Example link, you can try out this example. Try navigating to various employees and editing their data, toggling between the "Name and Role Info" and the "Compensation Info" for the current employee.

For one last "trick", try the following. After making one or more edits to employee information at runtime, while you are on the Compensation Info page as shown in Figure 43, try clicking the (Save All Changes) button. Recall from above that this button fires the SaveAll event, and the DataAction will invoke the SaveAll action binding in the binding container because the name matches. Then, since there happens to be a forward named SaveAll from UpdateEmp2.jsp back to UpdateEmp1.jsp, the clicking of this button will both invoke the action and perform the default page navigation back to the "Name Info Page".

The "Compensation Info Page" (UpdateEmp2.jsp)
Figure 43: The "Compensation Info Page" (UpdateEmp2.jsp)

This illustrates that you can use the declarative action-binding invocation behavior together with the declarative page navigation. In summary, we've seen through various examples that the ADF DataAction supports three key features around the handling of named events which you can use in any combination that you find useful in your applications. When an event named YourEvent fires, then...

  1. If you have apublic void onYourEvent(DataActionContext ctx) method in the data action class handling the request, it will be invoked to handle the event with custom code.
  2. If you have an action binding in the current binding container named YourEvent, it will be invoked.

    When used in combination with 1, your event-handler code needs to explicitly invoke the default action for the current event by using code like:

    if (ctx.getEventActionBinding() != null) ctx.getEventActionBinding().doIt();
  3. If you have a Struts forward named YourEvent, it will be used to determine the next page to forward control to.

    When used in combination with 1, if your event-handling code invokes ctx.setActionForward(), then your programmatically set forward takes precedence.

Understanding Method Results Data Binding

In the ADFBindingIntro example that we studied in the Understanding a Working Example Application section, we so far have only seen examples of data binding to collection-valued data control properties:

Following the JavaBeans standard, ADF data controls expose both properties and methods. The ADF design time extensions in JDeveloper 10g recognize methods with names like:

as being related to a data control's property named Something. Methods with any other names are treated as service methods. The ADF binding layer supports binding to data both from exposed properties, which are often collection-valued, as well as to the results of invoking service methods. We'll focus in more detail on exploring this case of doing data binding on method results in this section.

Binding to Results of Service Methods

If your data control exposes service methods you can perform data binding on the results returned by any of them. I've cobbled together a third little example (MethodBindingExample.zip [3]) that illustrates a few situations involving data binding with custom method results. The example revolves around the JavaBean-based data control implemented by the following MyService class:

package test.model;
public class MyService {
  public MyService() {}
  /**
   * Test exposing a collection of strings.
   */
  public String[] getArrayOfStrings() {
    return new String[] {
    "some",
    "strings",
    (new Date()).toString() }; // Return current date as a string
  }
  /**
   * Test exposing a collection of beans.
   */
  public MyType[] getArrayOfMyType() {
    return new MyType[] { new MyType("Tom", 5), new MyType("Sue", 6) };
  }
  /**
   * Test method returning an array of strings from method.
   */
  public String[] findArrayOfYourStrings(String x, String y) {
    return new String[] { x, y };
  }
  /**
   * Test method returning an array of MyType beans, accepting scalar args.
   */
  public MyType[] findArrayOfMyType(String name, int age, String name2, int age2) {
    return new MyType[] { new MyType(name, age), new MyType(name2, age2) };
  }
  /**
   * Test method returning an array of MyType beans, accepting a MyType argument.
   */
  public MyType[] findArrayOfMyType(MyType person1) {
    return new MyType[] {
      person1, new MyType(person1.getName() + ", Sr.", person1.getAge() + 20)
    };
  }
}

Figure 44 shows what the Data Control Palette looks like after I did the Create Data Control from this class' context menu in the Application Navigator. Notice that in addition to the collection-valued properties named arrayOfMyType and arrayOfStrings, we see our three custom methods under the Operations folder with the little "f()" icon. Expanding the method nodes, we see a return node in the palette that represents the return value of the method invocation.

Data Control Palette Showing Collections and Method Results
Figure 44: Data Control Palette Showing Collections and Method Results

If the method return value is a JavaBean or collection of JavaBeans, you can further expand the return node to see the substructure of the method result. If the method return value is a simple scalar, or array of scalar, then the return node represents that scalar value without further substructure.

To create the simple demo, I used the JDeveloper 10g page flow diagrammer to develop what you see in Figure 45. It shows the index.jsp page with links to try out four different data binding scenarios.

Page Flow Diagram for Method Binding Example
Figure 45: Page Flow Diagram for Method Binding Example

To create the contents of the data pages that display the results in an HTML table, I repeated the following basic steps for each data page:

  1. Double-clicked on the data page icon after creation and confirmed the name of the underlying JSP page.
  2. Used the JSP visual editor to:

  3. Switched the Component Palette to the "CSS" palette page, and dragged the "Blaf" (browser look and feel) stylesheet and dropped it on the page.

Running the ViewController project in the MethodBindingExample workspace will run the index.jsp, and you should see a page like the one shown in Figure 46.

Method Binding Example "Home Page"
Figure 46: Method Binding Example "Home Page"

Understanding Iterators Over Collection Valued Properties

The Collections of Strings and Beans link on the home page links to the CollectionsWithNoArgs data page. This page binds to the data from two collection-valued properties, arrayOfStrings and arrayOfMyType in our MyServiceDataControl. Notice in the source code of the MyService class above that the getArrayOfString() method returns a string representation of the current date in the array.

  public String[] getArrayOfStrings() {
    return new String[] {
    "some",
    "strings",
    (new Date()).toString() }; // Return current date as a string
  }

Each time the getArrayOfString() method is invoked, the third string in the array should reflect the current system time.

After clicking on Collections of Strings and Beans to see the CollectionsWithNoArgs data page, try refreshing your browser to request the page again. You'll notice that the current date stays the same, rather than being updated each time the page is refreshed. The ADF Binding layer is not invoking the getArrayOfString() method on each page refresh. Why is this? It's important to understand why the method is not called each time so that you understand how to control the behavior in your own applications.

During the prepareModel() phase of the ADF DataAction's page handling lifecycle, the ADF data action calls the refreshControl() method on your binding container. This has the effect of executing all of the "top-level" iterator bindings in the binding container for the current request if they have not previously been executed. By "top-level" iterator bindings, we mean iterator bindings that are bound to iterators over a "top-level" collection of data rows, rather than being bound to some nested properties of a data row. If you think of these nested object properties as "details" of the top-level objects being iterated, you can think of the "top-level" iterators as those that iterator over the "master" objects in a master/detail sense.

So the first time the CollectionsWithNoArgs page is displayed, the iterator over the collection-valued arrayOfString property is executed. This, in turn, causes the underlying property getter method getArrayOfStrings() in our service class to be invoked. On subsequent page refreshes, the iterator has already been executed, so by default, it is not forcibly re-executed each time.

In order to force the iterator to re-execute, we simply need to call the executeQuery() method on the iterator. You can see that there is a Refresh Results hyperlink in the page that allows the user to refresh the data, which is coded like this so that it causes a event named Refresh to fire:

<a href="CollectionsWithNoArgs.do?event=Refresh">Refresh Results</a>

We could have handled the event either programmatically or declaratively. Having done it programmatically would have involved:

  1. Creating a custom CollectionsWithNoArgsAction DataAction subclass class

    We would have done this by clicking on the data page icon in the page flow diagram and selecting the Go to Code menu item on the right-mouse context menu.

  2. Writing an onRefresh() method with the appropriate signature for being a DataAction event-handler method.
  3. Writing one line of code in the event handler method to find the iterator by name from the binding container and call executeQuery() on it.

    That code would have ended up looking like:

    public void onRefresh(DataActionContext ctx) {
      ctx.getBindingContainer()
         .findIteratorBinding("arrayOfStringsIterator").executeQuery();
    }

However, having honed our understanding of the ADF DataAction event-handling mechanism earlier, chosing the declarative approach seems very apropos. This is the approach that the MethodBindingExample is actually using.

Clicking on the "UI Model" tab of the Structure Pane with the CollectionsWithNoArgs.jsp page active, we see that there's a Refresh action binding, named to match my Refresh event name so it will be declaratively invoked when the Refresh event occurs. Clicking on the Refresh action binding and choosing Edit... from the context menu, you can see in the Action Binding Editor that the action binding is bound to the built-in Execute action of the arrayOfStringsIterator data collection.

So this will cause the iterator to be re-executed when the Refresh event occurs without requiring any custom code. Try clicking on the Refresh Results link at runtime in the CollectionsWithNoArgs.jsp page. You'll see that each time you click the link, the date will be updated on the page.

Binding to Method Passing String Arguments

In contrast to the property-based data binding we just explored, the remaining three page flows in this example all bind to the results returned from a custom method that accepts arguments. The first one we'll study has:

Setting Up the Basic Page Flow

To setup the basic structure of the page flow I used the Struts diagram to create the following using the tools in the Component Palette:

  1. A page named AcceptStringsParams.jsp, using the "Page" tool
  2. A DataAction named CallFindStrings, using the "DataAction" tool
  3. A DataPage named StringResults, using the "DataPage" tool

Before connecting the CallFindStrings DataAction to the StringResults DataPage using a Struts forward, I first declaratively setup the method invocation by flipping to the Data Control Palette, and dropping the findArrayOfYourStrings operation onto the CallFindStrings action. This resulted in the implicit creation of the CallFindStringsUIModel binding container in Figure 47 containing an action binding to invoke this method, as well as some custom Struts action properties that capture metadata about the method name to invoke and any parameter names it requires values for.


NOTE:

When the JDeveloper 10g design time implicitly creates a binding container for a DataAction, it uses value of the modelReference property in the struts-config.xml file to name the new binding container. If the action mapping does not yet have any modelReference property, or its value is empty, the icon in the page flow diagram will show a "warning" symbol like this to alert you that it is not completely configured yet. If you drop an operation from the Data Control palette onto a DataAction named YourDataAction with no associated binding container, it will add one with the value of YourDataActionUIModel. Another way that a DataAction without a binding container will get one assigned automatically is when you connect the DataAction to another action or page via a Struts forward. When you do this, if the originating DataAction has no binding container, it will get setup to use the same binding container as the target of that first forward created. Therefore, if you want your DataAction to have its own binding container for invoking a custom method, you should drag/drop the operation onto your DataAction before connecting it to another other actions or pages. We explain in the last section of this paper why it's important that the "invoke the method" data action should have its own binding container.


UI Model Tab Showing Binding Container for CallFindStrings DataAction
Figure 47: UI Model Tab Showing Binding Container for CallFindStrings DataAction

To show the method results in the StringResults page, I double-clicked on the page's icon in the Struts page editor and confirmed the name of the underlying JSP page (StringResults.jsp). Then, I selected the return node beneath the findArrayOfYourStrings operation in the Data Control Palette, and dragged it onto the empty design surface as a "Dynamic Read-Only Table". These few steps left me with the bindings in the StringResultsUIModel shown in Figure 48.

UI Model for StringResults.jsp Page
Figure 48: UI Model for StringResults.jsp Page

Next I connected the CallFindStrings DataAction to the StringResults DataPage using the "Forward" tool on the Component Palette in the Struts page flow editor.

Double-clicking on the AcceptStringsParams.jsp page, we see that it is a plain JSP page that collects the values of two parameters named one and two and posts the form to the CallFindStrings.do action:

<!-- Form from AcceptStringsParams.jsp -->
  <form action="CallFindStrings.do">
    <table cellspacing="0" cellpadding="2" border="1">
      <tr>
        <td>String One:</td>
        <td><input type="text" name="one"/></td>
      </tr>
      <tr>
        <td>String Two:</td>
        <td><input type="text" name="two"/></td>
      </tr>
    </table>
    <input type="submit" value="Submit"/>
  </form>

To reflect in the Struts page flow diagram the fact that this JSP page posts to CallFindStrings, I clicked on the AcceptStringsParams.jsp page and selected Refresh Diagram From Page in the right-mouse context menu. This updated the diagram with a dotted line between AcceptStringsParams.jsp and CallFindStrings to reflect the dependency between these elements.

Setting Up the EL Expressions for the Method Parameters

The last detail was to setup the declarative information to drive the values that ADF will pass to the two string arguments that the findArrayOfYourStrings expected. As we saw in the Using DataAction Features to Go Totally Declarative section earlier, metadata for declarative method invocation is captured in additional custom properties on the Struts action mapping. The drag and drop operation of the Data Control Palette operation onto the action automatically configured the basic metadata we need. We can see those extra properties like methodName, resultLocation, numParams, paramNames[0], and paramNames[1] in the struts-config.xml Structure Pane shown in Figure 49.

Details of CallFindStrings Action Mapping in Struts Pane
Figure 49: Details of CallFindStrings Action Mapping in Struts Pane

I just needed to go in and adjust the values of the paramNames[0] and paramNames[1] to reflect the EL expressions for the parameter values that I wanted to pass. Since I wanted the first argument to receive the value of the request parameter named one, using the Property Inspector I changed the value of paramNames[0] property to the EL expression ${param.one} accordingly. Likewise, I set the value of the paramNames[1] property to ${param.two} to have it use the value of the two parameter.


NOTE:

Recall that the ${param.paramname} syntax is the standard way in JSTL's expression language to refer to the request parameter named paramname.


Running the ViewController project and selecting the Results of Method Taking String Arguments link, you can experiment with the pages we've just described to verify that they are behaving as expected.

Why Doesn't the Action Binding Get Invoked Twice?

The careful reader will have noticed that there is an action binding named findArrayOfYourStrings bound in both the binding container for CallFindStrings DataAction as well as for the StringResults DataPage. You might wonder, "Doesn't this mean that the method will get invoked twice?" It might appear that way, but here's an explanation of why it does not.

Clicking on the findArrayOfYourString action binding in the CallFindStringsUIModel binding container (as shown in Figure 47) and looking in the Property Inspector, you can see that it has a property named ResultLocation. The value of the ResultLocation property provides an EL expression, interpreted at runtime relative to the binding context, that indicates where the method result should be stored for reference by the forwarding action. For the findArrayOfStrings action binding, the default value setup by the design time was:

MyServiceDataControl.methodResults.MyServiceDataControl_dataProvider_findArrayOfYourStrings_result

This asks the ADF binding layer to store the result of the method invocation in the MyServiceDataControl's methodResults hashmap with a string key name of MyServiceDataControl_dataProvider_findArrayOfYourStrings_result. If you now look at the findArrayOfYourString action binding in the StringResultsUIModel binding container, you'll see in the Property Inspector that its ResultLocation property is the same expression. During the span of a single request, if a method iterator binding is executed by the binding layer, and it notices that its ResultLocation evaluates to a non-null object -- indicating that another action earlier in the page flow "chain" has already invoked the method and retrieved the result -- then the current iterator binding will use that existing method result. This avoids inadvertently executing the underlying service method multiple times.

Binding to Method Passing String and int Arguments

Now that we understand the basic ingredients in doing data binding to method results, we'll study two more examples to help cement the concepts in our minds. The one we'll examine in this section is comprised of:

The service method accepts four different arguments of type String and int and this time returns an array of MyType. The MyType class is a simple JavaBean we've created with name and age properties. We've created the AcceptMyTypeArgs.jsp, CallFindMyType DataAction, and MyTypeResults DataPage in an similar way to how we did it in the previous section.

When compared to the last example, the only two aspects that are interesting to focus on this time are the following:

Setting Up Method Argument EL Expressions for Array-Valued Parameters

A quick peek into the AcceptMyTypeArgs.jsp reveals that its HTML form contains four text input controls, two named "name" and two named "age" as I've hightlighted in Figure 50. When you submit an HTML form with repeating control names, you automatically get parameters with String[] values for any of the repeated ones. It follows that in this form, we'll be getting array-valued parameters named name and age, each with two values in its array. The paramValues keyword in EL allows us to work with these array-valued HTTP parameters, so we just need to setup the right EL expressions using it.

Repeating Form Parameter Names in AcceptMyTypeArgs.jsp
Figure 50: Repeating Form Parameter Names in AcceptMyTypeArgs.jsp

With the Struts page flow diagram active (or the struts-config.xml file selected in the Application Navigator) by selecting the CallFindMyType action mapping in the structure pane, we can expand it to see its custom properties related to method invocation. Clicking on its four paramNames[n] properties, we can see in the Property Inspector that they have the following values:

Parameter Name Parameter Value
paramNames[0] ${paramValues.name[0]}
paramNames[1] ${paramValues.age[0]}
paramNames[2] ${paramValues.name[1]}
paramNames[3] ${paramValues.age[1]}

Remembering that both the argument names and the parameter array values are zero-based, this means that we'll be passing the value of the first name and the first age to the first and second method arguments, and the value of the second name and the second age to the third and fourth method arguments.

JSTL Expressions for Collection of MyType Beans

When I dragged and dropped the return node beneath the findArrayOfMyType(String,int,String,int) operation in the Data Control Palette onto my empty MyTypeResultsUIModel.jsp page as a "Read-Only Table," it implicitly created me:

We've seen JSTL in various other examples in this paper, as well as the use of ADF Range Bindings like the findArrayOfMyType1 range binding in the MyTypeResultsUIModel, so the only thing interesting to point out in the MyTypeResultsUIModel.jsp page is the alternative syntax in EL for referring to the attributes of the MyType bean properties in the collection of results.

The following snippet from MyTypeResults.jsp shows a <c:forEach> loop that iterates the items in the current set of rows in the range for the range binding named findArrayOfMyType1. As the method returns a MyType[], each "row" in the resulting array has the name and age properties of the array's MyType instances. Each time through the loop, the Row variable allows us to access the values of the current MyType instances' bean properties.

<!-- Snippet from MyTypeResults.jsp -->
<c:forEach var="Row" items="${bindings.findArrayOfMyType1.rangeSet}">
  <tr>
    <td><c:out value="${Row['age']}"/>&nbsp;</td>
    <td><c:out value="${Row['name']}"/>&nbsp;</td>
  </tr>
</c:forEach>

We just saw the use of the array-index notation with a numerical index value in an expression like ${paramValues.name[0]}. This syntax allows numerical access to the Nth element in an array-valued property. Here, notice the Row['age'] and Row['name'] EL expressions. When the array index is a string, the index works as an equivalent to named property access. So, Row['age'] is equivalent to Row.age, the style of value-navigation syntax that we've seen previously in this paper.

It's good to be aware of the string-valued array-index notation in EL since provides flexibility that the dot-notation does not. Assuming you have a variable named myvar with the value "age", you could use the EL expression ${Row[myvar]} to refer to the value of the age property in the current row of a loop. Of course, it works outside of a <c:forEach> loop as well. If you were to try this using dot-notation like ${Row.myvar}, it would refer to a property with a name of myvar in the current row (which likely would not have a property named myvar), so the expression would evaluate to null. The array index can be any string-valued expression, too, not just a single variable name. So if variable foo had value "a" and variable bar had value "ge", then ${Row[foo+bar]} would again evaluate to the value of the age property in the current row, since the foo+bar index expression would evaluate to "a"+"ge" = "age".

Using Bean Data Control as Argument

The last example we'll study shows two interesting twists on the examples we've seen so far:

  1. We pass an argument of a structure type (MyType) instead of scalars
  2. We turn MyType into a DataControl itself to involve it in data binding during parameter collection

The example is comprised of the following elements:

The MyTypeResults2.jsp page is virtually identical to the MyTypeResults.jsp in the last example, so it's not interesting to spend any time examining it. In this scenario, we've opted for a parameter page implemented as an ADF DataPage AcceptMyTypeArgs2. It's underlying AcceptMyTypeArgs2.jsp page is similar to the AcceptMyTypeArgs.jsp we studied previously, except that:

I performed a Create Data Control on the MyType.java class in the Application Navigator to create the MyTypeDataControl. This allows us to do databinding against an instance of the MyType bean. After dragging and dropping the name and age properties of MyTypeDataControl as Input Fields from the Data Control Palette to the empty AcceptMyTypeArgs2.jsp page, I ended up with the UI Model shown in Figure 51. It contains an iterator binding named MyTypeDataControl_rootIterator for the singleton instance of the MyType bean, along with the control value bindings for its name and age properties.

UI Model Tab Showing Bindings for the AcceptMyTypeArgs2.jsp
Figure 51: UI Model Tab Showing Bindings for the AcceptMyTypeArgs2.jsp

When the AcceptMyTypeArgs2 DataPage posts back to itself, the ADF data binding layer (via the BindingContainerActionForm we learned about earlier) will update the name and age properties of the MyTypeDataControl's instance of the MyType bean with the corresponding values from the HTML form.

Next, notice that the (Submit) button on the page is named event_Call, so pressing this button will cause the Call event to fire. Since there is a Struts forward named "Call" between the AcceptMyTypeArgs2 DataPage and the CallFindMyType2 DataAction, it will automatically be used since its name matches the name of the event being handled. This means that in addition to updating the model values, when the user presses the (Submit) button it will also navigate to the CallFindMyType2 action to invoke the method, which in turn forwards to the MyTypeResults2 DataPage to show the user the results.

Figure 52 shows the UI model for the CallFindMyType2 DataAction. In addition to the findArrayOfMyType action binding that was created when I dragged and dropped the operation from the Data Control Palette to the DataAction, I also manually created an iterator binding (using the right-mouse in the structure pane's UI Model tab) for the root MyTypeDataControl. While I could name it anything, to keep with the same naming convention we've used in other places, I renamed it to MyTypeDataControl_rootIter.

UI Model Tab Showing Bindings for CallFindMyType2 DataAction
Figure 52: UI Model Tab Showing Bindings for CallFindMyType2 DataAction

Having this iterator allows me to write the EL expression:

${bindings.MyTypeDataControl_rootIter.currentRow.dataProvider}

as the value of the paramNames[0] Struts action property for the CallFindMyType2 action, which results in declaratively passing the instance of MyType represented by MyTypeDataControl as the first argument to the findArrayOfMyType method being called.

Running the ViewController project and clicking on the Results of Method Passing Bean Data Control as Bean Argument link, you can experiment with this functionality at runtime. If you submit a form with values like name="Steve", and age=36, you'll get back an array containing both a MyType for "Steve", as well as MyType instance for "Steve, Sr." who will be 20 years older. You can consult the code of findMyType in the MyService.java class to see why this is the expected result.

Why We Use Separate DataActions to Invoke Custom Methods

As you can see back in the page flow diagram from Figure 45, one detail common to the three page flows that bind to method results is that each uses a separate DataAction to perform the custom method invocation. That DataAction then forwards to the appropriate "results" DataPage to render the method results. After studying these three examples, you might be wondering:

"Could we have dragged and dropped the method to execute directly onto the DataPage that renders its results, eliminating an extra node in the Struts page flow diagram? Or do we have to use a use a separate DataAction to invoke the method?

It's a great question, and it's definitely worth understanding the details behind the answer.

Closer Look at the DataAction Lifecycle Methods

Understanding why we need to invoke the custom method in a separate DataAction, requires a closer study of the order in which the DataAction lifecycle methods occur: Table 1 highlights the key methods that comprise the lifecycle. Remember that it's during the prepareModel() phase when any top-level iterator bindings in the binding container will be executed if needed, a first time. The invokeCustomMethod() phase is when a custom method associated to the current data action gets executed. Typically, that custom method, if any, gets associated to the data action as a result of dragging it from the Data Control Palette and dropping it onto the DataAction or DataPage in question.

Table 1: Key DataAction Lifecycle Methods
Step Method Description
1 buildEventList() Builds the list of events that need to be handled during this request, if any.
2 prepareModel() Prepare the model for updates, calling refreshControl() on the binding container in the process, to execute any "top-level" iterators that haven't yet been executed. NOTE: If this results in the invocation of an action binding (as would be case for method iterators) the initializeMethodParameters() method is not called before invoking the action binding's doIt() method.
3 shouldAllowModelUpdate() Returns true if the current request should apply updates to the model based on parameters submitted in the request.
4 processUpdateModel() If model updates were allowed, applies updates to the model (via the bindings) based on parameters submitted in the request.
5 validateModelUpdates() If model updates were allowed, validates updates made to the model layer.
6 processComponentEvents() If no errors have been signalled yet, handles events in the event list. If this results in the invocation of an action binding, the initializeMethodParameters() method is called before invoking the action binding's doIt() method.
7 invokeCustomMethod() If no errors have been signalled yet, invokes the method associated with the current data action if any after first calling initializeMethodParameters() to allow any parameter setup required. Methods dragged and dropped onto data actions or data pages from the Data Control Palette are invoked during this phase.
8 refreshModel() If no errors have been signalled yet, notifies the model layer that we're done making changes.
9 reportErrors() Gives action a chance to process any errors that might have occurred during the request (always invoked!).
10 findForward() Selects the page forward to use for the "next page" in the flow. If a regular DataAction, the default is the "success" action forward. If a DataPage, the default is the "companion page" that goes with the current action.

Recall that in ADF, iterator bindings are the key to working with collections of bindable data from the model layer. The story is no different for data returned by a custom method. ADF provides a special kind of "method results iterator" that iterates the results returned from methods. In the Understanding Iterators Over Collection Valued Properties section, we learned that the prepareModel() phase of the data action lifecycle will cause iterators in the current binding container to be executed a first time if needed. We also learned that iterator bindings support an executeQuery() method that we can use to refresh their iterator's contents when we need to.

When the executeQuery() method on an iterator binding for database query results has gets called, it causes the underlying iterator's query to be (re)executed to get a fresh set of query results to iterate over. Similarly, when executeQuery() is called on a method results iterator, it turns around and (re)invokes underlying method action binding's doIt() method to get a fresh set of method results to iterator over. This method action binding, in turn, invokes the actual service method, passing the parameters from the action binding's parameters collection, and storing the result it its result property, as well as references to it in the ResultLocation indicated in ADF binding metadata.

The Problem

Notice that the prepareModel() phase of the lifecycle occurs before the processModelUpdates() and invokeCustomMethod() phases. This means that any iterators related to method results will be executed if needed during the prepareModel() phase, before the framework gets around to invoking them during the invokeCustomMethod() phase. During the invokeCustomMethod() phase, when the controller layer explicitly executes the method action bindings, it engages the overridable initializeMethodParameters() lifecycle method to default the method parameters by evaluating the declarative EL expression parameter metadata, as well as to allow you a place to change or augment how the method parameter values will be provided.

However, during the prepareModel() phase, since the method action binding is implicitly executed by the model layer, it has no way to engage this controller-layer method.

So, the crux of the problem of trying to combine:

in the same binding container for the same DataAction is that the prepareModel() phase will cause the binding layer to execute the service method without having gone through the controller-layer steps to initialize the values for any method arguments. Except for the case when your service method accepts no parameters, this sequence of operations will inevitably cause exceptions like IllegalArgumentException or NullPointerException.


NOTE:

The ADF action binding's parameter metadata does allow specifying default method parameter values by providing an optional binding-context-relative EL expression. You would them up by clicking on the parameter names beneath the action binding in the binding container and setting their value property in the Property Inspector, and the expressions should appear without the ${} wrapping characters. However, these expressions are only useful to refer to objects in the binding context. For example, to pass the current value of an attribute binding named Deptno in the binding container named SomePageUIModel, you could set the value of the parameter defintion to be the expression "SomePageUIModel.Deptno". These delcarative expressions on the method action binding parameters cannot currently reference objects from the HTTP Request environment like HTTP parameters and such.


The Solution

The solution to the problem is avoiding the implicit execution of the method iterator before having had a chance to setup the parameter values that need to be passed to the method. Separating the invocation of the custom method into a separate DataAction, that forwards to the "results" page whose binding container has the related method iterator over the results, is the declarative solution to this dilemma.

This separation solves the problem because the DataAction lifecycle occurs twice: one time during the processing of the "invoke the method" DataAction, and a second time during the processing of the "render the results" data page. The first time through the lifecycle, the binding container for the "invoke the method" data action does not contain any method iterator binding, only an action binding. As such, the prepareModel() phase does not cause any implicit method execution during that "run" through the lifecycle since there are no iterators to execute. The invokeCustomMethod() phase occurs, calling the initializeMethodParameters() DataAction method to set up the parameter values, so the invocation of the service method works as expected.

When the lifecycle phases repeat during the processing of the "render the results" data page, the prepareModel() fires and the method iterator binding is executed. Its underlying method action peeks at the object identified by the EL expression in its ResultLocation property, and notices that during the current request the result has already been retrieved. So, to avoid invoking the method a second time during the same request, it returns this existing result to the method iterator.

Getting More Clever with a Programmatic Approach

Of course, now that we understand the problem better and we are more familiar with the data action lifecycle methods, some readers might be curious to explore how to implement a programmatic solution to this issue that does not require the separated DataAction to perform the custom method invocation.

Since the problem of doing the declarative method invocation and the method iterator binding in the same binding container occurs during the prepareModel() phase, we could override prepareModel() in a custom data action class for our DataPage and write code that populates the method action's parameters before calling super.prepareModel(). For our example findArrayOfMyType(MyType) method above, that code might look like:

/* Overrides DataAction.prepareModel() */
protected void prepareModel(DataActionContext ctx) throws Exception {
  MyType emma = new MyType("Emma",8);
  ArrayList params = new ArrayList();
  params.add(emma);
  DCBindingContainer bc = ctx.getBindingContainer();
  JUCtrlActionBinding ab = (JUCtrlActionBinding)bc.findCtrlBinding("findArrayOfMyType");
  ab.setParams(params);
  super.prepareModel(ctx);
}

For the findArrayOfMyType(String,int,String,int) that we looked at earlier, the code would look like this:

/* Overrides DataAction.prepareModel() */
protected void prepareModel(DataActionContext ctx) throws Exception {
  ArrayList params = new ArrayList();
  params.add("Emma");
  params.add(new Integer(8));
  params.add("Amina");
  params.add(new Integer(6));
  DCBindingContainer bc = ctx.getBindingContainer();
  JUCtrlActionBinding ab = (JUCtrlActionBinding)bc.findCtrlBinding("findArrayOfMyType");
  ab.setParams(params);
  super.prepareModel(ctx);
}

If we wanted to allow the DataAction to perform its default behavior of evaluating EL expressions to setup the parameter value defaults (based on the values of the Struts action mapping paramNames[n] properties) we would simply need to override prepareModel() and call initializeMethodParameters(), passing the correct action binding to it. That code might look like this:

/* Overrides DataAction.prepareModel() */
protected void prepareModel(DataActionContext ctx) throws Exception {
  DCBindingContainer bc = ctx.getBindingContainer();
  JUCtrlActionBinding ab = (JUCtrlActionBinding)bc.findCtrlBinding("findArrayOfMyType");
  initializeMethodParameters(ctx,ab);
  super.prepareModel(ctx);
}

To combine the declarative EL-population of the parameters with (perhaps conditional) programmatic modification of some/all of them, you would just need to add a call to:

/* Get ordered list of parameter objects */
ArrayList params = ab.getParams();

or

/*
 * Get parameter objects in a map, keyed by their parameter name as defined
 * in the action binding parameter properties.
 */
Map paramMap = ab.getParamsMap();

after the call to initializeMethodParameters() and before the call to super.prepareModel().

Remember that the implicit iterator execution (including the method results iterators) only happens the first time the binding container is refreshed, after that you would need to explicitly call executeQuery() on the method iterator bindings inside your overridden prepareModel() method to force them to re-execute on each request if that behavior were desired. In contrast, using the "invoke the custom method in a separate DataAction" approach we looked at above, the custom method is invoked every time the action is used since that is an explicit method invocation, rather than the implicit method execution caused by the method iterator's execution.

With this under our belts, we have now completed our technical tour of all of the ADF data binding layer's key functionality, and now thoroughly understand the key technical details about how ADF binding and Struts complement each other through various features provided by the ADF DataAction.

ADF Data Binding Q & A

We close with a few final questions and answers on the new ADF data binding functionality.



Q: What Happens When I Upgrade My 9.0.3 Application?
A:

When you upgrade your existing JDeveloper9i applications from the 9.0.3 or 9.0.4 releases, the format of the *.jpr file is changed slightly, but no migration is performed on existing code. This means that your 9.0.3/9.0.4 applications continue to function using the 9.0.3-style direct-to-AppModule data binding that they always have. This style of data binding is still supported, but of course does not support the new back-end pluggability, cleaner view/model decoupling offered by the binding objects, or the seamless use of JSTL and EL.

You can continue to use the Component Palette and JSP tag insight that you always have used in JDeveloper 9.0.3 to enhance and extend these existing applications. In order to use the functionality of the binding containers, binding objects, and data controls you will need to begin using the new ADF Binding layer.

 
 
Q: Can I Use the JBO DataTags Library with ADF Binding?
A: No. The JBO DataTags Library continues to support the direct-to-AppModule 9.0.3-style data binding approach for upward compatibility, but the tag library does not support the new ADF Binding layer via binding containers and bindings.
 
 
Q: Can I Use ADF Binding in Model 1 JSP Applications?
A:

Sure. The ADF Binding architecture is cleanly separated from the Controller layer, so you can integrate ADF Binding into your Model 1 JSP applications. The new <adf:uimodelreference> tag allows you to initialize a binding container in a Model 1 JSP page. The syntax looks like:

<adf:uimodelreference model="BindingContainerName"/>

As is routine for Model 1 JSP applications, you need to write code (either in beans or directly in the JSP page) to handle UI events and engage the bindings in the binding container programmatically. ADF provides a simple way to do this. For each page where you need to provide custom "Model-1" style page handling code, you can extend the basic PageLifecycle class in the oracle.adf.controller.lifecycle package with a class of your own. Then, you add one extra parameter to the <adf:uimodelreference> tag like this:

<adf:uimodelreference model="BindingContainerName"
                      lifecycle="fully.qualified.LifeCycleClassName"/>

Your Model-1 style ADF application can override the same PageLifecycle methods that the Struts-specific DataAction class allows, so you don't need to learn two different programming paradigms.

 
 
Q: Are There Two Different Binding Styles for JClient Applications, too?
A:

Yes and no.

No, because the ADF Bindings architecture is based on a generalization of the existing data bindings architecture used by JClient in JDeveloper 9.0.3. In this way, JClient bindings are now a specialization of the generic ADF Bindings. JClient's existing FormBinding (a container of bindings related to a form/panel) has now been generalized into the BindingContainer concept. FormBinding is now a subclass of BindingContainer.

Yes, because JClient in 9.0.3 did not use the declarative XML approach to describing the bindings in its Form Binding binding container. All of its binding metadata was previously captured in the values of arguments passed to binding factory method calls in the jbInit() method of the panel. These methods both created a control binding, and if required an iterator binding, and placed the binding into the Form Binding binding container. While that style of creating the binding objects continues to work, new JClient data binding uses the consistent, metadata-driven approach provided by the binding container to setup the bindings for a panel.

 
 

Conclusion

Hopefully this article has helped shed some light on the new ADF Binding architecture, capabilities, and design time, as well as how it integrates with Apache Struts. As you begin to explore JDeveloper 10g, please send us your comments on the new features (positive or negative) using the OTN JDeveloper Discussion Forum. We highly value your feedback!

Related Documents

  1. ADF Binding Info Example Application [ADFBindingIntro.zip]

  2. Data Action Events Example Application [DataActionEventsExample.zip]

  3. Method Binding Example Application [MethodBindingExample.zip]

  4. Oracle Application Development Framework Overview [http://otn.oracle.com/products/jdev/collateral/papers/10g/ADF_overview.pdf]

  5. Oracle Application Development Framework [http://otn.oracle.com/products/jdev/collateral/papers/10g/ADFFAQ/index.html]

  6. Simplifying J2EE and EJB Development with BC4J Whitepaper [http://otn.oracle.com/products/jdev/htdocs/j2ee_bc4j.html]

  7. JSP Standard Tag Library [http://java.sun.com/webservices/docs/1.0/tutorial/doc/JSTL.html]

  8. Expression Language [http://java.sun.com/webservices/docs/1.0/tutorial/doc/JSTL4.html]

  9. JSP 2.0 Overview [http://java.sun.com/j2ee/1.4/docs/tutorial/doc/JSPIntro7.html]

  10. Vendors Voting for JSR 227 [http://web1.jcp.org/en/jsr/results?id=2045]

  11. JSR 227 Information [http://otn.oracle.com/products/jdev/htdocs/techinfo/jsr227.html]

  12. Java Community Process [http://www.jcp.org]

  13. Tutorials [http://otn.oracle.com/products/jdev/collateral/10gtutorials.html]

  14. BC4J Toy Store Demo [http://otn.oracle.com/sample_code/products/jdev/bc4jtoystore/content.html]

  15. Struts ForwardAction JavaDoc [http://jakarta.apache.org/struts/api/org/apache/struts/actions/ForwardAction.html]

  16. JSTL Quick Reference by Bill Siggelkow [http://www.jadecove.com/jstl-quick-reference.pdf]

  17. JSTL Quick Reference from Manning Publication's "JSTL in Action" Book [http://www.manning-source.com/books/bayern/bayern_apxA.pdf]

  18. Swing [http://java.sun.com/products/jfc/index.jsp]

  19. JavaServer Faces 1.0 [http://java.sun.com/j2ee/javaserverfaces/index.jsp]

  20. Struts DispatchAction JavaDoc [http://jakarta.apache.org/struts/api/org/apache/struts/actions/DispatchAction.html]

Revision History

Date Comments
September 15, 2003 Created
April 21, 2004 Updated for JDeveloper 10g Production
June 1st, 2004 Updated with Major New Sections on DataAction Event Handling and Method Results Binding
May 11th, 2005 Updated links to JSTL Reference, Added Related Documents Section
Jan 11th, 2006 Clarify the best practice of avoiding cross UIModel binding references.