Showing posts with label TaskFlow. Show all posts
Showing posts with label TaskFlow. Show all posts

31 Jul 2013

URL Task Flow Call with HTTP POST Method

As we know a bounded task flow can be invoked by some URL either directly from a browser or from some external application. This feature is enabled if task flow's property "URL invoke" is set to "url-invoke-allowed" and it is commonly used in integration projects. Usually clients (or invokers) use HTTP GET method and pass their parameters in the URL. Let's consider some simple task flow with one required input parameter:
  <task-flow-definition id="task-flow-definition">    
    <input-parameter-definition id="__23">
      <name id="__24">userName</name>
      <value id="__67">#{requestScope.userName}</value>
      <class id="__63">java.lang.String</class>
      <required/>
    </input-parameter-definition>    
    ...


 The task flow can be invoked by the URL like this

http://127.0.0.1:7101/TestApp/faces/adf.task-flow?adf.tfId=task-flow-definition&adf.tfDoc=/WEB-INF/task-flow-definition.xml&userName=xammer


The client uses a simple html form to construct this GET request:

<html>
  <head>    
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  </head>
  <body>
   <form action="http://127.0.0.1:7101/TestApp/faces/adf.task-flow">
   <input type="hidden" name="adf.tfId" value="task-flow-definition"/>  
   <input type="hidden" name="adf.tfDoc" value="/WEB-INF/task-flow-definition.xml"/>  
   <label>     
        User Name 
      <input type="text" name="userName" value="xammer"/>  
   </label>
      <input type="submit" value="Submit"/>
    </form>
    </body>
</html> 


And it looks like this:

Everything is ok. So far. It works fine for R1 and for R2 as well.
Some clients prefer to use HTTP POST method, and moreover this is their requirement:

<html>
  <head>    
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
  </head>
  <body>
   <form action="http://127.0.0.1:7101/TestApp/faces/adf.task-flow" method="POST">
   <input type="hidden" name="adf.tfId" value="task-flow-definition"/>  
   <input type="hidden" name="adf.tfDoc" value="/WEB-INF/task-flow-definition.xml"/>  
   <label>     
        User Name 
      <input type="text" name="userName" value="xammer"/>  
   </label>
      <input type="submit" value="Submit"/>
   </form>
   </body>
</html> 

  

And it works fine as well. The URL in this case is going to look like this:

http://127.0.0.1:7101/TestApp/faces/adf.task-flow


All other necessary information like task flow id and parameter value is inside POST request. But the problem is that it works fine for R1 only. If we try it out on R2 we will get the following:

ADF_FACES-30179:For more information, please see the server's error log for an entry beginning with: The UIViewRoot is null. Fatal exception during PhaseId: RESTORE_VIEW 1.

Why? Because of that:

oracle.adfinternal.controller.application.InvokeTaskFlowException: ADFC-02006: A task flow ID is not found in the URL.
    at oracle.adfinternal.controller.util.UrlParams.getTaskFlowInfo(UrlParams.java:144)
    at oracle.adfinternal.controller.application.RemoteTaskFlowCallRequestHandler.invokeTaskFlowByUrl(RemoteTaskFlowCallRequestHandler.java:84)
    at oracle.adfinternal.controller.application.RemoteTaskFlowCallRequestHandler.doCreateView(RemoteTaskFlowCallRequestHandler.java:63)

All necessary data included task flow id which is supposed to be passed inside POST request is lost. Why?  Because of "loopback". If we discover requests sent from the browser to the server on clicking the Submit button we'll see the following:

Instead of one we have two requests. And the first one (POST) is ours. Exploring the response for this request we can see the following:


 So, instead of sending the "honest" response, the server sends some "loopback" script which generates "window id" and sends the following GET request with generated window id. Cool! But all post data is gone. The GET request is absolutely empty.
Fortunately, the framework doesn't generate any "loopbacks" if the initial POST request has already some "window id". So, the workaround for our case is to develop a servlet filter, setting the "window id" attribute for our request:
public void doFilter(ServletRequest servletRequest,
                     ServletResponse servletResponse,
                     FilterChain filterChain)
  throws IOException, ServletException
{
  HttpServletRequest r = (HttpServletRequest) servletRequest;
  HttpSession s = r.getSession();
  
  //May be this is not an initial request and window id has been generated earlier
  //We want all the following requests to work with the same window id 
  //For our use-case this is ok    
  String windowID = (String) s.getAttribute(_WINDOW_ID_KEY);
  if (windowID == null)
  {
    String pathInfo = r.getPathInfo();
    //This is an initial POST request to get access to the task flow
    if (("/adf.task-flow").equals(pathInfo) &&
        "POST".equals(r.getMethod()))
    {
      windowID = WINDOW_ID;
      //Save window id in the session 
      s.setAttribute(_WINDOW_ID_KEY, windowID);
    }

  }

  //Setup attribute for the request
  //This will prevent generating of the loopback
  if (windowID != null)
    r.setAttribute(_WINDOW_ID_KEY, windowID);

  filterChain.doFilter(servletRequest, servletResponse);
}

