Managing ADF JClient Data Binding in a Multi-Form Application

An Oracle JDeveloper How To Document
Written by Ralph Gordon
December 2004

Content

      Prerequisites
      Introduction
      Data Binding Overview in JClient Forms
      Data Binding in a Serial-Forms Use Case
      Data Binding in a Concurrent-Forms Use Case
      Create the Multi-Form JClient Application
      Summary

Prerequisites

This document assumes the following:

  • That you will be developing with JDeveloper 10g. We recommend upgrading to JDeveloper 10g from earlier versions when possible.
  • That you have created a JDeveloper 10g workspace that contains an Oracle ADF Business Components project.
  • That the database schema used with ADF Business Components is the Oracle Orders schema.
Introduction

The runtime objects exposed by the Oracle ADF data binding API support the creation of multi-window applications wherein the end user interacts with a series of forms to complete a single task. For example, assume an order fulfillment application that displays a list of orders. In the multi-window application, the end-user would open a separate form to update the items in each order. In this scenario, data chaining needs to occur across two forms, and the end-user should be able to freely navigate the order list and view the current order's items in separate forms.

This document describes the runtime objects that support data chain in a multi-form JClient application. Utilizing these objects in a multi-form application requires some modification of the code generated by the JClient form wizards available in JDeveloper 10g. As a result of the code changes, you can display master and detail forms in separate windows and manage the end-user's interaction with these forms.

We will rely on these two use cases to depict the type of customization required to the JClient data binding code:

  • A basic modification will permit the end-user to open and edit one order at a time before continuing to edit a new order. Let's call this a serial forms-style application.
  • A more involved modification will permit the end-user to edit individual orders without needing to close a window, thus displaying more than one order form at the same time. Let's call this a concurrent forms-style application.

Before we analyze these use cases, you may want to review the following section to understand how JClient exposes the Oracle ADF data binding mechanism though its API.

Data Binding Overview in JClient Forms

Data binding in JClient is the ability to create Swing containers and components that are bound to data in back-end business services. To enable data binding, JClient provides a small API that works with the Oracle ADF model layer. The API is exposed in the application source code through a combination of JClient bootstrap code:

  • Call loadCpx() to load the application meta-data (specified in the DataBindings.cpx file) which specifies a connection to the business service implementation instance (for example, a Business Components application module instance) using the ADF data control for the instance, plus the ADF binding context.
  • Call setBindingContext() to make the ADF binding context available to the frame or panel.
  • Call createPanelBinding() to create an object that will access the business service's contained data collections through Swing component models.
  • Call bindUIControl() on the panel binding to set the ADF model for the individual components of the JClient form or panel.

To work with a data binding in your Swing application, each container (a frame or panel) must either create a panel binding object or get one from the source from which it originated. The frame that creates the first panel binding also contains the JClient bootstrap code, where the connection to the business services is created. Subsequent containers that your application creates either chain from the original panel binding or they create their panel binding in order to display unrelated data.

In the wizard-generated forms provided in JDeveloper, you can assume just one binding context and one panel binding object will be created for each databound frame or panel. In the following section we will see how to manage these objects to work with multiple forms.

Data Binding in a Serial-Forms Use Case

In the serially opened forms use case, the end-user should be able to select an order, update an order, and continue with the application. Each form gets its panel binding object to enable access to the same set of business objects in the application's runtime binding context. The panel binding and the binding context together permit data chaining across the forms. However, unless a new panel binding is created for each new detail form (to update order items), the next form the end-user opens won't display the current selection (from the orders list). We want the separate forms to look like this, with the master order form on the left and the detail form, displaying order items, on the right:

application with serially-opened detail forms

To enable data chaining in the above multi-form application, where the end-user interacts with forms sequentially, the JClient application must support these tasks:

  1. When the end-user opens the first detail form, the master form must create a panel binding to reference the binding container.
  2. When the end-user is finished with the form, the detail form must be able to release references to the business objects in its panel binding object.

