Tuesday, May 31, 2011

Checking for dirty data

Sometimes you may need to check for the modified state of the business data. If your data update is always triggered through ADF binding layer(where the UI component is bound with data using EL #{bindings.xxx.inputValue}), then its an easy job as all the data access is routed through a common entry point. You can safely use DCDataControl::isTransactionModified() - This returns true if you have modified any of the attribute values exposed by the DataControl. You can use this API even if you use EJB and JavaBean Data Control as well.

DCBindingContainer dcBindingContainer=(DCBindingContainer)
BindingContext.getCurrent().getCurrentBindingsEntry();
if(dcBindingContainer.getDataControl().isTransactionModified()){
//Code goes here...
}

Please note that for those Data Controls which does not have direct implementation for the 'transaction commit'(e.g: EJB or JavaBean Data Control ), its your duty to clear the modified flag once you persist the changes by calling the below given API.

dcBindingContainer.getDataControl().commitTransaction();

Tuesday, May 24, 2011

Setting the bind variable value for the destination View Object in the View Link

I'm sharing an interesting use case that has come up recently from an internal team. The developer was looking for some hooks to set bind variable value used in the destination View Object participating in a View Link. Steve Muench has shared a useful tip to solve this case, let me share the same here for your reference, thanks Steve :)

The use case requirement is to set bind variable value conditionally for the child view object. Interestingly, the bind variable value is supposed to set programmatically only when the child view object is executed as part of master child coordination(through view link), in all other cases it's left to the caller.

The solution is very straight forward. The 11.1.1.5.0 release has new method added ViewObjectImpl::prepareRowSetForQuery(ViewRowSetImpl vrs) to intercept the VO query preparation phase. You may need to override this method, and add check for parent rows(to see whether VO instance is participating in master child coordination) and set the bind variable accordingly.

 @Override  
 public void prepareRowSetForQuery(ViewRowSetImpl vrs) {  
  RowSetIterator[] parentRows = vrs.getMasterRowSetIterators();  
  /**  
  * Check for parent Rows. If its not null, then query execution is   
  * triggered through ViewLink, get the required attributes from   
  * parent row( as per the use case ), and set the bind variable value  
  */  
  if (parentRows != null && parentRows.length > 0 &&  
      parentRows[0].getCurrentRow() != null) {  
      Number locId =  
           (Number)parentRows[0].getCurrentRow().getAttribute("LocationId");  
      //This is just a demo, to hide the employees whose salry is less   
      //than 3000$ from Location = 1500  
      if (locId != null && locId.intValue() == 1500) {  
           vrs.ensureVariableManager().setVariableValue("bindVarSalary",  
                                                                   3000);  
      }  
  }  
  super.prepareRowSetForQuery(vrs);  
 }  

Download

You can download the sample workspace from here.
[Runs with Oracle JDeveloper 11.1.1.5.0 (11g R1 PS4) + HR Schema]

A glance at the implementation

This example displays two employee tables, the one which follows master child relation with department table displays only those employee records having salary > 3000. This filtering is done by following the approach detailed above.




Learn More ...

There are a lot more points like this. If  you are curious to learn the internals of the ADF Business Components and ADF Binding Layer,  the following book is for you - Oracle ADF Real World Developer’s Guide.
More details about this book can be found in this post- http://jobinesh.blogspot.in/2012/07/pre-order-your-copy-of-oracle-adf-real.html

Thursday, May 19, 2011

Why do you see a duplicate af:commandToolbarButton after partial page refresh?

This is a very widely observed issue if you have partialTriggers set for child components of <af:toolbar>.
 <af:toolbar>  
 <af:commandToolbarButton ... partialTriggers="someComp"/>  
 </af:toolbar>  

What goes wrong here?

Well, please see the tag documentation for <af:toolbar> . I'm copying the relevant content for your reference.

Updating through PartialTriggers

Note that if a toolbar child component is going to be updated through partial page rendering, you need to update the toolbar as well. You do this by adding the child components id to the toolbar's partialTriggers attribute. This way the toolbar can appropriately manage its children in respect to sizing and placement in overflow. Also note that many input components also update themselves through partial page rendering. If you include an input component (or quickQuery) on a toolbar, you should include that child's id in the toolbar's partialTriggers attribute so that the toolbar also updates when the input component is replaced. Because this partialTrigger will cause overflow to close during the toolbar ppr, toolbar child components that update themselves via ppr outside of a submit (like InputComboboxLOV) will work better when not in overflow.

In a nutshell, if you change the above tag snippet to refresh the af:toolbar (instead of child elements) as shown below, issue will be solved and you can see the partial page refresh works for <af:commandToolbarButton> without any side effects.

 <af:toolbar partialTriggers="someComp">  
 <af:commandToolbarButton ... />  
 </af:toolbar>  

Wednesday, May 18, 2011

Soft deletion of rows

In this post I'm sharing a simple example illustrating the 'soft deletion' of rows and undoing the delete later if the user opts for. The business use case requirement is that, whenever end user delete rows, system should not delete these rows from the underlying data base table, rather should mark(update) them as deleted. Later user can undo the deletion to restore the deleted rows(this operation should not rollback the transaction)


Download

You can download the sample workspace from here.
[Runs with Oracle JDeveloper 11g R1 PS4 + HR Schema]

A glance at the implementation

The 'Soft Delete' action has two steps,
1.The action first refreshes the row with database by calling row.refresh(Row.REFRESH_WITH_DB_FORGET_CHANGES) to discard the modifications that user might have done on the row before opting for delete(if any)
2. Next step is to remove the row from the iterator(effectively from entity object). Deleted row keys are stored in an ArrayList for performing 'undo' operation if needed.

Later when user commits transaction, entity instance will check for operation flag and will change it to UPDATE if the value is DELETE, and would flag an attribute as well to enable soft delete at database table level. In the above sample commits action updates DepartmentName attribute by prefixing '[DELETE]' token . The view object query has necessary where clause added to remove the soft deleted rows from the result list.

The roll back action of deleted rows refreshes the deleted entity rows with data base to restore them back to the original state.


Learn More ...

There are a lot more points like this. If  you are curious to learn the internals of the ADF Business Components and ADF Binding Layer,  the following book is for you - Oracle ADF Real World Developer’s Guide.
More details about this book can be found in this post- http://jobinesh.blogspot.in/2012/07/pre-order-your-copy-of-oracle-adf-real.html

Monday, May 16, 2011

ADF BC run time now calls refreshCollection(...) on your shared ViewObject !

There is an interesting enhancement available on ViewObjectImpl::executeQuery() with 11.1.1.5.0 release. Now the executeQuery() will check for the View Object's parent Application Module configuration to see if its defined as 'shared AM' and may call refreshCollection(...) for shared View Objects. So you don't need to worry on calling the confusing refreshCollection(...) to access cached query collection without reexecuting the query. You are allowed to call ViewObjectImpl::executeQuery() always in a seamless way :)

