RabbitTemplate.ReturnCallback() is called always in dynamic queue publish - spring

In our code we have attached RabbitTemplate.ReturnCallback() to get notification of not routed messages from consumer and it is working fine for the fixed queue.
But in case of synchronous publish by using rabbitTemplate.sendAndReceive, after receiving message when consumer tries to send response through dynamic queue , ReturnCallback is called always.
Is it the expected behavior?
Do we have any way to handle not routed message for dynamic queue.
Below are the beans created in my project , here we have attached one consumer to the queue "test", which receive the published "Message" from queue "test" then put a response to "reply-to" queue.
#Bean
SimpleMessageListenerContainer container( ConnectionFactory connectionFactory ) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory( connectionFactory );
container.setQueueNames( "test" );
container.setMessageListener( exampleListener() );
return container;
}
#Bean
public ConnectionFactory rabbitConnectionFactory() {
return new CachingConnectionFactory( "localhost" );
}
#Bean
public RabbitTemplate rabbitTemplate( ConnectionFactory connectionFactory ) {
RabbitTemplate rabbitTemplate = new RabbitTemplate( connectionFactory );
rabbitTemplate.setMandatory( true );
((CachingConnectionFactory)rabbitTemplate.getConnectionFactory()).setPublisherReturns( true );
rabbitTemplate.setReturnCallback( new RabbitTemplate.ReturnCallback() {
#Override
public void returnedMessage( Message arg0, int arg1, String arg2, String arg3, String arg4 ) {
System.out.println( "Returned message " + arg0 );
System.out.println( "Returned replyText " + arg2 );
}
} );
return rabbitTemplate;
}
#Bean
public RabbitAdmin rabbitAdmin( ConnectionFactory connectionFactory ){
return new RabbitAdmin( connectionFactory );
}
#Bean
public MessageListener exampleListener() {
return new MessageListener() {
public void onMessage( Message message ) {
System.out.println( "received: " + message );
RabbitTemplate rabbitTemplate = rabbitTemplate( rabbitConnectionFactory() );
// sending message to reply-to queue( dynamic queue in case of synchronous publish)
// even though the queue exist and messaage successfully published , RabbitTemplate.ReturnCallback() is called
rabbitTemplate.convertAndSend( message.getMessageProperties().getReplyTo(), "response from consumer" );
}
};
}
Rest end point to publish message synchronously to queue "test"
/**
* publish Message to Queue.
*
* #return response
*/
#RequestMapping ( value = "/publish", method = RequestMethod.POST )
public String sendMsgToQueue( ) {
MessageProperties messageProperties = new MessageProperties();
messageProperties.setCorrelationId("101".getBytes());
rabbitTemplate.setReplyTimeout( 30000 ); // timeout
return rabbitTemplate.sendAndReceive( "test", new SimpleMessageConverter().toMessage( "message to test", messageProperties ) ).toString();
}

Related

TopicSubscriber receive() not working Spring JMS - consumer closed

I am trying to receive messages using a TopicSubscriber created using execute method of JMSTemplate in Spring JMS. The objective is to test receiving of messages from a topic both synchronously and asynchronously. However, when trying to receive message from a durable topic subscriber, I get an error saying Consumer is closed.
I am new to Spring JMS. Can someone specify how to configure a JMSListener inside the test class? I have created two JMSListeners but they do not seem to work.
Here is my Test Class Code:
#SpringBootTest
class AqJmsExampleApplicationTests {
private ConnectionFactory connectionFactory;
private JmsTemplate jmsTemplate;
#BeforeEach
void setUp() {
// initialize jmstemplate with connectionFactory
// ......
}
#Test
void test() {
this.jmsTemplate.setExplicitQosEnabled(true);
this.jmsTemplate.setPubSubDomain(true);
TopicSubscriber tSub = this.jmsTemplate.execute(s -> {
return s.createDurableSubscriber(
(Topic)this.jmsTemplate
.getDestinationResolver()
.resolveDestinationName(s,
"testTopic4", true), "tSub");
}, true);
this.jmsTemplate.setPriority(7);
this.jmsTemplate.send("testTopic4", s -> {
return s.createTextMessage("hi this is first topic message");
});
try {
Message msg = tSub.receive(20000);
System.out.println("The message received is: " +
((TextMessage)msg).getText());
} catch (JMSException e) {
System.out.println("Got exception: " + e);
e.printStackTrace();
}
}
static class MyTestBean {
#JmsListener(destination = "testTopic4",
id = "top41",
subscription = "testTopic4"
)
public void receiveMessageTopic41(Message msg) throws JMSException{
System.out.println("Received for async subscriber 1<" +
((TextMessage)msg).getText() + ">");
}
#JmsListener(destination = "testTopic4",
id = "top42",
subscription = "testTopic4"
)
public void receiveMessageTopic42(Message msg) throws JMSException{
System.out.println("Received for async subscriber 2<" +
((TextMessage)msg).getText() + ">");
}
}
The error received is the following:
Got exception: javax.jms.IllegalStateException: JMS-115: Consumer is closed
Any help would be appreciated. Thanks!

