By Antony Reynolds on Mar 28, 2014
Using a Single Queue for Multiple Message Types with SOA Suite
Problem StatementYou use a single JMS queue for sending multiple message types / service requests. You use a single JMS queue for receiving multiple message types / service requests. You have multiple SOA JMS Adapter interfaces for reading and writing these queues. In a composite it is random which interface gets a message from the JMS queue. It is not a problem having multiple adapter instances writing to a single queue, the problem is only with having multiple readers because each reader gets the first message on the queue.
The JMS Adapter is unaware of who receives the messages. Each adapter instance just takes the message from the queue and delivers it to its own configured interface, one interface per adapter instance. The SOA infrastructure is then responsible for routing that message, usually via a database table and an in memory notification message, to a component within a composite. Each message will create a new composite but the BPEL engine and Mediator engine will attempt to match callback messages to the appropriate Mediator or BPEL instance.
Note that message type, including XML document type, has nothing to do with the preceding statements.
The net result is that if you have a sequence of two receives from the same queue using different adapters then the messages will be split equally between the two adapters, meaning that half the time the wrong adapter will receive the message. This blog entry looks at how to resolve this issue.
Note that the same problem occurs whenever you have more than 1 adapter listening to the same queue, whether they are in the same composite or different composites. The solution in this blog entry is also relevant to this use case.
SolutionsIn order to correctly deliver the messages to the correct interface we need to identify the interface they should be delivered to. This can be done by using JMS properties. For example the JMSType property can be used to identify the type of the message. A message selector can be added to the JMS inbound adapter that will cause the adapter to filter out messages intended for other interfaces. For example if we need to call three services that are implemented in a single application:
- Service 1 receives messages on the single outbound queue from SOA, it send responses back on the single inbound queue.
- Similarly Service 2 and Service 3 also receive messages on the single outbound queue from SOA, they send responses back on the single inbound queue.
- aThe inbound JMS adapter is configured with a JMS message selector. The message selector might be "JMSType='Service1'" for responses from Service 1. Similarly the selector would be "JMSType='Service2'" for the adapter waiting on a response from Service 2. The message selector ensures that each adapter instance will retrieve the first message from the queue that matches its selector.
- The sending service needs to set the JMS property (JMSType in our example) that is used in the message selector.
- We can do manual correlation with a correlation set, identifying parts of the outbound message that uniquely identify our instance and matching them with parts of the inbound message to make the correlation.
- We can use a Request-Reply JMS adapter which by default expects the response to contain a JMSCorrelationID equal to the outgoing JMSMessageID. Although no configuration is required for this on the SOA client side, the service needs to copy the incoming JMSMessageID to the outgoing JMSCorrelationID.
Special Case - Request-Reply Synchronous JMS Adapter
When using a synchronous Request-Reply JMS adapter we can omit to specify the message selector because the Request-Reply JMS adapter will immediately do a listen with a message selector for the correlation ID rather than processing the incoming message asynchronously.
The synchronous request-reply will block the BPEL process thread and hold open the BPEL transaction until a response is received, so this should only be used when you expect the request to be completed in a few seconds.
The JCA Connection Factory used must point to a non-XA JMS Connection Factory and must have the isTransacted property set to “false”. See the documentation for more details.
I developed a JDeveloper SOA project that demonstrates using a single queue for multiple incoming adapters. The overall process flow is shown in the picture below. The BPEL process on the left receives messages from the jms/TestQueue2 and sends messages to the jms/Test Queue1. A Mediator is used to simulate multiple services and also provide a web interface to initiate the process. The correct adapter is identified by using JMS message properties and a selector.
The flow above shows that the process is initiated from EM using a web service binding on mediator. The mediator, acting as a client, posts the request to the inbound queue with a JMSType property set to "Initiate".
|Inbound Request||Client receives web service request and posts the request to the inbound queue with JMSType='Initiate'||The JMS adapter with a message selector "JMSType='Initiate'" receives the message and causes a composite to be created. The composite in turn causes the BPEL process to start executing.
The BPEL process then sends a request to Service 1 on the outbound queue.
|Service 1 receives the request and sends a response on the inbound queue with JMSType='Service1' and JMSCorrelationID= incoming JMS Message ID.|
|Separate Request and Reply Adapters||The JMS adapter with a message selector "JMSType='Service1'" receives the message and causes a composite to be created. The composite uses a correlation set to in turn deliver the message to BPEL which correlates it with the existing BPEL process.
The BPEL process then sends a request to Service 2 on the outbound queue.
|Service 2 receives the request and sends a response on the inbound queue with JMSType='Service2' and JMSCorrelationID= incoming JMS Message ID.|
|Asynchronous Request-Reply Adapter||The JMS adapter with a message selector "JMSType='Service2'" receives the message and causes a composite to be created. The composite in turn delivers the message to the existing BPEL process using native JMS correlation.
|The BPEL process then sends a request to Service 3 on the outbound queue using a synchronous request-reply.|
Service 3 receives the request and sends a response on the inbound queue with JMSType='Service2' and JMSCorrelationID= incoming JMS Message ID.
|Synchronous Request-Reply Adapter||The synchronous JMS adapter receives the response without a message selector and correlates it to the BPEL process using native JMS correlation and sends the overall response to the outbound queue.
|Outbound Response||Client receives the response on an outbound queue.|
When using a single JMS queue for multiple purposes bear in mind the following:
- If multiple receives use the same queue then you need to have a message selector. The corollary to this is that the message sender must add a JMS property to the message that can be used in the message selector.
- When using a request-reply JMS adapter then there is no need for a correlation set, correlation is done in the adapter by matching the outbound JMS message ID to the inbound JMS correlation ID. The corollary to this is that the message sender must copy the JMS request message ID to the JMS response correlation ID.
- When using a synchronous request-reply JMS adapter then there is no need for the message selector because the message selection is done based on the JMS correlation ID.
- Synchronous request-reply adapter requires a non-XA connection factory to be used so that the request part of the interaction can be committed separately to the receive part of the interaction.
- Synchronous request-reply JMS adapter should only be used when the reply is expected to take just a few seconds. If the reply is expected to take longer then the asynchronous request-reply JMS adapter should be used.
Deploying the Sample
The sample is available to download here and makes use of the following JMS resources:
|jms/TestQueue||Queue||Outbound queue from the BPEL process|
|jms/TestQueue2||Queue||Inbound queue to the BPEL process|
|eis/wls/TestQueue||JMS Adapter Connector Factory||This can point to an XA or non-XA JMS Connection Factory such as weblogic.jms.XAConnectionFactory|
|eis/wls/TestQueue||None-XA JMS Adapter Connector Factory||This must point to a non-XA JMS Connection Factory such as weblogic.jms.ConnectionFactory and must have isTransacted set to “false”|
To run the sample then just use the test facility in the EM console or the soa-infra application.