private static final String __WINDOW_MANAGER_KEY = RichWindowManager.class.getName();
private static final String _WINDOW_ID_KEY = __WINDOW_MANAGER_KEY + "#WINDOW_ID";  
private static final String WINDOW_ID = "wextflow";

Notice, that this filter should stand before "trinidad" filter in the filter chain:
  <filter>
    <filter-name>ExtPostFilter</filter-name>
    <filter-class>com.cs.fusion.core.view.filter.ExtPostFilter</filter-class>
  </filter> 
  <filter>
    <filter-name>trinidad</filter-name>
    <filter-class>org.apache.myfaces.trinidad.webapp.TrinidadFilter</filter-class>
  </filter>
  <filter>
    <filter-name>ServletADFFilter</filter-name>
    <filter-class>oracle.adf.share.http.ServletADFFilter</filter-class>
  </filter>

That's it!

29 May 2013

Displaying ADF Task Flow Stack with BreadCrumbs

Let's consider a page with a region running a task flow. The task flow can invoke some internal task flow and this internal task flow can invoke its internal task flow and so on. After a few navigations of that sort our users are going to get lost. They have no idea how deep they are and how they got there. There is a special component in ADF Faces af:breadCrumbs. Usually it is used to show users their path through the application's menu, so users can know how they got to this page and how they can get back.  For example:

Let's use this component to help users in case of a deep task flow stack.In this post I am going to show how we can display the task flow stack with af:breadCrumbs component and how we can use it to stop currently running task flow and allow a user to get back.

So, we have a region:
<af:region value="#{bindings.taskflowdefinition1.regionModel}"
           text="#{RegionBean.taskFlowDisplayName}"
           id="r1"
           />
The region uses the technique described here to display the region's name. And we have af:breadCrumbs on the same page:
<af:breadCrumbs id="bc1" value="#{RegionBean.menuModel}"
                var="task">
    <f:facet name="nodeStamp">
        <af:commandNavigationItem id="comID"
                                  text="#{task.label}"
                                  actionListener="#{RegionBean.commandAction}">
             <f:attribute name="depth" value="#{task.depth}"/>
        </af:commandNavigationItem>
    </f:facet>
</af:breadCrumbs>


The value of the breadCrumbs is going to be some menu model, provided by a managed bean method:
public MenuModel getMenuModel() {

  TaskFlowLink taskFlowLink = getTaskFlowLink();
  if (taskFlowLink!=null)
    return new ChildPropertyMenuModel(taskFlowLink, "child",
            Collections.nCopies(taskFlowLink.getDepth(), 0));
  else
    return null;

}


The method uses some hierarchical data structure represented by TaskFlowLink class and converts it into a menu model using some internal helper class ChildPropertyMenuModel.  The TaskFlowLink class is our custom wrapper of the PageFlowStackEntry internal class. Furthermore, it supports the hierarchical structure by providing the child field.

public class TaskFlowLink {
  TaskFlowLink child;
  PageFlowStackEntry stackEntry;
  int depth;


  public TaskFlowLink(PageFlowStackEntry stackEntry, TaskFlowLink child, int depth) {
      this.stackEntry = stackEntry;
      this.child = child;
      this.depth = depth;
  }

  //Extracting the definition of the task flow 
  //corresponding to the stack entry
  private TaskFlowDefinition getTaskFlowDefinition() {
    MetadataService metadataService = MetadataService.getInstance();
    return metadataService.getTaskFlowDefinition(
                             stackEntry.getTaskFlowDefinitionId());
  }


  public String getLabel() {
      return getTaskFlowDefinition().getDisplayName();
  }

  public int getDepth() {
      return depth;
  }

  public TaskFlowLink getChild() {
      return child;
  }
    
}
  

And the getTaskFlowLink() method converts the page flow stack into the TaskFlowLink structure:
private TaskFlowLink getTaskFlowLink() {
  TaskFlowLink taskFlowLink = null;
  
  //Get the page flow stack for the region's view port
  PageFlowStack pfs = getViewPort().getPageFlowStack();
  
  //Convert the stack into array. Just for convenience. 
  PageFlowStackEntry[] pageFlowStack = 
      pfs.toArray(new PageFlowStackEntry[pfs.size()]);
  
  //Convert the array into the TaskFlowLink structure
  for (int i = pageFlowStack.length-1; i>=0; i--)
      taskFlowLink = new TaskFlowLink(pageFlowStack[i], 
                                      taskFlowLink, 
                                      pageFlowStack.length - i);

  return taskFlowLink;
}


