Spring JMS with Oracle AQ
This probably isn’t news to many but I only recently noticed that Oracle AQ can manage JMS queues. We don’t use a full blown J2EE application server so our options when using a JMS broker is to either have a standalone server/cluster (more work for SAs) or to embed the JMS server in to our middleware (more work for me). By using the database we add no new dependencies or software so this is a pretty neat solution. Another big advantage for us is that it looks like we would be able to create messages inside PL/SQL procedures but I may be getting ahead of myself here.
There isn’t much to it but it was a lot harder to get to the same point than with the other JMS brokers I have been testing. There is a lot of documentation to go through and it is all fairly general. I also couldn’t find a good example so I’ve created a quick howto…
First thing is to choose a user that is going to use the queue and set up the grants…
grant execute on sys.dbms_aqadm to mquser;
grant execute on sys.dbms_aq to mquser;
grant execute on sys.dbms_aqin to mquser;
grant execute on sys.dbms_aqjms to mquser;
exec dbms_aqadm.grant_system_privilege('ENQUEUE_ANY','mquser');
exec dbms_aqadm.grant_system_privilege('DEQUEUE_ANY','mquser');
And then as that user create the queue
exec dbms_aqadm.create_queue_table(queue_table=>'JMS_QUEUE_TABLE', queue_payload_type=>'sys.aq$_jms_text_message',multiple_consumers=>false); exec dbms_aqadm.create_queue(queue_name=>'testmq', queue_table=>'JMS_QUEUE_TABLE'); exec dbms_aqadm.start_queue(queue_name=>'testmq');
This example sets up a point to point queue. I haven’t tried using a pub/sub model but at the very least
multiple_consumers=>true
would need to be set.
For the Java/Spring side you’ll need to download the Oracle Containers for Java (OC4J) pack and add aqapi.jar to your classpath.
For sending JMS messages from Spring you need to set up at least three beans. The interesting one is an instance of JmsTemplate which saves us from writing any boiler-plate JMS code. This requires a JMS ConnectionFactory and a default destination which are the two other beans. These are not so trivial to set up with Oracle AQ and at the moment I am creating these with extra factory-beans.
public class OracleAqConnectionFactoryInitialiser {
public ConnectionFactory createConnectionFactory() throws Exception{
return oracle.jms.AQjmsFactory.getQueueConnectionFactory("jdbc:oracle:thin:mquser/mqpass@myhost:1521:testtom", new Properties());
}
}
(There is also a getQueueConnectionFactory(DataSource ds) method which is probably what you really want)
Finding the JMS destination has to be done via the connection so we have to create a Spring FactoryBean…
public class OracleAqDestinationFactoryBean implements FactoryBean { private QueueConnectionFactory connectionFactory; private String queueName; private String queueUser; @Required public void setConnectionFactory(QueueConnectionFactory connectionFactory) { this.connectionFactory = connectionFactory; } @Required public void setQueueName(String queueName) { this.queueName = queueName; } @Required public void setQueueUser(String queueUser) { this.queueUser = queueUser; } public Object getObject() throws Exception { // createQueueSession(true,0) - create a transactional session AQjmsSession session = (AQjmsSession) connectionFactory.createQueueConnection().createQueueSession(true, 0); return session.getQueue(queueUser, queueName); } public Class getObjectType() { return javax.jms.Queue.class; } public boolean isSingleton() { return false; } }
Wire up these factory-beans and the ConnectionFactory and Queue…
<bean id="aqConnectionFactoryInitialiser" class="uk.nominet.mq.OracleAqConnectionFactoryInitialiser">
<property name="dataSource" ref="oracleDs"/>
</bean>
<bean id="testmq" class="uk.nominet.mq.OracleAqDestinationFactoryBean">
<property name="connectionFactory" ref="aqConnectionFactory"/>
<property name="queueName" value="testmq"/>
<property name="queueUser" value="mquser"/>
</bean>
<bean id="aqConnectionFactory" factory-bean="aqConnectionFactoryInitialiser"
factory-method="createConnectionFactory" >
</bean>
…and the JmsTemplate…
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="aqConnectionFactory"/>
<property name="defaultDestination" ref="testmq"/>
<property name="receiveTimeout" value="30000"/>
</bean>
Once these are configured you can send a message by calling jmsTemplate.send() from anywhere you wish…
jmsTemplate.send(new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
TextMessage txtMsg = session.createTextMessage();
txtMsg.setText("Hello World!");
return txtMsg;
}
});
Configuring a listener is now simple
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="aqConnectionFactory"/>
<property name="destination" ref="testmq"/>
<property name="messageListener" ref="messageListener1" />
<property name="sessionTransacted" value="true"/>
</bean><bean id="messageListener1"
class="org.springframework.jms.listener.adapter.MessageListenerAdapter">
<constructor-arg>
<bean class="uk.nominet.contact.mq.TestMqMessageListener"/>
</constructor-arg>
</bean>
Read the docs for details on using the MessageListenerAdapter.
The documentation for managing queues and using JMS can be found here and here

