Tuesday Jul 17, 2012

How-to add new ADF bound table row at the end of a table

A frequent question on OTN, which I often see answered with new rows created on the ADF BC view object level instead the ADF binding layer, is about how to create a new row for a table and append it as the last row in the table. The following code below shows how to do this properly using the binding layer only. Note that while it adds the row as the last row of the table, it doesn't do this as the last row of all possible rows that haven't yet been queried from the database. There is a difference between the last row in a table and the last row in the database.

public String onRowCreate() {
 BindingContainer bindings = BindingContext.getCurrent().getCurrentBindingsEntry();
 //access the name of the iterator the table is bound to. Its "allDepartmentsIterator"
 //in this sample
 DCIteratorBinding dciter = (DCIteratorBinding) bindings.get("allDepartmentsIterator");
 //access the underlying RowSetIterator
 RowSetIterator rsi = dciter.getRowSetIterator();
 //get handle to the last row
 Row lastRow = rsi.last();
 //obtain the index of the last row
 int lastRowIndex = rsi.getRangeIndexOf(lastRow);
 //create a new row
 Row newRow = rsi.createRow();
 //initialize the row
 newRow.setNewRowState(Row.STATUS_INITIALIZED);
 //add row to last index + 1 so it becomes last in the range set
 rsi.insertRowAtRangeIndex(lastRowIndex +1, newRow); 
 //make row the current row so it is displayed correctly
 rsi.setCurrentRow(newRow);                          
 return null;
}  

For the table to show the newly created row after this ensure:

1. The table is configured to always show the current selected row by setting  its displayRow property to selected

2. The table is PPR'ed after the row is created, which can be done declarative using the PartialTriggers property of the table pointing to the ID of the command creating the new row

But what is wrong with the approach of doing the same on the View Object level in ADF BC if it works? Well, it works but it doesn't ensure separation of the layers unless the functionality is exposed as a method on the view object interface. The code in this blog entry is generic and works the same for other business services, like EJB, POJO and Web Services for creating new table rows (which then however you need to persist explicitly in opposite to what you have to do when using ADF BC). So while the other code works, the one in this is the proper solution.

Sunday Jun 24, 2012

How-to dynamically filter model-driven LOV

Often developers need to filter a LOV query with information obtained from an ADF Faces form or other where. The sample below shows how to define a launch popup listener configured on the launchPopupListener property of the af:inputListOfValues component to filter a list of values.

<af:inputListOfValues id="departmentIdId"
   value="#{bindings.DepartmentId.inputValue}"                                      
   model="#{bindings.DepartmentId.listOfValuesModel}"
   launchPopupListener="#{PopupLauncher.onPopupLaunch}" … >      
   …
</af:inputListOfValues>

A list of values is queried using a search binding that gets created in the PageDef file of a view when a lis of value component gets added. The managed bean code below looks this search binding up to then add a view criteria that filters the query.

public void onPopupLaunch(LaunchPopupEvent launchPopupEvent) {
  BindingContext bctx = BindingContext.getCurrent();
  BindingContainer bindings = bctx.getCurrentBindingsEntry();
  JUCtrlListBinding lov = 
       (JUCtrlListBinding)bindings.get("DepartmentId");
  ViewCriteriaManager vcm = 
       lov.getListIterBinding().getViewObject().getViewCriteriaManager();          
  //make sure the view criteria is cleared
  vcm.removeViewCriteria(vcm.DFLT_VIEW_CRITERIA_NAME);
  //create a new view criteria
  ViewCriteria vc = 
         new ViewCriteria(lov.getListIterBinding().getViewObject());
  //use the default view criteria name
  //"__DefaultViewCriteria__"
  vc.setName(vcm.DFLT_VIEW_CRITERIA_NAME);
  //create a view criteria row for all queryable attributes
  ViewCriteriaRow vcr = new ViewCriteriaRow(vc);
  //for this sample I set the query filter to DepartmentId 60. 
  //You may determine it at runtime by reading it from a managed bean
  //or binding layer 
  vcr.setAttribute("DepartmentId", 60);
  //also note that the view criteria row consists of all attributes 
  //that belong to the LOV list view object, which means that you can
  //filter on multiple attributes
  vc.addRow(vcr);           
  lov.getListIterBinding().getViewObject().applyViewCriteria(vc);
} 

