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. 

Comments:

Duncan,

Nice article, but this is so basic for reuse this actually needs to be part of the framework and ideally it must be handled automatically.

I understand this sort of "occurred" after the introduction of regions and fragments, but it's really something you need to provide for transparently in my opinion.

Not addressing this actually prohibits regions and fragments to fulfill their full reuse potential.

Many thanks!

Posted by guest on March 01, 2012 at 09:25 PM GMT #

I disagree - as I've shown it's pretty easy to set this up, so the usability of the framework is in no way compromised. From a design perspective we do have to decide where to draw the line on adding knobs and switches to allow more and more configuration to the framework. Too much and things just become too complex to understand.
In this case - yes we could add a switch to the TaskFlow that says "use a separate rowset iterator" but then we still have the problem of applying different criteria and filtering. To add switches for all that would get things really ugly really quickly. So the line is in about the right place I think.

Posted by Duncan on March 02, 2012 at 10:57 AM GMT #

Duncan,

Thank you for your reply.

One clarification, I did not mean you should include another switch to control for this. We do not need a switch, we need a change in the default framework behavior here. ADF should provide this transparently whenever a fragment-based task flow is bound many times in the same viewport with its transaction scope set to 'Shared'.

Even for this shared transaction case, the VO iterators should be automatically different, and the developer should get sensible behavior out of the box and without having to code or manually touch anything.

The default framework behavior as ofnow in this scenario is conceptually a bug. Both as a developer or user, if I include the same TF twice within a view, I expect the regions to function in isolation *at least* edit-wise and record-position-wise. The implementation is semantically 'leaky' if it suffers from side-effects that one can only rationalize when understanding the model's specifics. Transaction-wise, we are covered by the existing settings; all it would take is providing for iterator isolation automatically.

The resolution as you describe it may be workable, and implementation-wise it is the most elegant example I have seen to provide for this, and it will be usedout of necessity. But it still sort of breaks encapsulation. Why should the UI cater to providing unique names? why should we edit the bindings manually? Can you describe one valid use case where the default ADF behavior using the same TF in regions makes sense and would not be considered a bug by a user (asking as I may be missing something).

So I still think that the framework should address this as an integral part of the region / fragment concept and provide it automatically and without the need for a switch. It should be the default framework behavior and handled *transparently*. Then, there would be no need for deciding, at the ViewController level, on a unique name to pass to the model and so on. This would take all the great reuse plumbing you've put in already to its next logical conclusion.

Posted by guest on March 03, 2012 at 11:48 AM GMT #

But that takes a very limited view of what a task flow is, you might have a case if all task flows only contained a single page fragment and addressed a single iterator, but of course this is not the case. You could easily use the same TF on a single page where the different usages exposed differing UIs (e.g. chart on one, form on another) but you wanted both of these views to be coordinated. With your proposal how would this be possible? Likewise many task flows contain multiple iterator references and manage Master-detail across regions. Isolation would really mean that overall developers would have to work much harder to do the things that are easy to do today, and all to satisfy a rather rare use case in the majority of applications that I've seem.
Yes the data control integration is one of the leaky parts of the Task Flow encapsulation model (see my Task Flow design fundamentals paper on this) but the benefits generally out-weight the problems, and of course the framework does allow you to implement all of these use cases if you actually need to.

Posted by Duncan on March 05, 2012 at 08:34 AM GMT #

Post a Comment:
Comments are closed for this entry.
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