@OracleIMC Partner Resources & Training: Discover your Modernization options + Reach new potential through Innovation

ADF Dynamic tabs shell and contextual events

Guest Author

Hello to everyone. Inspired by Frank Nimphius’ and Lynn Munsinger’s excellent book “Oracle Fusion Developer Guide” I have been
investigating recently the contextual events framework in ADF. I came up with a conclusion that it complements ideally another nice feature – dynamic tab shell template. While the latter allows you to easily and dynamically launch the task flows in separate tabs, the former adds generic and flexible way to provide communication between them. So wiring together those two features gives you complete framework for advanced MDI-like applications. Unfortunately so far dynamic tab template does not cover contextual events communication so we need to add this manually.

I have created a little PoC (you can download it here) showing an example of such cooperation. The application is very simple yet - by using generic mechanism – quite agile and elastic. The main idea is to create a web application which could be used to edit different entities in HR schema. Dynamic tab template allows us to provide navigation area on the left side of the page with the navigation facet. I have used this facet to place a region with a taskflow showing a tree of all HR entities: Regions, Countries, Locations, Departments and, of course, Employees. Right-clicking on the tree node and choosing Edit allows opening a tab with the editor for given node. Each node type has its own taskflow for edition. The goal is to provide generic way to open correct editor for any node and signal the parent tab about operations setting dirty flag so the shell can mark given tab as “dirty” or cleanup the mark when needed. Following the best ADF practices both editor and structure tree taskflows should be generic and reusable in other applications so they should not assume being used by dynamic tab page. The only technology flexible enough to meet those requirements is contextual events framework. So we will use the following types of contextual events:

  • EditNodeEvent: event triggered by the context
    menu in the tree. The payload for the event is a JUCtrlHierNodeBinding class
    instance taken directly from the tree
  • CommitEvent: triggered when the editor taskflow
    commits the changes and should be dismissed
  • RollbackEvent: the same as above but for signaling
    the rollback operation. Both commit and rollback should also close the
    corresponding tab. They don’t have any payload.
  • DirtyEvent: event which signals change of the
    dirty flag. It has a simple payload with Boolean type holding true or false.

Those four types of events are enough to support our framework. Below is a picture showing all those events and their usage.




The consumer of all of those events is the main page with a dynamic tab template. The navigation taskflow (the HR tree) is generating the EditNodeEvent, the editor taskflows publish commit, rollback and dirty events. The only requirement for the editor taskflows is that they should have one input parameter for passing the key value of the corresponding node.

In my application there are only two editor taskflows: for departments and for employees. Of course adding additional editors is relatively easy – just follow the generic rule about passing the key value and producing required events when needed. Let’s discuss in more details all the events we use in the example.

DirtyEvent should be fired whenever we change any field in the editor. We can define it declaratively in the binding layer, more precisely on the attribute binding. Below is an example from employee editor’s page definition:



<attributeValues IterBinding="EmployeesIterator" id="CommissionPct">

<events xmlns="http://xmlns.oracle.com/adfm/contextualEvent">
<event name="DirtyEvent" customPayLoad="#{true}"/>


<Item Value="CommissionPct"/>





When we attach the event to the attribute binding it will fire it automatically when the value is changed. To make it work as soon as we change the value in the UI we need to set the AutoSubmit flag on each field as well.

On this particular page the event is fired only for the First Name and Commission fields but for other bindings we can declare it in similar way. Notice that we pass static value “true” here because every time some value changes on the page we need to flag the tab as dirty. But how do we clean the dirty flag? There is only one situation when we need to clean this flag – when we reset the editor to its initial values. If it comes to resetting the form – there are couple of issues here as well. We cannot simply reload the values from the model because the model itself may have been changed (remember
the auto submit flag!). The easiest way to reset the model is to invoke application-wise rollback. Then we can fire the DirtyEvent with payload set to
false and re-execute the editor taskflow by going to its SetCurrentEmployee activity. This is exactly what Reset button does. Below is a snippet from the code in the backing bean:


