Spring integration java config - spring

EmailQueueListener
#Component
public class EmailQueueListener{
public String handleMessage(String string) {
System.out.println("Message printing"); // this was printed several times
System.out.println(rabbitTemplate.receiveAndConvert()); //received null here
return string;
}
}
configuration
#Configuration
public class RabbitMQConfiguration {
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory =
new CachingConnectionFactory("localhost");
return connectionFactory;
}
#Bean
public AmqpAdmin amqpAdmin() {
RabbitAdmin admin=new RabbitAdmin(connectionFactory());
admin.declareQueue(queue());
return admin;
}
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate rabbitTemplate=new RabbitTemplate(connectionFactory());
rabbitTemplate.setRoutingKey("eventsQueue");
rabbitTemplate.setQueue("eventsQueue");
return rabbitTemplate;
}
#Bean
public Queue queue() {
return new Queue("eventsQueue");
}
#Bean
#Autowired
public SimpleMessageListenerContainer messageListenerContainer(EmailQueueListener listener){
SimpleMessageListenerContainer container=new SimpleMessageListenerContainer(connectionFactory());
MessageListenerAdapter adapter=new MessageListenerAdapter(listener, "handleMessage");
container.setMessageListener(adapter);
container.addQueues(queue());
return container;
}
}
Sender
rabbitTemplate.convertAndSend("hello");
I updated the code basing on what you said. But this is not working. i could not see the message which i printed to the console in Listener method. Is their anything wrong in my configuration

Here is another option to register any POJO listener:
#Bean
public SimpleMessageListenerContainer serviceListenerContainer() {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(rabbitConnectionFactory());
container.setQueues(requestQueue());
container.setMessageListener(new MessageListenerAdapter(new PojoListener()));
return container;
}
Where PojoListener is:
public class PojoListener {
public String handleMessage(String foo) {
return foo.toUpperCase();
}
}
For the MessageListener implementation you should use org.springframework.amqp.support.converter.MessageConverter to extract Message body and convert it to desired domain object.

Related

How to configure RabbitMQ (in Spring Boot 2.x) so manual acknowledge works

So I read all the examples, I have the com.rabbitmq.client.Channel and the #Header(AmqpHeaders.DELIVERY_TAG), but when I try to call Channel.basicNack(long deliveryTag, boolean multiple, boolean requeue) the result is "java.lang.IllegalStateException: Channel closed; cannot ack/nack". I can see that the CachingConnectionFactory does not support any of the acknowledge methods. So my question is what ConnectionFactory do I have to use and howto configure it, so that basicAck/basicNack works?
Spring Boot Version 2.1.0.RELEASE
application.yaml:
spring:
rabbitmq:
host: ${RABBITMQ_HOST:localhost}
port: ${RABBITMQ_PORT:5672}
username: ${RABBITMQ_USERNAME:guest}
password: ${RABBITMQ_PASSWORD:guest}
listener:
type: simple
simple:
acknowledge-mode: manual
Config class:
#EnableRabbit
#Configuration
public class RabbitMqConfig implements RabbitListenerConfigurer {
#Value("${app.rabbitmq.incoming-queue}")
private String incomingQueue;
private AmqpAdmin amqpAdmin;
#Autowired
public RabbitMqConfig(AmqpAdmin amqpAdmin) {
this.amqpAdmin = amqpAdmin;
}
#Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setChannelTransacted(true);
rabbitTemplate.setMessageConverter(jsonMessageConverter());
return rabbitTemplate;
}
#Bean
MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
#Override
public void configureRabbitListeners(RabbitListenerEndpointRegistrar registar) {
registar.setMessageHandlerMethodFactory(createDefaultMessageHandlerMethodFactory());
}
#Bean
public DefaultMessageHandlerMethodFactory createDefaultMessageHandlerMethodFactory() {
DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
factory.setMessageConverter(new MappingJackson2MessageConverter());
return factory;
}
#Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(
ConnectionFactory connectionFactory, CommonAmqpErrorHandler commonAmqpErrorHandler) {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(jsonMessageConverter());
factory.setErrorHandler(commonAmqpErrorHandler);
return factory;
}
#PostConstruct
public void afterInit() {
amqpAdmin.declareQueue(new Queue(getDeadLetterQueueName(incomingQueue), true));
amqpAdmin.declareQueue(
QueueBuilder.durable(incomingQueue).withArgument("x-dead-letter-exchange", "")
.withArgument("x-dead-letter-routing-key",
getDeadLetterQueueName(incomingQueue)).build());
}
private String getDeadLetterQueueName(String queueName) {
return queueName + ".dead-letter.queue";
}
}
Listener code:
#Transactional(rollbackOn = Exception.class)
#RabbitListener(queues = "${app.rabbitmq.incoming-queue}", errorHandler = "notificationListenerErrorHandler")
public void onMessage(#Valid #Payload NotificationDto notification, Message message,
Channel channel, #Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
System.out.println("00000 > " + tag);
System.out.println("11111");
channel.basicNack(tag, false, true);
System.out.println("222222");
}
After starting it from scratch, it turns out that the
#Transactional(rollbackOn = Exception.class)
is causing the problem. If I remove it, it's working

