Oracle ADF Data Binding Primer and ADF/Struts Overview
Author: Steve Muench, JDeveloper/ADF Development TeamAbstract
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
IntroductionIntroduction
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 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:
Data Controls
A data control abstracts the implementation of a business service, allowing the binding layer to access the data from all services in a consistent way.
Iterator Bindings and Control Bindings
Bindings are lightweight objects that decouple back-end data and front-end UI display. An iterator binding provides a consistent way to work with a collection of data objects supplied by a data control. Control bindings provide a standard interface for UI components to interact with an iterator's data or to invoke "action" methods for preparing model data and handling events. Bindings also expose key metadata to simplify building dynamic, multi-lingual user interfaces.
Binding Containers
A binding container is a named group of of related iterator and control bindings that you use together for a particular page (or panel) of your application. A binding container is also known as a "UI Model" since it provides the appropriate subset of model data for a specific UI.
Binding Context
The binding context provides the data environment for your application. It contains all of the data controls and binding containers that your application can access.
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:
Forum and Threads data
collections are supplied by the ForumService, while the
Teams data collection is supplied by the LookupCodes
service.
Three control bindings to support the UI elements on the page:
<c:forEach> tag to loop over a selected range of multiple
attributes from the Teams data
source.
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.

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.

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
|
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.

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.

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.

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.

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.

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
|
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.

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. |

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.

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.

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.

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).

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.

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

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.

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

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.

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.

| 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 |
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.

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:
DataAction, which
implements the basic page lifecycle for you.
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.

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.

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:
ADFBindingFilter is a servlet filter in the
oracle.adf.model.servlet package that now handles
initializing the binding context as well as resource cleanup as part of
end-of-request handling.
DataAction in the
oracle.adf.controller.struts.actions package implements a
pluggable request handling lifecycle that is ADF
binding-container-aware.
DataActionMapping action mapping class in the
oracle.adf.controller.struts.actions package extends the
basic Struts action mapping to support a number of custom action properties
related to ADF data binding.
BindingContainerActionForm in the
oracle.adf.controller.struts.forms exposes the data in the
binding container as a Struts
DynaActionForm.
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:
ActionForwardSince 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:
ShowEmpActionCentricActionShowEmpsTypesafeActionShowEmpsActionBindingActionextend 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:
ActionForward to decide where the flow of
control should go next, on to a page for rendering or on to another action.
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.

| NOTE: |
Similar to the
Struts |
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.

| 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. |

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:
create a binding in the current page's binding container that refers to the same iterator(s) and attributes and use the expression:
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
" |
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.

| 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:
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.

| NOTE: |
JDeveloper
knows which page to run when you run the |
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.

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:
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");
}
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");
}
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:

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:
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.
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(). |
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.

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:
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);
}
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);
}
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:
onEventname, instead of
Eventname.
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. |
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.
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.

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
|
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.

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:
One Iterator Binding
To reference the
singleton collection holding the root ExampleBean
instance.
One Control Value Binding
To support the data-bound HTML form field that
displays the current value of the value property of the
exampleBean business service property, and
Two Control Action Bindings
One each for the business service's increment() and
decrement() methods. This will allow us to declaratively
invoke these methods as part of our
implementation.
| NOTE: |
These two
|
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:
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".

This drag/drop operation resulted in the implicit creation of the:
DataPageDeclarativeUIModelExampleBeanDataControl_rootIter for the
ExampleBeanvalue for the value property of the
ExampleBean.
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.

