Monday, November 1, 2010

ADF UI - Performance Tip: Lazy load task flows for better performance

Before discussing the performance improvement by implementing lazy loading for the task flows, let us go through the task flow basics.

How task flows are loaded normally?
If you're dropping task flow as a region in your jsff, the task flow binding will be added in the pagedef file as an executable. So, anything under executables section including the task flows in pagedef file will be loaded by default when the jsff page is loaded.

How it impacts the performance?
As ADF loads all the task flows that are inlcuded in a page, even if they are not rendered by default, it'll be a performance hit as we're loading the task flows which are not needed by default. Examples below:
Popups: Task Flows on a popup should be loaded  only when user clicks on Pop-up link but ADF loads them during the page invocation itself and hence reduce the perfomrance of the page .
Hidden Taks Flows or Conditionally Rendered Task Flows: ADF page could have some of the task flows that are hidden based on a condition. In this scenario as well ADF loads the task flows during page loading even if those task flows are hidden and the model content in that will get executed during the page loads hence results in lower  performance of the page.
Panel Tabs: Page could have multiple tabs on a page with respective task flows , ADF loads all the task flows even though only one tab will be showed at a time.

So, What is lazy loading and how it improve the performance?
Lazy loading means loading the task flows only when they are required to be rendered. i.e., we won't load the task flows that are included inside popups, and those hidden initially, and the ones that are present in other than the default tab. We'll load them only when the popup is invoked, when the task flow region is unhidden/shown, on clicking the tab that has the task flow respectively.

How to implement lazy loading for the task flows?
1. For task flows in popups
i. Define a af:setPropertyListener of type 'popupFetch' and set some constant to one pageFlowScope variable.

<af:popup id="popup1" contentDelivery="lazyUncached"> <af:dialog id="dialog1" type="okCancel" title="Employee Details" inlineStyle="width:340px; height:125.0px;"> <af:panelGroupLayout id="panelGroupLayout1"> <af:region value="#{bindings.EmpDetailsTaskFlow1.regionModel}" id="r2"/> </af:panelGroupLayout> </af:dialog> <af:setPropertyListener to="#{pageFlowScope.popupFetched}" type="popupFetch" from="EMP_DETAILS"/> </af:popup>

ii. In the pagedef, set 'Activation' property for task flow binding to 'Conditional' and 'Active' property to above defined pageFlowScope variable with EL expression which evaluates to true when the popup is fetched.
<taskFlow id="EmpDetailsTaskFlow1" taskFlowId="/com/demo/taskflows/EmpDetailsTaskFlow.xml#EmpDetailsTaskFlow" activation="conditional" xmlns="http://xmlns.oracle.com/adf/controller/binding" active="#{pageFlowScope.popupFetched=='EMP_DETAILS'}"/>

2. For the hidden or conditionally rendered task flows:
i. Conditionally rendered task flows will have the 'rendered' property set to some condition defined as EL expression based on which the region will be hidden or shown. You need to specify the same condition for the 'Active' property of the task flow binding in the pagedef. And, don't forget to set the 'Activation'  property for task flow binding to 'Conditional'.

Jsff containing taskflow:
<af:showDetailHeader text="Emp Details" disclosed="true" id="sdh3"> <af:region value="#{bindings.ShowHideTaskFlow1.regionModel}" id="r2" rendered="#{pageFlowScope.showHideCondition==true}"/> </af:showDetailHeader>

The task flow in the corresponding pagedef file:
<taskFlow id="ShowHideTaskFlow1" taskFlowId="/com/demo/taskflows/ShowHideTaskFlow.xml#ShowHideTaskFlow" activation="conditional" xmlns="http://xmlns.oracle.com/adf/controller/binding" active="#{pageFlowScope.showHideCondition==true}"/>

3. For panel tabs (af:panelTabbed) components:
i. Add a af:setPropertyListener of type 'disclosure' in each af:showDetailItem under af:panelTabbed and set some constant value to a pageFlowScope variable. You need to set value to the same pageFlowScope variable in each tab. On reading the pageFlowScope variable value, you should be able to tell in which you're currently in.


