Skip Headers
Oracle® Application Development Framework Developer's Guide For Forms/4GL Developers
10g Release 3 (10.1.3.0)

Part Number B25947-02
Go to Documentation Home
Home
Go to Table of Contents
Contents
Go to Index
Index
Go to Feedback page
Contact Us

Go to previous page
Previous
Go to next page
Next
View PDF

10 Overview of Application Module Data Binding

Using the SRDemo application's SRService as an example, this chapter describes how an application module's active data model and business service interface methods appear at design time for drag and drop data binding and how they are accessible at runtime by the ADF Model data binding layer using the application module data control.

This chapter includes the following sections:

10.1 Overview of Data Controls and Declarative Bindings

The Oracle ADF Model layer is a declarative data binding facility. It implements the two concepts in the JSR-227 specification that enable decoupling the user interface technology from the business service implementation: data controls and declarative bindings. Data controls and declarative bindings enable a unified design time and runtime approach to bind any user interface to any backend business service without code.

10.1.1 Data Controls Abstract the Implementation Technology of a Business Service

Data controls abstract the implementation technology of a business service by using standard metadata interfaces to describe the service's operations and data collections. This includes information about the properties, methods, and types involved. At design time, visual tools like JDeveloper can leverage the standard service metadata to simplify binding UI components any data control operation or data collection. At runtime, the generic Oracle ADF Model layer reads the information describing your data controls and bindings from appropriate XML files and implements the two-way "wiring" that connects your user interface to your business service.

10.1.2 Bindings Connect UI Controls to Data Collections and Operations

Declarative bindings abstract the details of accessing data from data collections in a data control and of invoking its operations. There are three basic kinds of declarative binding objects that automate the key aspects of data binding that all enterprise applications require:

  • Iterator bindings, which bind to an iterator that tracks the current row in a data collection

  • Value bindings, which connect UI components to attributes in a data collection

  • Action bindings, which invoke custom or built-it operations on a data control or its data collections

Iterator bindings simplify building user interfaces that allow scrolling and paging through collections of data and drilling-down from summary to detail information. UI components that display data use value bindings. Value bindings range from the most basic variety that works with a simple text field to more sophisticated list, table, and tree bindings that support the additional needs of list, table, and tree UI controls. An action binding is used by UI components like hyperlinks or buttons to invoke methods. An action binding allows the user to click on the component to invoke a business service without code. There are two kinds of action bindings: a regular action binding that invokes a built-in operation, and a method action binding that invokes a custom operation.

Figure 10-1 Bindings Connect UI Components to Data Control Collections and Operations

Image shows UI data bindings flow

Note:

Value bindings are bindings that have a bound attribute value. All value bindings implement the oracle.binding.AttributeBinding interface. The interface for action bindings is oracle.binding.OperationBinding. Since both of these kinds of binding interfaces are related to UI controls, they both extend the oracle.binding.ControlBinding interface. The term control bindings is used in this guide to describe things that are common to both value bindings and action bindings.

10.2 Understanding the Application Module Data Control

The application module data control is one of the several data control implementations supplied with Oracle ADF. Its job is to be a thin adapter over an application module pool that automatically acquires an available application module instance at the beginning of the request. During the current request the application module data control holds a reference to the application module instance on behalf of the current user session. At the end of the request, the application module data control releases the application module instance back to the pool. Importantly, the application module component directly implements the interfaces that the binding objects expect for data collections, built-in operations, and service methods. This allows the bindings to work directly with the application modules and the view object instances in its active data model. Specifically, this optimized interaction allows:

Figure 10-2 illustrates the pool management role the application module data control plays and highlights the direct link between the bindings and the application module instance.

Figure 10-2 Bindings Connect Directly to View Objects and Methods of an Application Module from a Pool

Image of how bindings work with a pool

10.3 How an Application Module Appears in the Data Control Palette

At design time, you use the Data Control Palette to perform drag and drop data binding for JSF, JSP/Struts, and Swing applications. Each application module in the workspace appears automatically in the Data Control Palette. Using the SRService application module from the SRDemo application as an example this section highlights exactly what you'll see in the Data Control Palette when you work with your own application modules.

10.3.1 Overview of the SRService Application Module

Figure 10-3 shows the SRService application module that implements the business service layer of the SRDemo application. Notice that its data model includes numerous view object instances, including several master/detail hierarchies. The view layer of the demo consists of JSF pages whose UI components are bound to data from the view object instances in the SRService's active data model, and to built-in operations and service methods on its client interface. In Section 10.6, "Overview of How SRDemo Pages Use the SRService", you'll find an overview of exactly what aspects of this application module each page uses.

Figure 10-3 UML Diagram of the SRDemo Application's oracle.srdemo.model.SRService Application Module

Image of UML diagram for SRDemo

10.3.2 How to Change the Data Control Name Before You Begin Building Pages

By default, an application module will appear in the Data Control Palette as a data control named AppModuleNameDataControl. For example, the SRService originally had the name SRServiceDataControl. To change the default data control name to a shorter, or simply more preferable name, do the following:

To change the application module name:

  1. Open the application module in the Application Module Editor.

  2. Open the Custom Properties page.

  3. In the Name combobox, select the DATA_CONTROL_NAME property from the dropdown list.

  4. Enter your preferred data control name in the Value field and click OK to close the wizard.

Figure 10-4 shows the custom property setting that changed the data control name of the SRService from the default SRServiceDataControl to the shorter SRService name that matches the name of the application module. You'll notice the change immediately in the Data Control Palette.

Figure 10-4 Setting the Custom Application Module Property to Override the Data Control Name

Image of Custom Properties dialog

Note that as you begin to bind data from the application module to your application pages or panels, the data control name for your application module will appear in the DataBindings.cpx file in your user interface project and in each data binding page definition XML file. In addition, you might refer to the data control name in code when needing to work programmatically with the application module service interface. For this reason, if you plan to change the name of your application module, Oracle recommends doing this change before you being building your view layer.

Note:

In JDeveloper Release 3, if you decide to change the application module's data control name after you have already referenced it in one or more pages, you will need to open the page definition files where it is referenced and update the old name to the new name manually. Future releases of JDeveloper may extend its refactoring support to make renaming a data control simpler.

10.3.3 How the Data Model and Service Methods Appear in the Data Control Palette

Figure 10-5 illustrates how the Data Control Palette displays the view object instances in the SRService's active data model. Each view object instance appears as a named data collection whose name matches the view object instance name. Note the hierarchical structure of the data collections, and that for viewing simplicity, the figure omits some details in the tree that appear for each view object. These additional view object details are highlighted in Section 10.3.6, "How View Objects Appear in the Data Control Palette". The Data Control Palette reflects the master/detail hierarchies in your application module data model by displaying detail data collections nested under their master data collection.

The Data Control Palette also displays each custom method on the application module's client interface as a named data control custom operation whose name matches the method name. If a method accepts arguments, they appear in a Parameters folder as operation parameters nested inside the operation node.

Figure 10-5 How the Active Data Model Appears in the Data Control Palette

Image shows data model in data control palette

10.3.4 How to Change View Instance Names Before You Begin Building Pages

When you initially add a view object instance to the data model, if you haven't typed in an instance name yourself, it gets added with a name composed of the view object name with a numeric suffix appended. For example, adding an instance of a ServiceRequests view object to the data model, the default view object instance name would be ServiceRequests1. You can easily rename the view object instances in the Data Model page of the Application Module Editor.

