Cross Column

Showing posts with label ADF Model. Show all posts
Showing posts with label ADF Model. Show all posts

Tuesday, May 17, 2011

Exposing List Model Instance as Table Component in Oracle ADF

Oracle ADF is based on the Model-View-Controller (MVC) design pattern. Separating applications into these layers simplifies maintenance and reuse of components across applications.
The Task

In this article, we will show how to create a table component (i.e., af:table) using a custom method in the Application Module. This custom method returns a List instance whose items will be displayed as rows in the table.

Here af:table exists in the View layer. Our custom method which provides table contents exists in the Business layer. This custom method is exposed on the client interface of an Application Module and exported to the Model layer as data control and binding object.

In this article, we will show how to achieve this. Before you start, read this companion article first.

The Background

The Table component uses a model to access the data in the underlying list. The specific model class is oracle.adf.view.rich.model.CollectionModel. You may also use other model instances, e.g., java.util.List, array, and javax.faces.model.DataModel. The Table will automatically convert the instance into a CollectionModel.

Normally, you would create table using ADF View Object which is exported to the Model layer. You create an ADF bound ADF Faces table by dragging a collection from the ADF Data Controls panel to a page or page fragment. Using this approach, you can get access to its CollectionModel easily. However, for some cases, creating a View Object can be difficult. For example, our query using Oracle Semantic Technology doesn't allow us to use bind variable.
select  postab.post_id, postab.content_value
from table(sem_match('(?s <http://xmlns.oracle.com/rdfctx/property/hasXxxOf>   <http://xmlns.oracle.com/rdfctx/organization/oracle>)',
  sem_models('smm_vpd_model_1','smm_ontology_model'),
  sem_rulebases('owlprime'), null, null))  graph,
  smm.smm_vpd_rdf_data_1 app,
  smm.smm_posts postab
where app.triple.rdf_p_id=oracle_orardf_res2vid('http://xmlns.oracle.com/rdfctx/property/hasXxxOf')
  and app.triple.rdf_c_id=oracle_orardf_res2vid('http://xmlns.oracle.com/rdfctx/organization/oracle')
  and graph.s$RDFVID=app.triple.rdf_s_id
  and postab.post_id=app.post_id
;
Ideally, we would like to make object in the RDF triple a bind variable. Since sem_match is an operator and doesn't allow that. The workaround will be creating a custom method which returns a List instance. Within the method, we create a custom query statement based on the name of an object (i.e., "oracle"). The signature of the custom method will be:
public List<postobj> positivePostsByBrand(String brandName) {
}
The Steps

Because of dependencies, we will work from Business Layer back to View Layer. All Fusion Web Applications are created with two projects:
  • Model
  • ViewController
For step 1 below, we will work in the Model project. For other steps, we will work in the ViewController project.

Step 1: Add Custom Method to the AM

Add our custom method (i.e., positivePostsByBrand) to the Application Module:
Shuttle custom method from the available list to the selected list:
In the Data Controls panel, you should find the new custom method listed under your application module.

Step 2: Create MethodAction Binding Object

Select the JSF page to which you want to add the new table. In its Bindings view, create a new binding object using the new data control object (i.e. our custom method positivePostsByBrand). By default, the binding objects are named after the data control object that was used to create it.
Our new binding object will be methodAction (under Generic Bindings) item type:

On Create Action Binding, select your AM (i.e., SemAppModuleDataControl). No, don't expand its contents. Look underneath for an Operation drop-down list. Select your custom method. Take a note of the name of its parameter (i.e., brandName) which you need to use later.
LinkStep 3: Create a New Getter Method on a Managed Bean

We need a getter method on a managed bean, which will fetch table contents on behave of View layer. For this exercise, we have created a managed bean named ActionBean which it has been registered on the ADF's unbounded task flow (i.e., adfc-conf.xml):

This managed bean has a session scope. Our getter method in ActionBean.java looks like this:
public List<PostObj> getPositivesByBrand() {
  if (getBrandName() == null || getBrandName().length() == 0)
    return new ArrayList<PostObj>();
  else {
    OperationBinding oper = getOperBindings("positivePostsByBrand")  ;
    Map params = oper.getParamsMap();
    params.put("brandName", brandName);
    oper.execute();
    return (List<PostObj>)oper.getResult();
  }
}
Within it, it uses a binding object named positivePostsByBrand to fetch table contents from the business layer (i.e., via our custom method defined in the Application Module implementation file). To execute our custom method, we also need a parameter named brandName. We will discuss how to provide this parameter at runtime later.

Step 4: Create New Table on JSF Page

Select the component (i.e., af:panelHeader) that you want to insert the new table to and insert a new ADF Faces component (i.e., af:table):
On Insert ADF Faces Item, select Table:
On Create ADF Faces Table, check Bind Data Now and click Browse...:
You will find our variable named positivesByBrand (i.e., exposed by the getter method) under ADF Managed Bean > actionBean. Select it and an EL will be created automatically for you.
Next we will create new columns mapped to the properties of the List instance object (i.e., PostObj). Click New:
Add new columns as shown. Their values will be mapped to the property's on our List instance object (i.e.,PostObj).
The Missing Link

As noted above, to execute the custom method we need a parameter (i.e., brandName). To provide that, we need to dynamically select that name from the UI. For example, in a master table, you may display rows with different brand name on each row. The brand name (or the count in our case) can then be implemented as an af:commandLink:

<af:commandLink  id="cl1" actionListener="#{actionsBean.positivePostsByBrand}">
  <af:outputText value="#{row.count}" id="ot5"/>
  <f:attribute name="posBrandName" value="#{row.brand}"/>
</af:commandLink>

