Saturday, February 6, 2010

Using Bind Variable to Implement Range Selection Declaratively

A bind variable is a placeholder in a SQL command for a value that will be supplied at runtime by the application. Because you can change the value of the bind variable as many times as needed without changing the actual text of the SQL command, the database can reuse the same statement over and over without incurring the overhead of reparsing the command each time it's executed.

In Oracle ADF, all bind variables are defined at the level of the view object. Bind variables provide you with the means to supply attribute values at runtime to the view object or view criteria. You can define a default value for the bind variable or write Groovy Expressions for the bind variable that includes dot notation access to attribute property values. If you do not supply a default value for your named bind variable, it defaults to the NULL value at runtime.

After defining the bind variables, the next step is to reference them in the SQL statement. While SQL syntax allows bind variables to appear both in the SELECT list and in the WHERE clause, you'll typically use them in the latter context, as part of your WHERE clause.

There are many uses of bind variables and three styles of bind variables (i.e., '?' for JDBC Positional, :1, :2, ... for Oracle Positional, and :var1, :var2, ... for Oracle Named). In this article, we'll show one usage of it with Oracle named binding style.


The Task

In the following UI design, a time range can be specified by the user and used to display test results in that period. The test results will be displayed in line graph provided by Oracle Faces Rich Client Components. The line shows the percentage of failures in a specific test run. The Figure below shows what to expect when the design is finished.

To support this design, we need to create a read-only view object named TestTrendVO in which its query statement looks like this:
SQL Statement 1:
SELECT
    TRUNC(XTM_SEL_TEST_RUNS.START_TIME) AS START_DATE,
   (SUM((SELECT count(*) from XTM_SEL_TESTS xst where
      xst.TEST_RUN_ID = XTM_SEL_TEST_RUNS.TEST_RUN_ID AND
      xst.STATUS = 'Failure')) /
    SUM((SELECT count(*) from XTM_SEL_TESTS xst  where
      xst.TEST_RUN_ID = XTM_SEL_TEST_RUNS.TEST_RUN_ID)) * 100) AS FAILURE_PERCENTAGE
FROM
    XTM_SEL_TEST_RUNS
WHERE
  ((TRUNC(XTM_SEL_TEST_RUNS.START_TIME) >= :TSBegin )
  AND
  (TRUNC(XTM_SEL_TEST_RUNS.START_TIME) <= :TSEnd ) ) GROUP BY                        TRUNC(XTM_SEL_TEST_RUNS.START_TIME) 


Notice that there are two bind variables (i.e., TSBegin and TSEnd) being referenced in the query statement which needs to be defined later in the TestTrendVO view object. This query will also be ordered by START_DATE by using the Order By clause in the Create View Object wizard. In the view, we have defined two view attributes (or query columns):
  • StartDate(Calculated 'START_DATE')
  • FailurePercentage(Calculated 'FAILURE_PERCENTAGE')
Note that TRUNC function is used to retrieve the date part of a timestamp (i.e., XTM_SEL_TEST_RUNS.START_TIME ).

The Steps

To achieve this task, the required steps are outlined below:
  1. Creating a Read-only view object
  2. Adding TestTrendVO view object to the application module
  3. Creating a databound graph with a time selector

Step 1 — Creating a Read-only View Object

For our design, we will use a read-only view object (i.e., with no entity usage). Read-only view objects do not pick up entity-derived default values; they do not reflect pending changes; and they do not reflect updated reference information. In contrast to entity-based view objects, read-only view objects require you to write the query using the SQL query language. To create a read-only view object (i.e., TestTrendVO), you can use the Create View Object wizard, which is available from the New Gallery.

To create a read-only view object:
  1. In the Application Navigator, right-click the project in which you want to create the view object and choose New.
  2. In the New Gallery, expand Business Tier, select ADF Business Components and then View Object, and click OK.

    If this is the first component you're creating in the project, the Initialize Business Components Project dialog appears to allow you to select a database connection.
  3. In the Initialize Business Components Project dialog, select the database connection or choose New to create a connection. Click OK.
  4. In the Create View Object wizard, on the Name page, enter a package name and a view object name (i.e., TestTrendVO). Select Read-only access through SQL query to indicate that you want this view object to manage data with read-only access. Click Next.
  5. On the Query page, paste SQL Statement 1 into the Query Statement box and enter START_DATE into the Order By clause.
  6. After entering the query statement, click Next.
  7. On the Bind Variables page, enter the name (i.e., TSBegin) and data type (i.e., Date) for the new bind variable by following the instructions here. Because the bind variables share the same namespace as view object attributes, specify names that don't conflict with existing view object attribute names. As with view objects attributes, by convention bind variable names are created with an initial capital letter, but you can rename it as desired.
  8. On the Attribute Mappings page, click Finish.