As you begin to bind data from the data collections in the Data Control Palette to your application pages or panels, in addition to the data control name, the data collection names will be referenced in the page definition XML files used by the ADF Model data binding layer. Since the names of your view object instance in the application module data model are used as the names of these data collections, Oracle recommends reviewing your view object instance names before using them to build data bound pages to ensure the names are descriptive.

Note:

In JDeveloper Release 3, if you decide to change a view object instance name after you have already referenced it in one or more pages, you will need to open the page definition files where it is referenced and update the old name to the new name manually. Future releases of JDeveloper may extend its refactoring support to make renaming a view object instance simpler.

10.3.5 How Transaction Control Operations Appear in the Data Control Palette

The application module data control exposes two data control built-in operations named Commit and Rollback as shown in Figure 10-6. At runtime, when these operations are invoked by the data binding layer, they delegate to the commit() and rollback() methods of the Transaction object associated with the current application module instance. Note that the Operations folder in the data controls tree omits all of the data collections and custom operations for a more streamlined view.

Note:

In an application module with many view object instances and custom methods, you may need to scroll the Data Control Palette display to find the Operations folder that is the direct child node of the data control. This folder is the one that contains its built-in operations.

Figure 10-6 How Transaction Control Operations Appear in the Data Control Palette

Transaction control operations in data control palette

10.3.6 How View Objects Appear in the Data Control Palette

Figure 10-7 shows how each view object instance in the application module's data model appears in the Data Control Palette. The view object attributes are displayed as immediate child nodes of the corresponding data collection. If you have selected any custom methods to appear on the view object's client interface, like the performSearch() method in the figure, they appear as custom operations immediately following the view object attribute at the same level. If the method accepts arguments, these appear in a nested Parameters folder as operation parameters.

Figure 10-7 How View Objects Appear in the Data Control Palette

Image of view objects in Data Control Palette

10.3.6.1 Built-in Operations for View Object Data Collections

As shown in Figure 10-7, the Operations folder under the data collection displays all its available built-in operations. If an operation accepts one or more parameters, then they appear in a nested Parameters folder. At runtime, when one of these data collection operations is invoked by name by the data binding layer, the application module data control delegates the call to an appropriate method on the ViewObject interface to handle the built-in functionality. The built-in operations fall into three categories: operations that affect the current row, operations that refresh the data collection, and all other operations.

10.3.6.1.1 Operations that affect the current row
  • Create — creates a new row that becomes the current row

  • Delete — deletes the current row

  • First — sets the current row to the first row in the row set

  • Last — sets the current row to the last row in the row set

  • Previous — sets the current row to the previous row in the row set

  • Next — sets the row to the next row in the row set

  • Previous Set — navigates forward one full page of rows

  • Next Set — navigates backward one full page of rows

  • setCurrentRowWithKey — tries to finds a row using the serialized string representation of row key passed as a parameter. If found, it becomes the current row.

  • setCurrentRowWithKeyValue — tries to finds a row using the primary key attribute value passed as a parameter. If found, it becomes the current row.

10.3.6.1.2 Operations that refresh the data collection
  • Execute — refreshes the data collection by (re)executing the view object's query, leaving any bind parameters at their current values.

  • ExecuteWithParams — refreshes the data collection by first assigning new values to the named bind variables passed as parameters, then (re)executing the view object's query.

Note:

The ExecuteWithParams operation only appears for view objects that have defined one or more named bind variables at design time.
10.3.6.1.3 All other operations
  • removeRowWithKey — tries to finds a row using the serialized string representation of row key passed as a parameter. If found, the row is removed.

  • Find — toggles "Find Mode" on and off for data collection

10.3.7 How Nested Application Modules Appear in the Data Control Palette

If you build composite application modules, by including nested instances of other application modules, the Data Control Palette reflects this component assembly in the tree hierarchy. For example, assume that in addition to the SRDemo application's oracle.srdemo.model.SRService application module that you have also created the following application modules in the same package:

  • An application module named ProductService, and renamed its data control to ProductService

  • An application module named CompositeService, and renamed its data control to CompositeService

Then assume that you've added two view object instances named OtherViewObject and AnotherViewObject to the data model of CompositeService and that on Application Modules page of the Application Module Editor you have added an instance of the SRService application module and an instance of the ProductMaintenance application module to reuse them as part of CompositeService. Figure 10-8 illustrates how your CompositeService would appear in the Data Control Palette. The nested instances of SRService and ProductService appear in the palette tree display nested inside of the CompositeService data control. The entire data model and set of client methods that the nested application module instances expose to clients are automatically available as part of the CompositeService that reuses them.

One possibly confusing point is that even though you have reused nested instances of SRService and ProductService inside of CompositeService, the SRService and ProductService application modules also appear themselves as top-level data control nodes in the palette tree. JDeveloper assumes that you might want to sometimes use SRService or ProductService on their own as a separate data controls from CompositeService, so it displays all three of them. You need to be careful to perform your drag and drop data binding from the correct data control. If you want your page to use a view object instance from the nested SRService instance's data model that is an aggregated part of the CompositeService data control, then ensure you select the data collection that appears as part of the CompositeService data control node in the palette.

Figure 10-8 How Nested Application Modules Appear in the Data Control Palette

Image of nested application modules in Data Control Palette

It is important to do the drag and drop operation that corresponds to your intended usage. When you drop a data collection from the top-level SRService data control node in the palette, at runtime your page will use an instance of the SRService application module acquired from a pool of SRService components. When you drop a data collection from the nested instance of SRService that is part of CompositeService, at runtime your page will use an instance of the CompositeService application module acquired from a pool of CompositeService components. Since different types of application module data controls will have distinct transactions and database connections, inadvertently mixing and matching data collections from both a nested application module and a top-level data control will lead to unexpected runtime behavior. Forewarned is forearmed.

10.4 How to Add a Create Button on a Page

To add a Create button, drag and drop a Create operation for a data collection from the Data Control Palette onto a JSP page or Swing panel.

Note:

The built-in Create operation behaves differently in a web-based application than in a Swing-based desktop application. For web applications, depending on the situation, you may want to use CreateInsert instead of Create.

10.4.1 What Happens When You Drop a Create Button on a Web Page

If you drag a Create operation for a data collection from the Data Control Palette onto a JSP page — whether using JSF or not — you'll get a Create button on your page that is declaratively bound to the built-in Create operation. The Create operation creates a new row for the data collection, but does not insert it into the data collection's row set. By not inserting the new row into the row set at creation time, it helps avoid having an unwanted blank row appear in other pages should the user navigate away from your create page before actually entering any data for the new row. After you've invoked a Create operation, the iterator binding temporarily points to this new row as if it were the current row. When the user successfully submits data for the attributes in the new row, the new row gets inserted into the row set at that time. This is the declarative row creation approach that works best for most web application use cases.

10.4.2 What Happens When You Drop a Create Operation Onto a Swing Panel

If you drag a Create operation for a data collection from the Data Control Palette onto a Swing panel, you'll get a Create button on your panel that is declaratively bound to a built-in operation called CreateInsert instead. The CreateInsert operation creates a new row in the data collection and inserts that new row into the collection just before the current row. CreateInsert is the best approach for Swing applications.