The getTaskFlowLink() method uses a couple of helper methods to get access to the view port:
//Get the task flow binding
private DCTaskFlowBinding getTaskFlowBinding() {  
  BindingContainer bc = BindingContext.getCurrent().getCurrentBindingsEntry();  
  
  //taskflowdefinition1 is Id of the task flow binding in the page def file  
  //like  <taskFlow id="taskflowdefinition1" ...  
  DCTaskFlowBinding dtb = (DCTaskFlowBinding) 
    ((DCBindingContainer) bc).findExecutableBinding("taskflowdefinition1");  
  
  return dtb;  
} 

//Get the view port
private ViewPortContextImpl getViewPort() {
    DCTaskFlowBinding dtb = getTaskFlowBinding();
    return (ViewPortContextImpl) dtb.getViewPort();
}


And we are almost happy:

So, we built a menu model acceptable by the af:breadCrumbs component based on the page flow stack. That's all indeed cool, but it would be better if a user could click on a crumb and return back to the corresponding task flow. For example, clicking on "Task Flow One" I want to abandon currently running "Task Flow Two" and return back to the "Task Flow One". Moreover, I want to return exactly to the same view activity from which I got to the "Task Flow Two".
Alrighty, let's do it! Did you notice that our af:breadCrumbs has a commandNavigationItem within its nodeStamp facet. So, we're going to do something in the commandAction method when we're clicking on the item:
public void commandAction(ActionEvent actionEvent) {
    UIComponent component = actionEvent.getComponent();
    
    //Get the flow's depth in the stack 
    int depth = Integer.valueOf(component.getAttributes().get("depth").toString());
    
    //Abandon all deepper flows and return 
    //to the calling view activity 
    popTaskFlow(depth);
}


And, finally, let's have a look at the popTaskFlow method:
private void popTaskFlow(int depth) {

  //Remember current view port
  AdfcContext adfcContext = AdfcContext.getCurrentInstance();
  ViewPortContextImpl currViewPort = adfcContext.getCurrentViewPort();


  try
  {
     //Set region's view port as a current one
     //This allows task flow's finalizers to work correctly
     ViewPortContextImpl viewPort = getViewPort();
     adfcContext.getControllerState().setCurrentViewPort(adfcContext,
                                                         viewPort.getViewPortId());
     viewPort.makeCurrent(adfcContext);

     PageFlowStack stack = viewPort.getPageFlowStack();
     PageFlowStackEntry entry = null;

     //Abandon all deeper flows
     for (int i=1; i<depth; i++) {
       TASK_FLOW_RETURN_LOGIC.abandonTaskFlow(adfcContext, stack.peek());
       entry = stack.pop(adfcContext);
      }

     //Update the view port's current view activity ID to point 
     //to the view that was displayed before the popped 
     //task flow was called.         
     ActivityId newViewActivityId = entry.getCallingViewActivity();
     viewPort.setViewActivityId(adfcContext, newViewActivityId);

  }
  finally
  {//Restore current view port
   adfcContext.getControllerState().setCurrentViewPort(adfcContext, 
                                                      currViewPort.getViewPortId());
   currViewPort.makeCurrent(adfcContext);
  }

}

private static final TaskFlowReturnActivityLogic TASK_FLOW_RETURN_LOGIC 
   = new TaskFlowReturnActivityLogic();


The sample application for this post is available here. It requires JDeveloper R2.

That's it!

25 May 2013

ADF Task Flow Display Name

ADF Task Flow definition has a set of description properties:



In this post I am going to show how we can make use of these properties on example of Display Name attribute. Let's consider a region with a running task flow within it. In common case the region is dynamic one, so we don't know in advance which task flow it is going to run. Moreover, the task flaw can invoke another inner task flow, and the inner task flow can invoke its inner task flow, etc. Our goal is to show in the region's title the name of the currently running task flow. Let's assume, that all our task flow definitions have initialized property Display Name with a corresponding name as it is shown on the screenshot above. We can use a managed bean method returning a display name of the currently running task flow:

public String getTaskFlowDisplayName() {
    MetadataService metadataService = MetadataService.getInstance();

    //Get the task flow binding defined in our pageDef file
    DCTaskFlowBinding taskFlowBinding = getTaskFlowBinding();

    //Get Id of the currently running task flow
    TaskFlowId taskFlowId = taskFlowBinding.getViewPort().getTaskFlowContext().getTaskFlowId();

    //Get the definition of the currently running task flow by its Id
    //and return its display name
    return metadataService.getTaskFlowDefinition(taskFlowId).getDisplayName();
}

The getTaskFlowDisplayName() method uses some helper method getTaskFlowBinding() returning a task flow binding defined in our page definition file:

