Friday, September 29, 2006

Propagating JMS Messages to Multiple Targets

I was investigating the Oracle JMS Router (Doc: http://download-west.oracle.com/docs/cd/B25221_04/web.1013/b14427/jms.htm#sthref378 ) for a customer today as a mechanism for propagating messages from queues and topics to other queues and topics and thought I would point out a couple of things.

I was trying to implement a picture that looks like the one below using the JMS Router - the JMS router maintains a backlog of jobs that run watching whatever source queues and topics you set up and propagates them to your target destination queues and topics:



What the customer was trying to do was propagate an inbound message to 3 different systems. The strategy they were looking at was to have a topic and then on each system have an MDB that listened to the topic and moved it into a queue for processing by each system. The issue with the approach was the number of MDB that had to be written and maintained over time. With one topic and three target queues this may be a reasonable approach but once you get to dozens of topics each with many targets, writing code gets pretty unreasonable.

The JMS Router lets you make this a configuration exercise. You simply add a job to the JMS router environment to listen to the inbound topic and tell it which queue you want it propagated to. In this case because there are 3 target queues, I simply need to add 3 jobs.

How hard is this to do? Well, in OracleAS 10.1.3 the JMS Router is pre-deployed and ready to go. It took me about 10 minutes to configure this up - I had a handy Java client for throwing messages at a topic so once configured, I could see it in action pretty quickly using Hermes as discussed in an earlier post. You can configure it in two ways:

1. Edit the jms.xml file and add its configuration
2. Use the MBean browser in OracleAS 10.1.3 to do this

For a step by step how-to check out this document on OTN:

http://www.oracle.com/technology/tech/java/oc4j/1013/how_to/how-to-use-JMS-router/doc/How-to-Use-JMS-Router.html

For my configuration I have included the bits from jms.xml here - while this looks verbose you will see the configuration is identical per job with one change being the job name and the target queue:

<jms-router max-local-concurrency="-1" >
<router-job
job-name="job1"
max-retries="16"
polling-interval="5"
pause-job="false"
use-exception-queue="false"
batch-size="30"
>
<message-source>OracleASjms/Topics/jms/demoTopic</message-source>
<source-connection-factory>OracleASjms/MyCF</source-connection-factory>
<message-selector></message-selector>
<subscriber-name>OracleASRouter_job1</subscriber-name>
<source-log-queue>OracleASjms/Queues/OracleASRouter_LOGQ</source-log-queue>
<message-target>OracleASjms/Queues/jms/demoQueue</message-target>
<target-connection-factory>OracleASjms/MyCF</target-connection-factory>
<target-log-queue>OracleASjms/Queues/OracleASRouter_LOGQ</target-log-queue>
<exception-queue>default</exception-queue>
</router-job>
<router-job
job-name="job2"
max-retries="16"
polling-interval="5"
pause-job="false"
use-exception-queue="false"
batch-size="30"
>
<message-source>OracleASjms/Topics/jms/demoTopic</message-source>
<source-connection-factory>OracleASjms/MyCF</source-connection-factory>
<message-selector></message-selector>
<subscriber-name>OracleASRouter_job2</subscriber-name>
<source-log-queue>OracleASjms/Queues/OracleASRouter_LOGQ</source-log-queue>
<message-target>OracleASjms/Queues/jms/demoQueue2</message-target>
<target-connection-factory>OracleASjms/MyCF</target-connection-factory>
<target-log-queue>OracleASjms/Queues/OracleASRouter_LOGQ</target-log-queue>
<exception-queue>default</exception-queue>
</router-job>

<jms-router max-local-concurrency="-1" >
<router-job
job-name="job3"
max-retries="16"
polling-interval="5"
pause-job="false"
use-exception-queue="false"
batch-size="30"
>
<message-source>OracleASjms/Topics/jms/demoTopic</message-source>
<source-connection-factory>OracleASjms/MyCF</source-connection-factory>
<message-selector></message-selector>
<subscriber-name>OracleASRouter_job3</subscriber-name>
<source-log-queue>OracleASjms/Queues/OracleASRouter_LOGQ</source-log-queue>
<message-target>OracleASjms/Queues/jms/demoQueue3</message-target>
<target-connection-factory>OracleASjms/MyCF</target-connection-factory>
<target-log-queue>OracleASjms/Queues/OracleASRouter_LOGQ</target-log-queue>
<exception-queue>default</exception-queue>
</router-job>

</jms-router>


and a screen shot of me adding one job via the MBean browser here - this defaults a number of parameters so the minimal needed here is just the source message, source connection factory, target destination and target connection factory. Because I am lazy, this is how I did it -- then once I had one job entered I copied and pasted in jms.xml and res-start the server. Using ASControl this way lets you do it without a re-start as it goes through the proper JMX entry points. This one begs for a groovy script - more on that later - to automate configuration.



If you need a simple way to route messages to multiple targets and don't want to be writing a lot of code to make it happen manually, this could be your answer. Nice to see this baked right into the product with a minimum of fuss or muss to make it work.

2 comments:

Anonymous said...

This is somewhat unrelated, but here's a question. We have a service running in Oracle app server 10.1.3 that is responsible for keeping track of "stuff" from a bunch of clients. If one client deletes something on their screen, they send a message to this service to delete the object, then the service (via a JMS topic) says "I deleted this object". Each client has a "listener" subscribed to the topic, and once the clients get the update, they remove the object from their screen. (Warning - I'm a manager more than a developer so hopefully I describe this well enough for you to understand).
The problem that we are seeing is that there is a lot of "chatter" between each client and the server running this service. It appears that each client is polling the server asking, "Anything new on this JMS topic?", "Anything new on this JMS topic?" over and over again, once per second (using a little over a kbyte of bandwidth per second I think was the numbers).
So my question is, really, can't we configure this so that the clients get notified when the server has something new for them (rather than the clients all each polling the server)?
Thanks for any help,
Jeff

Mike Lehmann said...

Jeff, not sure what your clients are - MDB, Servlet, J2EE or J2SE clients. And also don't know if you are file/memory based JMS or AQ backed JMS from OC4J.

A couple of pointers that give the flavour of the answer once we know your client and JMS provider to OracleAS include:

File/memory based JMS Provider: http://download-west.oracle.com/docs/cd/B31017_01/web.1013/b28958/jms.htm#g1088175

There is an equivalent for AQ JMS as well in the activation properties of MDBs:

http://download-west.oracle.com/docs/cd/B31017_01/web.1013/b28221/actcfgprop.htm#BCGHADGF

Mike.