10.4.3 When to Use CreateInsert Instead of Create

There are some situations in a web application where you need to use CreateInsert instead of Create. Use CreateInsert when creating:

  • An editable table control

  • A table with a single, current editable row

  • A Master/detail page and want the newly created master row to correctly show no existing detail rows

CreateInsert is used when a new row needs to be inserted into the row set. Since only the one Create operation shows in the Data Control Palette, to use a CreateInsert operation in a web application involves a couple of additional steps.

How to Change a Create Operation to CreateInsert:

  1. Drop the Create operation from the Data Control Palette onto your page

  2. Select the button in the visual editor and choose Edit Binding... from the context menu

  3. In the Action Binding Editor use the Select an Action dropdown list to change the action binding from using the Create to using CreateInsert instead.

10.4.4 What You May Need to Know About Create and CreateInsert

When you use the Create or CreateInsert operations to declaratively create a new row, under the covers they end up performing the following lines of code:

// create a new row for the view object
Row newRow = yourViewObject.createRow();
// mark the row as being "initialized", but not yet new
newRow.setNewRowState(Row.STATUS_INITIALIZED);

In addition, if you are using the CreateInsert operation, it performs the additional line of code to insert the row into the row set:

// insert the new row into the view object's default rowset
yourViewObject.insertRow(newRow);

When you create a row in an entity-based view object, the Transaction object associated to the current application module immediately takes note of the fact. The new entity row that gets created behind the view row is already part of the Transaction's list of pending changes. When a newly created row is marked as having the initialized state, it is removed from the Transaction's pending changes list and is considered a blank row in which the end user has not yet entered any data values. The term initialized is appropriate since the end user will see the new row initialized with any default values that the underlying entity object has defined. If the user never enters any data into any attribute of that initialized row, then it is as if the row never existed. At transaction commit time, since that row is not part of the Transaction's pending changes list, no INSERT statement will be attempted for it.

As soon as at least one attribute in an initialized row is set, it automatically transitions from the initialized status to the new status (Row.STATUS_NEW). At that time, the underlying entity row gets enrolled in the Transaction's list of pending changes, and the new row will be permanently saved the next time you commit the transaction.

Note:

If the end user performs steps while using your application that result in creating many initialized rows but never populating them, it might seem like a recipe for a slow memory leak. Not to worry, however. The memory used by an initialized row that never transitions to the new state will eventually be reclaimed by the Java virtual machine's garbage collector.

10.5 Application Module Databinding Tips and Techniques

{para}?>

10.5.1 How to Create a Record Status Display

When building pages you'll often want to display some kind of record status indicator like: "Record 5 of 25". If you display multiple rows on a page, then you may also want to display a variant like "Record 5-10 of 25". You can build a record indicator like this using simple text components, each of which displays an appropriate value from an iterator binding or table binding using an EL expression. The iterator binding's rangeSize property defines how many rows per page it makes available to display in the user interface. If your page definition contains either an iterator binding named SomeViewIter or a table binding named SomeView, you can reference the following EL expressions:

  • Number of Rows per Page

    #{bindings.SomeViewIter.rangeSize}

    #{bindings.SomeView.rangeSize}

  • Total Rows

    #{bindings.SomeViewIter.estimatedRowCount}

    #{bindings.SomeView.estimatedRowCount}

  • First Row on the Current Page

    #{bindings.SomeViewIter.rangeStart + 1}

    #{bindings.SomeView.rangeStart + 1}

  • Last Row on the Current Page

    #{bindings.SomeViewIter.rangeStart + bindings.SomeViewIter.rangeSize}

    #{bindings.SomeView.rangeStart + bindings.SomeView.rangeSize}

  • Current Row Number

    #{bindings.SomeViewIter.rangeStart + bindings.SomeViewIter.currentRowIndexInRange + 1}

    #{bindings.SomeView.currentRowIndex + 1}

10.5.2 How to Work with Named View Object Bind Variables

When a view object has named bind variables, an additional ExecuteWithParams operation appears in the corresponding data collection's Operations folder. As shown in Figure 10-9, this built-in operation accepts one parameter corresponding to each named bind variable. For example, the StaffListByEmailNameRole view object in the figure has four named bind variables — EmailAddress, Role, TheFirstName, and TheLastName — so the parameters appear for the ExecuteWithParams action having these same names. At runtime, when the ExecuteWithParams built-in operation is invoked by name by the data binding layer, each named bind variable value is set to the respective parameter value passed by the binding layer, and then the view object's query is executed to refresh its row set of results.

Figure 10-9 Work with Named Bind Variables Using Either a Built-in or Custom Operation

Image shows ways of working wtih named bind variables

An alternative approach, also shown in Figure 10-9, involves creating a custom view object "finder" method that accepts the arguments you want to allow the user to set and then including this method on the client interface of the view object in the View Object Editor. Example 10-1 shows what the code for such a custom method would look like. It is taken from the StaffListByEmailNameRole view object in the SRDemo application. Notice that due to the application module's active data model, the method does not need to return the data to the client. The method has a void return type, sets the bind variable values to the parameter values passed into the method using the generated bind variable accessor methods setEmailAddress(), setRole(), setTheFirstName(), and setTheLastName(). Then, it calls executeQuery() to execute the query to refresh the view object's row set of results.

Example 10-1 Custom View Object Method Sets Named Bind Variables and Executes Query

// From SRDemo Sample's StaffListByEmailNameRoleImpl.java
public void findStaffByEmailNameRole(String email,
                                     String firstName,
                                     String lastName,
                                     String role) 
{
  setEmailAddress(email);
  setRole(role);
  setTheFirstName(firstName);
  setTheLastName(lastName);
  executeQuery();
}

Both of these approaches accomplish the same end result. Both can be used with equal ease from the Data Control Palette to create a search form. The key differences between the approaches are the following:

Using the built-in ExecuteWithParams operation
  • You don't need to write code since it's a built-in feature.

  • You can drop the operation from the Data Control Palette to create an ADF Search Form to allow users to search the view object on the named bind variable values.

  • Your search fields corresponding to the named bind variables inherit bind variables UI control hints for their label text that you can define as part of the view object component.

Using the custom View Object "finder" operation
  • You need to write a little custom code, but you see a top-level operation in the Data Control Palette whose name can help clarify the intent of the find operation.

  • You can drop the custom operation from the Data Control Palette to create an ADF Search Form to allow users to search the view object on the parameter values.

  • Your search fields corresponding to the method arguments do not inherit label text UI control hints you define on the view object's named bind variables themselves. Instead, you need to define UI control hints for their label text by using the Properties... choice from the context menu of the page definition variable corresponding to the method argument as shown in Figure 10-10.

Figure 10-10 Accessing Properties of Page Definition Variables to Set UI Control Hints

Image of context menus in Application Navigator

In short, it's good to be aware of both approaches and you can decide for yourself which approach you prefer. As described in Section 10.6.5, "The SRStaffSearch Page", while the StaffListByEmailNameRole view object contains the above findStaffByEmailNameRole() custom method for educational purposes, the demo's JSF page uses the declarative ExecuteWithParams built-in action.

10.5.3 How to Use Find Mode to Implement Query-by-Example