This drag/drop
operation resulted in the creation of a new control action binding named
increment in the binding container.
Finally, I used the explicit approach to create a second action
binding for invoking the decrement method by doing the
following:
DataPageDeclarativeUIModel node and picked
Create Binding >
Actions > Action from
the right-mouse context menu.
ExampleBeanDataControl, picked the
decrement() method from the Select an
Action poplist, and clicked
(OK).
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.

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
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 " |
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:

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 Had it been important to keep the initial-capital event
names of |
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

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 |
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.

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.

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.

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:
name="event_Commit" and
value="Commit"name="event_Rollback" and
value="Rollback" 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:
For the existing (Commit) button, its:
name property to
"event_SaveAll"
value
property to "Save All Changes"
Matching action
binding's id property to "SaveAll"
For the existing (Rollback) button, its:
name property to
"event_CancelAll"
value
property to "Cancel Changes"
Matching action
binding's id property to
"CancelAll"
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.

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".

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...
public
void onYourEvent(DataActionContext ctx)
method in the data action class handling the request, it will be invoked to
handle the event with custom code.
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();
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:
CommissionService exposed a collection valued property named
availableCommissionPlans via its getter method
getAvailableCommissionPlans().
EmpServiceDataControl, being an ADF application
module, automatically exposes each of the named view object instances in its
data model as collection-valued properties of those same
names.
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:
getSomething()setSomething(Type
value) 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.

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.

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:
Used the JSP visual editor to:
return node as a "Read-Only Table" to the
empty page from the Data Control Palette.
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.

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:
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.
onRefresh() method with the appropriate signature for being
a DataAction event-handler method.
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:
AcceptStringsParams.jspString[]
findArrayOfYourStrings(String,String)StringResultsSetting 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:
AcceptStringsParams.jsp, using the "Page"
tool
CallFindStrings, using the "DataAction"
tool
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 |

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.

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.

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
|
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:
AcceptMyTypeArgs.jsp MyType[]
findArrayOfMyType(String,int,String,int)MyTypeResultsThe
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.

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:
findArrayOfMyType bound
to that method
findArrayOfMyTypeIterator to iterate the method
results
findArrayOfMyType1 to (related to the iterator) to handle
the table display of attributes from the method result, and expose
.
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']}"/> </td>
<td><c:out value="${Row['name']}"/> </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:
MyType) instead of
scalars
MyType
into a DataControl itself to involve it in data binding during parameter
collection
The example is comprised of the following elements:
AcceptMyTypeArgs2 DataPage
MyType[]
findArrayOfMyType(MyType)MyTypeResults2The 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:
name and age fields, it just has a single
name and age field.
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.

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.

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.
| 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
|
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
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
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
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 |
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
ADF Binding Info Example Application [ADFBindingIntro.zip]
Data Action Events Example Application [DataActionEventsExample.zip]
Method Binding Example Application [MethodBindingExample.zip]
Oracle Application Development Framework Overview [http://otn.oracle.com/products/jdev/collateral/papers/10g/ADF_overview.pdf]
Oracle Application Development Framework [http://otn.oracle.com/products/jdev/collateral/papers/10g/ADFFAQ/index.html]
Simplifying J2EE and EJB Development with BC4J Whitepaper [http://otn.oracle.com/products/jdev/htdocs/j2ee_bc4j.html]
JSP Standard Tag Library [http://java.sun.com/webservices/docs/1.0/tutorial/doc/JSTL.html]
Expression Language [http://java.sun.com/webservices/docs/1.0/tutorial/doc/JSTL4.html]
JSP 2.0 Overview [http://java.sun.com/j2ee/1.4/docs/tutorial/doc/JSPIntro7.html]
Vendors Voting for JSR 227 [http://web1.jcp.org/en/jsr/results?id=2045]
JSR 227 Information [http://otn.oracle.com/products/jdev/htdocs/techinfo/jsr227.html]
Java Community Process [http://www.jcp.org]
Tutorials [http://otn.oracle.com/products/jdev/collateral/10gtutorials.html]
BC4J Toy Store Demo [http://otn.oracle.com/sample_code/products/jdev/bc4jtoystore/content.html]
Struts ForwardAction JavaDoc [http://jakarta.apache.org/struts/api/org/apache/struts/actions/ForwardAction.html]
JSTL Quick Reference by Bill Siggelkow [http://www.jadecove.com/jstl-quick-reference.pdf]
JSTL Quick Reference from Manning Publication's "JSTL in Action" Book [http://www.manning-source.com/books/bayern/bayern_apxA.pdf]
JavaServer Faces 1.0 [http://java.sun.com/j2ee/javaserverfaces/index.jsp]
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. |