Note: Instead of using the vcm.DFLT_VIEW_CRITERIA_NAME name you can also define a custom name for the view criteria.

Tuesday May 29, 2012

Common mistake when iterating rows

In a blog entry from 2010, Oracle's Jobinesh Purushothaman writes about exceptions that are getting raised after developers iterate the collection of a RichTable instance using calls to setRowKey(key).

http://jobinesh.blogspot.co.uk/2010/02/common-mistake-while-iterating-through.html

The reason for the exception is that the selected row state is change on the table without synchronization to happen with the ADF binding layer. The failure of synchronizing the table selected row state with the ADF binding layer is because no row selection event is raised on the table if the row currency is changed programmatically. In theory, if you call #{bindings.treebinding.treeModel.makeCurrent}using a method expression in EL

The failed binding synchronization is because no row selection event is fired when the selected row state is changed on the table using Java in a managed bean.

A solution to this problem could be to call #{bindings.treebinding.treeModel.makeCurrent}from a method expression in Java each time you called setRowKey(key). But, assuming the iteration of rows in a table is for aggregating numeric values or to compare two rows, do you really want to change the current row in the ADF binding layer for each row you need to access?

A better implementation for this use case in my opinion is to read the data from the ADF binding layer instead of the RichTable table instance, which then will not change the row currency at all.

RichTable table = ... get table reference ...
Iterator iter = selectedRowKeys.iterator();
CollectionModel model = (CollectionModel ) table.getValue();
JUCtrlHierBinding treeBinding = (JUCtrlHierBinding )model.getWrappedData();
if (iter != null && iter.hasNext()) {
  Object rowKey = iter.next();
  JUCtrlHierNodeBinding rowData = treeBinding.findNodeByKeyPath(rowKey);     
  //Do something here

}

Note that the reported problem in Jobinesh's blog is for ADF bound tables configured for single row selection. Still I would prefer my approach over changing the selected row state on a table.


Tuesday Feb 28, 2012

Best practice invoking business services methods from JSF beans

Recently there was an increasing interest on OTN in best practices for invoking methods exposed on the ADF Business Components client interface. There exist two options to access methods exposed on ADF Business Components application modules and view objects from a managed bean through ADF.

  • Call findDataControl(String) on the BindingContext and access the application module to invoke a method exposed on the Application Module or View Object client interface.

  • Create a method binding on a PageDef level that binds to a method exposed on the Application Module or View Object client interface

Speaking of best practices, it’s the second option  to use a method binding on the PageDef file that I recommend for the following reasons

  • ADF is about abstracting the view and controller layer from the business service implementation details. A method binding instead exposes an ID (the name o the binding) to the JSF developer, which they use to access the binding from a managed bean using the OperationBinding API. Signature or name changes of a method exposed on the business service thus don't require a change in the managed bean(s) referencing it. All changes would be applied in metadata.

  • ADF provides the OperationBinding class as an abstraction for business services methods. Configuring business service method access on the PageDef file using method bindings exposes a single and consistent API to application developers.

  • Programmatic access to a method exposed on the business service require more lines of code than accessing the same method through the binding layer

  • Direct business service access violates the recommendation to always work through the ADF binding layer and not to bypass it.