Step 2 — Adding TestTrendVO View Object to the Application Module

An application module can expose its data model of view objects to clients without requiring any custom Java code. In this article, we assume you have created an application module (i.e., SelTestPortalAM) to be used in your UI components. The next step is adding the new TestTrendVO view object to it.

After you've created a new application module, you can edit any of its settings by using the Edit Application Module dialog. To launch the editor, choose Open from the context menu in the Application Navigator, or double-click the application module.

To add TestTrendVO view object to the application module:
  1. In the Edit Application Module dialog, select Data Model page
  2. Select TestTrendVO view object from the tree of available view objects and click '>' to create a named instance of the view object in the data model.
  3. Click the pencil icon next to View Instance and change its name to TestTrend
  4. From the Data Controls panel, click the refresh icon to include new view instance in the data model of SelTestPortalAMDataControl


Step 3 — Creating a Databound Graph with a Time Selector


To create a databound graph with a time selector:
  1. From the Data Controls panel, select the collection (i.e., TestTrend) and expand the Operations node to display the ExecuteWithParams operation.
  2. Drag the ExecuteWithParams operation and drop it onto the page in the visual editor.
  3. In the Create menu, select Parameter and then ADF Parameter Form...
  4. In the Edit Form Fields, click OK
  5. Drag the TestTrend collection and drop it onto the page below the form
  6. In the Create menu, select Graph...
  7. In the Component Gallery, select Line category and click OK
  8. In the Create Line Graph dialog, drag FailurePercentage attribute from the available list to Lines box and drag StartDate attribute from the availabe list to X Axis box
  9. Click OK





Explanation

The new UI allows user to specify the values of two bind variables: TSBegin and TSEnd at runtime. When the user clicks on the Execute button, SQL query statement associated with the view object is fired. The resulting rowset is then displayed in the graph underneath.

Reference(s)

  1. How-to deploy and run the ADF Faces Rich Client Components Demo on Oracle WebLogic Server
  2. Using Bind Variables
  3. Working with Bind Variables
  4. Book Review: Developing Web Applications with Oracle ADF Essentials

Wednesday, February 3, 2010

Expression/Method Builder in Oracle JDeveloper 11g

In ADF Faces applications, you use Expression Language (EL) expressions to bind attributes to object values determined at runtime.

At runtime, the value of certain JSF UI components (such as an inputText component or an outputText component) is determined by its value attribute. While a component can have static text as its value, typically the value attribute will contain an EL expression that the runtime infrastructure evaluates to determine what data to display. For example, an outputText component that displays the name of the currently logged-in user might have its value attribute set to the expression #{UserInfo.name}.

In a typical JSF application, you would create objects like UserInfo as a managed bean. The JSF runtime manages instantiating these beans on demand when any EL expression references them for the first time. When displaying a value, the runtime evaluates the EL expression and pulls the value from the managed bean to populate the component with data when the page is displayed. If the user updates data in the UI component, the JSF runtime pushes the value back into the corresponding managed bean based on the same EL expression.

Value vs. Method Expression

EL expression can be:

  • Value expressions that can set as well as get data
  • Method expressions which can invoke methods.
The context in which Expression Language is used can tell you whether the accessed resource is a property or a method. For example, the following access is to a property since the reference is from a component's value attribute:


  <af:inputtext label="Last Name" value="#{mybean.firstname}">

The access in the following is to a method since it is referenced from a listener attribute:


  <af:inputtext label="Job" valuechangelistener="#{mybean.onValueChange}">

Expression Syntax

If an EL expression is used to access managed beans, it use the following synatx:


  #{<bean>.<method>}

A managed bean can have the following standard JSF scopes: request, session, and application. In Oracle ADF, additional scopes are added:

  • pageFlow
  • view
  • backingBean
