Yesterday I blogged about difficulties in routing messages from an AQ adpater to different outputs using the Oracle ESB (https://technology.amis.nl/blog/?p=1395). I was unable to create a single router that could route incoming messages based on the correlation ID. With help from Dave Berry on the OTN forum, he pointed me to a good ESB sample provided by OTN.
Now I am able to create a single routing service that routes incoming messages to different output channels, based on the correlation ID or whatever attributes located in the messaeg header. And here’s a brief howto…
I call this a mesage based routing service because routing is not based on message content (content based routing), but based on information in the message header. The ESB system is shown in figure 1. Shown is a single input AQ-adapter named GetAlleTickers (This is Klingon for "get all tickers"). This adapter is dequeueing the Q explained in my previous blog.
Figure 1: ESB system (instance view)
The router service (named GetAlleTickersAdapter_RS) has 3 filters (also shown in figure 1). The upper filter filters out all messages with correlation ID = 3 and sends the message content to file adapter LogCorrel3. The middle filter filters out all messages with correlation ID = 2 and outputs to file adapter LogCorrel2. The lower filter filters out all messages with correlation ID other than 2 or 3 and outputs those to file adapter Logfile.
Now for the tricky part: how to properly route everything. In the JDeveloper ESB project, when you double-click on the routing service, you can enter filters and transformations. When clicked on Filter figure 2 is shown. Pick out Header functions: getRequestHeader (not function getInboundRepsonseHeader!. After selecting it into the Expression area, you must manually prefix the function with ehdr: (this should happen automatically I think, but on my system it didn’t). Then you have to fill in an XPath value for argument ‘headerXPath’ and a value for ‘namespaceDeclarations’. How do you know what to fill in here? The wsdl file for the AQ adapter knows it all!
Figure 2: Expression builder for router filter
Check out file aqAdapterInboundHeader.wsdl. Either it will have a definition for an element called Header or it will have an include node pointing to a file aqAdapterInboundHeader.xsd. Figure 3 shows the content of my schema definition file. The XPath expression to get to the correlation ID = /Header/Correlation. Whenever this expression is used it has to be prefixed with the proper namespace. In this case the targetNamespace shown highlighted in figure 3.
Figure 3: aqAdapterInboundHeader.xsd
So, these are almost all building blocks for putting together the routing filter: there is 1 building block to go. In the Expression builder the value for headerXPath becomes: /hdr:Headers/hdr:Correlation and the namespaceDeclaration must (?) be kept empty. The result of the filter expressions are shown in figure 4. One of the filter expressions is shown highlighted. As you can see an extra namespace area is added that was not there in the Expression builder. The namespace with alias hdr is automatically added by JDeveloper when you close the Expression builder. The namespace of ehdr is added manually. JDeveloper did not add it there, though it should. That namespace declaration is apparently crucial, because when omitted the router does not work. The hdr namespace is exactly the same as the targetNamespace in the xsd file (fig 3) So, from the hsr-alias I now know how to prefix the headerXPath expression. That is where my original hdr-prefix was comming from.
The final filter expression as shown in fig 4 is added with help from a text editor and paste the whole filter string including namespaces into the routing service. Ough!
Figure 4: XML-code fragment of the router service.
I can only hope that I am still missing an important feature to properly and more easily create filter expressions using jdeveloper, because this is a very clumsy and error prone way to create expressions.
After registering the esb system I gave it a try with a couple of messages. My enqueue procedure is simple. The message is a random number. The correlation ID is calculated by MOD(message number,4)+1, thus generating correlation IDs from 1 to 4.
Let’s see how the ESB handles this. To get a little more insight in what the ESB is doing you can track attributes within the messages. The tracking data is displayed in the instance view of the ESB console (fig 1, at the bottom right). I added tracking of payload and correlation ID (request header correlation). The results are shown in the table below.
Table 1: Enqueued en dequeued messages
time |
enqueued |
dequeued message |
output routed to file adapter |
t0 | payload=2 corr_id=3 |
payload=2 corr_id=1 |
LogCorrel3 |
t1 | payload=1 corr_id=2 |
payload=1 corr_id=3 |
LogCorrel2 |
t2 | payload=3 corr_id=4 |
payload=3 corr_id=2 |
Logfile |
t3 |
payload=4 |
payload=4 corr_id=4 |
Logfile |
t4 | payload=1 corr_id=2 |
payload=1 corr_id=1 |
LogCorrel2 |
t5 | payload=4 corr_id=1 |
payload=4 corr_id=2 |
Logfile |
t6 | payload=1 corr_id=2 |
payload=1 corr_id=1 |
LogCorrel2 |
t7 | payload=5 corr_id=2 |
payload=5 corr_id=2 |
LogCorrel2 |
t8 | payload=5 corr_id=2 |
payload=5 corr_id=2 |
LogCorrel2 |
t9 | payload=5 corr_id=2 |
payload=5 corr_id=2 |
LogCorrel2 |
So what’s happening here?!
1) messages are dequeued by FIFO principle (first in – first out). That’s very good. Messages are not entangled.
2) messages are routed though the proper output channel. That’s very good. The subscriber gets what it wants; no messages are delivered to wrong subscriber(s).
3) The correlation ID tracked is different from the correlation ID of the message. That’s strange! Carefull examination reveals that the tracked correlation ID is showing the correlation ID of the previous message. I predict that a message send at t9 will have coor_id=2, whatever the payload (I am not using OLAP here to make the prediction). The expression for tracked corr_id = {ehdr:getResponseHeader(/hdr:Headers/hdr:Correlation)};{namespace ehdr=… hdr=…} (the same expression as used in the routing filter).
Is this a bug in the tracker mechanism?
We will be adding a proper GUI for Header manipulation in the Routing Service / Mediator in AS11 to make this pattern much easier to implement.