As Published In
Oracle Magazine
July/August 2007

DEVELOPER: Frameworks


Producing Parameterized Pages

By Steve Muench Oracle Employee ACE

Use HTTP request parameters to retrieve rows to view and edit.

How many times have you clicked a hyperlink in an e-mail or Web page to access information about a specific book or airline reservation? These hyperlinks usually contain one or more parameters that reference the particular item you want to view, and the parameter values are used to retrieve the appropriate information for display. In this column, I explore how to create parameterized pages declaratively by using Oracle JDeveloper, Oracle Application Development Framework (Oracle ADF) Business Components, and Oracle ADF Faces. These techniques have been tested with Oracle JDeveloper 10.1.3.2, available as a free download on Oracle Technology Network (OTN) at oracle.com/technetwork/jdev.

Using Declarative Actions to Retrieve Data

A parameterized page is essentially one in which the back-end logic retrieves desired data from an appropriate view object, based on specified parameter values, before the page displays. Oracle ADF provides three built-in actions you can use in your page definition to build parameterized pages without writing any code.

The action you select depends on the specific use case you need to support: 

  • Use the setCurrentRowWithKeyValue action if the view object has a single-attribute primary key and your parameter supplies the value of that key.

  • Use the ExecuteWithParams action if the view object has one or more named bind parameters whose values you need to set to locate the desired data.

  • Use the setCurrentRowWithKey action in other cases.

Let's start the walk-through in Oracle JDeveloper by creating a simple set of Oracle ADF Business Components to work with data from the EMP table in the familiar SCOTT schema. Begin by creating a new application using the Web Application [JSF, ADF BC] template, and use the Business Components from Tables wizard in the Model project to create an Emp entity object for the EMP table, an updatable EmpView view object based on that entity, and an HRModule application module. Then use the Application Module Editor to change the view object instance name from EmpView1 to the more friendly-looking name Employees. (For more details on creating this "starter" application, refer to previous Frameworks columns.)

Next, create a new ViewEmployee.jspx page, by right-clicking the ViewController project in the Application Navigator and selecting Open JSF Navigation . Drop a JSF (JavaServer Faces) page from the Component Palette to the diagram, double-click the page icon, type the correct page name, and click Finish . Next, add a read-only form to the page, by opening the Data Control Palette , expanding the HRModuleDataControl item, and selecting the Employees data collection. Drag this data collection to the center of the page, and select Forms -> ADF Read-only Form... from the Create menu. In the Edit Form Fields dialog box click OK to create a form with all of the available attributes.

At this point, you have a page that displays a row of employee data. The next step is to configure the page to display the specific row you want, based on parameter data in the URL.

Creating an Action Binding to Find a Row

It takes two steps to find a row based on a parameter value. The first step is to create an action binding in the page definition to set the current row based on a key value passed in a parameter. The second step is to configure the page definition to automatically invoke that action binding when the page loads. Because these actions are nonvisual in nature, you'll directly modify the page definition rather than perform drag-and-drop operations.

In the visual editor, right-click anywhere on the ViewEmployee.jspx page created above, and select Go to Page Definition . Oracle JDeveloper opens the related ViewEmployeePageDef.xml page definition file and displays the structure of its contents in the Structure window. In that window, expand the Bindings folder to see the existing bindings. To create a new action binding, right-click the Bindings folder and select Insert inside bindings -> action . When the Action Binding Editor appears, click the Select an Iterator list at the bottom of the editor window and select EmployeesIterator . Next, click the Select an Action list and select setCurrentRowWithKeyValue . Note that when you select this action, the table in the Parameters section updates to show a parameter named rowKey, which this action requires. The value of this key attribute should come from the URL parameter, so you need to provide an appropriate expression language (EL) expression in the corresponding cell. To do so, double-click the Value cell in the table for the rowKey parameter and type the EL expression #{param.id} (including the initial pound sign and the surrounding curly braces). This syntax references a parameter named id in the URL's list of parameters. At runtime, Oracle ADF will evaluate this expression to supply the value of the rowKey parameter whenever this action binding is invoked.

Finally, click OK to create the new action binding. The first step of the procedure is now complete. In many cases, an action binding will be invoked when an end user clicks a button or a hyperlink. However, in this case, you want to invoke it automatically when the page loads. The following additional step accomplishes this task declaratively.

Invoking the Action Before the Page Displays

In the Structure window for the page definition, expand the Executables folder to see the existing executable items. This folder typically contains iterator bindings, but there are other kinds of executable items you can create, including invokeAction, which allows you to invoke an action binding at any particular phase of the page processing lifecycle. In this example, let's create an invokeAction executable item to automatically trigger the setCurrentRowWithKeyValue action created earlier. Do this by right-clicking the Executables folder and selecting Insert inside executables -> invokeAction . When the Insert invokeAction dialog box appears, select the Common Properties tab and give the executable item a meaningful name, by setting the value of its id attribute to callSetCurrentRow . Then select the setCurrentRowWithKeyValue action binding from the Binds list.

Next, select the Advanced Properties tab. The Refresh property specifies the phase of the page-processing lifecycle during which the action should be invoked. Because you want to retrieve the data when the model layer is being prepared, select prepareModel from the Refresh list. The corresponding Refresh Condition property provides more-fine-grained control for specifying when the action should fire. If you provide a Boolean-valued EL expression for this property's value, the related action binding will be automatically invoked only if that expression evaluates to true. In this case, you want the action invoked only when the page is first rendered. You can implement this requirement by providing an EL expression that references the handy postback property of the Oracle ADF Faces context. This property evaluates to false at runtime when the page first renders, and to true when the end user subsequently interacts with UI components on the page. So set the Refresh Condition property to the EL expression #{adfFacesContext .postback == false} to achieve the desired result. Finally, click OK to create the new invokeAction executable item.

