How can I get messageId after sending to RabbitMq? - spring-boot

I want some messageId kind of thing when I am sending to Rabbit Mq Queue as I will get when sending to IBM MQ using jms. I am using spring MQ amqp starter dependency with Spring Boot. Configuration is done only in application.yml (property file). I am using Rabbit template for sending.
rabbitMqTemplate.convertAndSend(EMPTY_STRING,queueName, message, messagePostProcessor);
I have tried messagePostProcessor. Any help is appreciated. I had a look into below content. But didnt understand how to implement. Does it require special configuration (connectionfactory/ container)?
https://www.rabbitmq.com/confirms.html

Unlike JMS, the rabbit client doesn't assign message ids.
However, you can configure the RabbitTemplate's MessageConverter to create an id, which you can then retrieve with a post processor.
See AbstractMessageConverter...
/**
* Flag to indicate that new messages should have unique identifiers added to their properties before sending.
* Default false.
* #param createMessageIds the flag value to set
*/
public void setCreateMessageIds(boolean createMessageIds) {
this.createMessageIds = createMessageIds;
}
For message confirmations, see the reference manual. But that is unrelated to the message id property.

Related

How to set ContentType in qpid JMS

Hello this question is related to this and I'd like to know if there is any way to set ContentType header in qpid in JMS context. We are using JmsConnectionFactory and spring's JMS library.
I was trying to find some way using extension but can't find the way or if that's even possible.
final org.apache.qpid.jms.JmsConnectionFactory jmsConnectionFactory = new org.apache.qpid.jms.JmsConnectionFactory();
jmsConnectionFactory.setExtension("headers", (connection, uri) -> {
if (connection instanceof JmsConnection) {
}
});
There is no exposed means of setting the content-type on the messages sent from the Qpid JMS client. The client itself uses this field as part of the JMS mapping to AMQP to distinguish certain message types that it sends and to determine at receive time what certain messages should be presented as.
It is technically possible to use reflection to reach in and so the value but the APIs you have to use from the JmsMessageFacade class are not public and could change with any release so choosing to do so comes with significant risk.

Set brokerDurSubQueue property in Spring Boot+JMS+IBM MQ Durable Topic listener

I am trying to listen message through Spring boot application using IBM MQ topic subscription.
Available info (Provided by MQ Admin):
Topic name
Host
Port
QueueManager
BrokerDurableSubscriptionQueue
I am trying to set BrokerDurableSubscriptionQueue property in MQConnectionFactory.
I can find mqConnectionFactory.setBrokerSubQueue(queueName) which I guess can be used for Non-Durable Subscription.
But I cannot find similar property for Durable subscription.
However I can see MQTopic class has setBrokerDurSubQueue property but I am not sure how can I make use of MQTopic object in my case.
I am using below code:
MQConnectionFactory:
#Bean
public MQTopicConnectionFactory topicConnectionFactory(){
MQTopicConnectionFactory mqTopicConnectionFactory= new MQConnectionFactory();
mqTopicConnectionFactory.setHostName(); //mq host name
mqTopicConnectionFactory.setPort(); // mq port
mqTopicConnectionFactory.setQueueManager(); //mq queue manager
mqTopicConnectionFactory.setChannel(); //mq channel name
mqTopicConnectionFactory.setTransportType(1);
mqTopicConnectionFactory.setSSLCipherSuite(); //tls cipher suite name
return mqTopicConnectionFactory;
}
#Bean
public JmsListenerContainerFactory<?> topicListenerFactory(MQTopicConnectionFactory mqtopicConnectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer)
{
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
configurer.configure(factory, mqtopicConnectionFactory);
factory.setPubSubDomain(true);
factory.setSubscriptionDurable(true);
return factory;
}
Listener:
#JmsListener(
destination = "someTopic",
subscription = "someTopic",
containerFactory = "topicListenerFactory"
)
public void receiveMessage(String msg) {
repository.save(msg);
}
Background:
When you provide a specific queue for IBM MQ to use when you subscribe to a topic it is called a unmanaged subscription because MQ is not managing the underlying queue since you have provided it.
When a queue is not provided it is called a managed subscription, in this case MQ creates a queue for you to hold the published messages.
If it is a non-durable subscription the queue created is a temporary dynamic queue with a name like:
SYSTEM.MANAGED.NDURABLE.<8 hex characters>
If it is a durable subscription the queue created is a permanent dynamic queue with a name like:
SYSTEM.MANAGED.DURABLE.<8 hex characters>
What you have uncovered is that the IBM MQ classes for JMS API only supports managed subscriptions.
Suggestions:
I can suggest two options if you want to use IBM MQ classes for JMS API to receive messages published to a topic on a specific queue:
Have a MQ admin setup an administrative subscription on the queue manager. You can do this a few different ways. The example below would be using a MQSC command.
DEFINE SUB('XYZ') TOPICSTR('SOME/TOPIC') DEST(SOME.QUEUE)
Create a utility app using IBM MQ classes for Java that can open a queue and create a durable subscription with a provided queue, the only purpose of this app would be to subscribe and unsubscribe a provided queue, it would not be used to consume any of the published messages.
For both options above you would have the IBM MQ classes for JMS API application open the queue to consume the published message, for all purposes it would not know or need to know the messages were published to a topic. The message will still contain JMS headers showing the topic string where the message was published, so you can inquire this if required. You could also subscribe multiple topics to a single queue if you like.

How to send messages with selectors in websocket topic in Spring Boot