In the ADF Model layer, an iterator binding supports a feature called "find mode" when working with data collections that support query-by-example. As cited in Section 5.8, "Filtering Results Using Query-By-Example View Criteria", view objects support query-by-example when view criteria row set of view criteria rows has been applied. The view criteria rows have the same structure as the rows in the view object, but the datatype of each attribute is String to allow criteria like "> 304" or "IN (314,326)" to be entered as search criteria.

In support of the find mode feature, an iterator binding has a boolean findMode property that defaults to false. As shown in Figure 10-11, when findMode is false the iterator binding points at the row set containing the rows of data in the data collection. In contrast, when you set findMode to true the iterator binding switches to point at the row set of view criteria rows.

Figure 10-11 When Find Mode is True, Iterator Binding Points at ViewCriteria Row Set Instead

Image describes ADF Find Mode feature

In the binding layer, action bindings that invoke built-in data control operations are associated to an iterator binding in the page definition metadata. This enables the binding layer to apply an operation like Create or Delete, for example, to the correct data collection at runtime. When an operation like Create or Delete is invoked for an iterator binding that has findMode set to false, these operations affect the row set containing the data. In contrast, if the operations are invoked for an iterator binding that has findMode set to true, then they affect the row set containing the view criteria row, effectively creating or deleting a row of query-by-example criteria.

The built-in Find operation allows you to toggle an iterator binding's findMode flag. If an iterator binding's findMode is false, invoking the Find operation for that iterator binding sets the flag to true. The UI components that are bound to attribute bindings related to this iterator switch accordingly to displaying the current view criteria row in the view criteria row set. If the user modifies the values of UI components while their related iterator is in find mode, the values are applied to the view criteria row in the view criteria row set. If you invoke the Execute or ExecuteWithParams built-in operation on an iterator that is in find mode, each will first toggle find mode to false, apply the find mode criteria, and refresh the data collection.

If an iterator binding's findMode is true invoking the Find operation sets it to false and removes the view criteria rows in the view criteria row set. This effectively cancels the query-by-example mode.

10.5.4 How to Customize the ADF Page Lifecycle to Work Programmatically with Bindings

The ADF Controller layer integrates the JSF page lifecycle with the ADF Model data binding layer. You can customize this integration either globally for your entire application, or on a per-page basis. This section highlights some tips related to how the SRDemo application illustrates using both of these techniques.

10.5.4.1 Globally Customizing the ADF Page Lifecycle

To globally customize the ADF Page Lifecycle, do the following:

  • Create a class that extends oracle.adf.controller.faces.lifecycle.FacesPageLifecycle

  • Create a class that extends ADFPhaseListener and overrides the createPageLifecycle() method to return an instance of your custom page lifecycle class.

  • Change your faces-config.xml file to use your subclass of ADFPhaseListener instead of the default ADFPhaseListener. As shown in Figure 10-12, you can do this on the Overview tab of the JDeveloper faces-config.xml editor, in the Life Cycle category.

Note:

Make sure to replace the existing ADFPhaseListener with your custom subclass of ADFPhaseListener, or everything in the JSF / ADF lifecycle coordination will happen twice!

Figure 10-12 Setting Up a Custom ADFPhaseListener To Install a Custom Page Lifecycle Globally

Image of faces-config.xml editor

The SRDemo application includes a SRDemoPageLifecycle class that globally overrides the reportErrors() method of the page lifecycle to change the default way that exceptions caught and cached by the ADF Model layer are reported to JSF. The changed implementation reduces the exceptions reported to the user to include only the exceptions that they can directly act upon, suppressing additional "wrapping" exceptions that will not make much sense to the end user.

10.5.4.2 Customizing the Page Lifecycle for a Single Page

You can customize the lifecycle of a single page setting the ControllerClass attribute of the pageDefinition to identify a class that either:

  • Extends oracle.adf.controller.v2.PageController class

  • Implements oracle.adf.controller.v2.PagePhaseListener interface

The value of the page definition's ControllerClass attribute can either be:

  • A fully qualified class name

  • An EL expression that resolves to a class that meets the requirements above

Using an EL expression for the value of the ControllerClass, it is possible to specify the name of a custom page controller class (or page phase listener implementation) that you've configured as a managed bean in the faces-config.xml file. This includes a backing bean for a JSF page, provided that it either extended PageController or implements PagePhaseListener.

Figure 10-13 illustrates how to select the root node of the page definition in the Structure window to set.

Figure 10-13 Setting the ControllerClass of a Page Definition

Image of setting a controller class in Property Inspector

Note:

When using an EL expression for the value of the ControllerClass attribute, the Structure window may show a warning, saying the "#{YourExpression}" is not a valid class. You can safely ignore this warning.

10.5.4.3 Using Custom ADF Page Lifecycle to Invoke an onPageLoad Backing Bean Method

The SRDemo application contains a OnPageLoadBackingBeanBase class in the oracle.srdemo.view.util package that implements the PagePhaseListener interface described above using code like what's shown in Example 10-2. The class implements the interface's beforePhase() and afterPhase() methods so that in invokes an onPageLoad() method before the normal ADF prepare model phase, and an onPagePreRender() method after the normal ADF prepare render phase.

Example 10-2 PagePhaseListener to Invoke an onPageLoad() and onPagePreRender() Method

// In class oracle.srdemo.view.util.OnPageLoadBackingBeanBase
/**
 * Before the ADF page lifecycle's prepareModel phase, invoke a
 * custom onPageLoad() method. Subclasses override the onPageLoad()
 * to do something interesting during this event.
 * @param event
 */
public void beforePhase(PagePhaseEvent event) {
  FacesPageLifecycleContext ctx =
    (FacesPageLifecycleContext)event.getLifecycleContext();
  if (event.getPhaseId() == Lifecycle.PREPARE_MODEL_ID) {
    bc = ctx.getBindingContainer();
    onPageLoad();
    bc = null;
  }
}
/**
 * After the ADF page lifecycle's prepareRender phase, invoke a
 * custom onPagePreRender() method. Subclasses override the onPagePreRender()
 * to do something interesting during this event.
 * @param event
 */    
public void afterPhase(PagePhaseEvent event) {
  FacesPageLifecycleContext ctx =
   (FacesPageLifecycleContext)event.getLifecycleContext();
  if (event.getPhaseId() == Lifecycle.PREPARE_RENDER_ID) {
    bc = ctx.getBindingContainer();
    onPagePreRender();
    bc = null;
  }
}
public void onPageLoad() {
  // Subclasses can override this.
}
public void onPagePreRender() {
  // Subclasses can override this.
}

If a managed bean extends the OnPageLoadBackingBeanBase class, then it can be used as an ADF page phase listener because it inherits the implementation of this interface from the base class. If that backing bean then overrides either or both of the onPageLoad() or onPagePreRender() method, that method will be invoked by the ADF Page Lifecycle at the appropriate time during the page request lifecycle. The last step in getting such a backing bean to work, is to tell the ADF page definition to use the backing bean as its page controller for that page. As described above, this is done by setting the ControllerClass attribute on the page definition in question to an EL expression that evaluates to the backing bean.

The SRMain page in the SRDemo application uses the technique described in this section to illustrate writing programmatic code in the onPageLoad() method of the SRMain backing bean in the oracle.srdemo.view.backing package. Since that backing bean is named backing_SRMain in faces-config.xml, the ControllerClass property of the SRMain page's page definition is set to the EL expression "#{backing_SRMain}".

10.5.5 How to Use Refresh Correctly for InvokeAction and Iterator Bindings