Issue with RabbitMQ Delay message in spring boot

I am facing an issue in Rabbit MQ regarding x-delay while connecting to spring boot. I need to schedule the messages for a variable delay according to the message type. It can be one of the units MINUTE, DAY, WEEK, MONTH, and so on…
Below is my configuration class :
private final RabbitProperties rabbitProperties;
private final Environment environment;
#Bean
public Queue rabbitMQueue() {
return new Queue(environment.getProperty(RABBITMQ_QUEUE_NAME), false);
}
#Bean
public Exchange rabbitExchange() {
String exchangeName = "test_exchange";
Map<String, Object> exchangeArgs = new HashMap<>();
exchangeArgs.put("x-delayed-type", exchangeType.toLowerCase());
exchangeArgs.put("x-delayed-message",true);
exchangeArgs.put("x-message-ttl",9922);
log.info("Loading {} exchange with name {}.", exchangeType, exchangeName);
switch (exchangeType){
default: return new CustomExchange(exchangeName, exchangeType, true, false, exchangeArgs);
case "DIRECT" : return directExchange(exchangeName, exchangeArgs);
}
}
private Exchange directExchange(String exchangeName, Map<String, Object> exchangeArgs) {
// log.info("Generating directExchange");
// DirectExchange directExchange = new DirectExchange(exchangeName,true, false, exchangeArgs);
// directExchange.setDelayed(true);
// return directExchange;
return ExchangeBuilder.directExchange(exchangeName).withArguments(exchangeArgs)
.delayed()
.build();
}
#Bean
public Binding rabbitBinding(final Queue rabbitMQueue, final Exchange rabbitExchange){
log.info("Exchange to bind : {}", rabbitExchange.getName());
return BindingBuilder
.bind(rabbitMQueue)
.to(rabbitExchange)
.with(environment.getProperty(RABBITMQ_ROUTING_KEY)).noargs();
}
#Bean
public AmqpTemplate amqpTemplate(final ConnectionFactory rabbitMQConnectionFactory,
final MessageConverter rabbitMessageConvertor,
final Exchange rabbitExchange){
RabbitTemplate rabbitTemplate = new RabbitTemplate(rabbitMQConnectionFactory);
rabbitTemplate.setMessageConverter(rabbitMessageConvertor);
rabbitTemplate.setExchange(rabbitExchange.getName());
return rabbitTemplate;
}
#Bean
public ConnectionFactory rabbitMQConnectionFactory(){
Boolean isUriBased = environment.getProperty(URI_BASED_CONNECTION_ENABLED, Boolean.class);
CachingConnectionFactory connectionFactory;
if(!Objects.isNull(isUriBased) && isUriBased){
connectionFactory = new CachingConnectionFactory();
connectionFactory.setUri(environment.getProperty(RABBITMQ_URI));
}
else{
connectionFactory = new CachingConnectionFactory(rabbitProperties.getHost(), rabbitProperties.getPort());
connectionFactory.setUsername(rabbitProperties.getUsername());
connectionFactory.setPassword(rabbitProperties.getPassword());
}
return connectionFactory;
}
#Bean
public MessageConverter rabbitMessageConvertor(){
return new Jackson2JsonMessageConverter();
}
And publisher code :
public boolean sendMessage(String tenant, T message, int delay){
MyQueueMessage<T> myQueueMessage = getQueueMessage(tenant, message);
try{
amqpTemplate.convertAndSend(exchangeName, routingKey, myQueueMessage, messagePostProcessor -> {
// MessageProperties messageProperties = messagePostProcessor.getMessageProperties();
messagePostProcessor.getMessageProperties().setHeader("x-message-ttl", 5011);
messagePostProcessor.getMessageProperties().setHeader(MessageProperties.X_DELAY, 5012);
messagePostProcessor.getMessageProperties().setDelay(5013);
messagePostProcessor.getMessageProperties().setReceivedDelay(5014);
log.info("Setting delay in properties : {}", messagePostProcessor.getMessageProperties().getHeader(MessageProperties.X_DELAY).toString());
return messagePostProcessor;
});
} catch (Exception e){
return false;
}
return true;
}
And receiver :
#RabbitListener(queues = "INVOICE")
public void receiveMessage(Message message){
log.info("Message Received : " + message.toString() + " with delay " + message.getMessageProperties().getDelay());
}
}
Issue :
The value
message.getMessageProperties().getDelay()
comes as NULL in the receiver and the message is also not delayed. It’s getting received instantly.
Did I miss anything?
Please note that I am using docker, rabbitmq-management-3, and have already installed the rabbitmq_delayed_message_exchange plugin.