private DCTaskFlowBinding getTaskFlowBinding() {
  BindingContainer bc = BindingContext.getCurrent().getCurrentBindingsEntry();

  //taskflowdefinition1 is Id of the task flow binding in the page def file
  //like  <taskFlow id="taskflowdefinition1" ...
  DCTaskFlowBinding dtb = (DCTaskFlowBinding) ((DCBindingContainer) bc).findExecutableBinding("taskflowdefinition1");

  return dtb;
}


And a code snippet for our region should look like this:

<af:region value="#{bindings.taskflowdefinition1.regionModel}"
           text="#{RegionBean.taskFlowDisplayName}"
           id="r1"
           />


That's it!

29 Apr 2012

Backing bean scope in ADF task flow finalizer

Introduction
This is very common and recommended practice to use task flow finalizers when we need to do some final job (clean resources, close connections, etc) before the task flow is gone. As usual we work with managed beans declared inside the task flow. The managed beans can have different scopes - request, page flow, view, backing bean, etc. The scope depends on what the bean is actually used for. There is a small problem when we access to the backingBean scope managed bean in the finalizer. Let's have a look at the example bellow.

We have a bounded task flow with page fragments:

And we have manged beans inside the task flow of three different scopes - page flow, view and backingBean:

 <managed-bean id="__3">
  <managed-bean-name id="__5">FlowBean</managed-bean-name>
  <managed-bean-class id="__4">view.BackBean</managed-bean-class>
  <managed-bean-scope id="__2">pageFlow</managed-bean-scope>
 </managed-bean>
 <managed-bean id="__9">
  <managed-bean-name id="__6">ViewBean</managed-bean-name>
  <managed-bean-class id="__7">view.BackBean</managed-bean-class>
  <managed-bean-scope id="__8">view</managed-bean-scope>
 </managed-bean>
 <managed-bean id="__10">
  <managed-bean-name id="__11">BackBean</managed-bean-name>
  <managed-bean-class id="__12">view.BackBean</managed-bean-class>
  <managed-bean-scope id="__13">backingBean</managed-bean-scope>
 </managed-bean>


On the page we have three buttons binded to managed beans of each scope:
  <af:commandButton text="commandButton 1" id="cb1"
     action="go" binding="#{backingBeanScope.BackBean.button}">
  </af:commandButton>

  <af:commandButton text="commandButton 1" id="cb2"  
    binding="#{viewScope.ViewBean.button}"/>

  <af:commandButton text="commandButton 1" id="cb3"  
    binding="#{pageFlowScope.FlowBean.button}"/>

The bean class has the button attribute and testString attribute that signals whether the button is assigned:
  private RichCommandButton button;
  
  public void setButton(RichCommandButton button)
  {
    this.button = button;
  }

  public RichCommandButton getButton()
  {
    return button;
  }

  public String getTestString()
  {
    if (this.button == null)
      return "The button is not assigned";
    else
      return "The button is assigned";
  }



When we press cb1 we go to the return activity and the finalizer gets executed:

public static String resolveExpression(String expression)
 {
   FacesContext fc = FacesContext.getCurrentInstance();
   return (String) fc.getApplication().evaluateExpressionGet(fc, expression,
                                                    String.class);
 }

public void theFinalizer() 
{
  //Just to have test access to the managed beans
  //and to be sure we work with the same instances
  System.out.println(resolveExpression("#{pageFlowScope.FlowBean.testString}")+
                     " " + resolveExpression("#{pageFlowScope.FlowBean.button}"));
  System.out.println(resolveExpression("#{viewScope.ViewBean.testString}")+
                     " " + resolveExpression("#{viewScope.ViewBean.button}"));
  System.out.println(resolveExpression("#{backingBeanScope.BackBean.testString}")+
                     " " + resolveExpression("#{backingBeanScope.BackBean.button}"));
}




Run the application, press the cb1 button and see the following in the system log:

The button is assigned RichCommandButton[UIXFacesBeanImpl, id=cb3]
The button is assigned RichCommandButton[UIXFacesBeanImpl, id=cb2]
The button is assigned RichCommandButton[UIXFacesBeanImpl, id=cb1]

Everything seems to be ok. The task flow is finished and in the finalizer we work with correct managed bean instances. In this test the task flow is finished correctly using Return activity.
And now let's abandon our task flow - just go away from the page the task flow is put on. The finalizer is executed as well, and have a look at system out:

The button is assigned RichCommandButton[UIXFacesBeanImpl, id=cb3]
The button is assigned RichCommandButton[UIXFacesBeanImpl, id=cb2]
The button is not assigned 

This means that we work with different instance of the backingBeanScope.BackBean! In case of abounded task flow the controller don't see correct backingBeanScope in the finalizer, it is empty and the controller create new instance of the BackBean. At the same time pageFlowScope and viewScope work perfect. So, be careful when you use backingBean scope managed beans within task flows, especially when you access them in finalizers. But in any case you can use the same trick described in the previous post.