Figure 10-14 illustrates how the JSF page lifecycle relates to the extended page processing phases that the ADF page lifecycle adds. You can use the Refresh property on iterator bindings and invokeAction executables in your page definition to control when each are evaluated during the ADF page lifecycle, either during the prepareModel phase, the prepareRender phase, or both. Since the term "refresh" isn't exactly second-nature, this section clarifies what it means for each kind of executable and how you should set their Refresh property correctly to achieve the behavior you need.

Figure 10-14 How JSF Page Lifecycle and ADF Page Lifecycle Phases Relate

Image shows relationships between JSF and ADF lifecycles

10.5.5.1 Correctly Configuring the Refresh Property of Iterator Bindings

In practice, when working with iterator bindings for view object instances in an application module, you can simply leave the default setting of Refresh="ifNeeded". You may complement this with a boolean-valued EL expression in the RefreshCondition property to conditionally avoid refreshing the iterator if desired. However, you may still be asking yourself, "What does refreshing an iterator binding mean anyway?"

An iterator binding, as its name implies, is a binding that points to an iterator. For scalability reasons, at runtime the iterator bindings in the binding container release any reference they have to a row set iterator at the end of each request. During the next request, the iterator bindings are refreshed to again point at a "live" row set iterator that is tracking the current row of some data collection. The act of refreshing an ADF iterator binding during the ADF page lifecycle is precisely the operation of accessing the row set iterator to "reunite" the binding to the row set iterator to which it is bound.

If an iterator binding is not refreshed during the lifecycle, it is not pointing to any row set iterator for that request. This results in the value bindings related to that iterator binding not having any data to display. This can be a desirable result, for example, if you want a page like a search page to initially show no data. To achieve this, you can use the RefreshCondition of:

#{adfFacesContext.postback == true}

The adfFacesContext.postback boolean property evaluates to false when a page is first rendered, or rendered due to navigation from another page. It evaluates to true when the end user has interacted with some UI control on the page, causing a postback to that page to handle the event. By using this expression as the RefreshCondition for a particular iterator binding, it will refresh the iterator binding only when the user interacts with a control on the page.

The valid values for the Refresh property of an iterator binding are as follows. If you want to refresh the iterator during:

  • Both the prepareModel and prepareRender phases, use Refresh="ifNeeded" (default)

  • Just during the prepareModel phase, use Refresh="prepareModel"

  • Just during the prepareRender phase, use Refresh="renderModel"

If you only want the iterator binding to refresh when your own code calls getRowSetIterator() on the iterator binding, set Refresh="never". Other values of Refresh are either not relevant to iterator bindings, or reserved for future use.

10.5.5.2 Refreshing an Iterator Binding Does Not Forcibly Re-Execute Query

It is important to understand that when working with iterator bindings related to a view object instance in an application module, refreshing an iterator binding does not forcibly re-execute its query each time. The first time the view object instance's row set iterator is accessed during a particular user's unit of work, this will implicitly execute the view object's query if it was not already executed. Subsequent refreshing of the iterator binding related to that view object instance on page requests that are part of the same logical unit of work will only access the row set iterator again, not forcibly re-execute the query. Should you desire re-executing the query to refresh its data, use the Execute or ExecuteWithParams built-in operation, or programmatically call the executeQuery() method on the iterator binding.

10.5.5.3 Correctly Configuring Refresh Property of InvokeAction Executables

Several page definitions in the SRDemo application use the declarative invokeAction binding to trigger either built-in operations or custom operations during this extended ADF page processing lifecycle. Each invokeAction has an id property that give the binding a name, and then three other properties of interest:

  • The Binds property controls what the invokeAction will do if it fires

    Its value is the name of an action binding or method action binding in the same binding container.

  • The Refresh property controls when the invokeAction will invoke the action binding

    To have it fire during the ADF page lifecycle's:

    • prepareModel phase, use Refresh=prepareModel

    • prepareRender phase, use Refresh=renderModel

    • prepareModel and prepareRender phases, use Refresh=ifNeeded

  • The RefreshCondition property can be used to control whether it will fire at all

    Its value, if supplied, is a boolean-valued EL expression. If the expression evaluates to true when the invokeAction is considered during the page lifecycle, the related action binding is invoked. If it evaluates to false, then the action binding is not invoked.

Note:

Other values of the Refresh property not described here are reserved for future use.

Notice in Figure 10-14 that the key distinction between the ADF prepareModel phase and the prepareRender phase is that one comes before JSF's invokeApplication phase, and one after. Since JSF's invokeApplication phase is when action listeners fire, if you need your invokeAction to trigger after these action listeners have performed their processing, you'll want to use the Refresh="renderModel" setting on it.

If the invokeAction binds to a method action binding that accepts parameters, then two additional values can be supplied for the Refresh property: prepareModelIfNeeded and renderModelIfNeeded. These have the same meaning as their companion settings without the *IfNeeded suffix, except that they perform an optimization to compare the current set of evaluated parameter values with the set that was used to invoke the method action binding the previous time. If the parameter values for the current invocation are exactly the same as the ones used previously, the invokeAction does not invoke its bound method action binding.

Note:

The default value of the Refresh property is ifNeeded. This means that if you do not supply a RefreshCondition expression to further refine its firing, the related action binding will be invoked twice during each request. Oracle recommends either adding an appropriate RefreshCondition expression (if you want it evaluated during both phases) or changing the default Refresh setting for invokeAction bindings to either prepareModel or renderModel, depending on whether you want your invokeAction to occur before or after the JSF invokeApplication phase.

10.5.6 Understanding the Difference Between setCurrentRowWithKey and setCurrentRowWithKeyValue

You can call the getKey() method on any view row get a Key object that encapsulates the one or more key attributes that identify the row. As you've seen in various examples, you can also use a Key object like this to find a view row in a row set using the findByKey(). At runtime, when either the setCurrentRowWithKey or the setCurrentRowWithKeyValue built-in operations is invoked by name by the data binding layer, the findByKey() method is used to find the row based on the value passed in as a parameter before setting the found row as the current row.

Confusingly, as shown in Figure 10-15, the setCurrentRowWithKey and setCurrentRowWithKeyValue operations both expect a parameter named rowKey, but they differ precisely by what each expects that rowKey parameter value to be at runtime:

setCurrentRowWithKey

setCurrentRowWithKey expects the rowKey parameter value to be the serialized string representation of a view row key. This is a hexadecimal-encoded string that looks like this:

000200000002C20200000002C102000000010000010A5AB7DAD9

The serialized string representation of a key encodes all of the key attributes that might comprise a view row's key in a way that can be conveniently passed as a single value in a browser URL string or form parameter. At runtime, if you inadvertently pass a parameter value that is not a legal serialized string key, you may receive exceptions like oracle.jbo.InvalidParamException or java.io.EOFException as a result. In your web page, you can access the value of the serialized string key of a row by referencing the rowKeyStr property of an ADF control binding (e.g. #{bindings.SomeAttrName.rowKeyStr}) or the row variable of an ADF Faces table (e.g. #{row.rowKeyStr}).

setCurrentRowWithKeyValue

setCurrentRowWithKeyValue expects the rowKey parameter value to be the literal value representing the key of the view row. For example, it's value would be simply "201" to find service request number 201.

Figure 10-15 The setCurrentRowWithKeyValue Operation Expects a Literal Attribute Value as the Key