If you really need to forcefully execute query on shared View Object, then please use ViewObjectImpl::forceExecuteQueryOfSharedVO() - API available since 11.1.1.5.0

Saturday, May 14, 2011

Preparing ViewObject's query for execution

If you need to assign bind variable values or modify where clause conditionally just before the the query execution, then 11.1.1.5.0(PS4) release has a new method added ViewObjectImpl::prepareRowSetForQuery(ViewRowSetImpl vrs). This method engages well with ViewObjectImpl::getQueryHitCount()and ViewObjectImpl::getEstimatedRowCount() as well. I mean the prepareRowSetForQuery(...) will get invoked before calling the the above methods by the runtime. This is useful if you need to set bind variable values before the query execution for List Of Values or ViewAccssors used for displaying Tree components.

You can use this method safely(zero side effect) to add your logic for tweaking default query by setting bind variable values or additional where clause at run time.
Example:

public class DepartmentsViewObjectImpl extends ViewObjectImpl{
...
@Override
public void prepareRowSetForQuery(ViewRowSetImpl vrs) {
vrs.ensureVariableManager().setVariableValue("bindVarDeptId", deptIdRuntimeValue);
super.prepareRowSetForQuery(vrs);
}
...

Learn More ...

There are a lot more points like this. If  you are curious to learn the internals of the ADF Business Components and ADF Binding Layer,  the following book is for you - Oracle ADF Real World Developer’s Guide.
More details about this book can be found in this post- http://jobinesh.blogspot.in/2012/07/pre-order-your-copy-of-oracle-adf-real.html

Friday, May 6, 2011

Oracle JDeveloper and Oracle ADF 11g Release 1 Patch Set 4 is now available on OTN

Oracle JDeveloper and Oracle ADF 11g Release 1 Patch Set 4 (11.1.1.5.0)(Build 6013) is available on OTN. Check it out

Wednesday, May 4, 2011

Turning off the stretchable behavior of <af:inputComboboxListOfValues>

The <af:inputComboboxListOfValues> has been enhanced recently(11.1.1.4.0) to stretch the contents of the drop down list to reflect the display width of the attribute that you set on a view object.


This feature is controlled by a boolean ADF skin property, -tr-stretch-dropdown-table. Web UI Developers Guide says...
This property determines whether the table in the dropdown list stretches to show the content of the table columns or limits the width of the table to the width of the input field in the inputComboboxListOfValues component.

You can turn off the stretchable behavior using the following selector in your css file. Customizing the skinning is already discussed in one of my previous post- Style your application in your own way. More details on skinning can be found in the following link as well - Apache Trinidad Skinning

af|inputComboboxListOfValues{
-tr-stretch-dropdown-table: false;
}

Tuesday, May 3, 2011

Multiple root Application Modules sharing the same transaction context !

I'm not sure how many of you noticed the following section in Fusion Developers Guide - 10.2.6 What You May Need to Know About Shared Application Modules and Connection Pooling. This part talks about an AM configuration parameter 'jbo.shared.txn' which can be used for sharing entity cache and DB connection across multiple root application modules. You just need to key in same string value for 'jbo.shared.txn' using the properties page of the editor for the bc4j.xcfg file of the root application modules.

Please note that this property is applicable for shared as well as non shared Application Modules(though originally designed for non-shared AM). Interestingly, this is used internally by the declarative taskflow transaction to allow the ADF BC Data Controls to share a DBTransaction. I'm copying the relevant content from developers guide below for a quick reference...

When your application defines more than one shared application module, you can change the default to optimize database connection usage by defining a named transaction for each root application module to use. The transaction name is an arbitrary string that you set on the jbo.shared.txn property in the Properties page of the editor for the bc4j.xcfg file of the root application module. At runtime, the root application modules with the same jbo.shared.txn property setting (identified by the string you supply) will share the same database connection and etity cache

Note: Please note that in the above case the transaction would be shared (across user sessions) between all Application Modules having the same 'jbo.shared.txn' value. In case of task flow, the data binding layer supplies the jbo.shared.txn value derived from user session to ensure unique value for each user session(transaction context). And this is of course driven by the transaction attribute you set for task flow at design time.


Learn More ...

There are a lot more points like this. If  you are curious to learn the internals of the ADF Business Components and ADF Binding Layer,  the following book is for you - Oracle ADF Real World Developer’s Guide.
More details about this book can be found in this post- http://jobinesh.blogspot.in/2012/07/pre-order-your-copy-of-oracle-adf-real.html