That's it!


Get access to the BackingBeanScope of the inner TaskFlow

Let's say we have a page with a region and some bounded task flow inside the region. The task flow has some backingBean scope managed bean:

 <managed-bean id="__1">
  <managed-bean-name id="__2">BackBean</managed-bean-name>
  <managed-bean-class id="__3">view.BackBean</managed-bean-class>
  <managed-bean-scope id="__4">backingBean</managed-bean-scope>
 </managed-bean>

We need to have an access to this bean outside of the task flow. For example we need to disable a button on the main page depending on some property of the BackBean.  We can do the following:

  // taskflow is <taskFlow id="taskflowdefinition1" from our pageDef
  public Map getBackingBeanScope4TaskFlow(String taskflow) 
  {
    Map resultMap = null;
    
    //We need the full name of our taskflow
    DCBindingContainer dcb =
        (DCBindingContainer)BindingContext.getCurrent().getCurrentBindingsEntry();
    DCTaskFlowBinding dfb = (DCTaskFlowBinding) dcb.findExecutableBinding(taskflow);
    String fullName = dfb.getFullName();                                 
  
    //Get the provider
    BackingBeanScopeProviderImpl bbProvider =
            ((BackingBeanScopeProviderImpl)AdfFacesContext.getCurrentInstance().getBackingBeanScopeProvider());
    
    //Left parenthesis
    bbProvider.begin(fullName);
    
    //Now the current backing bean scope is the scope of our task flow
    try {
       resultMap = bbProvider.getCurrentScope();
    } finally {
    //Right parenthesis
        bbProvider.end();
    }
    
    return resultMap; 
  }

  public Object getBackBean() 
  {
    return getBackingBeanScope4TaskFlow("taskflowdefinition1").get("BackBean"); 
  }
  

 Of-course using this technique is a little bit against the encapsulation concept - we should not have access to the details of the inner/child objects. We have to be aware of that. And for our use-case it'd be probably better to use contextual events or something else. But in any case we know how to do the trick, and Knowledge is Power :).

That's it!

11 Mar 2012

Managed bean scopes for page fragments in ADF Task Flow

Introduction
When we work with ADF Task Flows and need to implement some flow specific business logic or store some information connected with the flow, we usually use pageFlowScope managed beans. And when we need to service view activities of the flow (pages or page fragments) we use shorter scopes for such managed beans. The common practice is to use requestScope, backingBeanScope and viewScope scopes for pages/fragments backing beans. In this post I'm going to play with these three options and discover the differences in the behavior of fragment based Task Flow.

Let's say I have some simple task flow template task-flow-template.xml:


   <managed-bean id="__5">
      <managed-bean-name id="__3">viewBean</managed-bean-name>
      <managed-bean-class id="__2">com.cs.blog.ViewBean</managed-bean-class>
      <managed-bean-scope id="__4">request</managed-bean-scope>
    </managed-bean>
    <managed-bean id="__15">
      <managed-bean-name id="__13">flowBean</managed-bean-name>
      <managed-bean-class id="__12">com.cs.blog.FlowBean</managed-bean-class>
      <managed-bean-scope id="__14">pageFlow</managed-bean-scope>
    </managed-bean>

    <view id="MainView">
      <page>/MainView.jsff</page>
    </view>
    

It has one view activity MainView and two backing beans. The flowBean has pageFlow scope and is responsible to store flow information. The viewBean has request scope (we will play with that) and it services the ManView view activity.

The flowBean has the following method returning the tittle of the task flow:

    public String getFlowTitle() {
        return null;
     }   

The viewBean has some string field testString to store input value:

    protected String testString;
    
    public void setTestString(String testString) {
        this.testString = testString;
    }

    public String getTestString() {
        return testString;
    }

The MainView shows the task flow's title and has an inputText for the testString. It looks like this:


We also have two task flows built on the task-flow-template - first-flow-definition and second-flow-definition. They have overridden managed beans.

For the  first-flow-definition:

    <managed-bean id="__5">
      <managed-bean-name id="__3">viewBean</managed-bean-name>
      <managed-bean-class id="__21">com.cs.blog.FirstViewBean</managed-bean-class>
      <managed-bean-scope id="__4">request</managed-bean-scope>
    </managed-bean>    
    
    <managed-bean id="__15">
      <managed-bean-name id="__13">flowBean</managed-bean-name>
      <managed-bean-class id="__12">com.cs.blog.FirstFlowBean</managed-bean-class>
      <managed-bean-scope id="__14">pageFlow</managed-bean-scope>
    </managed-bean>
 

