Spring Message Listener Names - spring

I have created several spring 4.1.6 #JmsListener methods for receiving messages from queues. I would like to get the list of listener names for admin purpose:
#JmsListener(
destination = "${jms.destination.name}"
, containerFactory = "myJmsContainerFactory"
, id ="myListener")
public void receiveMessage(String message) {
System.out.println("Received <" + message + ">");
}
and I want to display the id/name of the listeners for admin purposes. I can stop and start the listeners with the JmsListenerEndpointRegistry, but can't see how to get the name of the specific listeners.
JmsListenerEndpointRegistry registry = context.getBean(org.springframework.jms.config.JmsListenerEndpointRegistry.class);
Collection<MessageListenerContainer> listeners = registry.getListenerContainers();
MessageListenerContainer mlc = registry.getListenerContainer("myListener");
System.out.println("Running" + mlc.isRunning());
mlc.stop();
In debug the Collection can be seen as a Collections$UnmodifiableCollection with a LinkedHashMap that has the listener id ,myListener in this case, as the key value of the LinkedHashMap

This question was quite old, but I solved it this way:
Set<String> listenerContainerIds = registry.getListenerContainerIds();
for (String id : listenerContainerIds) {
MessageListenerContainer listenerContainer = registry.getListenerContainer(id);
...
}
I got the reference to the MessageListenerContainer und via getListenerContainerIds, I got the name of the ID to the reference.
Hope this helps...

Related

How to ensure multiple jms listener instances process messages only when currently executing listener instance acknowlege

I'm currently trying to build a messaging application using JMS Listener and IBM MQ, and I need to ensure that I can run two instances of the same listener at the same time. However, I want to make sure that the second instance waits until the first instance has fully processed and acknowledged the message.
I'm using Spring Boot for my application, JMS Listener and IBM MQ.
Below is the my config class which is annotated with #component and #EnableJMS
public class jmsconfig{
#Bean
public MQConnectionfactory getConnectionFactory(){
MQConnectionFactory connectionFactory = new MQConnectionFactory();
connectionFactory.setQueueManagerName("AA");
connectionFactory.setExpirationTimeOut(3600000);
connectionFactory.setPerformExpiration(true);
connectionFactory.setPerformValidation(true);
connectionFactory.setPerformOptimalSizeCheck(false);
connectionFactory.setValidationTimeOut(180000);
connectionFactory.setMinIdle(3);
connectionFactory.setMaxIdle(5);
reurn connectionFactory ;
}
#Bean(name="jmsListenerContainerFactory")
public JmsListenerContainerFactory jmsListenerContainerFactory(MQConnectionfactory mqConnectionfactory){
DefaultJmsListenerContainerFactory containerFactory = new DefaultJmsListenerContainerFactory();
containerFactory.setConnectionFactor(mqConnectionfactory);
containerFactory.setSessionTransacted(true);
containerFactory.setSessionAcknowledgementMode(Session.CLIENT_ACKNOWLEDE)
reurn containerFactory ;
}
}
Listener code:
#Component
public class Receiver {
public sttaic int count = 0;
#JmsListener(destination = "${inwardQueueName}", containerFactory = "jmsListenerContainerFactory")
public void receiveMessage(javax.jms.Message message) throws javax.jms.JMSException {
String messagetxt = "";
OrderObject order = null;
if (message instanceof javax.jms.TextMessage) {
messagetxt = ((TextMessage)message).getText();
OrderObject order = //code to covert messagetxt to object
System.out.println("Message pciked up with Order Id : " +order.getOrderId)
TimeUnit.SECONDS.sleep(15);
count++;
if(count<=4){
throw new Exception("Exception occurred");
}
}
TimeUnit.SECONDS.sleep(15);
System.out.println("Message Acknowledged for orderId: " +order.getOrderId)
message.acknowledge();
}
}
I start the first instance and the push first message to MQ with orderId 1. I can see the below output on the console. It prints the below statement and then waits for 15 seconds
Message picked up with Order Id : 1
Immediately, I start the second instance and the push second message to MQ with orderId 2. I can see the below output on the console.
First Instance picks first message with orderId 2
Message Acknowledged for orderId: 2
After some time, when the first instance completes(since there is a wait of 15 sec), I can see the below output.
Message pciked up with Order Id : 1
Message pciked up with Order Id : 1
Message pciked up with Order Id : 1
Message Acknowledged for orderId: 1
With the above output, the Second instance is picking up second messages from the MQ in parallel and processing them while the first instance is still processing the first message.
Can anyone help with what is wrong with the above implementation?

Bind RabbitMQ consumer using Spring Cloud Stream to RabbitMQ producer