You access objects in those scopes (under the hood they're java.util.Map's) with scope qualification in the EL expression. For instance, to reference the MyBean managed bean from pageFlowScope scope, your expression would be #{pageFlowScope.MyBean}.

Oracle ADF Model implements the two concepts in JSR-227 that enable decoupling the user interface technology from the business service implementation:

  • data controls
  • declarative bindings
In Oracle ADF, the group of bindings supporting the UI components on a page are described in a page-specific XML file called the page definition file. The ADF Model layer uses this file at runtime to instantiate the page’s bindings. These bindings are held in a request-scoped map called the binding container, accessible during each page request using the EL expression:

  #{bindings}

This expression always evaluates to the binding container for the current page. For example, to access the collectionModel of binding object named MyShoppingCart , you use the EL expression:

  #{bindings.MyShoppingCart.collectionModel}

Expression/Method Builder

As you see, EL expression can be used to access different kinds of runtime objects (i.e., managed bean, binding object, binding context, etc.) and its syntax can be convoluted with many levels. Fortunately, JDeveloper 11g has come to rescue on this. You can create EL expressions declaratively using either Expression Builder or Method Builder.

To open the builder, select a JSF component in the JDeveloper visual edeitor or the Structure window and open the property inspector. In the JDeveloper property inspector, click the arrow icon next to the property field — for example, the HeaderText property — and choose Expression Builder or Method Builder. In the Builder dialog, you can browse and select the object to which the attribute will be bound.

The Builder in JDeveloper 11g exposes objects under the following categories as shown in the Figure:
  • ADF Bindings — Contains references to the ADF binding layer of the current page and the ADF binding context, which exposes all configured binding definitions.
  • ADF controller objects — ADF task flows extend the JSF navigation model and expose a ControllerContext object for EL access that allows developers to access information about the current displayed view, exceptions, the task flow URL for remote access, the train model, and much more.
  • ADF managed beans — ADF managed beans are configured in the task flow configuration, not in the standard JSF faces-config.xml file. The node contains subnodes that represent the available scopes, making it easy to prefix the bean reference with the name of the scope in which it is defined.
  • Faces' Resource Bundles — Allows access to resource bundle messages prepackaged with the application.
  • JSF managed beans — Shows the managed beans that are defined in the faces-config.xml file, also categorized by the scopes in which they are defined.
  • JSP objects — Allows access to the objects exposed by the JSF and servlet APIs. It exposes the FacesContext object and gives developers access to the servlet request header, cookies, servlet initialization parameters, and request parameters.


Reference(s)

  1. Expression Language in Oracle Fusion Developer Guide
  2. Creating ADF Data Binding EL Expressions
  3. Creating EL Expressions

Monday, February 1, 2010

How to Modify an InputText Field to a Choice List in Oracle ADF

Sometimes it just happends — either you didn't set it up correctly at beginning or you're asked to modify your UI component after a review. In this article, we'll describe such a case that we need to change a table column from af:inputText to af:selectOneChoice. Because the change is minor, we'll try to patch up existing codes without a full replacement.


The Problem

We have created a new table. However, we find out that it'll be more user friendly if we can change Investigation Status column from an input text field to a choice list (with fixed values such as: UNRESOLVED, COMMENTED, REVIEWED, BUG, and OK).

From the code level perspective, we want to change:

<af:inputText value="#{row.bindings.InvestigationStatus.inputValue}" autoSubmit="true"
  label="#{bindings.TestDetailForTestRun.hints.InvestigationStatus.label}"
  required="#{bindings.TestDetailForTestRun.hints.InvestigationStatus.mandatory}"
  columns="#{bindings.TestDetailForTestRun.hints.InvestigationStatus.displayWidth}"
  maximumLength="#{bindings.TestDetailForTestRun.hints.InvestigationStatus.precision}"
  shortDesc="#{bindings.TestDetailForTestRun.hints.InvestigationStatus.tooltip}"
  id="it1">
<f:validator binding="#{row.bindings.InvestigationStatus.validator}"/>
</af:inputText>

to

<af:selectOneChoice value="#{row.bindings.InvestigationStatus.inputValue}"
    label="#{row.bindings.InvestigationStatus.label}"
    required="#{bindings.TestDetailForTestRun.hints.InvestigationStatus.mandatory}"
    shortDesc="#{bindings.TestDetailForTestRun.hints.InvestigationStatus.tooltip}"
    id="soc1" autoSubmit="true">