Spring Boot RabbitMQ Publisher and Receiver On the Same Project

I have an application (Microservice like) which should send and receives messages from other applications (Microservices). The application has several publishers with every publisher publishing to a specific queue as well as several subscriber classes with each subscriber subscribing to only one queue. Unfortunately, my subscriber classes are consuming the same messages I publish. How should I go about it?
Here is my code:
a) Publisher 1 - does not have a listener method since it only publishes to my.queues.queue1
#Configuration
public class RabbitQueue1Publisher{
private static final String QUEUE_NAME = "my.queues.queue1";
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("http://127.0.0.1:1675");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
return connectionFactory;
}
#Bean
public Queue simpleQueue() {
return new Queue(QUEUE_NAME);
}
#Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setRoutingKey(QUEUE_NAME);
template.setMessageConverter(jsonMessageConverter());
return template;
}
}
b) Publisher 2 - also does not have a listener method since it only publishes to my.queues.queue2
#Configuration
public class RabbitQueue2Publisher{
private static final String QUEUE_NAME = "my.queues.queue2";
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("http://127.0.0.1:1675");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
return connectionFactory;
}
#Bean
public Queue simpleQueue() {
return new Queue(QUEUE_NAME);
}
#Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setRoutingKey(QUEUE_NAME);
template.setMessageConverter(jsonMessageConverter());
return template;
}
}
c) Consumer 1 - consumes from queue3. Has a listener method
#Configuration
public class RabbitQueue3Subscriber{
private static final String QUEUE_NAME = "my.queue.queue3";
#Autowired
private Queue3Listener Queue3Listener;
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("http://127.0.0.1:15672");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
return connectionFactory;
}
#Bean
public Queue simpleQueue() {
return new Queue(QUEUE_NAME);
}
#Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setRoutingKey(QUEUE_NAME);
template.setMessageConverter(jsonMessageConverter());
return template;
}
#Bean
public SimpleMessageListenerContainer userListenerContainer() {
SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer();
listenerContainer.setConnectionFactory(connectionFactory());
listenerContainer.setQueues(simpleQueue());
listenerContainer.setMessageConverter(jsonMessageConverter());
listenerContainer.setMessageListener(Queue3Listener);
listenerContainer.setAcknowledgeMode(AcknowledgeMode.AUTO);
return listenerContainer;
}
}
d) Consumer 2 - consumes from queue4. Has a listener method
#Configuration
public class RabbitQueue4Subscriber{
private static final String QUEUE_NAME = "my.queue.queue4";
#Autowired
private Queue4Listener Queue4Listener;
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("http://127.0.0.1:15672");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
return connectionFactory;
}
#Bean
public Queue simpleQueue() {
return new Queue(QUEUE_NAME);
}
#Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setRoutingKey(QUEUE_NAME);
template.setMessageConverter(jsonMessageConverter());
return template;
}
#Bean
public SimpleMessageListenerContainer userListenerContainer() {
SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer();
listenerContainer.setConnectionFactory(connectionFactory());
listenerContainer.setQueues(simpleQueue());
listenerContainer.setMessageConverter(jsonMessageConverter());
listenerContainer.setMessageListener(Queue4Listener);
listenerContainer.setAcknowledgeMode(AcknowledgeMode.AUTO);
return listenerContainer;
}
}
Though I am publishing and consuming to/from different queues, I end up consuming the same messages I produce. Can someone point out what I am doing wrong or suggest the way to do it?
Here is how it works for me. I have publisher and a consumer of Rabbitmq. Doesn't mater if they are part of the same project or different.
Publisher:
Publisher Configuration
#Configuration
class PublisherConfig{
String queueName = "com.queueName";
String routingKey = "com.routingKey";
String exchange = "com.exchangeName";
#Bean
Queue queue() {
return new Queue(queueName, false);
}
#Bean
TopicExchange exchange() {
return new TopicExchange(exchange);
}
#Bean
Binding binding(Queue queueFoo, TopicExchange exchange) {
return BindingBuilder.bind(queueFoo).to(exchange).with(routingKey);
}
//Required only if you want to pass custom object as part of payload
#Bean
public MappingJackson2MessageConverter jackson2Converter() {
return new MappingJackson2MessageConverter();
}
}
Publish Message
#Autowired private RabbitMessagingTemplate rabbitMessagingTemplate;
#Autowired private MappingJackson2MessageConverter mappingJackson2MessageConverter;
rabbitMessagingTemplate.setMessageConverter(this.mappingJackson2MessageConverter);
rabbitMessagingTemplate.convertAndSend(exchange, routingKey, employObj)
Consumer
Consumer Configuration
#Configuration
public class RabbitMQConfiguration implements RabbitListenerConfigurer {
public MappingJackson2MessageConverter jackson2Converter() {
return new MappingJackson2MessageConverter();
}
#Bean
public DefaultMessageHandlerMethodFactory handlerMethodFactory() {
DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
factory.setMessageConverter(jackson2Converter());
return factory;
}
#Override
public void configureRabbitListeners(RabbitListenerEndpointRegistrar registrar) {
registrar.setMessageHandlerMethodFactory(handlerMethodFactory());
}
}
Listen a Message
#RabbitListener(queues = "com.queueName")
public void receiveMessage(Employee employee) {
// More code
}
You can encapsulate Publisher and Listener configurations in two different #configuration files.
Hope this helps you
P.S.
OP asked for explanation. Here it is:
Exchange and Routing Key
Publisher publishes a message to an exchange with a particular routing key. Routing key helps to differentiate the type of message it is.
Suppose:
Send all user logged in messages with routing key of 'user_logged_in'.
Send all email sent messages with 'email_sent'.
Queue:
Once the routing key is attached with the exchange there comes a queue.
Queue is attached a exchange and routing key and all the published messages will sit in this queue.
Now consumer explicitly, connects to such queues and listen messages.
So queue name in publisher config and consumer config has to be the same.
Once your publisher is up you can actually visit RabbitMq dashboard
and see the exchange, routing key and queue to see how it works.