Image shows possible confusion with key values

Note:

If you write custom code in an application module class and need to find a Row based on a serialized string key passed from the client, you can use the getRowFromKey() method in the JboUtil class in the oracle.jbo.client package:
static public Row getRowFromKey(RowSetIterator rsi, String sKey)

Pass the view object instance in which you'd like to find the row as the first parameter, and the serialized string format of the Key as the second parameter.

10.5.7 Understanding Bundled Exception Mode

An application module provides a feature called bundled exception mode which allows web applications to easily present a maximal set of failed validation exceptions to the end user, instead of presenting only the first error that gets raised. By default, the ADF Business Components application module pool enables bundled exception mode for web applications.

You typically will not need to change this default setting. However it is important to understand that it is enabled by default since it effects how validation exceptions are thrown. Since the Java language and runtime only support throwing a single exception object, the way that bundled validation exceptions are implemented is by wrapping a set of exceptions as details of a new "parent" exception that contains them. For example, if multiple attributes in a single entity object fail attribute-level validation, then these multiple ValidationException objects will be wrapped in a RowValException. This wrapping exception contains the row key of the row that has failed validation. At transaction commit time, if multiple rows do not successfully pass the validation performed during commit, then all of the RowValException objects will get wrapped in an enclosing TxnValException object.

When writing custom error processing code, as illustrated by the overridden reportErrors() method in the SRDemoPageLifecycle class in the SRDemo application, you can use the getDetails() method of the JboException base exception class to recursively process the bundled exceptions contained inside it.

Note:

All the exception classes mentioned here are in the oracle.jbo package.

10.6 Overview of How SRDemo Pages Use the SRService

This section provides a brief overview of how each page in the SRDemo application uses the SRService application module's view object instances and service methods.

10.6.1 The SRList Page

10.6.1.1 Overview of Data Binding in the SRList Page

Figure 10-16 illustrates the data binding for SRList page.

Iterator Bindings to View Object Instances

ServiceRequestsByStatusIterator for ServiceRequestsByStatus view object instance

Page Definition Variables

None

Action Bindings to Built-in Operations

setCurrentRowWithKey, ExecuteWithParams related to the ServiceRequestsByStatusIterator iterator binding

Method Action Bindings to Custom Operations

None

InvokeActions Customizing Page Lifecycle

None

Figure 10-16 View Object for SRList Page

Image of ViewObject for SRList Page

10.6.1.2 Business Service Notes for the SRList Page

View Object Instances

ServiceRequestsByStatus is an instance of the entity-based view object ServiceRequestsByStatus, which extends the ServiceRequests view object and adds a named bind variable called StatusCode.

Application Module Custom Methods

None

10.6.2 The SRMain Page

10.6.2.1 Overview of Data Binding in the SRMain Page

Figure 10-17 illustrates the data binding for the SRMain page.

Iterator Bindings to View Object Instances
  • ServiceRequestsIterator for ServiceRequests view object instance

  • ServiceHistoriesIterator for ServiceHistories view object instance

Page Definition Variables

None

Action Bindings to Built-in Operations
  • setCurrentRowWithKey, Delete related to the ServiceRequestsIterator iterator binding

  • Create, DeleteNewHistory (for Delete built-in) related to the ServiceHistoriesIterator iterator binding

  • Commit related to the SRService data control

Method Action Bindings to Custom Operations

deleteServiceHistoryNotes invokes deleteServiceHistoryNotes() method on the SRService client interface

InvokeActions Customizing Page Lifecycle

None

Note:

The SRMain backing bean for the SRMain page (in the oracle.srdemo.view.backing package) is using the technique described in Section 10.5.4.3, "Using Custom ADF Page Lifecycle to Invoke an onPageLoad Backing Bean Method" to programmatically accomplish exactly the same thing that the SREdit page is doing declaratively.

Figure 10-17 Service Method and View Objects for the SRManage Page

Service method and View Objects for the SRManage Page

10.6.2.2 Business Service Notes for the SRMain Page

View Object Instances
  • ServiceRequests is an instance of the entity-based view object ServiceRequests. It joins data from the main ServiceRequest entity usage and three additional reference entity usages: CreatedByUser (User entity object), AssignedToUser (User entity object), and Product. The ServiceRequests view object is linked master/detail to the ServiceHistories view object.

  • ServiceHistories is an instance of the entity-based view object ServiceHistories. It joins data from the main ServiceHistory entity usage and an additional reference entity usage SystemUser (User entity object). It is an XML-only view object, with no custom Java class.

Application Module Custom Methods

As shown in Example 10-3, the deleteServiceHistoryNotes() method deletes service history note rows corresponding to the Key objects in the key set passed as an argument.

Example 10-3 DeleteServiceHistoryNotes Method in SRServiceImpl.java

public void deleteServiceHistoryNotes(Set keySet) {
  if (keySet != null) {
    ViewObject histories = getServiceHistories();
    for (Key k: (Set<Key>)keySet) {
      Row[] rowToDelete = histories.findByKey(k, 1);
      if (rowToDelete == null || rowToDelete.length == 0) {
        throw new JboException("Failed to find row with serialized key" + 
                               k.toStringFormat(false));
      }
      rowToDelete[0].remove();
      getDBTransaction().commit();
    }
  }
}

10.6.3 The SREdit Page

10.6.3.1 Overview of Data Binding in the SREdit Page

Figure 10-18 illustrates the data binding in the SREdit page.

Iterator Bindings to View Object Instances
  • ServiceRequestsIterator for ServiceRequests view object instance

  • ServiceRequestStatusListIterator for ServiceRequestStatusList view object instance

Page Definition Variables

None

Action Bindings to Built-in Operations
  • setCurrentRowWithKey related to the ServiceRequestsIterator iterator binding

  • Commit related to the SRService data control

Method Action Bindings to Custom Operations

cancelEditsToCurrentServiceRequest invokes cancelEditsToCurrentServiceRequest() on the SRService client interface

InvokeActions Customizing Page Lifecycle

setRowToEditFromRequestParameter invokes the built-in setCurrentRowWithKey operation in prepare model phase (Refresh="prepareModel") when first navigating to the page (i.e. not processing a postback) and processScope.rowKeyStr attribute is not set (RefreshCondition="${adfFacesContext.postback == false and not empty processScope.rowKeyStr}").

Figure 10-18 Service Method and View Objects for the SREdit Page

Service Method and View Objects for the SREdit Page

10.6.3.2 Business Service Notes for the SREdit Page

View Object Instances
  • ServiceRequests is an instance of the entity-based view object ServiceRequests. It joins data from the main ServiceRequest entity usage and three additional reference entity usages: CreatedByUser (User entity object), AssignedToUser (User entity object), and Product.

  • ServiceRequestStatusList is an instance of the read-only view object ServiceRequestStatusList. Its data is provided by a static list supplied in the ServiceRequestStatusListImpl.java class. This class extends a SRStaticDataViewObjectImpl in the FrameworkExtensions project which provides the basic support for implementing a view object based on static data.

Application Module Custom Methods

As shown in Example 10-4, the cancelEditsToCurrentServiceRequest() method uses the refresh() method to cancel edits made in the current transaction to the current service request row.

Example 10-4 CancelEditsToCurrentServiceRequest Method in SRServiceImpl.java