<f:selectItems value="#{row.bindings.InvestigationStatus.items}"
     id="si1"/>
</af:selectOneChoice>

After the changes, the final UI should look like below:


The Task


Most of the changes can be made declaratively with JDeveloper 11g. The steps of the task can be outlined as follows:
  1. Deleting original af:inputText
  2. Creating a new af:selectOneChoice
  3. Fixing old references existing in the partialTriggers
We'll describe each step in more details below. However, before you proceed, you may take some precautions:
  1. If you're using Source Code Control System, check in all pending changes. If not, it'll be a good idea to save the old contents of all files that get involved (i.e., jsff page and its page definition file).
  2. Although JDeveloper will try to find all usages of the changing components in the application for you and give warning before you make the changes, it'll still be a good idea to do a full search on the old compoenent ID in the application. If necessary, you can clean up references to old component manually although most of the time those remnants may cause no harm at all.
  3. Take notes on what get changes before and after. This could be a good learning experience and may add another eyes on finding possible mistakes.

Step 1 — Deleting original af:inputText

To delete original af:inputText:
  1. In the Application Navigator, double-click the page that contains the table definition.
  2. In the Structure view, expand the hierarchy to reveal the target column (i.e., af:column) and its child af:inputText.
  3. Right click af:inputText and select Delete.
  4. A Delete Safely Dialog will show up and says that usages were found. Click on Ignore because we're going to fix those usages in Step 3.

Step 2 — Creating a new af:selectOneChoice

To create a list bound to a fixed list of values:
  1. From the Data Controls panel, drag and drop the attribute (i.e., investigationStatus) onto the empty af:column in the Structure view and choose Create > Single Selections > ADF Select One Choice.
    The Edit List Binding dialog displays. The view object collection containing the attribute you dropped on the af:column is selected by default in the Base Data Source list.
  2. Select the Fixed List radio button.
    The Fixed List option lets end users choose a value from a static list that you define.
  3. In the Base Data Source Attribute list, choose an attribute (i.e., investigationStatus).
    The Base Data Source Attribute list contains all of the attributes in the view data collection you selected in the Base Data Source list.
  4. In the Set of Values box, enter each value you want to appear in the list. Press the 'Enter key to set a value before typing the next value. Note that if all your values are on the same line and separated by ",", it won't work because it will be treated as one single value.
    The order in which you enter the values is the order in which the list items are displayed in the SelectOneChoice control at runtime.
  5. Click OK.


Step 3 — Fixing old references existing in the partialTriggers

ADF Faces components can be set so that one component refreshes based on an interaction with another component, without the whole page needing to be refreshed. This is known as partial page rendering.

When a new value is selected from the list for Investigation Status field, we need to enable both Commit and Rollback buttons which were grayed out at beginning. In this case, our choice list need to be configured to be a trigger that cause both buttons to refresh and its autoSubmit property need to be set to true. These steps can be made declaratively via Property Inspector.

After the changes, target components should look like this:
<af:selectOneChoice value="#{row.bindings.InvestigationStatus.inputValue}"
       label="#{row.bindings.InvestigationStatus.label}"
       required="#{bindings.TestDetailForTestRun.hints.InvestigationStatus.mandatory}"
       shortDesc="#{bindings.TestDetailForTestRun.hints.InvestigationStatus.tooltip}"
       id="soc1" autoSubmit="true">
<f:selectItems value="#{row.bindings.InvestigationStatus.items}"
        id="si1"/>
</af:selectOneChoice>

<af:commandToolbarButton text="Commit" id="ctb2"
  partialTriggers="ATt2:soc1 ATt2:it2"
  actionListener="#{bindings.Commit.execute}"
  disabled="#{!bindings.Commit.enabled}"/>
<af:commandToolbarButton text="Rollback" id="ctb3"
  partialTriggers="ATt2:soc1 ATt2:it2"
  actionListener="#{bindings.Rollback.execute}"
  disabled="#{!bindings.Rollback.enabled}"
  immediate="true">

References


  1. Simple AJAX-Style Partial Page Rendering (PPR) Example with AutoSubmit SelectOneChoice Control
  2. What You May Need to Know About Automatic Partial Page Rendering