<af:panelTabbed id="pt1"> <af:showDetailItem id="showDetailItem1" text="Basic Details"> <af:region value="#{bindings.BasicDetailsTaskFlow1.regionModel}" id="region1"/> <af:setPropertyListener to="#{pageFlowScope.tabClicked}" type="disclosure" from="BASIC_DETAILS"/> </af:showDetailItem> <af:showDetailItem text="MainDetails" id="showDetailItem2"> <af:region value="#{bindings.MainDetailsTaskFlow1.regionModel}" id="region2"/> <af:setPropertyListener to="#{pageFlowScope.tabClicked}" type="disclosure" from="MAIN_DETAILS"/> </af:showDetailItem> <af:showDetailItem text="Other Details" id="showDetailItem3"> <af:region value="#{bindings.OtherDetailsTaskFlow1.regionModel}" id="region3"/> <af:setPropertyListener to="#{pageFlowScope.tabClicked}" type="disclosure" from="OTHER_DETAILS"/> </af:showDetailItem> </af:panelTabbed>

ii. In the pagedef, set 'Activation' property for task flow binding to 'Conditional' and 'Active' property to above defined pageFlowScope variable with EL expression which returns true when the tab containing that particular taskflow is clicked.
<taskFlow id="region1" taskFlowId="/com/demo/taskflows/BasicDetailsTaskFlow.xml#BasicDetailsTaskFlow" activation="conditional" xmlns="http://xmlns.oracle.com/adf/controller/binding" active="#{pageFlowScope.tabClicked=='BASIC_DETAILS'}"/> <taskFlow id="region2" taskFlowId="/com/demo/taskflows/MainDetailsTaskFlow.xml#MainDetailsTaskFlow" activation="conditional" xmlns="http://xmlns.oracle.com/adf/controller/binding" active="#{pageFlowScope.tabClicked=='MAIN_DETAILS'}"/> <taskFlow id="region3" taskFlowId="/com/demo/taskflows/OtherDetailsTaskFlow.xml#OtherDetailsTaskFlow" activation="conditional" xmlns="http://xmlns.oracle.com/adf/controller/binding" active="#{pageFlowScope.tabClicked=='OTHER_DETAILS'}"/>


That's quite simple. Now, your pages will load double faster than previous even with multiple task flow in your pages. Bingo :) !

13 comments:

  1. Hi,
    Thank you for lazy loading concept of taskfows, i am not able to succeed as adf controller is giving exception as below,

    oracle.adf.controller.ControllerException: ADFC-12014: Attempted access to an object that has already been released. Is there any thing I am missing ?

    ReplyDelete
  2. I am in the same situation of shankar!

    ReplyDelete
  3. @shankar and Ghepardo,
    Is it possible to send me a simple testcase? It'll help me to debug your issue.

    -Murali.

    ReplyDelete
  4. Unfortunately no :( The app is quite big. I opened an oracle support ticket for this.

    ReplyDelete
  5. I also get the same problem of "oracle.adf.controller.ControllerException: ADFC-12014: Attempted access to an object that has already been released." using this technique.

    Any clue what could be the problem?

    ReplyDelete
  6. I applied an example, It is really nice features, thank you Murali Papana ...

    ReplyDelete
  7. Worked perfectly, thanks.

    ReplyDelete
  8. Why did you append a "1" to the end of this expression?

    <af:region value="#{bindings.MainDetailsTaskFlow1.regionModel}"

    In you pageDef, you have it defined without the 1. I'm having trouble making the relationship between the pageDef file and the code inside the JSF.

    ReplyDelete
  9. Ok I was able to get this working great. However, I'm having a hard time figuring out how to configure the "default" tab. Because all tabs are set to load upon a click, my "default" tab is not being displayed at login until another tab is clicked, then you click back on the default tab.

    ReplyDelete
  10. Anyway we can prevent the content in and tab to be reloaded when we switch from one tab to another?

    ReplyDelete
  11. I have the same problem as this :
    Ok I was able to get this working great. However, I'm having a hard time figuring out how to configure the "default" tab. Because all tabs are set to load upon a click, my "default" tab is not being displayed at login until another tab is clicked, then you click back on the default tab.

    ReplyDelete
  12. To configure the "default" tab, set active="true" on taskFlow only default page :




    ReplyDelete
  13. For af:panelTabbed, there is property called , childCreation. If I set to 'lazy' , do I need to implement the above steps? Is it equivalent to above steps?

    ReplyDelete

Related Posts with Thumbnails