ADF 11g: how events in one region cause other regions to refresh

One of the challenges in building Portal pages is to have events from one Portlet have the desired effect on other Portlets. For example when I select a location on a Map in one Portlet, other Portlets may need to be synchronized to show the weather, the best restaurants and the driving instructions for actually going there. The JSR-168 specification does not address this issue; the upcoming JSR-286 (“Portal 2.0”) specs do. Working with ADF 11g, we have some fairly new mechanisms at our disposal, such as Task Flows and Regions, that allow us to construct pages (and applications) that are somewhat reminiscent of Portlets – or at least some of the interesting sides of Portlets in terms of creating relatively simple pieces of (stand-alone) functionality that can be wired together in a larger mashup – where ideally the mashup framework can provide event propagation between the stand alone pieces. In this article, I want to investigate how for example an event occuring in region A – based on a bounded taskflow – can be passed on to region B that is based on another bounded taskflow.

In crude draft it would look somehing like this:

ADF 11g: how events in one region cause other regions to refresh regionevents

It is important in order to preserve the reuse potential that:

1. region A does not to know anything about the page it is called from/contained within. the bounded taskflow called from Region A published an event in a way it stipulates itself and the consuming page can do with the event whatever it feels like

2. region B does not need to know anything about either region A and its taskflow nor the page in which it happens to be consumed; the taskflow in region B has defined input parameters and the region can be refreshed whenever those input parameters are changed, but that is the only point of contact between the page and the taskflow.

I created a very simple and granted ugly application to demonstrate a fairly generic mechanism that could be used for synchronizing the regions that are both based on bounded taskflows. If anyone happens to know a mechanism that is part of ADF 11g – I have not been able to find one – I would be very interested.

The key to my approach is the EventHandler interface – an generic interface that transcends taskflows and pages. Any taskflow that may feel the need to reports events, can specify an input parameter based on the EventHandler interface. I think it would be best to specify an (optional) input parameter for every type of event the taskflow wants to publish – but perhaps a single event handler with more generic event handling logic will do as well.

Whenever something happens within the taskflow that should trigger the publication of one of the events, the taskflow should execute logic that will call the handleEvent method in the (appropriate) EventHandler – if that one has been configured (in the taskflow element in the pageDefinition of the calling page). Whatever the page decides to do with the event is not for the taskflow to know.

ADF already provides the mechanism to refresh a region-that-calls-a-bounded-taskflow whenever the input parameters for that call are changed.

With these pieces available, I create the following:

– a bounded task flow entry-task-flow-definition with a single page fragment that contains a single inputText; the inputText has its autoSubmit set to true and is linked to a valueChangeListener. The valueChangeListener will inspect whether the pageFlowScope contains an EventHandler and if it does (which means that the caller of the taskflow provided one through the taskflow’s input parameter), it will notify the event handler of the event that took place.

– a bounded task flow display-task-flow-definition with a single page fragment that contains an outputText that reads its value from the pageFlowScope. This task flow also defines a single input parameter. The value passed into this parameter is stored in the pageFlowScope at the location where the outputText looks for its value

– a page with two regions, one for each of the two task flows introduced above. the page definition specifies the input parameters for both taskflow usages: a managed bean is passed to handle the value change event published by the entry task flow and the value retrieved from the event is passed in to the second (display) taskflow.

Steps in more detail

1. Create the EventHandler interface

package nl.amis;

public interface EventHandler {

    public void processEvent(Object eventPayload);
}

2. Create the class that implements the value change listener and publishes the event

package nl.amis;

import javax.faces.event.ValueChangeEvent;

public class EntryManager {

    private EventHandler eventHandler;
    public EntryManager() {
    }

    public void handleValueChange( ValueChangeEvent e) {
        if (eventHandler!=null) {
            eventHandler.processEvent(e.getNewValue());
        }
    }

    public void setEventHandler(EventHandler eventHandler) {
        this.eventHandler = eventHandler;
    }

    public EventHandler getEventHandler() {
        return eventHandler;
    }

}

Note: this class is created specifically for the Entry Task Flow.

3. Create the class that implements EventHandler and processes the event

package nl.amis;

public class PageManager implements EventHandler {
    private String theValue ="The Value";

    public PageManager() {
    }

    public void setTheValue(String theValue) {
        this.theValue = theValue;
    }

    public String getTheValue() {
        return theValue;
    }

    public void processEvent(Object eventPayload) {
      setTheValue((String) eventPayload);
    }
}

Note: this class is created specifically for the Main Page that includes regions with the two bounded taskflows

4. Create the Entry Bounded Task Flow with a single View activity (enterValue) a single input parameter (valueEventHandler)

