Wednesday Jan 04, 2012

Maintaining Row Currency Separation in Task Flow Instances

So here's a quick thought experiment, how would one create a re-usable task flow which should tick the following boxes:

  1. Multiple copies of the flow are displayed on a single page
  2. Each instance of the taskflow should maintain it's own row currency and not be affected by navigation in any other instance
  3. All instances of the taskflow should be part of the same transactional context

Let's analyse that. Well, Requirement (1) is easy, that's one of the core capabilities of task flows.  Each has it's own pageFlowScope which is kept private so multiple instances on the page should be fine.

Requirement (2) can be achieved simply by setting the data control scope to <isolated/> in the task flow definition. 

Requirement (3) - oh dear.

So there's the problem, it seems that requirements (2) and (3) are mutually exclusive.  If you want everything to use the same transaction you also have to have them sharing the same VO instance and therefore being coordinated from the record currency point of view. 

To solve this let's just think a little outside the box and consider what row currency is and therefore how we could approach the problem.  Each View Object Instance has a primary RowSetIterator  which identifies the "current row" within the VOs collection. As all the instances of the TaskFlow are using the same bindings, they are therefore using the same VO instance and therefore all share the same RowSetIterator (RSI). Now a VO instance can have secondary RSIs but these are really only of use in the programmatic sense, and cannot be wired in through the binding layer.

So logically, one approach to this is to define a second VO instance based on the same VO definition and bind one taskflow to that and the other to the origional. Well that's fine, but we're painted into a corner here, in that we need to decide up-front how many concurrent views we need and then define explicit VO instances in the AM  and separate almost-identical-apart-from-the-bindings taskflows. So this may well work when you just have a couple of views but then it get's unworkable, plus it just feels wrong to duplicate all that taskflow content just to change the VO instance name.

However, in principle, the idea is along the right lines. We just need to simplify it so that we don't have to do any duplication or pre-definition of VO instances. Is it possible? Yes of course, and surprisingly easy.

The Approach 

Getting Started 

The first step here is to create your AM with an initial instance of the VO you need exposed (you can delete it later but having it there will make the design time easier). Once the VO instance is available in the data control palette you can go ahead and create your re-usable taskflows and test them you want to get the basic functionality right after all.  Of course at this stage if you use multiple instances  of the taskflow they will all be coordinated. 

Parameterise the Task Flow  

 At this stage define a task flow parameter which can be passed in to define the unique name for the VO instance that this instance of the taskflow will use. You might write some fancy generator method to create unique names in sequence (DeptView2, DeptView3, DeptView4 and so on) or just hardcode a value when you map the taskflow into a region, it's up to you. Just bear in mind that any taskflow instances that share the same instance name will also share the same RSI and therefore be coordinated - so you can mix and match both coordinated and uncoordinated instances. For the sake of example let's call that parameter pVOInstanceName.

Create a Method to Create  New Instances of the VO

Next we need to define a method that will create a VO instance on demand. This is very simple code, just generate up a Java Impl class for your AM and add a method something like this:

     public void createUniqueVOInstance(String voDefName, String instanceName){
        ViewObject existingVO = findViewObject(instanceName);
        if (existingVO == null) {
            _logger.info("Creating VO instance for " + instanceName);
            ViewObject newVO = this.createViewObject(instanceName, voDefName);
        }
        else {
            _logger.info("Reusing VO instance for " + instanceName);
        }
      }

Note how we check to see if that name is already in use first. 

Expose this method as part of the client interface so we can bind that into the taskflow.

Invoke the VO Instance Creation Method from the TaskFlow

Once you've refreshed the data control pallet you should then be able to drag this new AM method (createUniqueVOInstance in my case) into the task flow.  Set that as the default activity on on the flow so that it executes first. You'll pass the full name of the ViewObject definition (e.g. oracle.demo.model.DepartmentsView) into the voName parameter and the instance name you want will come from the parameter you defined for the taskflow (e.g. #{pageFlowScope.pVOInstanceName}). So now when you enter the flow you'll create a new VO instance based on the supplied definition with this name. 

Naturally you then wire this method activity to the rest of the taskflow as normal so that once the instance has been created you can continue to display the page fragments etc.

Rewire the Iterators. 

The final piece of the puzzle is how to tell all of the bindings that you've created within the taskflow to use this newly created VO instance rather than the one that they where created with. This final step is very simple and elegant. for the views and other activities in the taskflow just edit the pageDef XML files and locate all of the iterator bindings that relate to your template VO instance that you used when creating the UI through drag and drop. Now simply change the hard-coded reference to the VO Instance name in the Iterator binding Binds attribute to the expression pointing to your new instance name. e.g. 

  <executables>
    <iterator Binds="#{pageFlowScope.pVOInstanceName}" RangeSize="25"
              DataControl="AppModuleDataControl" id="DepartmentsView1Iterator"
              ChangeEventPolicy="ppr"/>
 </executables> 

And there you go, nothing else has to change.  Just be sure to make this change throughout all of the pageDefs used by the taskflow otherwise you're in for some funny results.

So there you have it, transactionally linked but independently scrolling instances of the same taskflow. 

And there's more

Once you start to think about it, things get even more interesting here. Because each instance of the taskflow has it's own VO  instance they can also have their own set of view criteria applied and yet all still be visible on the screen at once. 

About

Hawaii, Yes! Duncan has been around Oracle technology way too long but occasionally has interesting things to say. He works in the Development Tools Division at Oracle, but you guessed that right? In his spare time he contributes to the Hudson CI Server Project at Eclipse
Follow DuncanMills on Twitter

Note that comments on this blog are moderated so (1) There may be a delay before it gets published (2) I reserve the right to ignore silly questions and comment spam is not tolerated - it gets deleted so don't even bother, we all have better things to do with our lives.
However, don't be put off, I want to hear what you have to say!

Search

Archives
« April 2014
MonTueWedThuFriSatSun
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    
       
Today