I have two microservices, one for collecting XML files from internal FTP server ,transforming it to DTO objects and then publishing them as bytes in RabbitMQ and the other for deserializing the incoming bytes from RabbitMQ to DTO objects, mapping them to JPA entities and persisiting them to database.
I'd like configure RabbitMQ broker between these two microservices like below:
1) for microservice that collect XML files, I edited in application.properties as below:
spring.cloud.stream.bindings.output.destination=TOPIC
spring.cloud.stream.bindings.output.group=proactive-policy
2) for microservice that persist incoming DTO onjects, I configured in application.properties as following:
spring.cloud.stream.bindings.input.destination=TOPIC
spring.cloud.stream.bindings.input.group=proactive-policy
For receiving incoming bytes from RabbitMQ I'm using second microservice as sink:
#EnableJpaAuditing
#EnableBinding(Sink.class)
#SpringBootApplication(scanBasePackages = { "org.proactive.policy.data.cache" })
#RefreshScope
public class ProactivePolicyDataCacheApplication {
private static Logger logger = LoggerFactory.getLogger(ProactivePolicyDataCacheApplication.class);
#Autowired
PolicyService policyService;
public static void main(String[] args) {
SpringApplication.run(ProactivePolicyDataCacheApplication.class, args);
}
#StreamListener(Sink.INPUT)
public void input(Message<byte[]> message) throws Exception {
if (Objects.isNull(message) || Objects.isNull(message.getPayload())) {
logger.error("the message is null ");
throw new IllegalArgumentException("`message` and `message.payload` cannot be null");
}
byte[] data = message.getPayload();
if (data.length == 0) {
logger.warn("Received empty message");
return;
}
logger.info("Got data from policy-collector = " + new String(data, "UTF-8"));
PolicyListDto policyListDto = (PolicyListDto) SerializationUtils.deserialize(data);
logger.info("Policies.xml from policy-collector = " + policyListDto.getPolicy().toString());
policyService.save(policyListDto);
}
}
But when I open RabbitMQ console for looking at exchanges I didn't receive any thing in Queue TOPIC.proactive-policy But the incoming messages are received in another Queue that I haven't configured it named FTPSTREAM.proactive-policy-collector
Is there any suggestion for resolving this issue
Couple of points:
1. There is no such thing as 'group' for the output binding. Consumer Group is a consumer property. Here is the fragment of the javadocs.
/**
* Unique name that the binding belongs to (applies to consumers only). Multiple
* consumers within the same group share the subscription. A null or empty String
* value indicates an anonymous group that is not shared.
* #see org.springframework.cloud.stream.binder.Binder#bindConsumer(java.lang.String,
* java.lang.String, java.lang.Object,
* org.springframework.cloud.stream.binder.ConsumerProperties)
*/
private String group;
2. The name 'FTPSTREAM.proactive-policy-collector' is definitely not something that is generated by the spring-cloud-stream, so consider looking into your configuration and see what have you missed.
It tells me that you have some consumer that has its 'destination' named FTPSTREAM and its 'group' proactive-policy-collector. It also tells me that your producer sends messages to the FTPSTREAM exchange.

Sending a Message with Spring Cloud Stream and RabbitMq changes ID

I'm using Spring Cloud Stream and RabbitMq to exchange Messages between different microservices.
Thats my setup to publish a message.
public interface OutputChannels {
static final String OUTPUT_CHANNEL = "outputChannel";
#Output
MessageChannel outputChannel();
}
.
#EnableBinding(OutputChannels.class)
#Log4j
public class OutputProducer {
#Autowired
private OutputChannels outputChannels;
public void createMessage(MyContent myContent) {
Message<MyContent> message = MessageBuilder
.withPayload(myContent)
.build();
outputChannels.outputChannel().send(message);
log.info("Sent message: " + message.getHeaders().getId() + myContent);
}
}
And the setup to receive the message
public interface InputChannels {
String INPUT_CHANNEL = "inputChannel";
#Input
SubscribableChannel inputChannel();
}
.
#EnableBinding(InputChannels.class)
#Log
public class InputConsumer {
#StreamListener(InputChannels.INPUT_CHANNEL)
public void receive(Message<MyContent> message) {
MyContent myContent = message.getPayload();
log.info("Received message: " + message.getHeaders().getId() + ", " + myContent);
}
}
I am able to successfully exchange messages with this setup. I would expect, that the IDs of the sent message and the received message are equal. But they are always different UUIDs.
Is there a way that the message keeps the same ID all the way from the producer, through the RabbitMq, to the consumer?
Spring Messaging messages are immutable; they get a new ID each time they are mutated.
You can use a custom header or IntegrationMessageHeaderAccessor.CORRELATION_ID to convey a constant value; in most use cases, the correlation id header is set by the application to the ID header at the start of a message's journey.