To access a method binding from a managed bean, use the following code

  BindingContext bctx = BindingContext.getCurrent();
  BindingContainer bindings = bctx.getCurrentBindingsEntry();
  OperationBinding operationBinding = 
       bindings.getOperationBinding("name_of_method_binding");
  //optional
  operationBinding.getParamsMap().put("argumentName1",value1);
  operationBinding.getParamsMap().put("argumentName2",value2);
  //invoke method
  operationBinding.execute();
  if (!operationBinding.getErrors().isEmpty()) {
     //check errors
     List errors = operationBinding.getErrors();
     ...
  }
  //optional
  Object methodReturnValue = operationBinding.getResult();


Wednesday Jan 18, 2012

How to access selected rows in an af:selectManyChoice component

To create an ADF bound select many choice component, drag a collection from the Data Controls panel to a JSF page. In the opened context menu, choose Multiple Selection | ADF Select Many Choice.

This generates the page source code similar to this:

<af:selectManyChoice value="#{bindings.allDepartments.inputValue}"  
                     label="#{bindings.allDepartments.label}"
                     id="smc1">
  <f:selectItems value="#{bindings.allDepartments.items}" id="si1"/>
</af:selectManyChoice>

Note that the value property of the SelectManyChoice component pints to the sane list binding as the f:selectItems tag. To access the user selected values, you use a managed bean to access the ADF binding layer.

If you are only interested in the selected values, then you use code like shown below. Note that the sample I built used JDeveloper 11g R2, which is why the ID type is Integer. Doing the same with JDeveloper 11g R1 would require the casting to oracle.jbo.domain.Number for ID columns.

public String cb1_action() {
  BindingContext bctx = BindingContext.getCurrent();
  BindingContainer bindings = bctx.getCurrentBindingsEntry();
  JUCtrlListBinding allDepartsmentList = 
           (JUCtrlListBinding) bindings.get("allDepartments");
    Object[] selVals = allDepartsmentList.getSelectedValues();
    for (int i = 0; i < selVals.length; i++) {
      Integer val = (Integer)selVals[i];
      //...
    }
   return null;
}

However, what if you wanted to know more about the selected list value; for example the value of another attribute of the selected row? In this case you use similar code, with a little change though. Instead of calling getSelectedValues(), the call is to getSelectedIndices().


public String cb1_action() {
  BindingContext bctx = BindingContext.getCurrent();
  BindingContainer bindings = bctx.getCurrentBindingsEntry();
  JUCtrlListBinding allDepartsmentList = 
          (JUCtrlListBinding) bindings.get("allDepartments");
  int[] selVals = allDepartsmentList.getSelectedIndices();
  for (int indx : selVals ) {
    Row rw = allDepartsmentList.getRowAtRangeIndex(indx);
    //... do your stuff
  }
  return null;
}

Sunday Jan 15, 2012

How-to invoke ADF bindings in page templates

ADF Faces page templates may have their own PageDef file defined to access the ADF binding layer or have ADF bound components added to the template.

ADF Faces pages that use the template will reference the template's PageDef file in the Executable section of their own PageDef file. In this sample, the template binding reference in the the template consumer page PageDef file is ptb1.

The code below invokes a method binding in the template's PageDef file from a command button on the page. The "trick" for accessing the template's PageDef file is to know that it is another instance of BindingContainer.

public String cb1_action() {
  BindingContext bctx = BindingContext.getCurrent();
  DCBindingContainer bindings = 
                (DCBindingContainer)bctx.getCurrentBindingsEntry();
  //access the page template Pagedef file reference in the 
  //Executable section of the consumer page's Pagedef file
  DCBindingContainer templateBinding = 
                    (DCBindingContainer)bindings.get("ptb1");
  //get the MethodBinding 
  OperationBinding printMethod = 
        (OperationBinding)templateBinding .get("printThis");
  //invoke the print method exposed on the template's PageDef file
  printMethod.getParamsMap().put("message","Hello World");
  printMethod.execute();
  return null;
}
 
  

Wednesday Jan 11, 2012

Display selected table row number and total rows

A question on OTN was how to display the current row number and the number of totally available rows in a formatted output string. In the example used below, the format is
row no. [<row index>] of max rows [<total numbers of rows>]