public class FirstFlowBean extends FlowBean {
    public FirstFlowBean() {
        super();
    }
    
    public String getFlowTitle() {
        return "FirstFlow";
     }   
   
}

public class FirstViewBean extends ViewBean {
    public FirstViewBean() {
        super();
        
    }
    
    @PostConstruct
    public void init() {
        testString = "FirstFlow";  
    }
}

So the title and default value for testString is "FirstFlow".


For the  second-flow-definition:

    <managed-bean id="__5">
      <managed-bean-name id="__3">viewBean</managed-bean-name>
      <managed-bean-class id="__21">com.cs.blog.SecondViewBean</managed-bean-class>
      <managed-bean-scope id="__4">request</managed-bean-scope>
    </managed-bean>    
    
    <managed-bean id="__15">
      <managed-bean-name id="__13">flowBean</managed-bean-name>
      <managed-bean-class id="__12">com.cs.blog.SecondFlowBean</managed-bean-class>
      <managed-bean-scope id="__14">pageFlow</managed-bean-scope>
    </managed-bean>

public class SecondFlowBean extends FlowBean {
    public SecondfFowBean() {
        super();
    }
    
    public String getFlowTitle() {
        return "SecondFlow";
     }   
    
}

public class SecondViewBean extends ViewBean {
    public SecondViewBean() {
        super();
       
    }
   
    @PostConstruct
    public void init() {
        testString = "SecondFlow"; 
    }
   
}

So the title and default value for testString is "SecondFlow".


Ok. It's time to experiment. Let's put on our page two regions with first-flow-definition and second-flow-definition task flows:

              <af:region value="#{bindings.firstflowdefinition1.regionModel}"
                         id="r1"/>
              <af:separator id="s1"/>           
                         
              <af:region value="#{bindings.secondflowdefinition1.regionModel}"
                         id="r2" />

requestScope
Leaving the scope for the viewBean as requestScope we will get the following result:

In the SecondFlow we see the testString from the FirstViewBean instance. We can have only one instance of the requestScope bean per request. The viewBean was created for the FirstFlow task flow and the same instance was used again for the SecondFlow.

backingBeanScope
Somebody could recommend to use backingBeanScope for the viewBean instead of requestScope. The backingBeanScope is commonly used to manage regions and declarative components. It has the same length of life as the requestScope but for different instances of regions/declarative components you will have separate instances of backingBean scoped managed beans. In our case we have two different regions, so let's try:

And, Yes, the backingBeanScope has fixed the problem. We have two instances of the viewBean - for the regions r1 and r2.
But let's make the first-flow-definition task flow a bit more complicated:


Now we can call child task flow (of the same definition) from the MainView. And let's repeat the experiment. On the initial rendering:

So far, so good. Let's input something in the input text of the FirstFlow and press "call child task flow":


Oooops! We have only one instance of the viewBean for the region r1 during the request. So, value "FirstFlow111111" entered in the parent task flow was rendered again in the child task flow.


viewScope
And now let's change the viewBean's scope to the viewScope and have the same experiment. On the initial rendering:

 Ok. Inputting the same garbage in the inputText:

And pressing the "call child task flow":
And everything is ok. We have not only separate viewScope beans instances for different viewport IDs (for different regions and task flow instances), but additionally the controller is resetting the viewScope during the navigation process. But the cheese is not free. You have to pay by memory. If requestScope or backingBeanScope live not longer than the request, viewScope lives in memory until the viewport ID is changed. Perhaps in my further posts I will show how to manage the issue with the backingBeanScope.

So, when you choose the appropriate scope for your fragment managed beans, consider how the task flow is going to be used. Probably, in order to get really high reusable task flow, using the viewScope is the best approach for fragment beans.

That's it!


 

25 Dec 2011

Task Flow Exception Handler. You must have it.

Introduction