How to stop and restart consuming message from the RabbitMQ with #RabbitListener

I am able to stop the consuming and restart the consuming but the problem is that when I am restarting the consuming, I am able to process the already published message but when I publish the new messages those are not able to process.
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Consumer;
#Component
public class RabbitMqueue implements Consumer {
int count = 0;
#RabbitListener(queues="dataQueue")
public void receivedData(#Payload Event msg, Channel channel,
#Header(AmqpHeaders.CONSUMER_TAG) String tag) throws IOException,
InterruptedException {
count++;
System.out.println("\n Message recieved from the Dataqueue is " + msg);
//Canceling consuming working fine.
if(count == 1) {
channel.basicCancel(tag);
System.out.println("Consumer is cancle");
}
count++;
System.out.println("\n count is " + count + "\n");
Thread.sleep(5000);
//restarting consumer. able to process already consumed messages
//but not able to see the newly published messages to the queue I mean
//newly published message is moving from ready to unack state but nothing
//happening on the consumer side.
if(count == 2) {
channel.basicConsume("dataQueue", this);
System.out.println("Consumer is started ");
}
}
}
You must not do this channel.basicCancel(tag).
The channel/consumer are managed by Spring; the only thing you should do with the consumer argument is ack or nack messages (and even that is rarely needed - it's better to let the container do the acks).
To stop/start the consumer, use the endpoint registry as described in the documentation.
Containers created for annotations are not registered with the application context. You can obtain a collection of all containers by invoking getListenerContainers() on the RabbitListenerEndpointRegistry bean. You can then iterate over this collection, for example, to stop/start all containers or invoke the Lifecycle methods on the registry itself which will invoke the operations on each container.
e.g. registry.stop() will stop all the listeners.
You can also get a reference to an individual container using its id, using getListenerContainer(String id); for example registry.getListenerContainer("multi") for the container created by the snippet above.
If your are using AMQP/Rabbit, you can try one of these:
1) Prevent starting at startup in code:
#Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
//
//autoStartup = false, prevents handling messages immedeatly. You need to start each listener itselve.
//
factory.setAutoStartup(false);
factory.setMessageConverter(new Jackson2JsonMessageConverter());
return factory;
}
2) Prevent starting at startup in in app.yml/props:
rabbitmq.listener.auto-startup: false
rabbitmq.listener.simple.auto-startup: false
3) Start/stop individual listeners
give your #RabbitListener a id:
#RabbitListener(queues = "myQ", id = "myQ")
...
and :
#Autowired
private RabbitListenerEndpointRegistry rabbitListenerEndpointRegistry;
MessageListenerContainer listener =
rabbitListenerEndpointRegistry.getListenerContainer("myQ");
...
listener.start();
...
listener.stop();

Queue Size in Spring AMQP Java client

I am using Spring amqp 1.1 version as my java client.
I have a queue which has around 2000 messages. I want to have a service which checks this queue size and and if it is empty it will send out a message saying " All items processed".
I dont know how to get current queue size ? Please help
I googled and found a class "RabbitBrokerAdmin" that was present in earlier version 1.0.
I think it is not present in 1.1 now.
Any pointers in getting current queue size?
So I know this is a little late and a solution has already been found but here is another way to look message counts in your queues
This solution assumes that you are using the spring rabbitmq framework and have defined your queues in your application config with the following tags defined
<rabbit:queue>
<rabbit:admin>
The java class:
public class QueueStatsProcessor {
#Autowired
private RabbitAdmin admin;
#Autowired
private List<Queue> rabbitQueues;
public void getCounts(){
Properties props;
Integer messageCount;
for(Queue queue : rabbitQueues){
props = admin.getQueueProperties(queue.getName());
messageCount = Integer.parseInt(props.get("QUEUE_MESSAGE_COUNT").toString());
System.out.println(queue.getName() + " has " + messageCount + " messages");
}
}
}
You can also use this solution to read the current consumers attached to the queue
http://docs.spring.io/spring-amqp/docs/1.2.1.RELEASE/api/org/springframework/amqp/rabbit/core/RabbitAdmin.html#getQueueProperties(java.lang.String)
You can use the RabbitAdmin instance to get the details from the queue, as follows:
#Resource RabbitAdmin admin;
...
protected int getQueueCount(final String name) {
DeclareOk declareOk = admin.getRabbitTemplate().execute(new ChannelCallback<DeclareOk>() {
public DeclareOk doInRabbit(Channel channel) throws Exception {
return channel.queueDeclarePassive(name);
}
});
return declareOk.getMessageCount();
}

Resources