Cross Column

Showing posts with label ADF Business Components. Show all posts
Showing posts with label ADF Business Components. 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

Sunday, May 1, 2011

Invoking Application Module Custom Methods from Oracle ADF Frontend—Why and How?

In an Oracle Fusion web application, you should use managed beans[4] to store logic that is related to the UI rendering only. All application data and processing should be handled by logic in the business layer of the application.

In this article, we'll show how to refresh a calculated attribute[6] named Total Comments after user adds a new comment using Create Comment Dialog. As noted above, refreshing Total Comments should be handled in ADF backend (i.e., ADF Business Components), not ADF frontend (i.e., ADF Faces). Note that ADF backend refers to Model project and ADF frontend refers to ViewController project in a Fusion web application.

Background Information

In the described application, we have two view objects (i.e., TestVO and TestCommentVO) with master-detail relationship. "TotalComments" on the TestVO is a calculated attribute which computes total number of chronological comments (represented by TestCommentVO) added to a test.

On the Test View page, it displays Total Comments column which is also a link. By clicking on the link, user can navigate to the Test Comment page. On the Test Comment page, user is allowed to add new comments. After adding new comments, the Total Comments column on the original Test View page need to reflect the new total.



The Task

To achieve this task, we need to invoke a custom method implemented in ADF Business Components from the Test Comment component implemented in ADF Faces. Method invokation is triggered when user clicks the Save and Close button to save the newly created comment.
The bridge provided between ADF frontend and ADF backend is the Data Binding Layer in Fusion web application[3]. ADF data-binding layer implements the Data Controls and Bindings from JSR-227. The data controls abstract the back-end business service implementations and binding objects link front-end user interface controls in a declarative way to back-end data.

The steps of the task ordered from backend (in blue) to frontend (in black) are summarized here:
  1. Creating a custom method named refreshTotalComments in the application module Class (i.e., SelTestPortalAMImpl.java).
  2. Exporting refreshTotalComments by including the method on the application module's UI client interface.
  3. Adding a new action binding[5] named refreshTotalComments on TestCommentPageDef.xml.
  4. Creating a managed bean named TestCommentBean.java with a method action named saveAndClose. In the method, it will retrieve and execute refreshTotalComments operation binding.
  5. Registering new managed bean in adc-config.xml with the scope of request.
  6. Linking method action saveAndClose on the managed bean to the actionlistener of Save and Close button.
In the following sections, we'll describe those steps in details.

Step 1 — Adding Custom Method to the AppModuleImpl File

To add a custom service method (i.e., refreshTotalComments) to your application module, you must generate the application module class files first.

To generate Java files for your application module class:
  1. In the Application Navigator, double-click the application module.
  2. In the overview editor, click the Java navigation tab and click the Edit java options button.
  3. In the Select Java Options dialog, select Generate Application Module Class.
  4. Click OK.

In SelTestPortalAMImpl.java file, you add your custom service method — refreshTotalComments. Here we have left out the details of refreshTotalComments. For ADF developers need to write a custom method such as refreshTotalComments, you can read this document. It provides a high-level description of the key ADF Business Components classes in the Oracle Application Development Framework and it summarizes the methods that ADF developers write, call, and override most frequently.

Step 2 — Publishing Custom Method on the Application Module’s Client Interface

To include a public method from your application module's custom Java class on the client interface, use the Java Classes page of the overview editor for the application module, and then click the Edit icon in the Client Interface section of the page to display the Edit Client Interface dialog. Select method refreshTotalComments from the Available list and click the Add button to shuttle them into the Selected list. Then click OK to close the editor.


You can include any custom method in the client interface that obeys these implementation rules:
  • If the method has a non-void return type, the type must be serializable (i.e., implements the java.io.Serializable interface).
  • If the method accepts any parameters, all their types must be serializable.
  • If the method signature includes a throws clause, the exception must be an instance of JboException in the oracle.jbo package.

Step 3 — Adding a New Action Binding

In the ViewController project, select Test Comment page and right click it. From the context menu, select "Go to Page Definition." This will bring you to the TestCommentPageDef.xml.

In the Overview editor for the Test Comment page definition’s Bindings and Executables tab, click the Add icon in the Bindings section. Then, follow these steps:
  1. In the Insert Item dialog, select methodAction and click OK.
  2. In the Create Action Binding dialog:
    • Select the data collection (i.e., SelTestPortalAMDataControl) where you have created your handler.
    • From the Operation dropdown list, select the handler (i.e., refreshTotalComments).
    • Click OK.