Spring Boot RabbitMQ Null Pointer Exception Error

I am using RabbitMQ with Spring Boot to broker messages between two services. I am able to receive the message and format it but when I call a service class in the onMessage method, I get a null pointer exception error. Here is my message listener class which receives the message
public class QueueListener implements MessageListener{
#Autowired
private QueueProcessor queueProcessor;
#Override
public void onMessage(Message message) {
String msg = new String(message.getBody());
String output = msg.replaceAll("\\\\", "");
String jsonified = output.substring(1, output.length()-1);
JSONArray obj = new JSONArray(jsonified);
queueProcessor.processMessage(obj);
}
}
Calling the method processMessage throws null pointer exception
Can someone point to me what I ma doing wrong?
I found out the issue was in the RabbitMqConfig class. Here is the code which was causing the error:
#Configuration
public class RabbitMqConfig {
private static final String QUEUE_NAME = "my.queue.name";
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("<url.to.rabbit>");
connectionFactory.setUsername("<username>");
connectionFactory.setPassword("<password>");
return connectionFactory;;
}
#Bean
public Queue simpleQueue() {
return new Queue(QUEUE_NAME);
}
#Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setRoutingKey(QUEUE_NAME);
template.setMessageConverter(jsonMessageConverter());
return template;
}
#Bean
public SimpleMessageListenerContainer userListenerContainer() {
SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer();
listenerContainer.setConnectionFactory(connectionFactory());
listenerContainer.setQueues(simpleQueue());
listenerContainer.setMessageConverter(jsonMessageConverter());
listenerContainer.setMessageListener(new QueueListener());
listenerContainer.setAcknowledgeMode(AcknowledgeMode.AUTO);
return listenerContainer;
}
}
The line listenerContainer.setMessageListener(new QueueListener()); was the source of the error. I solved it by Autowiring the class instead of using new. Here is the working code
#Configuration
public class RabbitMqConfig {
private static final String QUEUE_NAME = "my.queue.name";
#Autowired
private QueueListener queueListener;
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory("<url.to.rabbit>");
connectionFactory.setUsername("<username>");
connectionFactory.setPassword("<password>");
return connectionFactory;
}
#Bean
public Queue simpleQueue() {
return new Queue(QUEUE_NAME);
}
#Bean
public MessageConverter jsonMessageConverter(){
return new Jackson2JsonMessageConverter();
}
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setRoutingKey(QUEUE_NAME);
template.setMessageConverter(jsonMessageConverter());
return template;
}
/*#Bean
public SimpleMessageListenerContainer userListenerContainer() {
SimpleMessageListenerContainer listenerContainer = new SimpleMessageListenerContainer();
listenerContainer.setConnectionFactory(connectionFactory());
listenerContainer.setQueues(simpleQueue());
listenerContainer.setMessageConverter(jsonMessageConverter());
listenerContainer.setMessageListener(queueListener);
listenerContainer.setAcknowledgeMode(AcknowledgeMode.AUTO);
return listenerContainer;
}
}
Hope this helps someone else
Make sure the QueueListener is a component class or service class that can be managed by the Spring IoC. Otherwise, the config class cannot make this a bean out of the box, since this is just a normal Java class that need to be in the container #runtime.
So when u write new QueueListener() in yr config class, then the Java class is not in the SpringContext at the time when the config class is instantiated and is therefore null.
Hope this helps clear out some of this issue!

rabbitmq overflow redirecting messages - issue

I am using rabbit mq 3.7.4 and have a queue with x-max-length:3 and x-overflow:reject-publish, I am trying to redirect those messages which comes once queue reaches it's max limit (i.efrom 4th message ...nth message) to a deadletter queue but was unable to achieve.
kibndly help with the same
My configuration:
#Configuration
#EnableAutoConfiguration
public abstract class AMQPConfig {
#Value("${overflow-queue.exchange}")
private String queueExchange;
#Value("${overflow-queue.routingkey}")
private String throttleRoutingKey;
#Value("${overflow.queue}")
private String queue;
/**
* abstract ampqTemplate method
*/
public abstract AmqpTemplate getAmqpTemplate();
#Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
#Bean
public TopicExchange salesExchange() {
return new TopicExchange(queueExchange);
}
#Bean
public Queue salesQueue() {
return //QueueBuilder.durable(throttleQueue)
QueueBuilder.durable(queue)
.withArgument("x-max-length", 3)
.withArgument("x-overflow", "reject-publish")
.withArgument("x-dead-letter-exchange",queueExchange)
.withArgument("x-dead-letter-routing-key","deadletter-routing-key")
.build();
}
#Bean
public Binding declareSalesQueueBinding() {
return BindingBuilder.bind(salesQueue()).to(salesExchange()).with(queue);//with(throttleQueue);
}
#Bean
public MappingJackson2MessageConverter consumerJackson2MessageConverter() {
return new MappingJackson2MessageConverter();
}
#Bean
public DefaultMessageHandlerMethodFactory messageHandlerMethodFactory() {
DefaultMessageHandlerMethodFactory factory = new DefaultMessageHandlerMethodFactory();
factory.setMessageConverter(consumerJackson2MessageConverter());
return factory;
}
}
MY PRODUCER:
#Service
public class Producer {
private static long sentMessageCount =0L;
#Autowired
private AmqpTemplate rabbitTemplate;
#Value("${overflow.queue}")
String queueName;
#Scheduled(initialDelay = 5000, fixedRate = 10)
public void queueSender() {
long x=++sentMessageCount;
rabbitTemplate.convertAndSend(queueName,"{'empId':'"+x+"','empName':'raj'}");
}
}