CLARIFICATION:
Thanks to #JustinBertram comment I realized that this question does not make sense.
STOMP protocol does not support selectors by itself, you have to use brokers such as ActiveMQ that implement it. STOMP supports headers that can be used by brokers for filtering messages by selectors.
In my case, I'm not using any broker, just frontend with Angular + Stomp + SocksJS and backend with Spring Boot, so I can't use selectors.
The documentation of STOMP protocol does not make this clear to me and I got confused. See these references:
the specification:
Stomp brokers may support the selector header which allows you to
specify an SQL 92 selector on the message headers which acts as a
filter for content based routing.
this article:
The subscribe() method takes an optional headers argument to specify
additional headers when subscribing to a destination:
var headers = {ack: 'client', 'selector': "location = 'Europe'"};
client.subscribe("/queue/test", message_callback, headers);
The client specifies that it will handle the message acknowledgement
and is interested to receive only messages matching the selector
location = 'Europe'.
I'm implementing a backend in Spring Boot. For two-way communications with the frontend I'm using stomp over websockets.
I have followed this Spring Boot + Angular example
It works, but one of my requirements is that the backend has to send messages with selectors so that the frontend subscribes to a topic and only receives the filtered data, to avoid performance issues with real time data.
i.e. { 'selector': "location = 'Europe'" }
For that purpose, I'm trying to make the backend send the messages with selectors, but I can't make it work.
I have followed this article to implement the frontend with selectors and it works correctly, the problem is only the backend.
I tried with #SendTo annotation but it seems it doesn't have any params for that as per the article:
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public Greeting greeting(HelloMessage message) throws Exception {
Thread.sleep(1000);
return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");
}
Also I tried with the MessagingTemplate, but I don't know how to set the selector properties in the header:
http://assets.spring.io/wp/WebSocketBlogPost.html
MessageSendingOperations<String> messagingTemplate;
messagingTemplate.convertAndSend(destination, quote);
I really appreciate any help, I have read many articles and docs but I don't find anything talking in particular about this with a solution.
Well, it's possible to use the JMS' selectors with Spring (Boot) Websocket and a STOMP client. I found a native way.
The key thing is that the selector is applied to the org.springframework.messaging.Message instance, and it uses Spring's Spel language to apply the condition (it's not the JMS SQL-like).
So using the default SimpMessagingTemplate, in the backend you can send header variables like this:
this.messagingTemplate.convertAndSend(
"/topic/something", //your destination
payload, //any kind of payload (body)
Map.of("id", 1) //header with key/value
);
In the frontend, to enter a selector that will be evaluated by the org.springframework.messaging.simp.broker.DefaultSubscriptionRegistry.filterSubscriptions, you must declare your Stomp/WebSockets headers as:
{"selector": "headers['nativeHeaders']['id'][0] == '999'"}
Yeah, it's horrible but it works.
As the default Message is GenericMessage, the headers are processed in a new key called "nativeHeaders".
The ['key'],[0] and == are Spring's Spel sintaxes.
Go ahead and filter your messages on the backend, not in the frontend, please!
The latest version of the STOMP specification doesn't include any specific statement about selectors and their syntax because it's really up to the broker implementation as to what is supported here. The specification now just states:
STOMP servers MAY support additional server specific headers to customize the delivery semantics of the subscription. Consult your server's documentation for details.
Brokers like ActiveMQ 5.x and ActiveMQ Artemis support the selector STOMP header and the syntax & behavior of the selector is based on JMS selectors.
Selectors in JMS are for selecting messages on consumption and are configured by the consuming client. You can't set the selector when sending the message.
JMS selectors select messages based on the headers or properties of the message, although some implementations go beyond this and allow selecting based on the content of the message itself. Therefore, if you want to have a selector location = 'Europe' on a consumer then you should set a header on the message when it is sent with the name location and the value of Europe.
The convertAndSend method is overloaded and provides a couple of ways to set a header:
Pass a map of key/value pairs to the convertAndSend method.
Implement a MessagePostProcessor and pass that to the convertAndSend method. Inside your post-processor you can invoke the javax.jms.Message#setStringProperty() method.

Can I get JMSXDeliveryCount and JMSDestination using org.springframework.messaging.Message

I am using Spring JMS 4.1.2, and I am using org.springframework.messaging.Message as the input payload in the JMS Listener. I am listening to an Active MQ Queue, and I would like to know if it is possible to obtain the JMSXDeliveryCount and JMSDestination using the org.springframework.messaging.Message, or if I have to use the javax.jms.Message class to accomplish this.
Thanks,
Juan
The delivery count property is mapped by default; I just ran a test...
message: GenericMessage [payload=jms test, headers={jms_redelivered=true, JMSXDeliveryCount=2, ...
Standard headers are mapped with a jms_ prefix all; message properties are mapped as-is.
The destination from which the message is received is not currently mapped to the message.
You would need to add a custom JmsHeaderMapper - perhaps a subclass of DefaultJmsHeaderMapper.
I opened a JIRA issue to make this standard.

Unit-Of-Order in WLI JMSControl

One of our clientes is intented to use the Unit-Of-Order Weblogic Server Feature (UOO).
Everything is OK using UOO in pure java code for sending JMS Messages with custom UOO Names, as well as propagating the UOO Name in Aqualogic Service Bus from the Proxy Service to a Business Service (both using JMS as the transfer protocol).
However, using UOO in Weblogic Integration along with WLI JMSControl, does no work properly.
Consider this code:
#com.bea.control.JMSControl.Properties(value = {
#com.bea.control.JMSControl.PropertyValue(name = "JMS_BEA_UnitOfOrder", value = "MyUOONameFromWLI"),
#com.bea.control.JMSControl.PropertyValue(name = "MyCustomProperty", value = "MyCustomValue") })
public void sendTextMessage(String payload);
It sends the property MyCustomProperty to the JMS consumer, but the property JMS_BEA_UnitOfOrder - related to UOO Name - is ignored. The default User-generated UOO name is used instead.
So, how to customize my UOO Name using Weblogic JMSControl?
I've found that it has no support!

Resources