Defining Exception handlers in your task flows is not only a good practice of ADF development.
Ignoring this practice can get you in some big problems, you will get unpredictable and difficult for understanding behavior of the system like Null Pointer Exceptions in trusted places or system's hanging.
Some time ago I faced a "performance" problem of ADF application. About a hundred of users (it's not too much load for that environment) were working with the system and it "hanged" every 20 minutes. A lot of performance tunings didn't help. There was a bug. One important Task Flow didn't have an exception handler.

Experiments
Let's say we have very simple bounded train task flow:
The task flow is executed as a region within some jspx page and it doesn't have an exception handler. NextView page fragment has the following source:

 <af:panelFormLayout>
  <af:group id="g1">
   <af:train id="pt_t2"
    value="#{controllerContext.currentViewPort.taskFlowContext.trainModel}"/>
   <af:commandButton text="Commit" id="cb1" action="*Commit"/>
   <af:commandToolbarButton text="Back" id="pt_cb1"
    action="#{controllerContext.currentViewPort.taskFlowContext.trainModel.getPrevious}"
    disabled="#{empty controllerContext.currentViewPort.taskFlowContext.trainModel.previous}"
    rendered="#{! empty controllerContext.currentViewPort.taskFlowContext.trainModel.previous}"
    immediate="true"/>

   <af:commandToolbarButton text="Forward" id="pt_cb2"
    action="#{pageFlowScope.WizzardFlowBean.nextButtonAction}"
    disabled="#{empty controllerContext.currentViewPort.taskFlowContext.trainModel.next}"/>
  </af:group>
 </af:panelFormLayout>

And it looks like this:

 
Managed bean method for BadCall activity has the following code:

    public void badCall() {
        int i = 1/0;
    }

In this example the situation is not very dangerous. We will just get the unsupported exception window like this:



But the system is still stable and is working correctly. We can click Back button, we can navigate using af:train at the top of the page. Andrejus Baranovskis explained in his blog how to define exception handler for the task flow to make the exception be caught and rendered correctly.

In the next experiment let's call our task flow from another one. So, let's make it inner.


In this scenario the same Arithmetic exception will crash the system. If we try to click Back button after the exception window, we will get Null Pointer Exception. Why???

The reason is in the controller's exception handling mechanism. In case of an exception during navigation within task flow it tries to find and execute an Exception Handler of the current task flow. If the current task flow does not specify an exception handling activity then the task flow will be popped from the task flow stack and control passed to the calling task flow's exception handler.  This process is continued until an exception handler is located or until all task flow's have been checked. So, in our example, when we don't have any exception handlers at all, our task flow (emp-flow-definition) is just popped out from the calling stack, control is passed to the calling flow, and the exception is thrown. That's why EL expression for Back button's action #{controllerContext.currentViewPort.taskFlowContext.trainModel.getPrevious}" will throw NPE. Current task flow at that moment is calling task flow, but we see the inner task flow on the screen. And off course calling task flow doesn't have any train model.

The situation is even worse. If we try to navigate using af:train, the system will hang!!! We will get a stuck thread!!! EL evaluator doesn't like when complex EL expression containing null values and it sometimes hangs instead of throwing exceptions like NPE. If we define any exception handler in our task flow  the problem is gone. It even doesn't matter what it does. It is just needed  by controller's exception handling mechanism.

Instead of adding exception handler to the inner task flow we can try to add it the calling task flow. And if this handler is just some method call activity without navigating to any View activity, we will face the same problem, because we will still see on the screen "dead" task flow.

So the solution is either to add any exception handler to the inner task flow or to add some handler with a navigation to a view activity to the calling task flow. But! The simplest and the most correct rule - don't have any task flows without exception handlers!


That's it.



11 Dec 2011

Dynamic ADF Train. Showing train stops programmatically.

In one of my previous posts I showed how to create train stops programmatically. And I got a comment with a question on the post - "Is it possible to show different pages on each of the dynamic train stop?". The answer is - Yes, off course!
In this post I'm going to give an example of showing train stops programmatically. So, I need to show or hide some stops dynamically at runtime. Everybody knows that TrainStop has Ignore attribute

 
And if we could dynamically change it's value or put there some EL expression that could be evaluated during the taskflow's life cycle, It would be the best approach of showing/hiding train stops at runtime. But Ignore attribute is evaluated only once, at the task flow initialization phase and cannot be modified further.  But! As it was shown in this post we can programmatically add (or remove) train stops to the train model of the task flow. So, we can do it!

Let's say I've got the following train TaskFlow:


PassenerView activity, LuggageView and MealView  by default have <ignore>true</ignore> attribute and at the very beginning, after task flow initialization, are hidden. The train model doesn't have stops for these activities. On the first stop of the task flow I'm going to decide which activity should be included in the train and which one is hidden. On the task flow initialization I call the following managed bean method:

    private static String START_VIEW = "StartView";
    //Fill map with all activities of the TaskFlow
    //except StartView
    private void InitIgnoredStops() {
        for (Iterator it= getTaskFlowDefinition().getActivities().values().iterator(); it.hasNext();) {
          Activity act = (Activity) it.next();
          if (!START_VIEW.equals(act.getIdAttribute())) {
                ignoredStops.put(act.getId(), "false");
            }

        }
        
    }

The ignoredStops map is shown on the StartView page using techniques explained in this post. So, I have the following page:



The following method is called on Submit button's action:

public String buttonPress() {

    TrainModel trainModel = TrainUtils.findCurrentTrainModel();

    //Loop over the map
    for (Object actid : ignoredStops.keySet().toArray()) {
        //If activity is not chosen 
        if (!Boolean.parseBoolean(ignoredStops.get(actid).toString())) {
            // if activity is included in the train then remove it
            if (trainModel.getTrainStops().get(actid) != null)
                 trainModel.getTrainStops().remove(actid);
        } else {
            //If activity is chosen and it is not included in the train 
            // then we need to include it
            if (trainModel.getTrainStops().get(actid) == null) {
                MetadataService metadataService =
                    MetadataService.getInstance();
                
                //Get activity and its train stop definition
                Activity activity =
                    metadataService.getActivity((ActivityId)actid);                
                TrainStopContainer stopContainer =
                        (TrainStopContainer)activity.getMetadataObject();
                TrainStop ts = stopContainer.getTrainStop();
                
                
                //Create new train stop model and add it to the train
                trainModel.getTrainStops().put((ActivityId)actid,
                                                   new TrainStopModel(ts,
                                                                      (ActivityId)actid));
                }
            }


        }
    
}

If needed activities are chosen we can press Submit  button and get the following picture:




That's it!

29 Jan 2011

Using Inheritance in View Controller Layer

Introduction
I like inheritance feature of ADF BC. It allows to create inheritance tree of entity objects and view objects. The feature is good described in Developer's Guide. Using this approach we can build really elegant business model. But how to use it in ViewConroller layer in the same way? How to use inheritance instead of copy-pasting?

Use Case
I have in my database three tables:
  • Deal - contains some common fields of some agreement with some customer
  • Loan - contains some extra fields specific for loan agreements
  • Forex - contains some extra fields specific for forex agreements



Model 
In my model I've created three read-only VO's: 
  • VDeal - selects all fields from Deal table
  • VLoan - extends VDeal. Selects all fields from Deal and Loan tables
  • VForex - extends VDeal. Selects all fields from Deal and Forex tables


ViewController
 Let's create TaskFlow template to work with our model. In real life the taskflow is going to contain number of different activities, but, just to simplify this post, my taskflow consists of one view activity only. FormView activity contains some form to show record from VLoan or VForex. 




Ok. TaskFlow template is created in draft.
Task flows that implements this template are going to attach some real page fragment to the FormView view activity in order to show a record of corresponding deal (Loan or Forex). Obviously, some of fields are common and it's preferable to have the same look-and-feel and UI logic for these fields in every implementation. I'm going to create page fragment template and put all common fields on it .






From the DataControls palette I'm dragging VDeal and dropping it on the page as ADF Read-only Form:
















After adding facet "extendFacet" for pages implementing this template and fixing some "design" issues I got the following page:



Everything seems to be OK. But!!! Let's have a look at the page definition file for our template page. The iterator binding points to VDeal  view object:


    
    
  



Actually VDeal is just ancestor definition. Its instance will hardly be created. Pages implementing our template will have their own real VO instances of VLoan and VForex. To fix the problem I'm going to add some managed bean to my task flow template:


  
    FormView
    
      DealFlowBean
      com.cs.blog.inherit.view.DealFlow
      request
    
    
    
  


The DealFlowBean has method getDealVOName. This method is going to be overridden and  it is responsible to return correct name of the corresponding VO:

package com.cs.blog.inherit.view;

public class DealFlow {
    public DealFlow() {
        super();
    }
    
    /*Extenders override this method and return correct name
     * of the corresponding VO
     * */
    public String getDealVOName() {
        return "VDeal";
    }
}

The next step is to change a little bit page definition file for page template:


    
    
  

I used EL expression to resolve VO's name.




Ok. Let's create taskflow to work with loans:





We have to add manually on the Loan taskflow view activity and give it the same name FormView. We are implementing it by creating new page fragment based on the DealViewTemplate.


We put loan specific extra fields (using drag-n-drop from the Data Controls palette) on the extendFacet of our page:



 After that, we have to change PageDef for the new page fragment: change Binds="VLoan" to Binds="#{DealFlowBean.dealVOName}" and change given by default iterator ID from VLoanIterator to VDealIterator.


  
  
    
    
    
  
  
    
      
        
      
    
    
      
        
      
    
    
      
        
      
    
  


Off-course we have to extend DealFlowBean and override the getDealVOName method.

package com.cs.blog.inherit.view;

public class LoanFlow extends DealFlow {
    public LoanFlow() {
        super();
    }
    
    public String getDealVOName() {
        return "VLoan";
    }
    
}


And we need to define LoanFlow class for DealFlowBean in the definition of our taskflow.


  
    
      /WEB-INF/deal-flow-template.xml
      deal-flow-template
    
    FormView
    
      DealFlowBean
      com.cs.blog.inherit.view.LoanFlow
      request
    
    
      /LoanFormView.jsff
    
    
  



Finishing... Task flow for loans is complete and ready to be used. Using the same approach we create task flow for forex deals.
As a reward for our work we can enjoy the following working(!) pages for loan and forex deals:



 That's it!
 You can download sample application for this post.