public void cancelEditsToCurrentServiceRequest() {
  Row svReq = getServiceRequests().getCurrentRow();
  if (svReq != null) {
    svReq.refresh(Row.REFRESH_WITH_DB_FORGET_CHANGES);
  }
}

10.6.4 The SRSearch Page

10.6.4.1 Overview of Data Binding in the SRSearch Page

Figure 10-19 illustrates the data binding in the SRSearch page.

Iterator Bindings to View Object Instances
  • SearchServiceRequestsIterator for SearchServiceRequests view object instance (Forced to stay in find mode by AlwaysFind invokeAction).

  • SearchServiceRequestsResultsIterator for SearchServiceRequests view object instance

  • ServiceRequestStatusListIterator for ServiceRequestStatusList view object instance

Page Definition Variables

None

Action Bindings to Built-in Operations
  • Execute, Delete, Create related to the SearchServiceRequestsIterator iterator binding

  • Find, First, Next, Previous, Last, setCurrentRowWithKey related to the SearchServiceRequestsResultsIterator iterator binding

Method Action Bindings to Custom Operations

None

InvokeActions Customizing Page Lifecycle
  • AlwaysFind invokes the built-in Find operation (for the SearchServiceRequestsIterator iterator binding) in either prepare model or render model phases (Refresh="ifNeeded") when the SearchServiceRequestsIterator is not in find mode ( ${bindings.SearchServiceRequestsIterator.findMode == false})

  • insertBlankViewCriteriaRowIfThereAreNone invokes the built-in Create operation in either prepare model or render model phases (Refresh="ifNeeded") when the SearchServiceRequestsIterator is not in find mode ( ${bindings.SearchServiceRequestsIterator.findMode == false})

Figure 10-19 View Object for the SRSearch Page

Image shows view object for the SRSearch page

10.6.4.2 Business Service Notes for the SRSearch Page

View Object Instances
  • SearchServiceRequests is an instance of the entity-based view object ServiceRequests. It joins data from the main ServiceRequest entity usage and three additional reference entity usages: CreatedByUser (User entity object), AssignedToUser (User entity object), and Product.

  • ServiceRequestStatusList is an instance of the read-only view object ServiceRequestStatusList. Its data is provided by a static list supplied in the ServiceRequestStatusListImpl.java class. This class extends a SRStaticDataViewObjectImpl in the FrameworkExtensions project which provides the basic support for implementing a view object based on static data.

Application Module Custom Methods

None

10.6.5 The SRStaffSearch Page

10.6.5.1 Overview of Data Binding in the SRStaffSearch Page

Figure 10-20 illustrates the data binding for the SRStaffSearch page.

Iterator Bindings to View Object Instances

StaffListByEmailNameRoleIterator for StaffListByEmailNameRole view object instance

Page Definition Variables

StaffListByEmailNameRole_Role, StaffListByEmailNameRole_EmailAddress, StaffListByEmailNameRole_TheFirstName, StaffListByEmailNameRole_TheLastName

Action Bindings to Built-in Operations

ExecuteWithParams related to StaffListByEmailNameRoleIterator iterator binding

Method Action Bindings to Custom Operations

None

InvokeActions Customizing Page Lifecycle

None

Figure 10-20 View Objects for the SRStaffSearch Page

Image shows view objects for the SRStaffSearch page

10.6.5.2 Business Service Notes for the SRStaffSearch Page

View Object Instances

StaffListByEmailNameRole is an instance of the entity-based view object StaffListByEmailNameRole, which extends the StaffList view object and adds name bind variables EmailAddress, Role, TheFirstName, and TheLastName.

Application Module Custom Methods

None

10.6.6 The SRManage Page

10.6.6.1 Overview of Data Binding in the SRManage Page

Figure 10-21 illustrates the data binding in the SRManage page.

Iterator Bindings to View Object Instances
  • StaffWithOpenRequestsIterator for StaffWithOpenRequests view object instance

  • ExpertiseAreasIterator for ExpertiseAreas view object instance

  • OpenOrPendingServiceRequestsIterator for OpenOrPendingServiceRequests view object instance

  • ServiceHistoriesForRequestIterator for ServiceHistoriesForRequest view object instance

Page Definition Variables

None

Action Bindings to Built-in Operations

setCurrentStaffRowWithKey (for setCurrentRowWithKey built-in) related to StaffWithOpenRequestsIterator iterator binding

Method Action Bindings to Custom Operations

setCurrentProblemAndAssigneeRows invokes setCurrentProblemAndAssigneeRows() on the SRService client interface

InvokeActions Customizing Page Lifecycle

None

Figure 10-21 Service Method and View Objects for the SRManage Page

Service Method and View Objects for the SRManage Page

10.6.6.2 Business Service Notes for the SRManage Page

View Object Instances
  • StaffWithOpenRequests is an instance of the read-only view object StaffWithOpenRequests. It queries information from the USERS and SERVICE_REQUESTS tables. It is an XML only view object, with no related Java class. This view object is linked master/detail with the ExpertiseAreas and OpenOrPendingServiceRequests view objects.

  • ExpertiseAreas is an instance of the entity-based view object ExpertiseAreas. It joins information from the primary ExpertiseArea entity usage and a reference Product entity usage. It is an XML only view object, with no related Java class.

  • OpenOrPendingServiceRequests is an instance of the read-only view object OpenOrPendingServiceRequests. It queries information from the SERVICE_REQUESTS table. It is an XML only view object, with no related Java class. This view object is linked master/detail with the ServiceHistories view object.

  • ServiceHistoriesForRequest is an instance of the entity-based view object ServiceHistories. It joins data from the main ServiceHistory entity usage and an additional reference entity usage SystemUser (User entity object). It is an XML-only view object, with no custom Java class.

Application Module Custom Methods

As shown in Example 10-5, the setCurrentProblemAndAssigneeRows() method uses a helper method to set the current row in the StaffWithOpenRequests view object instance and the OpenOrPendingServiceRequests view object instance based on the serialized string keys passed in.

Example 10-5 SetCurrentProblemAndAssigneeRows Method in SRServiceImpl.java

public void setCurrentProblemAndAssigneeRows(String requestKeyStr, 
                                               String staffKeyStr) {
    setRowWithKeyString(getStaffWithOpenRequests(), staffKeyStr);
    setRowWithKeyString(getOpenOrPendingServiceRequests(), requestKeyStr);
  }

10.6.7 The SRSkills Page

10.6.7.1 Overview of Data Binding in the SRSkills Page

Figure 10-21 illustrates the data binding for the SRSkills page.

Iterator Bindings to View Object Instances
  • StaffListIterator for StaffList view object instance

  • StaffExpertiseAreasIterator for StaffExpertiseAreas view object instance

  • ProductListIterator for ProductList view object instance

Page Definition Variables

None

Action Bindings to Built-in Operations

None

Method Action Bindings to Custom Operations

updateSkillsForCurrentStaff invokes the updateSkillsForCurrentStaff() method on the SRService client interface

InvokeActions Customizing Page Lifecycle

None

Figure 10-22 Service Method and View Objects for the SRSkills Page

Service Method and View Objects for the SRSkills Page

10.6.7.2 Business Service Notes for the SRSkills Page