The enterValue.jsff page fragment contains the inputText element:

  <af:inputText label="Please enter a value"  id="thefield" autoSubmit="true"
  value="#{pageFlowScope.valueEntered}" valueChangeListener="#{valueChangeHandler.handleValueChange}" />

Because of the way we read the value from the ValueChangeEvent, it does not really matter where we actually store the value entered in this component.

The task flow has single managed bean – valueChangeHandler based on class nl.amis.EntryManager.

    <managed-bean>
      <managed-bean-name>valueChangeHandler</managed-bean-name>
      <managed-bean-class>nl.amis.EntryManager</managed-bean-class>
      <managed-bean-scope>request</managed-bean-scope>
      <managed-property>
        <property-name>eventHandler</property-name>
        <property-class>nl.amis.EventHandler</property-class>
        <value>#{pageFlowScope.eventHandler}</value>
      </managed-property>
    </managed-bean>

The value of the valueEventHandler input parameter is stored in #{pageFlowScope.eventHandler}.

    <input-parameter-definition>
      <name>valueEventHandler</name>
      <value>#{pageFlowScope.eventHandler}</value>
      <class>nl.amis.EventHandler</class>
    </input-parameter-definition>

5. Create the Display Bounded Task Flow with a single View activity (displayData) a single input parameter (value)

The value of the input parameter is stored in #{pageFlowScope.displayValue}. It is read from that location in the outputText element in the displayData.jsff page fragment:

<af:outputText value="#{pageFlowScope.displayValue}" id="valueMonitor"/>

The input-parameter element looks like this:

    <input-parameter-definition>
      <name>value</name>
      <value>#{pageFlowScope.displayValue}</value>
      <class>java.lang.String</class>
    </input-parameter-definition>

6. The main unbounded taskflow has a single managed bean – pageManager based on PageManager. It implements the EventHandler interface and contains the value – property TheValue – returned from enter taskflow and passed to the display taskflow. It has the EnterAndDisplay.jspx page that includes two regions, one for each task flow.

The page definition for this page has the taskflow elements that specify the configuration of the task flow usages:

    <taskFlow id="entrytaskflowdefinition1"
              taskFlowId="/WEB-INF/entry-task-flow-definition.xml#entry-task-flow-definition"
              xmlns="http://xmlns.oracle.com/adf/controller/binding">
      <parameters>
        <parameter id="valueEventHandler" value="#{pageManager}"
                   xmlns="http://xmlns.oracle.com/adfm/uimodel"/>
      </parameters>
    </taskFlow>
    <taskFlow id="displaytaskflowdefinition1" Refresh="ifNeeded"
              taskFlowId="/WEB-INF/display-task-flow-definition.xml#display-task-flow-definition"
              xmlns="http://xmlns.oracle.com/adf/controller/binding">
      <parameters>
        <parameter id="value" value="#{pageManager.theValue}"
                   xmlns="http://xmlns.oracle.com/adfm/uimodel"/>
      </parameters>
    </taskFlow>

Here we can see how the entry taskflow gets passed in its valueEventHandler the managed bean pageManager that implements the EventHandler interface. The display taskflow gets the pageManager’s theValue property for its input parameter. Now whenever the value collected in the first region is changed, its valueChangeEvent handler is invoked, that in turn will notify the pageManager through its EventHandler interface implementation. The pageManager will set the TheValue property. At this point ADF proper takes over and refreshes the second region – because the input parametger value is tied to this property and the taskFlow element for the display taskflow usage has the Refresh attribute set to ifNeeded.

ADF 11g: how events in one region cause other regions to refresh regiionrefresh004

Summary

I managed to find a way that allows a bounded taskflow that is embedded in a page via a region to pass events to whomever is listening – presumably that page. Subsequently, it is fairly easy for the page to use the event to update input parameters for other embedded taskflows, thereby causing them to synchronize. This increases the reuse potential – as bounded task flows can do much more than only receive input and perform tasks based on that – they can have much more active interaction with the page.

I still have the nagging feeling that what I have done using the EventHandler must be part of ADF in a more intrinsic way. If I find that way – if it exists at all – I will update this post. If someone knows that more built-in way of sending results or events from bounded task flows embedded in pages via regions to the consuming page – please write a comment on this article.

Resources

Download the JDeveloper 11g Application RegionRefresh

9 Comments

  1. dp February 23, 2012
  2. Mark Woodman February 2, 2012
  3. Muthuvel January 11, 2010
  4. Madhav Vaidya May 12, 2009
  5. Istvan Kiss February 20, 2009
  6. Lucas Jellema September 17, 2008
  7. anonymous September 7, 2008
  8. PaKo September 6, 2008