Monday, December 10, 2012

Contextual Event - Interface based Consumer

It's no excuse to skip blogging all year but since it's not yet the end of the year, I still have enough time to post one last hoorah for 2012.

And to keep up with all the trend about contextual events, I've decided to publish an article in utilizing contextual events into an Observer Bean design pattern.

A bit of disclaimer here, I only came out with this idea while eating pizza and wondering why I can't publish a pizza order online and have any pizza company in the vicinity to cater it. All this happened an hour ago so I apologize for any small implementations I might miss... but I'll try to be brief and precise as always... moving on!

So, to start with - I'll assume that this is already the n-th time you've read contextual events. If not, I would recommend going through the online Oracle documentation, a couple blogs by some google'able ADF authors (F.Nimphius, A.Baranovskis, and etc.).

I do want to thank Jobinesh for pointing out a cool implementation in invoking contextual events. There are a couple of ways to invoke it, but I really like this one, particularly because it actually has a "contextualEvent" keyword in it. Why I like it? Well it's because of readability, and in the programmatic world - readability is key. Well good-code > readability > less-code.

I've setup a starting project that runs with a single entry point page (mainIndex.jspx).

mainIndex.jspx
The mainLayoutRegion is a simple bounded taskflow that has one fragment called mainFlowFgmt.jsff. This fragment will be our main fragment that wraps other regions as defined in the project structure. Please refer to the project structure as reference to the defined region.

BoundedTaskFlows: main-layout-flow, sub-flow-alpha, sub-flow-omega

mainFlowFgmt.jsff


Project Structure

If I run everything, things will basically look like this (Take Note, I've labeled where the region starts).

Running Sample
So here's the plan:

  • Wire up (queue the event) from the two buttons defined in Alpha. Both events will be unique so that we can test a couple scenarios. (Haven't got the chance to code the 2nd event but you'll get the gist of it soon)
  • Both events will have a unique eventName with a no parameter definition (meaning no-payload).
  • Calling this event mockEvent!
  • Listen for the event in mainFlowFragment.
  • Listen for the event in mainIndex page.
  • Listen for the event in Omega region.
  • Use a common handler for all of these BUT each of these fragments's backingBean will be the listener to a common event handler.
  • Let me repeat that last one... Handler (DataControl) one-is-to-many Listener (Beans). 
  • One more time... One datacontrol method... many backingBeans listening and being utilized by the same datacontrol method in different levels.
Here's a utility that invokes/queues the event to start the bubble of the contextualEvent. Again, code is thanks to Jobinesh.

Util.java
Going back to Alpha fragment. Here's the pageDef binding definition as well as the backingBean which invokes it.

Alpha pageDef - Contains the Event Declaration
AlphaBb.java

Here comes the fun part. I'll define a class to represent the datacontrol handler for the event but this class will cater a listener interface (Observer) so that whenever there is a listening pageDef (consumer) I will be able to define a/the oberserver (bean/method) which will handle the event. I'll also show in the code, a single backingBean handling two consuming definition from a single event (whoo!).

Quick ADF talk here: To be able to handle/consume an event, an event mapping has to be made. Part of the event mapping is to define the consumer as well as the method binding to handle the event. This method binding is defined by converting a class into a datacontrol (more in the developer guide).

Technically, the datacontrol (class) will still be the real listener, but because I want to allow the interface to start with the beans rather than the main listener - I am going to distribute the event with the observing beans.

What's the advantage?
  • Beans will be well defined in the xml of the event map.
  • Bean methods can provide the interface. 
  • So yes, you can actually point to use the pageFlowScope or any scope beans in that taskflow to handle it without touching the datacontrol again.
Less talk more code.

Interface Class - MockEventListener.java

DataControl Class - MockEventHandler.java

And here's a sample mapping to my mainLayoutBb.

Event Mapping pageDef - Double Consumer Same Bean!!! Notice the value.

Two Anonymous Implementation of listening!

So far is it making sense? If not consider the Observable pattern in java and see if it explains the idea. To expand it a bit, we'll make the MainIndex.jspx have it's own backing bean and use the same datacontrol.

MainIndexBb.java (defined in adfc-config)

mainIndex.jspx PageDefinition

Here's the console output when I hit the event - I'm also printing the lifecycle so that you can see that the invocation is all happening in InvokeApplication phase. It is worth noting though that the order of the events can vary a bit. I'm still trying to figure it out myself - but then again it shouldn't matter because you shouldn't rely on the order of the queued events. All the events should be nothing but observing factors.

console
So real quick, I've show you how to use/define one datacontrol to cater several listening interfaces. This is basically an application of the observable pattern in a contextual event framework. Things to keep in mind is that all of these is happening in one request - so ideally you wouldn't want to place too much logic when listening to events because it can get really complicated really fast. You would also want to keep a solid event handler that you'll rarely change that's why keeping it interfaced is always a plus.

No comments:

Post a Comment