For this example, I used an af:outputText component to display the information on the page. The value of the af:outputText component is calculated from expression language (EL) accessing the ADF binding layer as shown below. Note that for better readability the EL includes line breaks, which in your application you should not add.

 row no. [

#{(bindings.allDepartmentsIterator.rangeStart < 0 ?
1 : bindings.allDepartmentsIterator.rangeStart+1) +

( bindings.allDepartmentsIterator.currentRowIndexInRange == -1 ?

0 : bindings.allDepartmentsIterator.currentRowIndexInRange)}
]

of max rows[

#{bindings.allDepartmentsIterator.estimatedRowCount}]

Note how the expression uses the iterator binding in the ADF binding layer to determine the maximum number of table rows. It then uses the same iterator to determine the current row. Because iterators are zero based, the EL uses additional logic to compensate this.

The af:outputText component needs to have its PartialTriggers property pointing the the af:table component ID to ensure the displayed information is refreshed when the table row currency changes.

 <af:outputText value="row no. [#{(bindings.allDepartmentsIterator.rangeStart &lt; 0 ? 1  :  bindings.allDepartmentsIterator.rangeStart+1) +( bindings.allDepartmentsIterator.currentRowIndexInRange ==  -1 ? 0 : bindings.allDepartmentsIterator.currentRowIndexInRange)}] of max rows [#{bindings.allDepartmentsIterator.estimatedRowCount}]"
                id="ot1" partialTriggers="pc1:t1"/>


Friday Aug 05, 2011

row.attributeName vs. row.bindings.attributeName

ADF uses two different types of EL to reference the binding layer in af:tree, af:treeTable and af:table components.

For example, if a table is read-only, using af:outputText to render the table column values, the cell values are referenced as #{row.attributeName}. If the table is editable, the EL is #{row.bindings.attributeName.inputValue}.

In the case of #{row.attributeName}, the value object is of type String.

In the case of #{row.bindings.attributeName.inputValue}, the value object is of type, FacesCtrlAttrsBinding, an internal class that extends JUCtrlAttrsBinding, which implements AttributeBinding. This object is updateable and allows changing column cell data through its inputValue property.

Another example is #{node.attributeName}, which is used as a value reference in read-only tree components. "node" and "row" are EL variables defined on the tree or table component variable property.

Tuesday Aug 02, 2011

Accessing database connect information in ADF

To get the database connect information of an ADF BC model in ADF, expose the following code in a public method on the ApplicationModule Impl class

public String getDatabaseInformation(){
  DBTransaction dbTransaction = (DBTransaction) this.getTransaction();
  PreparedStatement prepStatement = 
              dbTransaction.createPreparedStatement("select * from dual", 0);    
  try {
    String dbSchema = 
                  prepStatement.getConnection().getMetaData().getUserName();
    String connectURL = 
                  prepStatement.getConnection().getMetaData().getURL();
    //returns schema_name@host:port:sid
    return dbSchema + connectURL.substring(connectURL.indexOf("@")-1);             
   } catch (SQLException e) {
      e.printStackTrace();
   }
    return null;
} 

Expose this method on the AM client interface if you need this to be accessible from the ADF client, for example a method binding in a PageDef file, or a managed bean. 

 

Monday Jun 20, 2011

Getting selected row in inputListOfValues returnPopupListener

Model driven list-of-values in Oracle ADF are configured on the ADF Business component attribute which should be updated with the user value selection. The value lookup can be configured to be displayed as a select list, combo box, input list of values or combo box with list of values.

Displaying the list in an af:inputListOfValues component shows the attribute value in an input text field and with an icon attached to it for the user to launch the list-of-values dialog.

The list-of-values dialog allows users to use a search form to filter the lookup data list and to select an entry, which return value then is added as the value of the af:inputListOfValues component.

Note: The model driven LOV can be configured in ADF Business Components to update multiple attributes with the user selection, though the most common use case is to update the value of a single attribute.

A question on OTN was how to access the row of the selected return value on the ADF Faces front end. For this, you need to know that there is a Model property defined on the af:inputListOfValues that references the ListOfValuesModel implementation in the model. It is the value of this Model property that you need to get access to.

The af:inputListOfValues has a ReturnPopupListener property that you can use to configure a managed bean method to receive notification when the user closes the LOV popup dialog by selecting the Ok button. This listener is not triggered when the cancel button is pressed. The managed bean signature can be created declaratively in Oracle JDeveloper 11g using the Edit option in the context menu next to the ReturnPopupListener field in the PropertyInspector. The empty method signature looks as shown below

public void returnListener(ReturnPopupEvent returnPopupEvent) { }

The ReturnPopupEvent object gives you access the RichInputListOfValues component instance, which represents the af:inputListOfValues component at runtime. From here you access the Model property of the component to then get a handle to the CollectionModel.

The CollectionModel returns an instance of JUCtrlHierBinding in its getWrappedData method. Though there is no tree binding definition for the list of values dialog defined in the PageDef, it exists. Once you have access to this, you can read the row the user selected in the list of values dialog. See the following code:

public void returnListener(ReturnPopupEvent returnPopupEvent) {  
  //access UI component instance from return event
  RichInputListOfValues lovField = 
       (RichInputListOfValues)returnPopupEvent.getSource();
  
  //The LOVModel gives us access to the Collection Model and 
  //ADF tree binding used to populate the lookup table
  ListOfValuesModel lovModel =  lovField.getModel();
  CollectionModel collectionModel =
         lovModel.getTableModel().getCollectionModel();   
  
  //The collection model wraps an instance of the ADF 
  //FacesCtrlHierBinding, which is casted to JUCtrlHierBinding
  JUCtrlHierBinding treeBinding =   
         (JUCtrlHierBinding) collectionModel.getWrappedData();
  
  //the selected rows are defined in a RowKeySet.As the LOV table only
  //supports single selections, there is only one entry in the rks
  RowKeySet rks = (RowKeySet) returnPopupEvent.getReturnValue();
  
  //the ADF Faces table row key is a list. The list contains the 
  //oracle.jbo.Key
  List tableRowKey = (List) rks.iterator().next();
  
  //get the iterator binding for the LOV lookup table binding  
  DCIteratorBinding dciter = treeBinding.getDCIteratorBinding();
  
  //get the selected row by its JBO key  
  Key key = (Key) tableRowKey.get(0);
  Row rw =  dciter.findRowByKeyString(key.toStringFormat(true));

  //work with the row
  // ...

}

Tuesday May 24, 2011

Determine the arguments of an operation binding at runtime

On the OTN forum, Navaneetha Krishnan answered a question of how to access an Operation Binding at runtime and determine the arguments it expects. Background of the question is that if you call getParamsMap() on the Operation Binding, it does not contain the keys of the method arguments.

The example code shown below accesses an OperationBinding that is defined in the PageDef file for a method. It then dynamically parses the binding definition for configured method argument names.

For example, a method to relocate employees may be exposed on the ADF Business Component model and exposed in the Data Control panel for drag and drop UI binding in ADF. The method signature of the sample may be as shown below

relocateEmployee(Number departmentId, Number employeeId,
Boolean withSalaryRaise, Long salaryRaiseInPercent)

The easiest way for you to create a method like the one below, is to drag and drop the method from the DataControls panel to the page and have it rendered as a button or link. A binding dialog opens for you to define default values or reference objects that provide the argument values at runtime.

Double click the command item to create a managed bean method that contains generated code for the operation to invoke ("relocateEmployee" in the sample). The following managed bean method was built this way and then extended with the code posted by Navaneetha.

The code sample has a place holder line for where your application would look up the argument values to pass to the method. For example, if there is a HashMap available (e.g. exposed by a managed bean, or passed as an input parameter to a bounded task flow) then you could check if it contains values for the operation argument names you read from the OperationBinding.

import
java.util.Map;

import oracle.adf.model.BindingContext;
import oracle.adf.model.OperationParameter;
import oracle.adf.model.binding.DCInvokeMethod;
import oracle.adf.model.binding.DCInvokeMethodDef;
import oracle.binding.BindingContainer;
import oracle.binding.OperationBinding;
 //method called from a
command item in ADF Faces 
public String onRelocateEmployee() {
    //generated binding access code
    BindingContainer bindings = getBindings();
    //access the method binding in the  PageDef file
    OperationBinding operationBinding = 
                     bindings.getOperationBinding("relocateEmployee");
    //to pass arguments to an operation binding, a Map is used.
    //the Map is retrieved by a call to getParamsMap on the operation
    //binding
    Map operationParamsMap = operationBinding.getParamsMap();
    //get access to the operation definition
    DCInvokeMethod method = 
                  (DCInvokeMethod)operationBinding.getOperationInfo();
    if (method != null) 
    {
      DCInvokeMethodDef methodDef = method.getDef();
        if(methodDef != null) 
        {
           OperationParameter[] operationParameters = null;
           operationParameters  = methodDef.getParameters();
           if (operationParameters != null) {
             for (OperationParameter operationParameter : operationParameters) 
             {
               String argumentName = operationParameter.getName();
               Object argumentType = operationParameter.getTypeName();
               Object defaultValue = operationParameter.getValue();
               if (argumentName != null) {
                 Object value = <determine value for argumentNname>; 
                 operationParamsMap.put(argumentName, value != null ? 
                                        value : defaultValue); 
               }
             }
           }
        }
    }
    //the operation arguments are provided. Now it is time to
   //execute it
   Object result = operationBinding.execute();
   if (!operationBinding.getErrors().isEmpty()) 
   {
     //TODO log error
     //TODO handle error
     return null;
  }
  return null;
}

Sunday May 15, 2011

TypeConversionException when using af:validateDateTimeRange

ADF Faces provides the af:validateDateTimeRange tag in the Operations category of the Component Palette for applications to enforce valid date and time ranges.

However adding the af:validateDateTimeRange to an ADF Business Components bound date field may cause a type conversion exception at runtime as shown below:

<LifecycleImpl> <_handleException> ADF_FACES-60098:Faces lifecycle receives unhandled exceptions in phase PROCESS_VALIDATIONS 3
org.apache.myfaces.trinidadinternal.convert.TypeConversionException: Could not convert instance:2011-05-17 of type:class java.lang.String into type:class java.util.Date
at org.apache.myfaces.trinidadinternal.convert.GenericConverterFactory.convert (GenericConverterFactory.java:289)
at org.apache.myfaces.trinidadinternal.validator.DateTimeRangeValidator. validate(DateTimeRangeValidator.java:63
)

The exception is thrown when a user updates the date field and submits the form. The reason for this exception is that the date field in ADF Business Components has a simple date format configured in its UI Control hints. To avoid this error, you need to unset the simple date format configuration on the View Object or Entity Object.

The UI Control hint is not automatically set when creating ADF Business Components models, which helps to control the setting of simple date patterns to date fields that don't require enforcing a specific date and time range

Thursday Apr 21, 2011

Using multiple Data Controls in ADF applications?

[Read More]

Friday Apr 15, 2011

Passing parameters to managed bean methods using EL

[Read More]

Thursday Apr 14, 2011

How to access the WS SOAP message using WS DC

[Read More]
About

The Oracle JDeveloper forum ranks in the Top 5 of the most active forums on the Oracle Technology Network (OTN).



The OTN Harvest blog is a summary of selected topics posted on the OTN Oracle JDeveloper forum.



It is an effort to turn knowledge exchange into an interesting read for developers who enjoy little nuggets of wisdom





Frank Nimphius

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today