Error - Rabbit Template publish Confirm - reply-code=403, reply-text=ACCESS_REFUSED - cannot publish to internal exchange

My Use Case is:
subscribe to Q1 and read messages in Batches of specified size.
Pass the read message collection for processing.
publish the collected messages to Q2 and ack message to Q1 upon sucessful confirmation of q2 publish.
Code
#Component
public class EPPQ2Subscriber {
private static final Logger LOGGER = LoggerFactory.getLogger(EPPQ2Subscriber.class);
#Autowired
RabbitMqConfig rabbitMqConfig;
#Autowired
AppConfig appConfig;
List<Message> messageList = new ArrayList<Message>();
List<Long> diliveryTag = new ArrayList<Long>();
/**
* Method is listener's receive message method , invoked when there is message ready to read
* #param message - Domain object of message encapsulated
* #param channel - rabitmq client channel
* #param messageId - #TODO Delete it later.
* #param messageProperties - amqp message properties contains message properties such as delivery tag etc..
*/
#RabbitListener(id="messageListener",queues = "#{rabbitMqConfig.getSubscriberQueueName()}",containerFactory="queueListenerContainer")
public void receiveMessage(Message message, Channel channel, #Header("id") String messageId,
MessageProperties messageProperties) {
LOGGER.info("Result:" + message.getClass() + ":" + message.toString());
if(messageList.size() <= appConfig.getSubscriberChunkSize() ) {
messageList.add(message);
diliveryTag.add(messageProperties.getDeliveryTag());
} else {
// call the service here to decrypt, read pan, call danger to scrub, encrypt pan and re-pack them in message again.
//after this branch messageList should have scrubbed and encrypted message objects ready to publish.
// Here is call for publish and ack messages..
}
}
}
#Component
#Configuration
public class TopicConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(TopicConfiguration.class);
#Autowired
RabbitMqConfig rabbitMqConfig;
#Autowired EPPQ2Publisher eppQ2Publisher;
/**
* Caching connection factory
* #return CachingConnectionFactory
*/
#Bean
public CachingConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(rabbitMqConfig.getPublisherHosts(), rabbitMqConfig.getPublisherPort());
connectionFactory.setUsername(rabbitMqConfig.getPublisherUsername());
connectionFactory.setPassword(rabbitMqConfig.getPublisherPassword());
return connectionFactory;
}
/**
* Bean RabbitTemplate
* #return RabbitTemplate
*/
#Bean
public RabbitTemplate rabbitTemplate() {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory());
rabbitTemplate.setMessageConverter(producerJackson2MessageConverter());
RetryTemplate retryTemplate = new RetryTemplate();
ExponentialBackOffPolicy backOffPolicy = new
ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(500);
backOffPolicy.setMultiplier(10.0);
backOffPolicy.setMaxInterval(10000);
retryTemplate.setBackOffPolicy(backOffPolicy);
rabbitTemplate.setRetryTemplate(retryTemplate);
/* rabbitTemplate.setExchange(rabbitMqConfig.getPublisherTopic());
rabbitTemplate.setRoutingKey(rabbitMqConfig.getRoutingKey());*/
rabbitTemplate.setConfirmCallback((correlation, ack, reason) ->
if(correlation != null ) {
LOGGER.info("Received " + (ack ? " ack " : " nack ") +
"for correlation: " + correlation);
if(ack) {
// this is confirmation received..
// here is code to ack Q1. correlation.getId and ack
eppQ2Publisher.ackMessage(new
Long(correlation.getId().toString()));
} else {
// no confirmation received and no need to do any
thing for retry..
}
}
});
rabbitTemplate.setReturnCallback((message, replyCode,
replyText, exchange, routingKey) ->
{
LOGGER.error("Returned: " + message + "\nreplyCode: " +
replyCode+ "\nreplyText: " + replyText +
"\nexchange/rk: " + exchange + "/" + routingKey);
});
return rabbitTemplate;
}
/**
* Bean Jackson2JsonMessageConverter
* #return Jackson2JsonMessageConverter
*/
#Bean
public Jackson2JsonMessageConverter producerJackson2MessageConverter() {
return new Jackson2JsonMessageConverter();
}
}
public interface EPPQ2Publisher {
public void sendMessage(Message msg,Long deliveryTag);
public void sendMessages(List<Message> msgList, Channel channel, List<Long> deliveryTagList);
public void ackMessage(Long deliveryTag);
}
#Component
public class EPPQ2PublisherImpl implements EPPQ2Publisher{
#Autowired
RabbitMqConfig rabbitMqConfig;
#Autowired
private RabbitTemplate rabbitTemplate;
private Channel channel;
/**
* Method sendMessage for sending individual scrubbed and encrypted message to publisher queue (Q2).
* #param msg - message domain object
* #param deliveryTag - is message delivery tag.
*/
#Override
public void sendMessage(Message msg,Long deliveryTag) {
rabbitTemplate.convertAndSend(rabbitMqConfig.getPublisherTopic(), rabbitMqConfig.getRoutingKey(), msg,new CorrelationData(deliveryTag.toString()));
}
/**
* sendMessages for sending list of scrubbed and encrypted messages to publisher queue (Q2)
* #param msgList - is list of scrubbed and encrypted messages
* #param channel - is ampq client channel
* #param deliveryTagList - is list of incoming message delivery tags.
*/
#Override
public void sendMessages(List<Message> msgList, Channel channel, List<Long>deliveryTagList) {
if(this.channel == null) {
this.channel = channel;
}
for (int i = 0 ; i < msgList.size(); i ++) {
sendMessage(msgList.get(i),deliveryTagList.get(i));
}
}
/**
* Method ackMessage for sending acknowledgement to subscriber Q1
* #param deliveryTag - is deliveryTag for each individual message.
*/
#Override
public void ackMessage(Long deliveryTag) {
try {
channel.basicAck(deliveryTag, false);
} catch (IOException e) {
e.printStackTrace();
}
}
org.springframework.amqp.rabbit.connection.CachingConnectionFactory Creating cached Rabbit Channel from AMQChannel(amqp://dftp_subscriber#10.15.190.18:5672/hydra.services,2)
I expected to be dftp_publisher and I guess my topic configuration is not injected properly.
Error Log:
org.springframework.amqp.rabbit.core.RabbitTemplate[0;39m: Executing callback RabbitTemplate$$Lambda$285/33478758 on RabbitMQ Channel: Cached Rabbit Channel: AMQChannel(amqp://dftp_subscriber#10.15.190.18:5672/hydra.services,2), conn: Proxy#1dc339f Shared Rabbit Connection: SimpleConnection#2bd7c8 [delegate=amqp://dftp_subscriber#10.15.190.18:5672/hydra.services, localPort= 55553]
org.springframework.amqp.rabbit.core.RabbitTemplate[0;39m: Publishing message (Body:'{"HEADER":{"RETRY_COUNT":0,"PUBLISH_EVENT_TYPE":"AUTH"},"PAYLOAD":{"MTI":"100","MTI_REQUEST":"100","PAN":"6011000000000000","PROCCODE":"00","PROCCODE_REQUEST":"00","FROM_ACCOUNT":"00","TO_ACCOUNT":"00","TRANSACTION_AMOUNT":"000000000100","TRANSMISSION_MMDDHHMMSS":"0518202930","STAN":"000001","LOCALTIME_HHMMSS":"010054","LOCALDATE_YYMMDD":"180522","EXPIRATION_DATE_YYMM":"2302","MERCHANT_TYPE":"5311","ACQUIRING_COUNTRY_CODE":"840","POS_ENTRY_MODE":"02","POS_PIN_ENTRY_CAPABILITIES":"0","FUNCTION_CODE":"100","ACQUIRING_ID_CODE":"000000","FORWARDING_ID_CODE":"000000","RETRIEVAL_REFERENCE_NUMBER":"1410N644D597","MERCHANT_NUMBER":"601100000000596","CARD_ACCEPTOR_NAME":"Discover Acq Simulator","CARD_ACCEPTOR_CITY":"Riverwoods","CARD_ACCEPTOR_STATE":"IL","CARD_ACCEPTOR_COUNTRY":"840","CARD_ACCEPTOR_COUNTRY_3NUMERIC":"840","NRID":"123456789012345","TRANSACTION_CURRENCY_CODE":"840","POS_TERMINAL_ATTENDANCE_INDICATOR":"0","POS_PARTIAL_APPROVAL_INDICATOR":"0","POS_TERMINAL_LOCATION_INDICATOR":"0","POS_TRANSACTION_STATUS_INDICATOR":"0","POS_ECOMMERCE_TRAN_INDICATOR":"0","POS_TYPE_OF_TERMINAL_DEVICE":"0","POS_CARD_PRESENCE_INDICATOR":"0","POS_CARD_CAPTURE_CAPABILITIES_INDICATOR":"0","POS_TRANSACTION_SECURITY_INDICATOR":"0","POS_CARD_DATA_TERMINAL_INPUT_CAPABILITY_INDICATOR":"C","POS_CARDHOLDER_PRESENCE_INDICATOR":"0","DFS_POS_DATA":"0000000000C00","GEODATA_STREET_ADDRESS":"2500 LAKE COOK ROAD ","GEODATA_POSTAL_CODE":"600150000","GEODATA_COUNTY_CODE":"840","GEODATA_STORE_NUMBER":"10001","GEODATA_MALL_NAME":"DISCOVER FINANCIAL SR","ISS_REFERENCE_ID":"72967956","ISS_PROCESSOR_REFERENCE_ID":"123459875","VERSION_INDICATOR":"03141"}}' MessageProperties [headers={TypeId=com.discover.dftp.scrubber.domain.Message}, contentType=application/json, contentEncoding=UTF-8, contentLength=1642, deliveryMode=PERSISTENT, priority=0, deliveryTag=0])on exchange [hydra.hash2Syphon.exc], routingKey = [100]
org.springframework.amqp.rabbit.connection.CachingConnectionFactory$DefaultChannelCloseLogger[0;39m: Channel shutdown: channel error; protocol method: #method(reply-code=403, reply-text=ACCESS_REFUSED - cannot publish to internal exchange 'hydra.hash2Syphon.exc' in vhost 'hydra.services', class-id=60, method-id=40)
EDIT 2.
#Component
#Configuration
public class ListenerContainerFactory {
static final Logger logger = LoggerFactory.getLogger(ListenerContainerFactory.class);
#Autowired
RabbitMqConfig rabbitMqConfig;
#Autowired
EPPQ2Subscriber receiver;
#Autowired
EPPQ2ChanelAwareSubscriber receiverChanel;
public ListenerContainerFactory(ConfigurableApplicationContext ctx) {
printContainerStartMsg();
}
private void printContainerStartMsg() {
logger.info("----------- Scrubber Container Starts --------------");
}
#Bean
public SimpleRabbitListenerContainerFactory queueListenerContainer(AbstractConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
connectionFactory.setAddresses(rabbitMqConfig.getSubscriberHosts());
connectionFactory.setVirtualHost("hydra.services");
connectionFactory.setPort(rabbitMqConfig.getSubscriberPort());
connectionFactory.setUsername(rabbitMqConfig.getSubscriberUsername());
connectionFactory.setPassword(rabbitMqConfig.getSubscriberPassword());
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setErrorHandler(errorHandler());
return factory;
}
#Bean
MessageListenerAdapter listenerAdapter(EPPQ2Subscriber receiver) {
return new MessageListenerAdapter(receiver, "receiveMessage");
}
/*#Bean
MessageListenerAdapter listenerAdapterWithChanel(EPPQ2ChanelAwareSubscriber receiverChanel) {
return new MessageListenerAdapter(receiverChanel);
}*/
#Bean
public ErrorHandler errorHandler() {
return new ConditionalRejectingErrorHandler(fatalExceptionStrategy());
}
#Bean
public ScrubberFatalExceptionStrategy fatalExceptionStrategy() {
return new ScrubberFatalExceptionStrategy();
}
}
and Latest Topic Configuration.
#Component
#Configuration
public class TopicConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(TopicConfiguration.class);
#Autowired
RabbitMqConfig rabbitMqConfig;
#Autowired EPPQ2Publisher eppQ2Publisher;
/**
* Bean Queue
* #return Queue
*/
#Bean
Queue queue() {
return new Queue(rabbitMqConfig.getPublisherQueueName(), false);
}
/**
* Bean TopicExchage
* #return TopicExchage
*/
#Bean
TopicExchange exchange() {
return new TopicExchange(rabbitMqConfig.getPublisherTopic());
}
/**
* Bean BindingBuilder
* #param queue - Queue
* #param exchange - TopicExchange
* #return BindingBuilder
*/
#Bean
Binding binding(Queue queue, TopicExchange exchange) {
return BindingBuilder.bind(queue).to(exchange).with(rabbitMqConfig.getRoutingKey());
}
/**
* Caching connection factory
* #return CachingConnectionFactory
*/
#Bean
public CachingConnectionFactory cachingConnectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(rabbitMqConfig.getPublisherHosts(),
rabbitMqConfig.getPublisherPort());
connectionFactory.setUsername(rabbitMqConfig.getPublisherUsername());
connectionFactory.setPassword(rabbitMqConfig.getPublisherPassword());
return connectionFactory;
}
/**
* Bean RabbitTemplate
* #return RabbitTemplate
*/
#Bean
public RabbitTemplate rabbitTemplate() {
final RabbitTemplate rabbitTemplate = new RabbitTemplate(cachingConnectionFactory());
rabbitTemplate.setMessageConverter(producerJackson2MessageConverter());
RetryTemplate retryTemplate = new RetryTemplate();
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(500);
backOffPolicy.setMultiplier(10.0);
backOffPolicy.setMaxInterval(10000);
retryTemplate.setBackOffPolicy(backOffPolicy);
rabbitTemplate.setRetryTemplate(retryTemplate);
/* rabbitTemplate.setExchange(rabbitMqConfig.getPublisherTopic());
rabbitTemplate.setRoutingKey(rabbitMqConfig.getRoutingKey());*/
rabbitTemplate.setConfirmCallback((correlation, ack, reason) -> {
if(correlation != null ) {
LOGGER.info("Received " + (ack ? " ack " : " nack ") + "for correlation: " + correlation);
if(ack) {
// this is confirmation received..
// here is code to ack Q1. correlation.getId() and ack
eppQ2Publisher.ackMessage(new
Long(correlation.getId().toString()));
} else {
// no confirmation received and no need to do any
}
}
});
rabbitTemplate.setReturnCallback(
(message, replyCode, replyText,
exchange, routingKey) ->
{
LOGGER.error("Returned: " + message + "\nreplyCode: " +
replyCode
+ "\nreplyText: " + replyText + "\nexchange/rk: " +
exchange + "/" + routingKey);
});
return rabbitTemplate;
}
/**
* Bean Jackson2JsonMessageConverter
* #return Jackson2JsonMessageConverter
*/
#Bean
public Jackson2JsonMessageConverter producerJackson2MessageConverter() {
return new Jackson2JsonMessageConverter();
}
}
It's not clear what you are asking. If you mean the subscriber user doesn't have permissions to write to that exchange, your wiring is wrong.
You don't show the subscriber configuration.
Is it possible the subscriber connection factory bean is also called connectionFactory? In that case one or the other will win.
They need different bean named.
Please check the permissions for your user if while initiating the consumer/producer we don't have the exchange name it will fallback to default

delay delivery of message in activeMQ in spring boot

I want to send a message at any time 't' that will be received by the receiver after 'x' sec.
for doing so, I have written sender code
#Autowired
private JmsTemplate jmsTemplate;
private Queue queue = new ActiveMQQueue("topicName");
public void show(String message) {
try {
System.out.println("Sending message " + message);
jmsTemplate.convertAndSend(queue, message, new MessagePostProcessor() {
#Override
public Message postProcessMessage(Message message) throws JMSException {
System.out.println("postProcessMessage executed ");
message.setLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY, 3 * 60 * 1000);
System.out.println("long time " + message
.getLongProperty(ScheduledMessage.AMQ_SCHEDULED_DELAY));
return message;
}
});
System.out.println("Sending done " + message + " at " + System.currentTimeMillis());
} catch (Exception er) {
er.printStackTrace();
}
}
and Reciever code
#JmsListener(destination = "topicName")
public void reciever(String message) {
System.out.println("receiving message " + message + " at " + System.currentTimeMillis());
}
But message received by receiver instant .without any delay.
output is
Sending message this is a message postProcessMessage executed long
time 180000 receiving message this is a message at 1514391984964
Sending done this is a message at 1514391984970
configuration file is
#Bean
JmsTemplate createJMSTemplate(ConnectionFactory connectionFactory) {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(connectionFactory);
return jmsTemplate;
}
#Bean
ConnectionFactory myActiveMQConnectionFactory() {
RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
redeliveryPolicy.setBackOffMultiplier(1);
redeliveryPolicy.setUseExponentialBackOff(false);
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
connectionFactory.setRedeliveryPolicy(redeliveryPolicy);
NetworkConnector networkConnector = new DiscoveryNetworkConnector();
networkConnector.setConsumerTTL(2);
return connectionFactory;
}
Delayed message is not supported by activemq with default config, you should turn it on at first.
adding schedulerSupport in activemq.conf
<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost" dataDirectory="${activemq.data}" schedulerSupport="true">

Spring rabbitmq retry message delivery not working

I am setting PubSub using RabbitMQ, so whenever there is some failure in message processing a negative acknowledgement is sent in Listener, ideally retry should be done after some preconfigured time interval but retries are done continuously for each second till time of message-ttl value. For retry added created Advice chain in SimpleMessageListenerContainer. Queues and consumers are created dynamically depending on subscription. Below is code for rabbitmq service
#Service
public class RabbitMQQueueServiceImpl {
private final String JAVA_OBJECT_SERIALIZER_HEADER_NAME = "Content-Type";
private final String JAVA_OBJECT_SERIALIZER_HEADER_VALUE = "application/x-java-serialized-object";
private final String TIME_TO_LIVE_HEADER_NAME = "x-message-ttl";
#Value("${rabbitmq.message.timetolive}")
private Long messageTimeToLive;
#Value("${rabbitmq.host}")
private String host;
#Value("${rabbitmq.username}")
private String userName;
#Value("${rabbitmq.password}")
private String password;
#Value("${rabbitmq.port}")
private Integer port;
private RabbitTemplate rabbitTemplate;
#PostConstruct
public void init() {
createRabitTemplate();
}
public void subscribe(WebHookSubscription webHookSubscription,String queueName) throws Exception {
createProducer(webHookSubscription,queueName);
createConsumer(webHookSubscription,queueName);
}
public void publish(PublishEvent publishEvent,String queueName) {
//Append eventId for queueName and exchange name as accountId_accountTypeId
String exchange = queueName;
queueName = queueName+"_"+publishEvent.getEventId();
BroadcastMessageBean broadcastMessageBean = new BroadcastMessageBean();
/*
* Set properties for message - message persistence, JAVA object serializer
*/
MessageProperties messageProperties = new MessageProperties();
messageProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
messageProperties.setHeader(JAVA_OBJECT_SERIALIZER_HEADER_NAME, JAVA_OBJECT_SERIALIZER_HEADER_VALUE);
BeanUtils.copyProperties(publishEvent, broadcastMessageBean);
Gson gson = new Gson();
Message messageBean = new Message(gson.toJson(broadcastMessageBean).getBytes(), messageProperties);
rabbitTemplate.send(exchange, publishEvent.getEventId().toString(), messageBean);
}
public void createQueue(String queueName) {
RabbitTemplate rabbitTemplate = getRabbitTemplate(queueName);
RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitTemplate.getConnectionFactory());
Queue queue = new Queue(queueName, true,false,false);
rabbitAdmin.declareQueue(queue);
}
private void createProducer(WebHookSubscription webHookSubscription, String queueName) {
queueName = queueName+"_"+webHookSubscription.getEventId();
RabbitAdmin rabbitAdmin = new RabbitAdmin(rabbitTemplate.getConnectionFactory());
String exchangeName = webHookSubscription.getAccountId()+"_"+webHookSubscription.getAccountTypeId();
String routingKey = webHookSubscription.getEventId().toString();
Map<String, Object> arguments = new HashMap<String, Object>();
arguments.put(TIME_TO_LIVE_HEADER_NAME, messageTimeToLive);
Queue queue = new Queue(queueName, true,false,false, arguments );
rabbitAdmin.declareQueue(queue);
Exchange exchange = new TopicExchange(exchangeName, true, false) ;
rabbitAdmin.declareExchange(exchange );
Binding binding = new Binding(queueName, DestinationType.QUEUE, exchangeName, routingKey, arguments);
rabbitAdmin.declareBinding(binding );
}
private void createConsumer(WebHookSubscription webHookSubscription,String queueName) throws Exception {
Map<String, String> headers = new HashMap<String, String>();
//Create exchange name same as queue name and append eventId to queueName for each event subscription for account
queueName = queueName+"_"+webHookSubscription.getEventId();
for(WebHookSubscriptionHeaders header : webHookSubscription.getWebHookHeaders()) {
headers.put(header.getName(), header.getValue());
}
/*
* Register consumer through SimpleMessageListenerContainer
* add consumer handler with eventId
*/
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setPrefetchCount(10);
container.setRecoveryInterval(1000);
ConsumerHandler consumerHandler = new ConsumerHandler(webHookSubscription.getEventId(),
webHookSubscription.getWebHookURL(),headers, webHookSubscription.getNotificationEmail());
container.setConnectionFactory(connectionFactory());
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
container.setAdviceChain(new Advice[]{retryAdvice()});
/*
* Register consumer to queue and all messages will be broadcasted to subscriber specific queue
*/
container.setQueues(new Queue(queueName,true,false,false));
container.setMessageListener(consumerHandler);
container.start();
}
private MethodInterceptor retryAdvice() {
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(15000);
backOffPolicy.setMultiplier(100);
backOffPolicy.setMaxInterval(604800);
return RetryInterceptorBuilder.stateless().backOffPolicy(backOffPolicy).maxAttempts(5).recoverer(new RejectAndDontRequeueRecoverer()).build();
}
/**
* Create Connection factory for RabbitMQ template
* #return
*/
private CachingConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(getHost());
connectionFactory.setUsername(getUserName());
connectionFactory.setPassword(getPassword());
connectionFactory.setPort(getPort());
return connectionFactory;
}
private RabbitTemplate createRabitTemplate() {
rabbitTemplate = new RabbitTemplate(connectionFactory());
rabbitTemplate.setMessageConverter(new JsonMessageConverter());
//Create retries configuration for message delivery
/*RetryTemplate retryTemplate = new RetryTemplate();
ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
backOffPolicy.setInitialInterval(messageRetriesInitialInterval);
backOffPolicy.setMaxInterval(messageRetriesMaxInterval);
backOffPolicy.setMultiplier(messageRetriesMultiplier);
retryTemplate.setBackOffPolicy(backOffPolicy);
SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
retryPolicy.setMaxAttempts(retries);
retryTemplate.setRetryPolicy(retryPolicy);
//Retries configuration ends here
rabbitTemplate.setRetryTemplate(retryTemplate);*/
return rabbitTemplate;
}
}
This is for consumer handler
public class ConsumerHandler implements ChannelAwareMessageListener {
public ConsumerHandler(Long eventId, String url, Map<String, String> headers, String email) {
this.eventId = eventId;
this.url = url;
this.headers = headers;
this.email = email;
}
#Override
public void onMessage(Message message, Channel channel) throws IOException, NoSuchAlgorithmException {
//process message body
if(error){
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
return;
}
//for success
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
}
What is missing in above code to make retry work?
logs:
2016-02-23 16:34:09 DEBUG [org.springframework.amqp.rabbit.listener.BlockingQueueConsumer] - <Storing delivery for Consumer: tags=[{amq.ctag-a1IonIIN7mf8uhIGXE4eIw=14947_0_4}], channel=Cached Rabbit Channel: AMQChannel(amqp://guest#127.0.0.1:5672/,1), acknowledgeMode=MANUAL local queue size=0>
2016-02-23 16:34:09 DEBUG [org.springframework.retry.interceptor.StatefulRetryOperationsInterceptor] - <Exiting proxied method in stateful retry with result: (null)>
2016-02-23 16:34:09 DEBUG [org.springframework.retry.interceptor.StatefulRetryOperationsInterceptor] - <Exiting proxied method in stateful retry with result: (null)>
2016-02-23 16:34:09 DEBUG [org.springframework.amqp.rabbit.listener.BlockingQueueConsumer] - <Retrieving delivery for Consumer: tags=[{amq.ctag-a1IonIIN7mf8uhIGXE4eIw=14947_0_4}], channel=Cached Rabbit Channel: AMQChannel(amqp://guest#127.0.0.1:5672/,1), acknowledgeMode=MANUAL local queue size=1>
2016-02-23 16:34:09 DEBUG [org.springframework.amqp.rabbit.listener.BlockingQueueConsumer] - <Received message: (Body:'[B#4109b410(byte[199])'MessageProperties [headers={eventId=4, retry=1, Content-Type=application/x-java-serialized-object}, timestamp=null, messageId=31b93494-d6ef-4d63-b90b-c7a7e73acb70, userId=null, appId=null, clusterId=null, type=null, correlationId=null, replyTo=null, contentType=application/octet-stream, contentEncoding=null, contentLength=0, deliveryMode=PERSISTENT, expiration=null, priority=0, redelivered=true, receivedExchange=14947_0, receivedRoutingKey=4, deliveryTag=10234, messageCount=0])>
2016-02-23 16:34:09 DEBUG [org.springframework.retry.interceptor.StatefulRetryOperationsInterceptor] - <Executing proxied method in stateful retry: public abstract void org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$ContainerDelegate.invokeListener(com.rabbitmq.client.Channel,org.springframework.amqp.core.Message) throws java.lang.Exception(27f49de4)>
2016-02-23 16:34:09 DEBUG [org.springframework.retry.support.RetryTemplate] - <Retry: count=0>
2016-02-23 16:34:09 DEBUG [org.springframework.retry.interceptor.StatefulRetryOperationsInterceptor] - <Executing proxied method in stateful retry: public abstract void org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$ContainerDelegate.invokeListener(com.rabbitmq.client.Channel,org.springframework.amqp.core.Message) throws java.lang.Exception(27f49de4)>
2016-02-23 16:34:09 DEBUG [org.springframework.retry.support.RetryTemplate] - <Retry: count=0>
There is no direct way to ensure delayed retry.
You may have to create a Dead letter exchange and create a retryQueue on that exchange with the message ttl set to the the delay interval you want.
Your main queue should look something like this
#Bean
public Queue mainQueue() {
Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", "retryExchangeName");
return new Queue("mainQueue", true, false, false, args);
}
and retry Queue
#Bean
public Queue retryQueue() {
Map<String, Object> args = new HashMap<>();
args.put("x-dead-letter-exchange", "mainExchangeName");
args.put("x-message-ttl", RETRY_MESSAGE_TTL);
return new Queue("retryQueue", true, false, false, args);
}
now for the messages you want a delayed retry Nack them with requeue=false
channel().basicNack(10, false, false);
now this message will be sent to retryQueue and after the specified ttl it will be put back to the mainQueue for reprocessing.

Resources