Sunday, February 5, 2017

ADF: Execute code before page load

Problem description: In ADF there are multiple ways to execute a piece of code before page load. In this blog I would like to point out those options and their usecases.

Solution
1. If you have a Bounded task-flow and a pagefragment. You want to run a piece of code before fragment load and you want to do it only once. It means if you take any action on your page you do not want to run that piece of code again.

In such case you can simply add a method activity before your page in task-flow. Such method activities gets executed only once before page load.

While writing such methods, you will not have any handle for page components and its binding because you have not yet reached to your page.

Generally we use this approach when we want to do something model side for example I want to execute a VO and before showing its data on page. We can write method in AMImpl, expose it and drop it before fragment on task-flow.


2. If you want a piece of code to get executed anytime you take action on your page. You don't want page components but you want bindings to be available. 

In such case you can add such methods and operation binding in pageDef file and also add corresponding executable. Set executable's refresh property as per your need. Also placing of this executable binding is important as it gets executed from top to bottom. If you expect some VO to have data then you may want to keep this executale binding after that.

If its a bean method that you want to execute in this approach, I would suggest you to write a separate class and keep only that method in that java class and expose it as datacontrol. In any way you are not going to get page components. Getter/Setter of bean will return null so no point in mixing bean with this method. Keep bean and this Java method separately. Actually its not a bean method you are invoking but a datacontrol pojo kind of method.

If you keep your method in different java file but you want to access your bean. You can invoke ADFUtils method to get beanscope first and then get bean instance.


3. If you want a piece of code to get executed anytime you take action on your page. You also want page components to be available. 

In such case you should add hidden outputtext (visible=false) to your page and bind it with bean. In its getter/setter method you can place your code. Keep outputText at the bottom of your page so that by the time its getter/setter runs all other page components are already bound to bean.



In approach 2 and 3 although your method will get execute everytime you make server request but you can control its behaviour by keeping a variable in pageFlowScope and set its value to Y when method got executed first time and next time ownwards check if pageFlowScope variable value is Y then only execute. Something like below

public void initMyPage(){

     String value = ADFUtils.getPageflowScope().get("isPageAlreadyInitialised");

    if(!"Y".equals(value)){

           //write your initialization code here. This piece will get executed only once.
 
         ADFUtils.getPageFlowScope().put("isPageAlreadyInitialised", "Y");


   }

}

4. Using PagePhaseListener: If you are working with jspx page you can have PhaseListeners as well. Here challenge is carefully picking the phase in which you want to execute your code.

You can use ViewScope variable if you want to make sure code only runs on very first load of page.

@Override
    public void afterPhase(PagePhaseEvent pagePhaseEvent) {

        if (pagePhaseEvent.getPhaseId() == Lifecycle.INIT_CONTEXT_ID) {
                
           String value = ADFUtils.getViewScope().get("isPageAlreadyInitialised");

             if(!"Y".equals(value)){

                        initMyPage();

                      ADFUtils.getViewScope().put("isPageAlreadyInitialised", "Y");


            }


         }
    }



As bindings are already in place so you should be able to use bindingContext and bindingContainer to use bindings. Also to get UI component you can get handle of bean and get getter for bean. To get bean handle you can use expressions like
MyBean myBean = ADFUtils.evalueageEL("#{backingBeanScope.myBean}");



Thanks
Sanjeev