The application can implement these two tasks in the action listener of a button in each form. For example, when the end-user clicks the Open Order Items to Edit button in the master form, the action listener will determine whether a panel binding exists. Upon the first Open button click, no panel binding will exist and the action listener will initialize the binding container and create the panel binding that references the binding container for the detail form. Then, when the end-user clicks the Close button in the detail form, that button's listener will release the panel binding's business object references without disposing of the object for the current binding context. The end-user can return to the master form and open the next detail form, thereby "recycling" the original panel binding object with a new business object reference.

When the next detail form gets displayed, the component bindings in that form will create their own business object references and enable the form to display the detail information for the current master object selection. An added benefit of Oracle ADF data binding is that the ADF iterator bindings associated with each form manages synchronization of data between the forms and preserves master-detail navigation. In this case, when the end-user scrolls the list of orders, the order items form will reflect the change.

Data Binding in a Concurrent-Forms Use Case

In this use case, the forms are the familiar master and detail, with the added ability to display multiple detail forms at the same time. We want the separate forms to look like this, with the master order form on the left and the multiple, detail order items forms on the right:

application with concurrent detail forms

In the previous example, data binding each detail form was managed by recycling the panel binding object from the original binding container. This was possible when only a single form was open at a time. However, this strategy won't suffice if the end-user is to open and interact with multiple databound forms from the same master form. In this case, recycling the panel binding object would terminate the data binding of the previously opened form. Instead, to open multiple, independent forms that share the same data, the JClient the application must support these tasks:

  1. When the end-user opens the detail form, the master form will create a new binding container for each form.
  2. When the end-user is finished with a detail form, the detail form need only dispose of the panel binding since it will be unique for each form.

This technique ensures that each detail form will have a unique panel binding and will therefore display the appropriate detail information. Specifically, we must modify the behavior of the action listener for the master form's Open Another Order to Edit button which will use an integer variable to create a unique binding container name before creating the panel binding object that ultimately will reference this uniquely named binding container for the detail form.

Furthermore, in this concurrent-forms scenario, it would be desirable to disable the navigation provided by the ADF iterator binding between the open detail and master forms. In other words, we want to isolate navigation so the end-user does not see all the open detail forms scroll together. Support for this task is provided by the Oracle ADF Business Components secondary rowset iterator (RSI), which lets you manage access to the master business object. Note that this requirement assumes the business service objects are implemented as Oracle ADF Business Components (as opposed to plain old JavaBeans or EJB components). Once the RSI object is created, the master form can set the iterator binding instance on its panel binding object.

Creating the Multi-Form JClient Application

With the aid of JDeveloper 10g, let's create the above applications and implement the changes to the data binding code in the JClient wizard-generated forms.

Note: This section assumes you are using JDeveloper 10g and have created a workspace containing a populated ADF Business Components project and an empty JClient project. Use the Orders schema (OE/oe) to generate the business components if you wish to follow along.

Serial Forms Use Case

To lay out the master and detail forms:

  1. In the JDeveloper New Gallery, open the JClient Empty Form wizard and create two empty forms in your JClient project: MasterForm.java and DetailForm.java.
  2. Open the master form in the Visual Editor and expand the master-detail views in the Data Control Palette. For example, in our use case, we have OrdersView1 and OrderItemsView2 related as master and detail:

    master and detail in data control palette

  3. Create the master form by dropping the desired attributes from the Data Control Palette as Text Fields onto the open form.
  4. Open the Component Palette and drop a JButton from the list of Swing components.
  5. Open DetailForm.java in the Visual Editor and drop a JScrollPane from the Swing Container components list (you will need to change the palette selection in the dropdown list to view the list of Swing containers).
  6. Return to the Data Control Palette and drop the detail collection ( OrderItemsView2) as a Table so it appears inside the scroll pane.
  7. Return to the Component Palette and drop a JButton into the detail form below the scroll pane.
  8. Drop JLabel components from the Swing component list to create labels for the text fields in each form. Use the Property Inspector to set the labels' text attribute.
  9. Use the Property Inspector to edit the text attribute of each JButton you dropped. For example, create buttons labeled Open Order Items to Edit in the master form and Close in the detail form.