Now test the ViewEmployees.jspx page, by right-clicking it in the Application Navigator and selecting Run . When the page appears in your browser, edit the link in your browser's address bar to append an id parameter at the end of the URL. You can do this by appending ?id=7934 to the end of the URL that Oracle JDeveloper creates. The URL should look something like the following: 

 
http://localhost:8988/oramag/faces/ViewEmployee.jspx?id=7934 

Then press the Enter key or click your browser's Go button to see the parameter-driven result. You can experiment with different values of the id parameter to confirm that the solution works for any valid employee number. Note that the technique used here works for any kind of Oracle ADF Faces JSF page.

Using the ExecuteWithParams or setCurrentRowWithKey Actions

I've just outlined how to use the setCurrentRowWithKeyValue action when building a parameterized page. I also mentioned two other actions you could use to build parameterized pages: ExecuteWithParams and setCurrentRowWithKey. To use these actions, you follow the same basic steps outlined above. However, in each case, there are a few differences worth noting.

When you are using the ExecuteWithParams action, your action binding won't have a single rowKey parameter. Instead, it will have as many parameters as the related view object has named bind variables. As a result, you will need to enter an EL expression for the value of each action parameter whose value you want to set. When you're using an invokeAction executable to trigger an ExecuteWithParams action binding, make sure that the action binding executes before the iterator binding to which it is related. Failure to do so may cause the view object's query to unnecessarily execute one additional time when an end user first accesses the page within a session. You can use the mouse to resequence the execution order of the executable items in the page definition to avoid this extra database round-trip.

Also note that the ExecuteWithParams action will appear in the Data Control Palette (in the Operations folder) only if the view object has at least one named bind variable defined for it. Because a view object with no named bind parameters doesn't need any named bind parameter values passed when a query is re-executed, you could use the Execute operation instead of ExecuteWithParams to re-execute the query.

Use the setCurrentRowWithKey action when you want to find a row based on a primary key comprising multiple attribute values. This operation also accepts a rowKey parameter; however, instead of a literal primary key value, Oracle ADF expects the value of the rowKey action binding parameter to be a serialized string representation of a view row key in this case. This parameter consists of a hexadecimal string that encodes all of the key attribute values that constitute a view row's key, stored in a format that can be conveniently passed as a single value in a browser URL string or form parameter. In any Oracle ADF-based JSF page, you can access the serialized string key of a row by referencing the rowKeyStr property of an Oracle ADF control binding (using #{bindings.SomeAttrName.rowKeyStr} ) or the row variable of an Oracle ADF Faces table, such as #{row.rowKeyStr} . Note that at runtime, if you inadvertently pass a parameter value that is not a legal serialized string key, you may receive an exception such as oracle.jbo .InvalidParamException or java.io.EOFException as a result.

Finally, you can optimize performance when using either the setCurrentRowWithKeyValue or the setCurrentRowWithKey action in cases in which you use the related view object only to retrieve a row by key and not to perform other kinds of queries. If you open the Tuning panel in the View Object Editor and set the No Rows option in the Retrieve from Database section, you can prevent any rows from being retrieved from the database unless you use one of these two variants of the find-by-key actions.

Passing Parameters Within a Single Oracle ADF Application

If you need to pass parameters between pages within the same Oracle ADF application, it is simpler to use request-scoped attributes than to use URL parameters. By using <af:setActionListener> as a child component inside <af:commandButton>, <af:commandLink>, or another control, you can declaratively assign the value of a property by using EL expressions. The from attribute of a setActionListener identifies the expression to evaluate, and the to attribute identifies the property that will receive the value.

Next Steps



READ more about Oracle JDeveloper 10g and Oracle ADF
oracle.com/technetwork/products/jdev
oracle.com/technetwork/products/adf/learnadf.html

 READ more Frameworks

 DOWNLOAD Oracle JDeveloper 10g

Listing 1 illustrates a command link that could be used inside a column of an Oracle ADF Faces table to display an employee's Ename as a hyperlink. When clicked, <af:setActionListener> sets the empToLocate request-scope attribute to the serialized string representation of the current row's key before navigating to the target page indicated by the viewDetails outcome in the action property.

Code Listing 1: Setting request scope attributes declaratively with setActionListener 

<af:commandLink text="#{row.Ename}" action="viewDetails">
  <af:setActionListener 
      from="#{row.rowKeyStr}"
      to="#{requestScope.empToLocate}"/>
</af:commandLink>
<af:commandLink text="#{row.Ename}" action="viewDetails">
  <af:setActionListener 
      from="#{row.rowKeyStr}"
      to="#{requestScope.empToLocate}"/>
</af:commandLink>

When you are using request-scoped attributes such as the one above to pass parameters between pages, your action binding parameter in the target page needs to reference the same #{requestScope.attributeName} variable, instead of #{param.urlParamName} as in earlier examples. For example, the page to which the command link in Listing 1 navigates would need to reference the #{requestScope.empToLocate} expression in the rowKey parameter of its setCurrentRowWithKey action binding.

Hopefully this overview has piqued your interest in learning more about the declarative data binding features in Oracle ADF for supporting JSF applications. For those who are interested in learning more, Oracle ADF Developer's Guide for Forms/4GL provides a lot of additional information. Chapter 10, "Overview of Application Module Data Binding," focuses on this topic.

 


Steve Muench has worked for Oracle since 1990. He is a consulting product manager for Oracle JDeveloper and an Oracle ACE. Muench coauthored Oracle ADF Developer's Guide for Forms/4GL Developers (Oracle, 2006), and he shares tips and tricks on OTN and in his Dive into BC4J and ADF blog.


Send us your comments