ADF 11g : What is the Currently Active Taskflow in a Dynamic Region ?

3

There are use cases in which you need to determine what the currently active taskflow is. This looks like simple, but in fact it is not ! Let’s rephrase that : Depending on the context, it can be a pretty confusing task.
Here is the situation. I have a MainContainer Page. that renders either TF-1 or TF-2 in a dynamic region. At any time in the application I need to be able to get the Id of the taskflow that is rendered in the dynamic region. Notice the regionManager class that I use to switch between the taskflows in the region.
Image

public class regionMananager {
private String taskFlowId = "/WEB-INF/TF-1.xml#TF-1";
public regionMananager() {
}
public TaskFlowId getDynamicTaskFlowId() {
return TaskFlowId.parse(taskFlowId);
}
public String tF2() {
taskFlowId = "/WEB-INF/TF-2.xml#TF-2";
return null;
}

The mainContainer page has two tabs that are actually commandNavigationItems in a NavigationPane. These tabs will set the active Taskflow in the dynamic region.

The code behind this page is pretty simple.

<af:form id="f1">
        <af:navigationPane id="np1" hint="tabs">
          <af:commandNavigationItem text="TF-2"
                                    action="#{viewScope.regionMananager.tF2}"
                                    id="cl1" partialSubmit="true"
                                    partialTriggers="cl2"
                                    disabled="#{viewScope.regionMananager.tf2Active}"/>
          <af:commandNavigationItem text="TF-1" action="#{viewScope.regionMananager.tF1}"
                                    id="cl2" partialSubmit="true"
                                    partialTriggers="cl1"
                                    disabled="#{viewScope.regionMananager.tf1Active}"/>
        </af:navigationPane>
        <af:region value="#{bindings.dynamicRegion1.regionModel}" id="r1"
                   partialTriggers="::np1:cl1 ::np1:cl2"/>
 </af:form>

There are a couple of situations.

Getting the TaskFlow Id From within the current taskflow

This is actually pretty simple. Configure a managed bean in the taskFlow.

<xml version="1.0" encoding="windows-1252" ?>
<adfc-config xmlns="<a href="http://xmlns.oracle.com/adf/controller">http://xmlns.oracle.com/adf/controller</a>" version="1.2">
<task-flow-definition id="TF-1">
<default-activity id="__1">TF-1-View</default-activity>
<managed-bean id="__8">
<managed-bean-name id="__6">tf1Bean</managed-bean-name>
<managed-bean-class id="__5">nl.amis.technology.view.beans.Tf1Bean</managed-bean-class>
<managed-bean-scope id="__7">pageFlow</managed-bean-scope>
</managed-bean>
<initializer id="__4">#{pageFlowScope.tf1Bean.initializer}</initializer>
<finalizer id="__2">#{pageFlowScope.tf1Bean.finalizer}</finalizer>
<view id="TF-1-View">
<page>/TF-1-View.jsff</page>
</view>
<use-page-fragments/>
</task-flow-definition>
</adfc-config>

Now you can ask the taskflows’ ID by just calling getTaskFlowId(). As an example I added an initializer and a finalizer to the taskflow. In both I call getTaskFlowId() and print it to the console.

package nl.amis.technology.view.beans;
import oracle.adf.controller.ControllerContext;
public class Tf1Bean {
public void initializer(){
System.out.println("in initializer of TaskFlow "+ ControllerContext.getInstance().getCurrentViewPort().getTaskFlowContext().getTaskFlowId());
}
public void finalizer(){
System.out.println("in finalizer of TaskFlow " + ControllerContext.getInstance().getCurrentViewPort().getTaskFlowContext().getTaskFlowId());
}
}

When you start up or shutdown the taskflow, you see the value of the taskflow ID.

Image

Getting the TaskFlow Id From within the page that defines a dynamic region

This is a use case that is somewhat more difficult. If, from within the MainContainer page, you need to get the ID of the taskflow that is rendered in a dynamic region, you need to access the dynamic regionbinding. This regionbinding is only accessible using an Internal ADF Package. This will result in an error unless you change the ADF Java Audit Rules to allow the use of internal packages. For this you can also refer to Frank Nimphius’ OTN Harvest.

Image

With that in place, you can now use the internal package to get the Dynamic Region Binding from the Binding Container of page that contains the dynamic region. And what I actually do here is that whenever I want render a taskflow in the region, I just check what the current taskflow in that region is.

package nl.amis.technology.view.beans;
import oracle.adf.controller.TaskFlowId;
import oracle.adf.model.BindingContext;
import oracle.binding.BindingContainer;
import oracle.adf.controller.internal.binding.DCTaskFlowBinding;
public class regionMananager {
 public String getCurrent() {
  BindingContext bctx = BindingContext.getCurrent();
  BindingContainer bindings = bctx.getCurrentBindingsEntry();
  DCTaskFlowBinding taskFlowBinding = (DCTaskFlowBinding) bindings.get("dynamicRegion1");
  System.out.println("the Currently active taskFlow in the Dynamic Region is " + taskFlowBinding.getTaskFlowId());
  return taskFlowBinding.getTaskFlowId();
 }
 public String tF2() {
   if (!getCurrent().equalsIgnoreCase("/WEB-INF/TF-2.xml#TF-2")){
      taskFlowId = "/WEB-INF/TF-2.xml#TF-2";
      setTf1Active(false);
      setTf2Active(true);
      }
     return null;
   }

What you see in the log below is that TF-1 is started first. Next, there is navigation to TF-2. That results first in the message ” the Currently active…is TF-1″, followed by the finalizer of TF-1. When navigating again, the message ” the Currently active…is TF-2″ is displayed followed by the TF-1 initializer.

Image

For what it is worth, I agree with everyone that is temped to say that I could also have asked getTaskFlowId() to the bean itself. However, this is a value that I set hard coded and that approach is not favored by me. It should always be possible to get the taskflow Id from either the BindingContext or from the ControllerContext.

Brainstorm : Now what are the options if you don’t want to use the internal classes ?

There are some alternative apporaches. One of them, suggested by my colleague Paco (http://twitter.com/#!/pavadeli), is to use a taskflow template that accepts a parameter that defines something like an TFContext class. This TFContext would be implementing a kind of ‘Stack’ that is pushed by the TF initializer and flushed by the TF finalizer. The ‘Stack’ (could be a map) contains relevant taskflow data such as TaskFlowId. If al Taskflows are based on this template, you will have a clear view of the run time at any time.

This looks to me to be totally the same as ADF Controller works, so you would be creating a custom copy of the ADF Controller stack. Better would be if Oracle would create a public API for oracle.adf.controller.internal.binding.DCTaskFlowBinding.

Download the sample workspace used for this post here.

Share.

About Author

Luc Bors is Expertise Lead ADF and technical specialist/architect at AMIS, Nieuwegein (The Netherlands). He developed several Workshops and training on ADF and also is an ADF and JHeadstart instructor. Luc is a member of the ADF Methodology group and publishes articles on ADF in oracle technology related magazines, on the AMIS technology blog, (http://technology.amis.nl/blog).

3 Comments

  1. Hi Luc,
    thanx for your great post. We have implementet sth. quite similar. Our problem is, when one of the view is too big and we have a scrollbar in the view we would like to keep the scroll position when switching between the tabs.
    Now after a switch it is always scrolled back to top.

    Do you have an idea how to implement a working solution?

    Thanx and cheers,
    Joachim

  2. I have a query,
    I have a taskflow, and i have embeded taskflows in that / inside JSFF i have dragged child taskflows as regions.

    There is a parent iniializer and a child initializer.
    Parent initializer executes and then child will get executed, if inside the child initializer i want to fetch the parent taskflow id,
    then how can i get that ?

    Normal way to fetch the taskflow id is :ControllerContext.
    getInstance().getCurrentViewPort().getTaskFlowContext().getTaskFlowId()

Leave a Reply