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

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.

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

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.

3 Comments

  1. Joachim April 16, 2014
  2. Anand August 3, 2011
  3. Luc Bors July 2, 2011