Step 4 — Creating a New Managed Bean

In the ViewController project, create a managed bean named TestCommentBean.java with a method action named saveAndClose as shown below.
public void saveAndClose(ActionEvent actionEvent) {
try{
// Commit the transaction
OperationBinding  oper = getOperBindings("Commit");
oper.execute();
// refresh total comments
oper = getOperBindings("refreshTotalComments");
oper.execute();
}catch(Exception e){
e.printStackTrace();
}
}
/**
* This methos returns the operation bindings of the passed method name
* @param operation name
* @return
*/
private OperationBinding getOperBindings(String operation) throws Exception
{
OperationBinding oper=null;

try
{
FacesContext facesContext = FacesContext.getCurrentInstance();
ExpressionFactory exp = facesContext.getApplication().getExpressionFactory();
DCBindingContainer bindingContainer =
(DCBindingContainer) exp.createValueExpression(facesContext.getELContext(),
                                      "#{bindings}",
                                       DCBindingContainer.class).getValue(facesContext.getELContext());
// A ControlBinding that binds an datacontrol action, including navigational
// actions such as "Next" and "Prev", or other actions such as "Commit" and
// "Rollback", to a view component.
oper = bindingContainer.getOperationBinding(operation);
}
catch(Exception e)
{
e.printStackTrace();
}
return oper;
} //end getOperBindings

As shown in the code, we need to retrieve two operation bindings (i.e., Commit and refreshTotalComments) from the binding container[7]. Operation binding refreshTotalComments is used at runtime to invoke our Application Module custom method.

Step 5 — Registering New Managed Bean in adc-config.xml

As a general rule for Fusion web applications, a bean that may be used in more than one page or task flow, or one that is used by pages within the main unbounded task flow (adfc-config), should be registered in the adfc-config.xml configuration file.

To register TotalCommentBean in adc-config.xml, do the following:
  1. In the Application Navigator, double-click the adfc-config.xml file in theWEB-INF folder.
  2. In the editor window for the adfc-config.xml file, click the Overview tab.
  3. In the Managed Beans page, in the Managed Beans section click the Add icon and enter TotalCommentBean as the name of the bean, enter the fully qualified class name, and select request scope[8].

Step 6 — Linking Method Action to the ActionListener of Button

The final step is to link method action saveAndClose on the managed bean to the actionListener of Save and Close button as shown below:
<af:commandbutton id="FAsc1"
textandaccesskey="#{applcoreBundle.SAVE_AND_CLOSE}"
shortdesc="#{applcoreBundle.SAVE_AND_CLOSE_SHORT_DESC}"
actionlistener="#{TestCommentBean.saveAndClose}">
</af:commandbutton>
Note that you can achieve this linking declaratively using Property Inspector.

The Explanation

An application module can expose its data model of view objects to clients without requiring any custom Java code. This allows client code to use the ApplicationModule, ViewObject, RowSet, and Row interfaces in the oracle.jbo package to work directly with any view object in the data model.

Whenever there is a need to provide custom codes as shown in our sample application, you should encapsulate the details by writing a custom method in your application module's Java class.

When working with Fusion web applications using the ADF Model layer for data binding, JDeveloper configures a servlet filter in your user interface project (i.e., ViewController) called the ADFBindingFilter. It orchestrates the automatic acquisition and release of an appropriate application module instance based on declarative binding metadata, and ensures that the service is available to be looked up as a data control using a known action binding or iterator binding, specified by any page definition file in the user interface project.

At runtime, you can access the application module's client interface from the DCBindingContainer by naming an ADF action binding or an ADF iterator binding (see step 4). You can also reference the binding context[7] and call methods on the custom client interface in any JSF managed bean.

Reference(s)

  1. Most Commonly Used Methods in ADF Business Components
  2. A detailed look at Binding Model Parameter Options (NDOption)
  3. Oracle ADF Model In Depth
  4. Managed Beans in Oracle Fusion Web Applications
  5. Action Bindings and Method Bindings in Oracle ADF 11g
  6. Examining View Object Attributes in Oracle ADF 11g
  7. Oracle ADF BindingContext and BindingContainer
  8. Types of Memory Scopes in Fusion Web Applications
  9. Oracle Fusion Developer Guide: Building Rich Internet Applications with Oracle ADF Business Components and Oracle ADF Faces
  10. Open Source sample applications for Oracle Fusion Middleware technology
  11. Using Bind Variable to Implement Range Selection Declaratively (Xml and More)
  12. Book Review: Developing Web Applications with Oracle ADF Essentials (Xml and More)