Spring integration MQTT publish & subscribe to multiple topics - spring

I am trying to build an application which subscribes to multiple mqtt topics, get the information, process it and form xmls and upon processing trigger an event so that these can be sent to some cloud server and the successful response from there to be sent back to the mqtt channel.
<int-mqtt:message-driven-channel-adapter
id="mqttAdapter" client-id="${clientId}" url="${brokerUrl}" topics="${topics}"
channel="startCase" auto-startup="true" />
<int:channel id="startCase" />
<int:service-activator id="startCaseService"
input-channel="startCase" ref="msgPollingService" method="pollMessages" />
<bean id="mqttTaskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5" />
<property name="maxPoolSize" value="10" />
</bean>
<bean id="msgPollingService" class="com.xxxx.xxx.mqttclient.mqtt.MsgPollingService">
<property name="taskExecutor" ref="mqttTaskExecutor" />
<property name="vendorId" value="${vendorId}" />
</bean>
My question is how do I publish this to multiple channels, i.e. if I have an option to publish X message to Y topic. At present I have the below:
<int:channel id="outbound" />
<int-mqtt:outbound-channel-adapter
id="mqtt-publish" client-id="kj" client-factory="clientFactory"
auto-startup="true" url="${brokerUrl}" default-qos="0"
default-retained="true" default-topic="${responseTopic}" channel="outbound" />
<bean id="eventListner" class="com.xxxx.xxxx.mqttclient.event.EventListener">
<property name="sccUrl" value="${url}" />
<property name="restTemplate" ref="restTemplate" />
<property name="channel" ref="outbound" />
</bean>
I can publish this like:
channel.send(MessageBuilder.withPayload("customResponse").build());
Can I do something like:
channel.send(Message<?>, topic)

Your configuration looks good. However the MessageChannel is an abstraction for loosely-coupling and gets deal only with Message.
So, you request a-la channel.send(Message<?>, topic) isn't correct for Messaging concepts.
However we have a trick for you. From AbstractMqttMessageHandler:
String topic = (String) message.getHeaders().get(MqttHeaders.TOPIC);
.....
this.publish(topic == null ? this.defaultTopic : topic, mqttMessage, message);
So, what you need from your code is this:
channel.send(MessageBuilder.withPayload("customResponse").setHeader(MqttHeaders.TOPIC, topic).build());
In other words you should send a Message with mqtt_topic header to achieve a dynamic publication from <int-mqtt:outbound-channel-adapter>.
From other side we don't recommend to use MessageChannels directly from the application. The <gateway> with service interface is for such a case for end-application. Where that topic can be one of service method argument marked as #Header(MqttHeaders.TOPIC)

Related

Not able to Stop MQueue listener

I have the following configuration for my MQueue:
<jms:listener-container container-type="default" connection-factory="cachedConnectionFactory" acknowledge="auto">
<jms:listener id="myListenerId" destination="myDestination" ref="myListener" method="onMessage" />
</jms:listener-container>
When I try to stop the reception of JMS messages, I write the following code
jmsManagement = myProject.instance.getContext().getBean('myListenerId',Lifecycle.class);
jmsManagement.stop();
PS :
When I stop() my listener, the isRunning() return False, but I still get messages through the MQueue... the onMessage gets triggered.
jmsManagement is an instance of the class Lifecycle. Even when I changed it to DefaultMessageListenerContainer, same thing.
I'm receiving messages before calling start(), even when autoStartup is set to false.
jmsManagement.shutdown(); didn't stop the listener from being triggered.
Does anyone have an idea about how to stop this MQ listener ?
Is there something I'm missing ?
I actually had to set autoStartup to true.
Since I can't do that using jms:listener-container, I instanciated a DefaultMessageListenerContainer bean and set the autoStartup property to false.
Here's the code that worked for me :
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer" id="pitagorCPYListener">
<property name="autoStartup" value="false" />
<property name="connectionFactory" ref="cachedConnectionFactory" />
<property name="destination" ref="defaultDestination" />
<property name="messageListener" ref="listenerPitagorCPY" />
</bean>
<bean id="defaultDestination" class="com.ibm.mq.jms.MQQueue">
<constructor-arg value="#{mqConnectionFactory.destination}"/>
</bean>

Spring amqp not publishing message to the queue but to Exchange