To specify the custom data binding behavior for the button action listeners:

  1. Open the Property Inspector for the master form's Open button.
  2. Create the Open button action listener by clicking the Events tab ( event tab icon) in the Property Inspector and clicking the ellipses ( ellipses button) in the actionPerformed event field.
  3. Use the actionPerformed dialog to name the event openButton_actionPerformed and click OK. JDeveloper opens the Code Editor with the new action listener appearing in MasterForm.java.
  4. Modify the Open button action listener in MasterForm.java to look like this:
                                             
    private void openButton_actionPerformed(ActionEvent e)
    {
           createDetailBinding();
           DetailForm df = new DetailForm();
           df.setBindingContext(panelBinding.getBindingContext());
           df.setVisible(true);
    }
                                          

    The above custom data binding code calls createDetailBinding() (will add in the next step) to create a new panel binding for the detail form before opening the detail form.

  5. Just below the Open button action listener, add this createDetailBinding() method to MasterForm.java:
                                             
    private void createDetailBinding()
    {
        String detailBCName = "DetailFormUIModel";
        if (panelBinding.getBindingContext().get(detailBCName) == null)
        {
          DCBindingContainerDef bcdef = (DCBindingContainerDef)JUMetaObjectManager.getJUMom().findLoadedObject("sample.DetailFormUIModel");
          DCBindingContainer bc = bcdef.createBindingContainer(panelBinding.getBindingContext());
          bc.setName(detailBCName);
          panelBinding.getBindingContext().put(detailBCName, bc);
        }
    }
                                          

    The above custom method is called from the Open button action listener before the form is opened. This code creates a new binding container and sets it on the panel binding object. This task is required only once for the serial forms application.

  6. Repeat the steps to add a closeButton_actionPerformed action listener to the detail form.
  7. Modify the Close button action listener in the DetailForm.java to look like this:
                                             
    private void closeButton_actionPerformed(ActionEvent e)
    {
        panelBinding.release(DCDataControl.REL_VIEW_REFS);
        panelBinding.getBindingContext().remove(panelBinding.getName());
        this.dispose();
    }
                                          

    The above custom data binding code recycles the panel binding by specifying REL_VIEW_REFS (releases only view references in the panel binding object) before disposing of the detail form.

No other changes are required. In JDeveloper, compile the application and run MasterForm.java. Click the Open button to display a detail form and edit the contents. Because this is the serial forms use case, you must close the detail form before opening another form. Try clicking the navigation button in the master form toolbar to observe the master-detail synchronization.

Concurrent Form Use Case

Create a new emtpy JClient project if you intend to use the same workspace. Lay out the master and detail forms by following the previously stated steps. You should have another set of MasterForm.java and DetailForm.java files. The only difference at this point will be the label of the master form's Open button (for example, we used Open Another Form to Edit to signify that multiple forms may be opened at once).