View Object Instances
  • StaffList is an instance of the entity-based view object StaffList. It queries information from the primary entity usage SystemUser (User entity object). This view object is linked master/detail with the ExpertiseAreas view object.

  • StaffExpertiseAreas is an instance of the entity-based view object ExpertiseAreas. It joins information from the primary ExpertiseArea entity usage and a reference Product entity usage. It is an XML only view object, with no related Java class.

  • ProductList is an instance of the entity-based view object ProductList. It queries data from the primary entity usage Products (Product entity object). It is an XML only view object, with no related Java class.

Application Module Custom Methods

As shown in Example 10-6, the updateSkillsForCurrentStaff() method performs the following steps:

  1. Clones the list of product ids

  2. Creates a secondary RowSetIterator to do programmatic iteration over the ExpertiseAreas

  3. Removes rows for current user for products that are not in the list of products ids

  4. Closes the secondary row set iterator when done iterating

  5. Adds new rows for the keys that are left

  6. Commits the transaction.

Example 10-6 UpdateSkillsForCurrentStaff Method in SRServiceImpl.java

public void updateSkillsForCurrentStaff(List productIds) {
    if (productIds != null && productIds.size() > 0) {
      // 1. Cone the list of product ids
      List<Number> copyOfProductIds = (List<Number>)Utils.cloneList(productIds);
      ViewObject skills = getStaffExpertiseAreas();
      // 2. Create a secondary rowset iterator for iteration
      RowSetIterator rsi = skills.createRowSetIterator(null);
      // 3. Remove rows for current user for products not in list of products
      while (rsi.hasNext()) {
        Row r = rsi.next();
        Number productId = (Number)r.getAttribute("ProdId");
        // if the existing row is in the list, we're ok, so remove from list.
        if (copyOfProductIds.contains(productId)) {
          copyOfProductIds.remove(productId);
        }
        // if the existing row is in not list, remove it.
        else {
          r.remove();
        }
      }
      // 4. Close the secondary row set iterator when we're done
      rsi.closeRowSetIterator();
      // 5. Add new rows for the keys that are left
      for (Number productIdToAdd: copyOfProductIds) {
        Row newRow = skills.createRow();
        skills.insertRow(newRow);
        newRow.setAttribute("ProdId", productIdToAdd);
      }
      // 6. Commit the transaction
      getDBTransaction().commit();
    }
  }

Note:

Since the iterator bindings in the ADF Model layer bind by default to the default row set iterator for the default rowset of the view object instance to which they are related, it's best practice to create a secondary row set iterator to perform programmatic iteration of a view object's row set in your application business logic. This way you do not affect the current row that the user sees in the user interface. The secondary iterator can have a developer-assigned name, but if you pass null the system assigns it a name. Since you'll typically always be closing it as soon as you're done iterating, using the system-assigned name is fine.

10.6.8 The SRCreate Page

10.6.8.1 Overview of Data Binding in the SRCreate Page

Figure 10-23 illustrates the data binding for the SRCreate page.

Iterator Bindings to View Object Instances
  • GlobalsIterator for Globals view object instance

  • ProductListIterator for ProductList view object instance

Page Definition Variables

None

Action Bindings to Built-in Operations

None

Method Action Bindings to Custom Operations

cancelNewServiceRequest invokes the cancelNewServiceRequest method on the SRService client interface

InvokeActions Customizing Page Lifecycle

clearServiceRequestFieldsIfNotInTrain invokes the cancelNewServiceRequest method action binding during prepare model phase (Refresh="prepareModel") when the page is not handling postback events and the requestScope.processChoice attribute is not set (RefreshCondition="${adfFacesContext.postback == false and empty requestScope.processChoice}").

Figure 10-23 Service Method and View Objects for the SRCreate Page

Service Method and View Objects for the SRCreate Page

10.6.8.2 Business Service Notes for the SRCreate Page

View Object Instances
  • ProductList is an instance of the entity-based view object ProductList. It queries data from the primary entity usage Products (Product entity object). It is an XML only view object, with no related Java class.

  • Globals is an instance of the transient view object Globals. It contains transient attributes to temporarily store information about ProductId, ProductName, and ProductDescription across pages.

Note:

A transient view object is one that has no SQL query and all transient attributes. It can contain rows that are populated either programmatically by your application module business logic or declaratively using action bindings for built-in operations in the ADF Model layer. For users familiar with Oracle Forms, this is similar to what you know as a "non database block" in Forms.
Application Module Custom Methods

As shown in Example 10-7, the cancelNewServiceRequest() method ensures there is a single blank row in the Globals view object instance.

Example 10-7 CancelNewServiceRequest Method in SRServiceImpl.java

public void cancelNewServiceRequest() {
  ViewObject globals = getGlobals();
  globals.clearCache();
  globals.insertRow(globals.createRow());
}

10.6.9 The SRConfirmCreate Page

10.6.9.1 Overview of Data Binding in the SRConfirmCreate Page

Figure 10-24 illustrates the data binding for the SRConfirmCreate page.

Iterator Bindings to View Object Instances
  • GlobalsIterator for Globals view object instance

  • LoggedInUserIterator for LoggedInUser view object instance

Page Definition Variables

None

Action Bindings to Built-in Operations

None

Method Action Bindings to Custom Operations
  • cancelNewServiceRequest invokes the cancelNewServiceRequest() on the SRService client interface

  • createServiceRequest invokes the createServiceRequest() on the SRService client interface

InvokeActions Customizing Page Lifecycle

None

Figure 10-24 Service Method and View Objects for the SRConfirmCreate Page

Service Method and View Objects for the SRConfirmCreate Page

10.6.9.2 Business Service Notes for the SRCreate Page

View Object Instances
  • Globals is an instance of the transient view object Globals. It contains transient attributes to temporarily store information about ProductId, ProductName, and ProductDescription across pages.

  • LoggedInUser is an instance of the entity-based view object LoggedInUser. It queries data from the primary entity usage Users (User entity object). The LoggedInUser view object is linked master/detail with the ServiceRequestsByStatus view object.

Application Module Custom Methods

As shown in Example 10-8, the createServiceRequest() method performs the following basic steps:

  1. Gets the entity definition for ServiceRequest

  2. Creates a new ServiceRequest entity row (ServiceRequestImpl class)

  3. Accesses the current row in Globals as a strongly-typed GlobalsRowImpl

  4. Set the problem description and product ID for new service request

  5. Commits the transaction

  6. Returns an Integer representing the database-trigger assigned SR number for the new service request.

Example 10-8 CreateServiceRequest Method in SRServiceImpl.java

public Integer createServiceRequest() {
  // 1. Get the entity definition for ServiceRequest
  EntityDefImpl svcReqDef = ServiceRequestImpl.getDefinitionObject();
  // 2. Create a new ServiceRequest entity row
  ServiceRequestImpl newReq = 
    (ServiceRequestImpl)svcReqDef.createInstance2(getDBTransaction(),null);
  // 3. Access the current row in Globals as a strongly-typed GlobalsRowImpl
  GlobalsRowImpl globalsRow = (GlobalsRowImpl)getGlobals().getCurrentRow();
  // 4. Set the problem description and product id for new service request
  newReq.setProblemDescription(globalsRow.getProblemDescription());
  newReq.setProdId(globalsRow.getProductId());
  // 5. Commit the transaction
  getDBTransaction().commit();
  // 6. Return an integer representing the database-assigned SR Number
  return newReq.getSvrId().getSequenceNumber().intValue();
}