Spring Rabbit MQ Publish and Acknowledge

In my project i am having 3 Classes App(Publisher), PojoListener(Receiver) , Config(bind both App and PojoListner). But i need to separate out the PojoListener from my project and deploy somewhere else so that my Listener class will continuous listen to the Rabbit Mq and ack the messages back to queue and then to App Class for any messages published by my App class.
As my config file is common for both Publisher and Receiver. Is there any way to seperate out them. I need to deploy my receiver on different server and Publisher on different. Both will listen to common queues.
**App.java** :
public class App {
public static void main(String[] args) throws UnsupportedEncodingException {
ApplicationContext context = new AnnotationConfigApplicationContext(RabbitMQConfig.class);
RabbitTemplate rabbitTemplate = context.getBean(RabbitTemplate.class);
String response = (String) rabbitTemplate.convertSendAndReceive("message from publisher");
System.out.println("message sent");
System.out.println("response from :" + response);
}
}
PojoListener.java :
public class PojoListener {
public String handleMessage(String msg) {
System.out.println("IN POJO RECEIVER!!!");
return msg.toUpperCase();
}
}
RabbitMQConfig.java :
#Configuration
public class RabbitMQConfig {
#Bean
public ConnectionFactory rabbitConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setHost("localhost");
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
return connectionFactory;
}
#Bean
public RabbitTemplate fixedReplyQRabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(rabbitConnectionFactory());
template.setExchange(ex().getName());
template.setRoutingKey("test");
template.setReplyQueue(replyQueue());
template.setReplyTimeout(60000);
return template;
}
#Bean
public SimpleMessageListenerContainer replyListenerContainer() {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(rabbitConnectionFactory());
container.setQueues(replyQueue());
container.setMessageListener(fixedReplyQRabbitTemplate());
return container;
}
#Bean
public SimpleMessageListenerContainer serviceListenerContainer() {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(rabbitConnectionFactory());
container.setQueues(requestQueue());
container.setMessageListener(new MessageListenerAdapter(new PojoListener()));
return container;
}
#Bean
public DirectExchange ex() {
return new DirectExchange("rabbit-exchange", false, true);
}
#Bean
public Binding binding() {
return BindingBuilder.bind(requestQueue()).to(ex()).with("test");
}
#Bean
public Queue requestQueue() {
return new Queue("request-queue-spring");
}
#Bean
public Queue replyQueue() {
return new Queue("response-queue-spring");
}
#Bean
public RabbitAdmin admin() {
return new RabbitAdmin(rabbitConnectionFactory());
}
}

Resources