Saturday, August 22, 2009

How to Skip Validation?

ADF has a very robust validation framework. Validations can be added at different layers (view, model and business services) based on your application's requirement. To learn more about the ADF validation framework, please go through the Fusion Developer's Guide for Oracle Application Development Framework.
That said, however, in a real business scenario there are cases where the validation needs to be skipped (or by passed) conditionally while submitting the form. This post discusses this topic with some common use cases.

Keep immediate=true

A very common way to skip validation is by keeping the value for immediate attribute as 'true' for the UIComponents. Immediate attribute allow processing of components to move up to the Apply Request Values phase of the lifecycle.

Use case scenario

While canceling a specific action, system should not perform the validation. One possible way to achieve this is by keeping immediate=true associated with UI element. To know more abut this feature, please refer this wiki.
Sometimes you may need to update UI controls with model data as part of the ‘immediate’ action. Make sure that you call resetValue() on the UIInput in this scenario. This causes to reset the submitted value and would ensure that UI gets updated with right value from the model.

SkipValidation

Another approach is to use SkipValidation property associated with the page definition file. SkipValidation by pass the model layer validation based on the specified parameter value.

Please note that:
1. This doesn’t have any impact on JSF/ADF Lifecycle.
2. This property doesn’t have any effect on the client side validation (even if it is generated using the EL bound with model layer)


Where can I locate SkipValidation?

SkipValidation can be located as part of the root node of the page definition file.



Possible values for SkipValidation are given below.



a. SkipValidation=true

Setting ‘SkipValidation’ property to true will bypass the ADF Model validation. In this case validation happens only when you commit the transaction( i.e: when you call transaction.commit()). Please note that SkipValidation skips only the entity level validation. The attribute level validation fire irrespective of the value for the boolean flag

Use case Scenario

This is useful where you need to delay the validation till the point where transaction gets committed to Database. There are cases where business data needs to be captured in bulk. A typical example would be capturing transaction data using a grid form/tabular list. Business user must be able to key in values in any order. i.e. even without completing the data capture for the current record, user must be able to move to next set of records. Here model level validation needs to be delayed till we really save the data. SkipValidation=true is the right option to achieve this sort of use case, which does the validation across records, only while committing the transaction.

b. SkipValidation=custom

In this case, framework validates only the 'CustomValidator' specified in page definition file.
The value of the 'CustomValidator' property would be an EL expression that evaluates to a bean that implements the oracle.binding.BindingContainerValidator interface



Use case Scenario

This gives more control to the developer while validating the data. Only validates(or rather executes) what is given in custom implementation for BindingContainerValidator. It is much useful in complex business scenarios where developer needs finer control on validation.

c. SkipValidation=skipDataControls

skipDataControls means that it will skip the data control level validation (also known as Transaction Level validation) and instead only will validate data in the row or rows which got updated through iterator in the current binding container.

Use case Scenario

The property is very much useful while dealing with below given scenarios.
1. A single entity is mapped to multiple tabs in a page.
2. Same page is making use of multiple Data Controls. Here commit on one Data Control should not trigger validation on other Data Controls defined in the same page.


d. SkipValidation=false

This is the default value for SkipValidation, apparently this setting just ensures the normal flow for validation during form submission.

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, August 17, 2009

How to keep track of modified values of controls in a JSF application?

I recently encountered an interesting use case where ADF Faces is used for the view layer and the data(model) is persisted in LDAP Store. Now the requirement is to identify the modified records/attributes and perform the update only on these records. There may be different ways to achieve this, say for example using container like Spring with AOP. But, is there any other smart approach without adding extra layer to my application's technology stack? Answer is 'Yes'. Let us explore one possible approach for this use case scenario.

ELResolver

The solution is based on customized ELResolver. We know that backing beans are bound to the view(jsf pages) using Expression Language(EL). ELResolver actually resolves thesa 'binding expressions' at runtime. So idea is to intercept in the invocation of setters for the backing beans through a customized ELResolver. And add the logic to track the modified attributes at this point. JSF specification enables to add customized javax.el.ELResolver by modifying faces-config.xml.

Example

<faces-config version="1.2" xmlns="http://java.sun.com/xml/ns/javaee">
<application>
<default-render-kit-id>
oracle.adf.rich
</default-render-kit-id>
<el-resolver>
view.extension.SmartELResolver
</el-resolver>
</application>
</faces-config>


Now we can hook the custom logic to address the above requirement by overriding the ELResolver::setValue(...). Adding this logic is left to you, needless to say your backing bean may need to follow predefined contract to enable the centralized processing at ELResolver. What I meant is, bean may need to implement a predefined interface and the ELResolver can work on the 'instance of' this specific object type.

You can download the sample workspace from here.

Wednesday, August 5, 2009

Tips on LOV Runtime

Do you know how does LOV query get executed if there doesn't exist any entity instance to back up the source ViewObject? The scenario usually arises if you have LOV defined for an attribute, which is part of query panel. This article discusses this specific scenario with a use case.

Use case

Let us take the classical Employee entity as example. This 'Employee' is based on HR schema. The detailed entity is shown below.