I am trying to test & benchmark spring-amqp for RabbitMQ with multiple queues so I was creating rabbit template for each queue and using it to send message. The message sent is successful and I can see a message published in the exchange but I don't see anything in the queue. I am guessing it's very minor setting but can't figure it out.
This is my applicationContext.xml
<bean id="banchmarkConnectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
<constructor-arg ref="benchmarkAmqpHost"/>
<property name="username" ref="benchmarkAmqpUser"/>
<property name="password" ref="benchmarkAmqpPass"/>
<property name="virtualHost" ref="benchmarkAmqpVHost"/>
<property name="channelCacheSize" value="10"/>
</bean>
<rabbit:template id="benchmarkAmqpTemplate"
connection-factory="banchmarkConnectionFactory"
exchange="my_exchange"
queue="BenchmarkQueue"
routing-key="BenchmarkQueue" />
<rabbit:admin connection-factory="banchmarkConnectionFactory"/>
<rabbit:queue name="BenchmarkQueue" auto-delete="true" durable="false" auto-declare="true"/>
This is my code which uses the benchmarkAmqpTemplate to publish to the queue.
public class publishMessage {
#Autowired
private RabbitTemplate benchmarkAmqpTemplate;
protected void publish(String payload) {
benchmarkAmqpTemplate.setQueue("BenchmarkQueue");
benchmarkAmqpTemplate.convertAndSend("my_exchange", "BenchmarkQueue", payload);
}
}
When I used the HelloWorld example it did publish a message in the queue so was wondering if I am doing something wrong.
UPDATE
I was able to solve this by adding direct-exchange tag in my context xml. My full xml looks like this:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:rabbit="http://www.springframework.org/schema/rabbit"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsdhttp://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit.xsd">
<bean id="banchmarkConnectionFactory" class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
<constructor-arg ref="benchmarkAmqpHost"/>
<property name="username" ref="benchmarkAmqpUser"/>
<property name="password" ref="benchmarkAmqpPass"/>
<property name="virtualHost" ref="benchmarkAmqpVHost"/>
<property name="channelCacheSize" value="10"/>
</bean>
<rabbit:template id="benchmarkAmqpTemplate"
connection-factory="banchmarkConnectionFactory"
exchange="my_exchange"
queue="BenchmarkQueue"
routing-key="BenchmarkQueue" />
<rabbit:admin connection-factory="banchmarkConnectionFactory"/>
<rabbit:queue name="BenchmarkQueue" auto-delete="true" durable="false" auto-declare="true"/>
<rabbit:direct-exchange name="my_exchange">
<rabbit:bindings>
<rabbit:binding queue="BenchmarkQueue" key="BenchmarkQueue" />
</rabbit:bindings>
</rabbit:direct-exchange>
</beans>
Sorry, but it looks like you misunderstood AMQP protocol a bit.
The message is published to the Exchange with the proper routingKey.
The publisher (RabbitTemplate) doesn't need to know about queues at all.
The queue is a part of receiver, subscriber to the queue.
There is one more feature in between - binding. The queue is bound to the Exchange under the appropriate routingKey. One queue can be bound to several exchanges with different routing keys. By default all queues are bound to the default exchange ("") with routingKeys equal to their names.
Please, refer for more info to the RabbitMQ site.

How to stop/start spring DefaultMessageListenerContainer?

I have developed project using Spring JMS to receive the message from Queue. and it is deployed Websphere application Server (WAS 7.5) clustered environment.
it is working fine once it is deployed in server.Later i have update my logger information and deployed in to server. it seems server not picking the latest code base. Even though i have stop/start the cluster.
Please refer below config xml.
<bean id="connectionFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="hostName" value="${hostName}"/>
<property name="port" value="${port}"/>
<property name="queueManager" value="${queueManager}"/>
<property name="transportType" value="${transportType}"/>
<property name="channel" value="${channel}"/>
</bean>
<jms:listener-container container-type="default"
connection-factory="connectionFactory" acknowledge="auto" concurrency="5" >
<jms:listener destination="DEV.TESTQUEUE" ref="jmsMessageListener"
</jms:listener-container>
<bean id="jmsMessageListener" class="JmsMessageListener"/>
public class JmsMessageListener implements MessageListener {
public void onMessage(Message message) {
}
}
Could you please advise how to stop/start the container?
Here is my solution:
final Map<String, DefaultMessageListenerContainer> containers = ctx.getBeansOfType(DefaultMessageListenerContainer.class);
if (containers != null && !containers.isEmpty()) {
for (DefaultMessageListenerContainer container : containers.values()) {
container.stop();
}
}
At last i found answer.
Default executor of DMLC is SimpleAsyncTaskExecutor.
SimpleAsyncTaskExecutor: This implementation does not reuse any threads, rather it starts up a new thread for each invocation.
However, it does support a concurrency limit which will block any invocations that are over the limit until a slot has been freed up.
If you’re looking for true pooling, keep scrolling further down the page. Spring Framework Task Execution and Scheduling.
So thread keep on running in container. this root cause of my issue. then i have restarted my JVM(with the support WAS Admin) and implemented ThreadPoolExecutor.
<jms:listener-container container-type="default"
connection-factory="connectionFactory" acknowledge="auto" concurrency="5" task-executor="taskExecutor">
<jms:listener destination="topCli_Service" ref="jmsMessageListener"
</jms:listener-container>
<bean id="taskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5" />
<property name="maxPoolSize" value="10" />
<property name="WaitForTasksToCompleteOnShutdown" value="true" />
</bean>