public void onReset(ActionEvent actionEvent) {
BindingContext bctx =

BindingContainer bindings =

to reset the form first we need to rollback all changes

made in the model during auto-submit

the control flow from current page to the SetCurrentRow activity in the task
flow will finish the reset

OperationBinding rollback =

JUEventBinding eventBinding = null;

then we can programatically fire the DirtyEvent event using event binding

eventBinding = (JUEventBinding)
ActionListener al =




As you see we invoke both rollback and event firing programmatically using binding definitions. To complete this we need to define the dirtyEventBinding in page definition which is responsible for cleaning the dirty flag:


<eventBinding id="dirtyEventBinding" Listener="javax.faces.event.ActionListener">
<events xmlns="http://xmlns.oracle.com/adfm/contextualEvent">
<event name="DirtyEvent" customPayLoad="#{false}"/>




There is also a way to do it completely declaratively – rollback can be added in the editor taskflow as an activity between the wild card and SetCurrentEmployee and event firing can also be invoked with #{bindings.dirtyEventBinding.listener.processAction} expression in ActionListener property (btw this is how generic rollback and commit events have been defined on the Cancel and Save buttons). But the goal here is to show both ways as you will probably encounter situations when you cannot do it declaratively.

As I mentioned before RollbackEvent and CommitEvent are fired declaratively using EL expression in the buttons and the proper entries in the page definition file:



<eventBinding id="commitEventBinding" Listener="javax.faces.event.ActionListener">

<events xmlns="http://xmlns.oracle.com/adfm/contextualEvent">
<event name="CommitEvent"/>

<eventBinding id="rollbackEventBinding" Listener="javax.faces.event.ActionListener">
<events xmlns="http://xmlns.oracle.com/adfm/contextualEvent">
<event name="RollbackEvent"/>



The last event to explain is the EditNodeEvent. It is fired when user chooses the edit option from context menu or tree menu after selecting particular node in the structure tree. This event invokes opening a new tab with appropriate editor task flow. The payload of this event is the whole node object (JUCtrlHierNodeBinding) from the tree which contains all necessary information about the node we want to edit. Of course before we can 

use that event it should be declared in the hr-structure page definition file:


<eventBinding id="eventBinding" Listener="javax.faces.event.ActionListener">

<events xmlns="http://xmlns.oracle.com/adfm/contextualEvent">
<event name="EditNodeEvent" eventType="Edit" customPayLoad="#{pageFlowScope.structure_helper.currentNode}"/>




Please notice the payload declaration – this is custom payload taken (by EL) from a helper managed bean. This managed bean (StructureHelperBean)
has a property currentNode which is set in our custom selection listener tied
to the tree component. Below is the code of this listener:


public void onNodeSelection(SelectionEvent event) {

get the collection model from injected RichTree object

CollectionModel treeModel =
(CollectionModel) tree.getValue();

extract the binding object

JUCtrlHierBinding treeBinding =
(JUCtrlHierBinding) treeModel.getWrappedData();

get the selected keys

RowKeySet rks = tree.getSelectedRowKeys();
// here we get only first key set assuming single selection on the
source tree

List firstSet =

// find the current node binding
JUCtrlHierNodeBinding treeNode =

// eagerly initialize the node default name
// if we omit this the binding object may lose connection with
underlying row

// later during the event consumption and then it cannot
reinitialize the node string


// save the node
this.currentNode = treeNode;





We should call toString() method on the node to make the
internal structure of the JUCtrlHierNodeBinding class fully initialize itself
to make sure we have all necessary information on the consumer side.

So this is the way we are firing all framework events. Of
course in your own editor taskflows you can do it in completely different way
but as long as you provide correct event names and payloads (when applicable) the
main page will behave identically managing the editor tabs.

Now let’s see how to handle all those events. As I mentioned
at the beginning there is only one single consumer of all events. This is the
main page based on Dynamic Tab Shell template. All events are fired and handled
in the binding layer so we need to provide appropriate method bindings to
handle the consumption of our events. The quickest way is writing a java class
with all necessary methods and then creating a data control out of it to be
able to reach those methods from the binding layer. In our application it is
the CEHandler class. It has four methods to handle four framework events.
Handling commit, rollback and dirty events is really simple – we should call
the tab shell API methods to close or set the dirty flag respectively. Also
notice we don’t need to bother with checking which tab we need handle – the
events always come from the current tab. Below we can see how it is done in the


public void handleCommit() {

public void handleRollback() {

public void handleDirty(Boolean dirty) {




The EditNode event is more difficult to handle but due to generic nature of our framework we are flexible enough to add another types of nodes later on without major changes to this method. We can even make it fullyconfigurable in declarative way but that’s out of the scope of this post. Basically all we need to do is to extract the type, row key and tab name from the JUCtrlHierNodeBinding object passed as a payload and open the appropriate taskflow in a new tab using the tab shell API:


public void handleCEEdit(Object payload) {

check if payload is not null

if (payload != null) {
// check the type of the payload. If during the event firing
// the custom payload expression evaluates to null then ADF is
// replacing the payload with default one e.g.

// thus ALWAYS check the payload class!
if (payload instanceof
JUCtrlHierNodeBinding) {
node = (JUCtrlHierNodeBinding)payload;

// get type of the node
String definition =

// get the key value
String rowKeyValue = node.getRowKey().getKeyValues()[0].toString();
// get the node default string (defined in the binding)
String tabName =

HashMap<String,Object> params = new HashMap<String,Object>();

if (definition.equalsIgnoreCase("test.hr.model.vo.EmployeesVO")) {
try {

TabContext.getCurrentInstance().addTab(tabName, "/WEB-INF/edit-employee-tf.xml#edit-employee-tf", params);
} catch (TabOverflowException e) {
} else if (definition.equalsIgnoreCase("test.hr.model.vo.DepartmentsVO")) {
try {

TabContext.getCurrentInstance().addTab(tabName, "/WEB-INF/edit-department-tf.xml#edit-department-tf", params);
} catch (TabOverflowException e) {




The type is simply the definition name of the node object.
It should be mapped then to particular taskflow definition. Btw here is the
room for improvement e.g. by reading this mapping from external configuration
file or database etc.

The last thing we need to do is to make sure that all events
fired inside the editor taskflows are properly mapped to their handlers. This
can be done inside the page definition of the main page by using event mapping


<eventMap xmlns="http://xmlns.oracle.com/adfm/contextualEvent">

<event name="EditNodeEvent">
<producer region="*">
<consumer region="" handler="handleCEEdit" handleCondition="">
<parameter name="payload" value="#{payLoad}"/>
<event name="CommitEvent">
<producer region="*">
<consumer handler="handleCommit"/>
<event name="RollbackEvent">
<producer region="*">
<consumer handler="handleRollback"/>
<event name="DirtyEvent">
<producer region="*">
<consumer handler="handleDirty">
<parameter name="dirty" value="#{payLoad}"/>




As you can see for each possible framework event we are providing producer and consumer configuration. Producer is in fact each and every region (no matter how deeply nested) – hence the definition region=”*”. Consumer is a method exposed in the binding layer with parameters derived usually from “#{payLoad}” expression. Please pay attention to case – it is always “payLoad” with capital “L” inside.

So that’s it for today, feel free to play with this example and extend it with more functionality. It would be interesting for example to add drag&drop feature from the tree to the tabs area. Stay tuned for more examples!

Join the discussion

Comments ( 1 )
  • Bhargav Tuesday, November 22, 2011

    Excellent post. Good Job.

Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.