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:
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.
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
Thanks for this very useful information. Â The people who suggest contextual event framework didn’t understand your goal. Â While some of this could be done using the framework, your idea of truly isolated portlet style applications is very useful. Â For example, one application (portlet) allows the visitor to browse, search and select a product. Â That application returns a single UPC as a parameter. Â There may be 12 other portlet style task flows that accept a UPC as a parameter and do something with it. Â Maybe it’s an inventory look up, maybe it’s an open order summary, etc. etc. Â Heck, maybe there’s an app that takes the UPC, finds the manufacturer and maps their location. Â The point is that the individual apps don’t know anything about the other apps other than “I’m getting a UPC” or “I’m sending a UPC.”
Very nicely done. Â It’s just like the parameter passing within portlets, but only between ADF task flows.
Note for those having trouble with this:
The pageManager managed bean created in Step 6 needs to have a “session” scope.  Download the sample and look at \ViewController\public_html\WEB-INF\adfc-config.xml if needed.
Dear Luc Bors
I tried implementing this in my application. But no luck.
the eventHandler instance is not getting created.
Can you give some lights on that…..
I am using JDeveloper Studio Edition Version 11.1.1.1.0.
I am implementing this approach for SelectionEvent.
if a single row selected, capturing in SelectionListener and the selected value put in the pageFlowScope, and the value is referenced and another region. Whenever selection changes, the value change should refresh the region.
In this implementation, managed-property value of #{pageFlowScope.eventHandler} value not initialized.
…..
Thanks
Â
Â
Â
Â
I am using Oracle JDev Studio Edition Version 11.1.1.0.1
I created application and noticed that displaytaskflowdefinition1 is not getting refreshed although refresh is set to ‘ifNeeded’
Hence although pageManager.theValue is changed it’s not getting reflected in displaytaskflowdefinition1.
It shows old value.
Help appreciated.
Another solution
http://biemond.blogspot.com/2009/01/passing-adf-events-between-task-flow.html
I was wondering if there was any followup from Pako, how Contextual Events can be used. If yes, could you let us know?
BTW, I have a slightly different requirement, I would like to have a task flow creating an event, which than can be used as a partial trigger in the page containing the task flow. In your soultion, the Refresh=”ifNeeded” forces a task flow to refresh, but this can not be used on arbitrary ADF user interface components.
Regards, Istvan
Thanks PaKo and Annonymous for your comments. However, I am aware of the contextual events framework as such. What I do not readily see or learn from your comments is how you would apply that framework to this specific situation. Could you clarify whether that is really applicable to the situation at hand?
regards, Lucas
I like your investigations and solutions. However, in the 11g there is already a framwork solution with the name Contextual event framework for event-based communication between regions in a page. See also: http://www.oracle.com/technology/products/jdev/collateral/papers/11/newfeatures/index.html
There are Contextual Events that ADF regions expose built-in.