I've a problem: I need to send messages (only send) to IBM MQ 7.5. In my case I have a lot of dynamic queues on several managers, so it isn't possible to send messages via jndi (like described in wso2 doc).
IBM MQ has a jms extension where I can send messages directly to queue manager and queue by specifying in q name (for example "queue://QM1/qName1"). But to do this I need to specify Connection Factory like these
<bean id="mqConnectionFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="port" value="${mq.port}"/>
<property name="transportType" value="${mq.transportType}"/>
<property name="hostName" value="#{props['host']}"/>
<property name="queueManager" value="#{props['mq.manager']}"/>
<property name="channel" value="#{props['mq.chanel']}"/>
</bean>
But I doesn't find how I can specify factory in wso2esb. Can you help me?
The obvious question here is "If you are using dynamic queues, how do you know the name of the queue that you want to send to?"
The obvious answer to that question is "It's in the Reply-To fields."
Assuming that the message just received is in rqstMsg and that you want to send replyMsg, it might look something like this:
MessageProducer replyProducer = session.createProducer((Queue rqstMsg.getJMSReplyTo());
// create replyMsge here
replyProducer.send(replyMsg);
You specified "only send" so there's a possibility you are not replying to request messages. (Although if that is the case, how you are determining the dynamic queue names outside the context of a request message is an interesting question.) If in fact you do not have a message with ReplyTo fields set, you can just specify the fully-qualified name of the destination:
MQQueue destQ = new MQQueue(rQMName, rName);
Destination dest = (Destination) destQ;
You would set the remote QMgr name in rQMName and the remote queue name in rName, then use the destination as normal.
Related
I need to connect from a web application running on a WebSphere AS 6.1 application server to a remote WebSphere MQ on z/OS queue. On WebSphere AS, I configured both QueueConnectionFactory and Queue (an object containing a part of the remote queue data), with most of the settings set to their default values - I just needed to set queue name, channel, host, port, and transport type which is CLIENT. I inject them in the following Spring 3.2 configuration using JNDI lookup:
<jee:jndi-lookup id="destination" jndi-name="MyMQQueue" expected-type="javax.jms.Queue" />
<jee:jndi-lookup id="targetConnectionFactory" jndi-name="MyMQQCF" expected-type="javax.jms.QueueConnectionFactory" />
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate"
p:connectionFactory-ref="targetConnectionFactory"
p:defaultDestination-ref="destination" />
<bean id="simpleMessageListener" class="my.own.SimpleMessageListener"/>
<bean id="msgListenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="targetConnectionFactory" />
<property name="destination" ref="destination" />
<property name="messageListener" ref="simpleMessageListener" />
<property name="taskExecutor" ref="managedThreadsTaskExecutor" />
<property name="receiveTimeout" value="5000" />
<property name="recoveryInterval" value="5000" />
</bean>
<bean id="managedThreadsTaskExecutor" class="org.springframework.scheduling.commonj.WorkManagerTaskExecutor">
<property name="workManagerName" value="wm/default" />
</bean>
JmsTemplate sends and receives (synchronously) messages correctly. DefaultMessageListenerContainer, an asynchronous message receiver, reads some (previously sent) messages off the MQ queue during WebSphere AS start, but chokes soon afterwards, and begins to throw repeatedly "connection closed" exception. On each such occasion it notifies me that
DefaultMessag W org.springframework.jms.listener.DefaultMessageListenerContainer handleListenerSetupFailure Setup of JMS message listener invoker failed for destination 'queue://myqueue' - trying to recover. Cause: Connection closed
DefaultMessag I org.springframework.jms.listener.DefaultMessageListenerContainer refreshConnectionUntilSuccessful Successfully refreshed JMS Connection
but stops taking messages off the queue.
Digging a bit into Spring code, I found that setting on DefaultMessageListenerContainer
<property name="cacheLevel" value="0"/>
solves the problem, in the sense that the messages are now being read off the queue every time I send them. However, looking at the TCP traffic to WebSphere MQ I find that MQCLOSE/MQOPEN commands are sent to it repeatedly as in:
Wireshark captured traffic
which probably means that the connection gets continuously closed and reopened.
Can anybody suggest what might be the cause for caching not working properly, and whether there is perhaps a relatively simple way to modify Spring code (extending DefaultMessageListenerContainer, for example), or perhaps set some property on MQ queue connection factory/queue, to get it working?
EDIT:
Searching further the internet, I have found the following link
http://forum.spring.io/forum/spring-projects/integration/jms/89532-defaultmessagelistenercontainer-cachingconnectionfactory-tomcat-and-websphere-mq
which seems to describe a similar problem occurring on Tomcat. The solution there is to set a certain exceptionListener on DefaultMessageListenerContainer. However, trying to do this on WebSphere throws the exception "javax.jms.IllegalStateException: Method setExceptionListener not permitted". The underlying cause seems to be that J2EE 1.4 spec forbids calling setExceptionListener on JMS connections.
https://www.ibm.com/developerworks/library/j-getmess/j-getmess-pdf.pdf
It seems that setting
<property name="cacheLevel" value="0"/>
on DefaultMessageListenerContainer is actually the correct solution.
I mislead myself by interpreting MQCLOSE/MQOPEN I saw on Wireshark captured TCP traffic in this case, as the heavyweight connection opening.
First, the newly created Connection Factory on the administrative console WebSphere AS 6.1 has by default a JMS connection pool (max size 10). By debugging the base class of DefaultMessageListenerContainer, AbstractPollingMessageListenerContainer, (specifically the method
protected boolean doReceiveAndExecute(
Object invoker, Session session, MessageConsumer consumer, TransactionStatus status)
)
one sees that neither the call to create a connection, neither the call to create a session from connection generate TCP traffic, and TCP traffic is generated only by creating a consumer (considered to be a "lightweight operation" if I understand correctly), trying to receive a message from the queue, and closing the consumer.
So it seems that the connection is taken from the respective pool, and also the session is somehow "cached".
So instead of caching by Spring, the caching appears to be done here by the application server.
I have a spring message listener configured with
<bean id="processListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="concurrentConsumers" value="1"/>
<property name="clientId" value="process-execute"/>
<property name="connectionFactory" ref="topicConnectionFactory"/>
<property name="destination" ref="processExecuteQueue"/>
<property name="messageListener" ref="processExecuteListener"/>
</bean>
This is running on a cluster with 2 nodes. I see that it's creating 1 consumer per node rather than 1 per cluster. They're both configured with the above xml so they have the same clientId. Yet, when 2 notifications are posted to the queue, both of the listeners are running, each gets a notification, and both execute in parallel. This is a problem because the notifications need to be handled sequentially.
I can't seem to find out how to make it have only one message listener per cluster rather than per node.
I solved the problem by having the jms queue block the next consumer until the previous returned. This is a feature in the weblogic server I'm using called Unit of Order. The documentation says you just need to enable it on the queue (I used hash). However, I found that I needed to enable it on the connection factory as well and set a default name. Now I see an MDP per node but 1 waits for 2 to complete before processing and vice versa. Not the solution I intended but it's working nontheless. While oracle specific, it's actually slightly better than a single MDP solution.
Note: I did not set the unit of order name in the spring jmstemplate producer as I do not know if that's possible. I have weblogic setting a default name when none is provided by the producer.
I have a JMS queue implementation with JmsTemplate. I want to have more than one listener when a message is put in the queue, i.e. I want to use topic instead of queue.
I have configuration without JMS namespacing. What are the changes that need to be made to have multiple listeners listen on a topic when someone sends a message in a topic.
I guess you are probably using DefaultMessageListenerContainer. Just to be sure, you want that several individual components receive the same message (i.e. you don't want to process messages in parallel).
Assuming I got this right and component A and compoent B should receive the same message, you simply create two DefaultMessageListenerContainer instance on the same topic and you set the pubSubDomain property to true. Make sure you haven't set any concurrency on the listener container, or better yet, set the concurrency to 1 to make that explicit.
This would give something like
<bean id="listener1"
class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="pubSubDomain" value="true"/>
<property name="concurrency" value="1"/>
<property name="destinationName=" value="...."/> <!-- topic name -->
<property name="messageListener" ref="...."/>
</bean>
Then you should create a similar bean for the second component.
I don't understand something.
I'm using Spring Integration to send and receive messages from RabbitMQ.
My topology is pretty simple:
One JVM produce messages using the RabbitTemplate of Spring
<rabbit:template id="rabbitTemplate" connection-factory="rabbitConnectionFactory" />
<bean id="amqpTemplate" parent="rabbitTemplate">
<property name="queue" value="${queue.name}" />
<property name="routingKey" value="${queue.name}" />
</bean>
RabbitMQ queue receive the message
<rabbit:queue name="${queue.name}" durable="true" />
Another JVM consume the message (to launch a Spring-Batch job, but that's not the point):
<int-amqp:inbound-channel-adapter
queue-names="${queue.name}"
channel="amqp-requests"
connection-factory="rabbitConnectionFactory" />
The send method used is:
/**
* Convert a Java object to an Amqp {#link Message} and send it to a default exchange with a default routing key.
*
* #param message a message to send
* #throws AmqpException if there is a problem
*/
void convertAndSend(Object message) throws AmqpException;
It works fine but according to the documentation, I don't think the routingKey is mandatory in my usecase. I don't know why someone put a routingKey.
So I tried to remove the routingKey:
<bean id="amqpTemplate" parent="rabbitTemplate">
<property name="queue" value="${queue.name}" />
</bean>
Then I can still send the messages to the queue, but they are never consumed anymore!
Can someone explain me what is going on?
Can't I send messages from one JVM to another without a routingKey?
...but according to the documentation, I don't think the routingKey is mandatory...
Which "documentation" are you referring to?
With AMQP, producers do not know about queues; they send messages to various types of exchanges which have bindings for routing to queues.
Maybe you are mis-understanding the notion of the default exchange, to which every queue is bound, with a routing key equal to its queue name.
This allows simple routing to specific queues (by way of their names). The default exchange is a convenience to provide a quick on-ramp to amqp messaging. This works fine, but you might want to consider using explicitly declared exhanges instead, because it further decouples the producer from the consumer. With the default exchange the producer has to know the name of the queue that the consumer is listening on.
Further, on the RabbitTemplate, the queue property is only used for receiving (consuming) messages, it has no bearing on sending messages; as I said producers don't know about queues.
You should use the following...
<bean id="amqpTemplate" parent="rabbitTemplate">
<property name="routing-key" value="${queue.name}" />
</bean>
In my program, I have two modules :- Publisher and Subscriber which communicate via Topic.
I understand that in order to receive messages by subscriber, it should be started before publisher. But there may be a scenario where the subscriber goes down for some reason and needs to be restarted. Is there any way, by which if I start the Subscriber after Publisher, then also it should be able to receive message?
Adding a code example by using spring DMLC and durable subscribers. It's harder to achieve this with a plain JMSTemplate (you tagged this, so I guess you are using JMS Templates to receive?), since you have to grab the session from the template and create the durable consumer yourself. This is automatically handled for you if you use the DMLC approach.
<bean id="myDurableConsumer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="myCf" />
<property name="sessionTransacted" value="true" />
<property name="subscriptionDurable" value="true"/>
<property name="durableSubscriberName" value="myDurableNameThatIsUniqueForThisInstance" />
<property name="destinationName" value="someTopic" />
<property name="messageListener" ref="myListener" />
< /bean>
If you are only interested in the disconnect-reconnect scenario, I think a durable subscriber is what you are looking for.
http://activemq.apache.org/how-do-durable-queues-and-topics-work.html
In general if you want to account for a subscriber going offline and returning without missing any messages you would use JMS Durable Subscriptions. This allows your subscriber to receive any messages it missed while offline. Note that the caveat here is that is needs to have subscribed once first before it will start to collect offline messages.
Besides the standard JMS Durable consumer model ActiveMQ also provides the retroactive consumer. Another possibility is Virtual destinations.