To specify the custom data binding behavior for the button action listeners in this use case:

  1. Open MasterForm.java in the Visual Editor and display the Property Inspector for the Open button.
  2. Create the Open button action listener by clicking the Events tab ( event tab icon) in the Property Inspector and clicking the ellipses ( ellipses button) in the actionPerformed event field.
  3. Use the actionPerformed dialog to name the event openButton_actionPerformed and click OK. JDeveloper opens the Code Editor with the new action listener appearing in MasterForm.java.
  4. Modify the Open button action listener in MasterForm.java to look like this:
                                             
    // specify the action listener
    private void openButton_actionPerformed(ActionEvent e)
    {
        DetailForm df = new DetailForm();
        // note that the setBindingContainer() method will be defined in DetailForm.java
        df.setBindingContainer(createDetailBinding());
        DCIteratorBinding iterBinding = getPanelBinding().findIteratorBinding("OrdersView1Iterator");
        // get master current row, get detail accessor iterator, then bind detail form iterator binding to 
        // detail accessor iterator
        Row row = iterBinding.getCurrentRow();
        RowSetIterator detailAccessor = (RowSetIterator)row.getAttribute("OrderItemsView"); 
        df.getPanelBinding().findIteratorBinding("OrderItemsView2Iterator").bindRowSetIterator(detailAccessor, false);
        df.setVisible(true);
    }
                                          

    Note: The names of business objects and iterator binding objects in the above code are consistent with the Orders and OrderItems views described in this paper. Modify the code to match your business objects when you do not use the Order schema to generate the Business Components project.

    The above custom data binding code calls the createDetailBinding() method (will add in the next step) to create the binding container. The rest of the code uses the ADF Business Components API to bind a unique rowset iterator to the detail form's panel binding, thus ensuring that the new detail form will navigate independently from the master form. Without this code, the application would cause all open detail forms to navigate from the master form.

  5. Just below the Open button action listener, add this createDetailBinding() method to MasterForm.java:
                                             
    int count = 0;
    private DCBindingContainer createDetailBinding() 
    { 
        String detailBCName = "DetailFormUIModel"+count;
        if (panelBinding.getBindingContext().get(detailBCName) == null) 
        { 
          DCBindingContainerDef bcdef = DCBindingContainerDef.findDefObject("sample.DetailFormUIModel"); 
          DCBindingContainer bc = bcdef.createBindingContainer(panelBinding.getBindingContext()); 
          bc.setName(detailBCName); 
          panelBinding.getBindingContext().put(detailBCName, bc);
          ++count; //make sure the next name is unused thus far. 
          return bc; 
        } 
        return null;
    }
                                          

    The above custom method is called from the Open button action listener before the form is opened. This code creates a new binding container from a uniquely named binding container definition ( "DetailFormUIModel" + count) and sets it on the panel binding object. Creating a new binding container ensures that each new detail form will have its own panel binding object.

  6. Add the following import statement to MasterForm.java.

    import oracle.jbo.RowSetIterator;

  7. Open DetailForm.java in the Visual Editor and display the Property Inspector for the Close button.
  8. Using the Property Inspector again, create a closeButton_actionPerformed action listener for DetailForm.java.
  9. Modify the Close button action listener in the DetailForm.java to look like this:
                                             
    private void closeButton_actionPerformed(ActionEvent e)
    { 
        panelBinding.release();
        panelBinding.getBindingContext().remove(panelBinding.getName());
        this.dispose();
    }
                                          

    The above custom data binding code just releases the panel binding before closing the detail form. In this use case, each new detail form receives a new panel binding object.

  10. Just below the Close button action listener, add this setBindingContainer() method to DetailForm.java
                                             
    public void setBindingContainer(DCBindingContainer ctr) 
    {
        panelBinding = (JUPanelBinding)ctr; 
        setBindingContext(ctr.getBindingContext()); 
    }
                                          

    The above custom method is called in the master form's Open button action listener. This code ensures that the new panel binding object and binding container are available to the application through the ADF binding context.

No other changes are required. In JDeveloper, compile the application and run MasterForm.java. Click the Open button to see the detail form and edit the contents. Because this is the concurrent use case, you can open as many detail form as desired. Try clicking the navigation buttons in the master form toolbar to observe the independent scrolling behavior.

Summary

How you want to manage the data views of in a multi-form application determines whether a container recycles a panel binding or gets a new one:

  • If you want to display forms on the business services views serially, then the master form should reuse a single binding container to be referenced by each displayed detail form:

    panelBinding.getBindingContext().put(detail2BCName, bc);

    Because the detail form cannot dispose of its panel binding, it is necessary to recycle the panel binding by releasing the business object references before closing the form:

    panelBinding.release(DCDataControl.REL_VIEW_REFS);

  • If you want to display forms of the business services views concurrently, then the master form should assign a uniquely named binding container to be referenced individually by each displayed detail form:

    String detail2BCName = "Detail2FormUIModel"+count;
    ...
    panelBinding.getBindingContext().put(detail2BCName, bc);

    Because each detail form will have a unique panel binding instance, it is possible to merely dispose of the panel binding upon closing the detail form:

    panelBinding.release();
    panelBinding.getBindingContext().remove(panelBinding.getName());
    this.dispose();

v1.1 February-2005

false ,,,,,,,,,,,,,,,