Spring Integration, jms Inbound gateway for WMQ; Unable to consume messages

I have recently started exploring Spring Integration as that is one of the option we want to evaluate for our project.
The issue i am facing is below.
I have created a JMS inbound gateway to listen to WMQ queue and i am expecting the inboudnd gateway (using DML) should pick up the messages as and when they are available on queue(event - driven).
But some how the example isn't working. It fails to pick messages from the queue. However i can see (using a tool) that there are consumers created on the queue.
Help here is really appreaciated.
Code snippet below.
<bean id="mqFactory" class="com.ibm.mq.jms.MQConnectionFactory">
<property name="hostName" value="${mq.hostName}"/>
<property name="port" value="${mq.port}"/>
<property name="queueManager" value="${mq.queueManager}"/>
<property name="channel" value="${mq.channel}"/>
<property name="transportType" value="${mq.transportType}"/>
<property name="SSLCipherSuite" value="${mq.SSLCipherSuite}"/>
</bean>
<bean id="inCachingConnectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="mqFactory" />
<property name="sessionCacheSize" value="5" />
</bean>
<bean id="requestQueue-mq" class="com.ibm.mq.jms.MQQueue">
<constructor-arg value="${mq.example.queue}"/>
</bean>
<bean id="demoBean" class="com.jpmchase.example.spring.DemoBean">
</bean>
<jms:inbound-gateway id="wMQ_in_gateway" concurrent-consumers="2" max-concurrent-consumers="5" connection-factory="inCachingConnectionFactory" request-destination="requestQueue-mq"
request-channel="demoChannel" />
<integration:channel id="demoChannel">
</integration:channel>
<integration:service-activator input-channel="demoChannel" ref="demoBean"/>
Below is the service-activator java code.
enter #MessageEndpoint
public class DemoBean {
#ServiceActivator
public String upperCase(String input) {
System.out.println("inside the service activator " + input);
return "JMS response: " + input.toUpperCase();
}
here

How to inject a message selector to message listener bean in jms-spring integration?

I'm working with JMS API (with HornetQ) and i'm using spring beans for message listener container and message listener:
<bean id="messageListener" class="core.messaging.handler.MessageListener">
<property name="postCommandService" ref="postCommandService" />
</bean>
<bean id="messageSender"
class="lsn.messaging.sender.MessageSender">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="destination" />
</bean>
<bean id="msgListenerContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer"
p:connectionFactory-ref="connectionFactory"
p:destination-ref="destination"
p:messageListener-ref="messageListener"
p:concurrentConsumers="10"
p:maxConcurrentConsumers="50"
p:receiveTimeout="5000"
p:idleTaskExecutionLimit="10"
p:idleConsumerLimit="5" />
If i want my message listener, only consume specific messages (that have same StringProperty) what should i do? Where should i define selector?
I have below solution, but i don't have MessageConsumer and so i can't add selector to it:
String redSelector = "color='red'";
MessageConsumer redConsumer = session.createConsumer(queue, redSelector);
redConsumer.setMessageListener(new SimpleMessageListener("red"));
TextMessage redMessage = session.createTextMessage("Red");
redMessage.setStringProperty("color", "red");
producer.send(redMessage);
You should be able to add it to the MessageListenerContainer this way:
p:messageSelector="color='red'"

Resources