Upon clicking, it will dispatch an ActionEvent which will be consumed by a listener (i.e., positivePostsByBrand method on our managed bean). Within the method, it will retrieve an attribute named posBrandName from the event's source object as such:
public void positivePostsByBrand( ActionEvent actionEvent){
  RichCommandLink _table = (RichCommandLink) actionEvent.getSource();
  String brandName = (String) _table.getAttributes().get("posBrandName");
  // set brand name to be used in getPositivesByBrand method
  setBrandName(brandName);
  getPositivesByBrand();
}

Note that attribute posBrandName will be set to be:
#{row.brand}
as defined in the af:commandLink.

References
  1. ADF Faces Rich Client
  2. Implement Contextual Event
  3. How-to Build a Generic Selection Listener for ADF bound Tables
  4. Invoking Application Module Custom Methods from Oracle ADF Frontend—Why and How?
  5. Action Bindings and Method Bindings in Oracle ADF 11g

Tuesday, November 24, 2009

Oracle ADF Model In Depth

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

  • data controls
    • A data control abstracts the implementation of a business service, allowing the binding layer to access the data from all services in a consistent way.
    • At runtime, the ADF Model layer reads the information describing the data controls and bindings from appropriate XML files and implements the two-way connection between the user interface and the business service.
  • declarative bindings
    • Declarative bindings abstract the details of accessing data from data collections in a data control and of invoking its operations.
    • There are 3 basic kinds of declarative binding objects:
      • Iterator bindings -- to allow scrolling and paging through collections of data and drilling-down from summary to detail information
      • Value bindings -- to display data
      • Action bindings -- to invoke built-in or custom operations on data collections of a data control. They are represented as operations in the Data Controls panel.
The following figure shows how bindings connect UI components to data control collections and methods.

In the following sections, we will see what kind of metadata are needed at design time to bind UI components (or widgets) to their associated data collections or methods declaratively and what kind of objects are generated at runtime to support the gluing.

Design Time

When you begin adding content to your page, you typically use the Component Palette and Data Controls panel of JDeveloper. The Component Palette contains all the ADF Faces components needed to declaratively design your page. Once you have the basic layout components placed on the page, you can then drag and drop items from the Data Controls panel to create ADF Model databound UI components.

From the item (i.e., accessor or attribute) dragged over from the Data Controls panel, JDeveloper first compiles a list of compatible BindingsTypes. From this list, JDeveloper can then determine the set of Creators that are applicable to drop on the page. The user is then presented with this list (organized by BindingsType). When the user selects a Creator to use, the Creator builds the databound UI component (by producing a DOM subtree representing what will be inserted into the page).
For example, If you drop the PrincipalName attribute from the Data Controls panel as an Input Text w/Label widget, JDeveloper creates an inputText component. The following code shows the code generated on the JSF page when you drop the PrincipalName attribute as an Input Text w/Label widget. Notice that the value of the component is bound to the inputValue property of the PrincipalName binding.


           shortDesc="#{bindings.PrincipalName.hints.tooltip}" id="it1">


You can change any of these properties to suit your needs. Reference this appendix for the properties of the ADF bindings.

If you build your application using these databound UI components, JDeveloper will create the following metadata files for you:
  • Add a page definition file. The page definition file (pageNamePageDef.xml) defines the group of bindings supporting the UI components on a page.
  • Create a DataBindings.cpx file and addes an entry for the page. The DataBindings.cpx file acts as the table of contents to the data controls in use in this application.
  • Create the adfm.xml file. This file creates a registry for the DataBindings.cpx file, which allows the application to locate it at runtime so that the binding context can be created.

Runtime

At runtime, multiple objects (i.e., binding context, binding containers, and binding objects) are created in memory to hold binding information. The ADF binding context is a runtime map of all data controls and page definitions within the application. The binding context is the one object that lives in the HttpSession for each end user, accessible using the EL expression #{data}.

The group of bindings supporting the UI components on a page are described in a 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.

  • Binding Context
    • The binding context provides the data environment for your application. It contains all of the data controls and binding containers that your application can access.
    • The ADF lifecycle creates the ADF binding context from the application module, DataBindings.cpx, and page definition files. Binding context contains references that become data control or binding container objects on demand.
    • You can look up ApplicationModule from a managed bean using the BindingContext:
      BindingContext bc = BindingContext.getCurrent();
      DCDataControl dc = bc.findDataControl("AppModuleDataControl");
      ApplicationModule am = (ApplicationModule)dc.getDataProvider();
    • You can retrieve a ControlBinding named "producerMethod" via BindingContext in this way:
      DCBindingContainer bc = (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
      JUCtrlActionBinding actionBnd =
      (JUCtrlActionBinding)bc.getControlBinding("producerMethod");
  • Binding Containers
    • A binding container holds value bindings and iterator bindings (or binding objects) for a page. It provides runtime access to all the ADF binding objects for a page.
At runtime, a web request for http://yourserver/yourapp/faces/some.jsp arrives from the client to the application server. The ADFBindingFilter (defined in web.xml) object looks for the ADF binding context in the HTTP session, and if it is not yet present, initializes it for the first time. If the appropriate binding container for the page has never been used before during the user's session, it is created. Also, if it is the first time an application module data control is referenced during the request, it acquires an instance of the application module from the application module pool. Then the control is forwarded to the page to be rendered. The UI components on the page access value bindings and iterator bindings in the page's binding container and render the formatted output to appear in the browser. Finally, the user sees the resulting page in the browser.

References

  1. Using Oracle ADF Model in a Fusion Web Application
  2. Understanding the Fusion Page Lifecycle
  3. Oracle Fusion Middleware Online Documentation Library 11g Release (11.1.1.2)
  4. Oracle ADF BindingContext and BindingContainer