Goodbye setActionListener, Hello setPropertyListener
So what did I learn today? This is always a good question to ask yourself and it's funny how sometimes the most fulfilling learning experiences can be totally random and unexpected. I was browsing for some information on ADF contextual events, which lead to me noticing a typo in the JDeveloper help file which in turn lead me to read a bit more of the name help topic and suddenly I learn something new. I love it!
In this case it was a code sample reference which mentioned <af:setPropertyListener>. Although I'd undoubtedly seen this mentioned before, it had just never registered as significant, but such was my mood this evening that I segued into the topic and started to drill down. So it turns out that this component is a sort of super version of our old friend <af:setActionListener>, but it adds much more control to the job of setting managed values. Unlike <af:setActionListener> which is restricted to operating in response to an action in the sense of commandButton or commandLink clicks, it seems that <af:setPropertyListener> is a much more promiscuous beast and can be configured to react to many different types of events and presumably nested within many more types of components as well. Unlike setActionListener which only sports a FROM and TO, setPropertyListener has an additional TYPE attribute which can be set to "action" but also many other event types. The legal values are: action, dialog, disclosure, focus, launch, launchPopup, poll, popupFetch, query, queryOperation, rangeChange, regionNavigation, return, returnPopupData, returnPopup, rowDisclosure, selection, sort, and valueChange.
At this stage I've not been able to drill down further into the possibilities of this as yet, but I can already conceive of a whole bunch of use cases where it might come in handy. Documentation is a little thin on the ground for this component right now but it all looks fairly self explanatory..
Thoughts on JavaOne
I spend last week up at the JavaOne conference in San Francisco, my schedule was pretty full but I did at least manage to touch base with some old acquaintances and catch up with a few friends.
I the end I only managed (keynotes aside) to get to three sessions, one on the semantic Web, once on Engineers as an endangered species and most importantly the JSF 2.0 specification.
The former sessions where pretty good and thought provoking, so certainly a good use of time. The JSF session, however, was, how shall I put this, disappointing...
Disappointing on two levels I have to point out, first of all the presentation itself was not at all well rehearsed or timed and much as I might admire Ed Burns and Roger Kitan from a technical perspective, the session and demos failed to impress.
However, I can be really forgiving on presentation, it's bread and butter to me so I'm probably overcritical. However the big let down was the content of the specification itself. This really worries me.
The theme of the release to borrow Ed's phrasing is "Sow and Harvest", basically the strategy of seeing how the wider JSF community takes the existing infrastructure and invents on top of it and then taking that invention and standardizing this in the Spec.
Now that's a good approach, but it really looks like too little has been harvested in this round, most things seem to be catchup with JSF 1.0 tweaks like Shale, not stuff building on top of 1.2 like ADF. For example, although there is mention of a new memory scope, there was no mention of addressing the inadequacies in the controller itself. How you can do one without the other?
Apparently we'll get a preview of the 2.0 specification document shortly, maybe there will be more nuggets of gold in there to cheer me up, I can only wish...
More on Dumping out Page Flow Scope
In the last piece on this subject I was using a method activity to call the dumpTaskFlowState method in the flow itself as the default activity. Now this works just fine, but it's a little intrusive, so I wanted to improve on that. The it occurred to me that we have the ideal spot. Task flows have an initializer plug point which is called before the flow is formally started. Just adding the call into that works beautifully. You can set the initializer from the Common tab of the task flow editor or of course just hack it into the XML:
<managed-bean>
<managed-bean-name>debugUtil</managed-bean-name>
<managed-bean-class>com.tuhra.ddt.view.util.TaskFlowUtils</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
<initializer>#{debugUtil.dumpTaskFlowState}</initializer>
Dumping out pageFlowScope
One of the biggest issues we see with complex applications based on ADF R11 and is the debugging of complex task flows which are nested within regions in pages. Many of these problems resolve down into the task flow simply not having the state that you thought. So you need a simple way to inspect the pageFlowScope variables. Debugging is one option but I also like to have a little interactive output onto the console whilst I'm in the process of running the app. So I put together this simple utility method which I call as the default activity in my bounded task flows:
public String dumpTaskFlowState(){
AdfFacesContext afctx = AdfFacesContext.getCurrentInstance();
Map flowScopeMap = afctx.getPageFlowScope();
String viewPortName =
ControllerState.getInstance().getCurrentViewPort().getClientId(); System.out.println("----------------------------------------------------");
System.out.println("Dumping pageFlowScope Variables for: ");
System.out.println(viewPortName);
Iterator iter = flowScopeMap.entrySet().iterator();
while (iter.hasNext()){
Map.Entry entry = (Map.Entry)iter.next();
String mapKey = entry.getKey().toString();
Object mapValue = entry.getValue();
StringBuilder bldr = new StringBuilder(" ");
bldr.append(mapKey);
bldr.append("=");
bldr.append((mapValue==null)?"<null>":mapValue.toString());
System.out.println(bldr.toString());
}
return "continue";
}
The corresponding method activity that calls this is registered in the task flow xml thus:
<method-call id="dumpState">
<method>#{debugUtil.dumpTaskFlowState}</method>
<outcome>
<to-string/>
</outcome>
</method-call>
when the class containing the dump method is defined in the debugUtil managed bean in the same task flow thus:
<managed-bean>
<managed-bean-name>debugUtil</managed-bean-name>
<managed-bean-class>com.tuhra.ddt.view.util.TaskFlowUtils</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
</managed-bean>
Finally to wire everything up we set the dumpState method as the default activity and wire up the "continue" outcome to the real start point of the flow.
In time I'll look at moving this onto a listener rather than having to have it in the flow itself, but it works pretty well as it is.
The output for this thing just appears in the console because I've used println() you could use a logger instead. Here's a sample of the output:
------------------------------------------------------------
Dumping pageFlowScope Variables for:
data.mainShellPageDef.tabletabflowdefinition1.tableTabContentsPageDef.dynamicQueryRegionflowdefinition1
currentOwner=FOD
currentObject=ADDRESSES
currentType=TABLE
currentSQL=<null>
Decorating the Train
If you've had a chance to play with the ADF Task Flow features of JDeveloper / ADF Release 11 you may have come across the rather nifty ability to create Task Flows as trains (it's just a checkbox on the create task flow dialog). When you do so, every page you add into the taskflow will be implicitly wired into a train model for you.
Furthermore, when you then drop a <af:tain> or a <af:tainButtonBar> control onto the page it gets (optionally) bound to a train model based automatically on the view activities in the task flow. (using the expression: #{controllerContext.currentViewPort.taskFlowContext.trainModel}. So pretty neat stuff, for free you get all this wiring done.
However, once you play with this four questions tend to spring to mind:
- How do I change the ordering in the train? - Easy, select the train stop (view activity) in the diagram and choose Train > from the context menu. You'll see options to move the stop forwards and backwards. Or you can just reorder the view elements in the XML source view!
- How do I change the label for the train stop in the train component? - Again easy. Select the train stop (view activity) in the diagram and look at the property inspector, choose the Train Stop tab and set the value of display-name property to a string or an EL Expression. If you look at the XML this results in a structure like this:
<view id="page1">
<page>/page1.jspx</page>
<train-stop>
<display-name>My Custom Label</display-name>
</train-stop>
</view>
- How can I prevent access to a stop? - Use an EL expression in the skip
property of the train stop. This expression needs to return a boolean value which can be
based on whatever criteria you have in mind - How do I execute stuff between stops - For example, between pages 3 and 4 of a wizard I want to create a new row in the database to display on page 4. This is slightly less intuitive so I've outlined it in detail below
Executing Activities between Train Stops
When you first look at task flows it seems that there is just no way to do this short of creating your own command buttons and wiring up manual navigation rules to effectively override the trains default navigation. That's OK but it kind of defeats the whole benefit of this auto-generated train model. But it turns out that the developers are much smarter than me (I knew that anyway) and have thought of this requirement.
You need to start out by perhaps turning your thinking on its head. If you want to execute say a router or a method activity between page 1 and page 2 of a wizard you should not think of it in terms of executing that activity after page 1, but rather of thinking about executing the activity before page 2. The user can use the train to jump between pages without passing through the intervening nodes so it's important to think this way around. So, what do you have to do? Let's take the simple example of a two page wizard where I want to execute a method called doSomething before I get to page 2. Here are the steps (assuming that I already have the task flow as a train and page 1 and page 2 in place):
- Drop on a method activity and wire it up to the actual method it's executing.
- Create a Wildcard Control Flow rule on the diagram (if you don't have one) - just drag and drop it from the component palette
- Wire the wildcard activity to your method activity with a meaningful name. I'll use callTheBeforePage2Method here.
- Draw an outcome from the method activity to the associated train stop (page 2 in this case). Call this rule something useful like continue
- Bring up the properties of the method activity and set its fixed-outcome property to the same outcome you used above i.e. continue (it will be in the drop-down list for you to choose)
- Finally select the train stop view activity that this will all execute before (page 2) and select the Train Stop tab in the property inspector, then set the outcome property to the name of your global navigation rule (i.e. callTheBeforePage2Method)
Here's a picture of it being defined.

In this case I've just put one non-view activity to be executed before a stop, in reality you could have several steps, including routers. There is no problem with that as long as the outcome defined in the train stop matches the entry point to the sequence of actions as defined by the global navigation rule. However, if you start to get complex like this, consider encapsulating the train stop and all it's activities in their own bounded task flow. that can still be a train stop and will keep everything neater.
:: Next Page >>