(3 votes, average: 4.67 out of 5)
October 10th, 2007 at 7:59 am
This article has proved indeed very useful. Thanks for that.
However i am facing some issues when the queue payload type is not standard AQ type. It is a clob in my case. Whenever the listener tries to initialise i get the JMS 137 Error asking me to specify the payload factory. I have tried setting up the queue reciever with the payload factory, it still fails. Can you please offer any further guidance as to where i should set the payload factory. The payload factory has been created using jpub.
Thanks for the help
Raj
October 10th, 2007 at 1:04 pm
If you are using Spring, it looks like you would have to extend DefaultMessageListenerContainer and overide the createConsumer() method so that it calls the correct createConsumer() method - http://download-west.oracle.com/docs/cd/B14117_01/server.101/b12023/oracle/jms/AQjmsSession.html#createConsumer(javax.jms.Destination,%20java.lang.String,%20java.lang.Object,%20java.lang.String,%20boolean)
I’ve stuck to using the standard JMS types so I’m afraid I don’t know much about the payload factories.
You might have more luck on the Oracle Advanced Queueing forum - http://forums.oracle.com/forums/forum.jspa?forumID=66
January 16th, 2008 at 6:18 am
Hi, this article very useful but I got error
oracle.jms.AQjmsException: JMS-190: Queue mquser.testmq not found
What’s wrong with my code?
January 16th, 2008 at 10:55 am
awy:
Either your queue table really doesn’t exist or the user in your JDBC connection properties doesn’t have permissions to access the queue.
You should be able to verify the queue has been set up properly by seeing if the queue table you created exists in your schema (’JMS_QUEUE_TABLE’).
If it does then try stopping and starting the queue with
exec dbms_aqadm.stop(queue_name=>’mquser.testmq’);
exec dbms_aqadm.start(queue_name=>’mquser.testmq’);
If this works then I’m not sure what might be wrong and can only suggest debugging OracleAqDestinationFactoryBean.getObject()
January 17th, 2008 at 5:48 am
It’s working now, I didn’t read your statement “And then as that user create the queue” and the queue tables created in sys schema.
Thanks Tom.
February 6th, 2008 at 8:43 pm
[…] standard JMS message types for AQ works just fine (see this great techblog’s article for description). To turn JTA transactions, add the following to your Spring configuration file: […]
February 7th, 2008 at 5:03 pm
I’ve tried to use Oracle AQ using JMS with code from this article and I can do almost everything but dequeue messages.
(When I start consumer - messges do move to fail queue with max retry attempts = 5, but my MessageListener never gets called)
Can you please post/send source code if it is possible. May be I’m missing something. I can upload my code as well.
Thank you.
April 5th, 2008 at 4:47 pm
I tried to use jms XA (distributed transaction) to connect oracle AQ, are there any reference?
April 7th, 2008 at 1:43 pm
For the moment I’ve been avoiding distributed transactions by publishing messages with stored procedures (which are then part of a normal JDBC transaction).
Whilst it’s on my list, I’m afraid I haven’t used XA so I don’t think I can be much help.