You can see that each Employee falls under specific Department and Manager. This means that while defining a new Employee record, it's required to associate an existing Employee as manager to the new record. To improve the usability, let us define a LOV against manager attribute in Employee ViewObject. Let us make this LOV much smarter by adding an additional requirement to filter down the values of ‘Manager LOV’ based on logged in user's Department Id. So 'Manager LOV' should display only those Employees who belong to the same Department as that of the business user who logged in to the system. There are many ways to query LOV based on this dynamic condition. One-way may be to override the ‘VewAccessor’ defined at EmployeesViewRowImpl and add the extra 'where' clause as shown below.

/**
* Gets the view accessor <code>RowSet</code> EmployeesImplVA.
*/
public RowSet getEmployeesVA() {

RowSet rs = (RowSet)getAttributeInternal(EMPLOYEESWRONGIMPLVA);
rs.setNamedWhereClauseParam("bindVariableDepartmentId", 80);
rs.executeQuery();
return rs;

}


Assume that we adopted this approach for realizing this use case. Now the real fun starts.
You can see that while running the application, if the LOV is invoked from query panel it displays all values without appending the extra 'where clause' added through 'getEmployeesVA()'. Where as LOV from a Data Capture Form or Table, filter out the values as we expected.

What has gone wrong?

When LOV is invoked from query panel (af:query) there doesn’t exist any record(ViewRow) for the source ViewObject. Hence framework uses slightly different execution path. LOV from query panel uses an internal class oracle.adf.model.bean.DCDataRow, which is different from EmployeesViewRowImpl. Obviously there is no chance to execute the overridden methods from EmployeesViewRowImpl.

However, there are solutions to this specific scenario.

Solutions

1. You can override the getCriteriaItemClause(..) method in EmployeesViewImpl which is the source ViewObject for the Manager LOV. Since the LOV uses EmpDeptViewCriteria, this method will be called for every ViewCriteriaItem in EmpDeptViewCriteria while generating where clause. This will be called irrespective of whether the LOV is invoked from Query Panel or from 'Data Capture Form’.
@Override
public String getCriteriaItemClause(ViewCriteriaItem viewCriteriaItem) {
if ("EmpDeptViewCriteria".equals(viewCriteriaItem.getViewCriteria().getName())
"DepartmentId".equals(viewCriteriaItem.getName()))
{
ensureVariableManager().setVariableValue("bindVariableDeptId", new Number(80));
}
return super.getCriteriaItemClause(viewCriteriaItem);
}


2. Another possible approach is to keep the 'DepartmentId' in User Session and access it using Groovy expression e.g.: adf.userSession.userData.SomeKey . Below diagram shows how does the ViewCriteria look like.



You can download the sample workspace from here.

I would like to thank Sathish Kumar, Prakash ILLANCHERAN and Srinathreddy Komatireddy for assisting me in writing this blog.

Saturday, August 1, 2009

Programmatic PPR on af:outputText / af:outputFormatted

Partial page rendering (PPR) allows small areas of a page to be refreshed without the need to redraw the entire page. I had a scenario where an af:outputText component needs to be refreshed programmatically. The <af:outputText> tag looks like as ashown below.
 <af:outputText id="op1"  value="#{myBean.someValue}"  />

To PPR the outputText component, tried calling the following API from backing bean method.
AdfFacesContext.getCurrentInstance().addPartialTarget(outputTextComp);
Unfortunately this didn't help me out. Also, noticed the following warning message in the server console at runtime.
oracle.adfinternal.view.faces.renderkit.rich.PprResponseWriter$PPRTag finish
WARNING: no PPR-capable ID found for elements of: RichOutputText[UIXFacesBeanImpl, id=op1]
Further investigation on the above issue revealed some interesting points. Let me try summarizing them below.

Why does Programmatic PPR fail for <af:outputText>?

In an attempt to reduce content size of the web page, framework tries optimize the handling of auto-generated ids for af:outputText / af:outputFormatted components. This mean that, id will be generated only if it's really required by the client. The same 'id suppression' optimization is applicable even if you have id set explicitly for the component. One scenario where the id required at client side is during the partial page rendering of components. So adding the outputText as a 'partial target' declaratively will result in client side id generation. But in the above use case outputText needs to be updated programmatically, so there doesn't exist any explicit partialTriggers set declaratively.

Solution

Having said that, however there exist two possible solutions to work around this scenario.
1. Set clientComponent="true" for af:outputText which would generate the required client side id.
 <af:outputText id="op1" clientComponent="true" value="#{myBean.someValue}"  />

2. Disable 'id suppression' optimization at application level by adding the following context parameter in web.xml
<context-param>
<param-name>
oracle.adf.view.rich.SUPPRESS_IDS
</param-name>
<param-value>auto</param-value>
</context-param>

The oracle.adf.view.rich.SUPPRESS_IDS parameter can take two values
auto : Components can suppress auto generated IDs, but explicitly set ID needs to be honored.
explicit : This is the default value for oracle.adf.view.rich.SUPPRESS_IDS parameter. In this case both auto generated IDs and explicitly set IDs would get suppressed