i'm trying to get the client-id (or connection-id) of a message consumer through a message in JMS.
Is there a way to get it, when i'm only have access to a received message (like in the onMessage method of the MessageListener interface)?
Short: i have a jms message and i want to know the client-id of the consumer who received the message
A Connection object is a client's active connection to its JMS provider. It typically
allocates provider resources outside the Java virtual machine (JVM).
Method of Connection Interface provides following method.
String getClientID() throws JMSException
This value is specific to the JMS provider. It is either preconfigured by an administrator in a ConnectionFactory object or assigned dynamically by the application by calling the setClientID method.
Related
We have an EIP flow which uses annotation-based spring:4.2.x APIs, spring-integration:4.2.x APIs and spring-integration-java-dsl:1.1.0 APIs to receive messages from a Websphere MQ Queue, do some processing and finally return responses to another Websphere MQ Queue. For this flow, we are using a JMS Inbound Gateway to synchronously receive messages from one queue, process them and send the responses back to another queue.
The JMS Inbound Gateway is configured with an errorChannel so that RuntimeExceptions are routed to it (this works fine). However during tests, when we purposely apply a PUT_INHIBIT on the flow's response Websphere MQ queue (i.e. to cause the flow to be unable to send responses back to the reply queue), the spring logs show the following WARNING log message:
WARN ... - Execution of JMS message listener failed, and no ErrorHandler has been set.
javax.jms.JMSException: MQJMS2007: failed to send message to MQ queue.
We know we can remove that WARNING log by configuring an ErrorHandler on the MLC itself but, the reason this is causing us problems is that when we route a response back, we actually route using a .routeToRecipients() call with .setIgnoreFailures(false) and two recipients - the first recipient routing to the JMS Inbound Gateway's replyChannel and the second routing to a post-send flow so that we can do DB updates, etc. The idea here being that if the first recipient send fails (i.e. when the response queue is not available), the post-send flow doesn't execute but instead an error handling flow executes instead (e.g. errorChannel flow). But in the described error scenario, we see the warning log, and the flow's post-send flow still executes instead of the errorChannel's flow...
It is as though, at this point, the JMS Inbound Gateway's errorChannel no longer applies. Is this correct? And is this intended behaviour? And if it is, does this mean that we should use Inbound/Outbound Adapters instead of an Inbound Gateway for our response post-send intent?
JMS MLC Configuration:
#Bean( destroyMethod = "shutdown")
public DefaultMessageListenerContainer serviceMLC() throws Exception {
DefaultMessageListenerContainer mlc = new DefaultMessageListenerContainer();
mlc.setAutoStartup(false);
mlc.setConnectionFactory(serviceCCF);
mlc.setDestination(requestMqQueue);
mlc.setAcceptMessagesWhileStopping(false);
return mlc;
}
JMS Inbound Gateway Configuration:
#Bean
public IntegrationFlow serviceFlow() {
return IntegrationFlows
.from(Jms
.inboundGateway(serviceMLC)
.autoStartup(true)
.defaultReplyDestination(responseMqQueue)
.replyChannel(responseOutCh)
.replyTimeout(180000)
.correlationKey("JMSCorrelationID")
.errorChannel(serviceErrorCh)
)
.channel(serviceInCh)
.get();
}
Yes; the gateway doesn't work that way.
When you send the reply to the gateway, it is queued in the gateway until the thread returns to the gateway; at which time, the reply is picked up and sent. So, the failure to send does not occur until later (after your second recipient flow is invoked).
Yes, to do what you want, you should use channel adapters instead because the failure will run directly on the calling thread.
I have a rabbitmq listener as a separate class and JSF 2 managed bean.
In my bean I send a message and need to wait for result. I can't use sendAndReceive... because I send the message to one queue but receive from another queue, so I assign correlationId before sending.
So I need to wait asynchronously, I need to wait until right message comes to the listener. How to do it in rmq?
Looking at javadoc and source of RabbitTemplate it seems that he waits for response in reply queue. Do you set 'reply-to' property in your messages? If yes, then RabbitTemplate sendAndReceive methods should wait for response in 'reply-to' queue. Be sure to populate replyTo field correctly and test it.
Side note:
In RabbitMQ you do not send messages to the queue.
You send messages to the exchanges. Exchanges are routing messages to the queue(s) using bindings. With default or direct exchange type it looks like you send directly to the queue, but this is over-simplification.
See https://www.rabbitmq.com/tutorials/amqp-concepts.html for details.
Edit:
It seems there are some fix for that in AMQP 1.4.5.RELEASE
https://spring.io/blog/2015/05/08/spring-amqp-1-4-5-release-and-1-5-0-m1-available
Configurable Exchange/Routing Key for Replies
Previously, when using request/reply messaging with the
RabbitTemplate, replies were routed to the default exchange and routed
with the queue name. It is now possible to supply a reply-address with
the form exchange/routingKey to route using a specific exchange and
routing key.
I would like to use Spring Messaging to create a real time notification system for logged users for my webapp.
I defined a AbstractWebSocketMessageBrokerConfigurer as follows:
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/notifications").withSockJS()
.setSessionCookieNeeded(true)
.setWebSocketEnabled(true);
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic/", "/queue/");
}
And, according to the documentation:
An application can send messages targeting a specific user. Spring’s STOMP support recognizes destinations prefixed with "/user/". For example, a client might subscribe to the destination "/user/queue/position-updates". This destination will be handled by the UserDestinationMessageHandler and transformed into a destination unique to the user session, e.g. "/queue/position-updates-user123". This provides the convenience of subscribing to a generically named destination while at the same time ensuring no collisions with other users subscribing to the same destination so that each user can receive unique stock position updates.
On the sending side messages can be sent to a destination such as "/user/{username}/queue/position-updates", which in turn will be translated by the UserDestinationMessageHandler into one or more destinations, one for each session associated with the user. This allows any component within the application to send messages targeting a specific user without necessarily knowing anything more than their name and the generic destination. This is also supported through an annotation as well as a messaging template.
By sending a message to /user/{username}/queue/something, it will be delivered only to the specific user identified by {username}.
Now, I'm looking for a solution that allows me to use an external Message Broker (for instance, RabbitMQ), with Spring just as Broker Relay:
registry.enableStompBrokerRelay("/topic/", "/queue/");
After configuring the External Message Broker in Spring:
Is it possible to send a message on Message Broker by using as channel /user/{username/}/queue/something? If yes, how?
By sending a message on Message Broker by using as channel /user/{username/}/queue/something, is Spring able to send that message only to {username} according to the current Principal?
Yes it is possible, if you enable an external broker, every #MessageMapping return value will be serialized to JSON and sent to the broker, have a look to the Flow of Messages section of the reference documentation for more details. So it is basically the same that with the simple broker.
You can also inject a SimpMessagingTemplate or SimpMessageSendingOperations bean, like it is done in my OpenSnap example application. You can use this from a Controller, but also from any other class in a pure push context.
You can retrieve the principal by adding a Principal parameter to your #MessageMapping or #SubscribeMapping handler method, like it is done here, the current principal will be automatically injected.
My application is running under Tomcat with a number of Spring's DefaultMessageListenerContainer listening to a number of different JMS queues running under Oracle 11g Weblogic server.
DefaultMessageListenerContainer configuration is.. well.. default with sessionTransacted = false and sessionAcknowledgeMode = AUTO_ACKNOWLEDGE. The type of messages my application receives is javax.jms.TextMessage. The actual body of the message (message.getText()) is an XML string.
I was faced with a problem when a number of application instances (dev boxes, test boxes, etc) needed to be pointed to the same JMS server, so once message comes to the queue it is unknown which server will consume it (I believe the one that runs receive() method first). The problem is that any given application instance is only interested in messages dedicated to that particular application instance, so with a current configuration most of the messages get lost (consumed by other application instances and ignored in message processing business logic).
I have no control on JMS server implementation, but I can force it to set a particular XML element in the message body to an application instance specific value, so I can read it and decide what application instance should consume it.
The most natural way to do that would be setting messageSelector property on DefaultMessageListenerContainer so decision is made on JMS server which consumer should receive what message. I also learned about Weblogic specific JMS_BEA_SELECT message selector expression that works with XML message types. Unfortunately it doesn't seem to work with javax.jms.TextMessage messages with XML payload (or at least I couldn't make it to work). I was trying the following expression with no luck:
<property name="messageSelector" value="JMS_BEA_SELECT('xpath', '//Event/CorrelationID/text()') = 'MY_SELECTOR_TEST_3'"/>
According to this article the other options are:
Use a transacted session, then rollback the session so the message will go back to the queue and can be consumed by other application instance.
Use Session.CLIENT_ACKNOWLEDGE when creating a session, then recover the session so the message will go back to the queue and can be consumed by other application instance.
I understand that I need to set sessionTransacted and sessionAcknowledgeMode on DefaultMessageListenerContainer to non-default values (what values?) and then rollback the session in the message processor code (option 1) or don't call message.acknowledge() (option 2).
It looks like DefaultMessageListenerContainer controls message processing / session life cycle. How can I customise it?
Solution with rollbacks looks really weird.
Setting message selector should be enough. I didn't work with BEA JMS implementation but i guess you can take care with regular "SELECT" and selecting from header .
<property name="messageSelector" value="CorrelationID='MY_SELECTOR_TEST_3'/>
Do you work on both side of communication points (server,client) to control correlation id?
We've a Spring JMS message listener container for receiving messages asynchronously. Using DefaultMessageListenerContainer and in sessionTransacted mode. I understand being in sessionTransacted mode means in case of an exception the message will be put back into the queue. But how can I make sure the message won't be deleted from the queue even if the receiver (which is picked the message) crashes or just the machine running it looses power?
At first I thought CLIENT_ACKNOWLEDGE acknowledge mode should save me, but apparently it's not the case, Spring calls .acknowledge() no matter what.
So here's my question, how can I guarantee the delivery? Using a custom MessageListenerContainer? Using a transaction manager?
Use a transacted session and indicate successful message processing by invoking the Session class's commit() method.
Check the section 19.4.5. Processing messages within transactions for the configuration. (you can use a DefaultMessageListenerContainer). Depending on what you're doing with the messages, you may need a JTA transaction manager.
Spring message listener with Client_Acknowledge mode will acknowledge the message when the client calls message.acknowledge().
However, if after successful execution of the message, the consumer does not find any acknowledgement from the client side, spring assumes the execution was successful and acknowledges the message.
If at any point of time, consumer got an exception while processing the message, spring listener needs to know some exception has occured in order to redeliver the message to the queue for another consumer thread to pick it up. If you're catching the exception, spring assumes everything was handled and execution was smooth and hence acknowledges the message.
Spring message listener only allows to throw JMS exception from onMessage listener. Catching your custom exception and throwing a JMS exception from listener (after logging the error for future reference) will allow you to redeliver the message.
or you can use Session.AUTO_ACKNOWLEDGE with nontransacted session, see quote below from this article
A message is automatically
acknowledged when it successfully
returns from the receive() method. If
the receiver uses the MessageListener
interface, the message is
automatically acknowledged when it
successfully returns from the
onMessage() method. If a failure
occurs while executing the receive()
method or the onMessage() method, the
message is automatically redelivered.
The JMS provider carefully manages
message redelivery and guarantees
once-only delivery semantics.