RabbitMQ set queue Parameters while connecting through camel - rabbitmq

I am trying to connect to RabbitMQ queues present in the server using apache-camel configuration.
It works fine when I create the queues with durable field false and auto-delete field true. But doesn't work when either of them is otherwise.
applicationContext.xml file looks like this -
<bean id="customConnectionFactory" class="com.rabbitmq.client.ConnectionFactory">
<property name="host" value="localhost" />
<property name="port" value="5672" />
<property name="username" value="guest" />
<property name="password" value="guest" />
<property name="virtualHost" value="Test" />
</bean>
<bean id="testBean" class="test.TestBean" />
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<route>
<from
uri="rabbitmq://localhost:5672/ex1?connectionFactory=#customConnectionFactory&queue=Q1&autoDelete=true&durable=true" />
<to uri="bean:testBean?method=hello" /> <!-- This method consumes and prints the message -->
</route>
</camelContext>
Here I need to specify the properties autoDelete and durable for the queue Q1 not the exchange ex1. (I have already specified for the exchange in the URI)
As the error is -
Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - inequivalent arg 'auto_delete' for queue 'Q1' in vhost 'Test': received 'true' but current is 'false', class-id=50, method-id=10)
Here reply-code=406 indicates that the parameters of queues/exchanges are not matching with the actual configuration. Its because of the queues properties here.
As I don't have the access to the remote queues, I cannot change the properties of queues. (Example I stated here is localhost)
Note: I have a requirement of doing this using spring beans only.

Related

How to see Dequeued messages in ActiveMQ

While reading messages from a dynamic queue(ActiveMQ)(Pending Messages=1000), i had acknowledge each message,now the number of Messages Dequeued=1000.
Is there any way to place all dequeued messages again into Queue.
Any solution to get all messages backup physically.
Thanks in advance
Once broker get acknowledgment from consumer, it removes message from persistence store [KahaDB/database as per configuration] of broker.
Hence if you have sent all messages to another queue or broker from your queue, you can resend those messages to your original queue. However all depends what you did with messages. If you have consumed it using MDB/java code etc, you will not be able to place them again to original queue.
Dequeued messages are not designed can be seen in activemq, you need your own logic to save them elsewhere.
ActivemMQ also give some functions to make that easier, like Mirrored Queues(http://activemq.apache.org/mirrored-queues.html), or the log plugin.
But still you need to save messages elsewhere and backup them by yourself.
to backup enqueued messages when you need the body of messages :
you can add this to your activemq.xml
this will save a copy of messages to a file under ${activemq.base}/bin/data/activemq/
a file by day and queue
<bean id="ActiveMQVMConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://localhost?create=false&waitForStart=10000"/>
<property name="userName" value="${activemq.username}"/>
<property name="password" value="${activemq.password}"/>
</bean>
<bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent" >
<property name="connectionFactory" ref="ActiveMQVMConnectionFactory"/>
</bean>
<camelContext id="camel" trace="false" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="activemq:queue:*?mapJmsMessage=false&selector=CamelFileNameProduced IS NULL" />
<choice>
<when>
<simple>${in.headers.CamelFileNameProduced} == null</simple>
<setHeader headerName="CamelJmsDestinationName">
<simple>${in.header.JMSDestination.physicalName}</simple>
</setHeader>
<transform>
<simple>${in.body}\n</simple>
</transform>
<to uri="file://data/activemq/?fileExist=Append&fileName=routeMessages-${in.header.JMSDestination.physicalName}-${date:now:yyyyMMdd}.txt" />
<to uri="activemq:dummy" />
</when>
</choice>
</route>
</camelContext>
If you only need metadata :
Destination advisoryDestination = session.createTopic("ActiveMQ.Advisory.MessageConsumed.>");
MessageConsumer consumer = session.createConsumer(advisoryDestination);
consumer.setMessageListener(new MessageListener() {
#Override
public void onMessage(Message msg) {
System.out.println(msg);
System.out.println(((ActiveMQMessage) msg).getMessageId());
}
});
http://activemq.apache.org/advisory-message.html

Spring - ActiveMQ - Durable Subscription - Close Connection and Resubscribe to get the offline messages

I want to implement a solution in Spring-JMS with activeMQ where I want to create a durable subscription to a topic. The purpose is that if a subscriber closes the subscription for a while and once again recreates the durablesubscription with same client id and subscription name, the subscriber should receive all the messages which were delivered during the time subscription was closed.
I want to implement the following logic mentioned in the ORACLE URL for durable subscriptions: https://docs.oracle.com/cd/E19798-01/821-1841/bncgd/index.html
But I am unable to perform this using spring-jms. As per the URL I need to get messageConsumer instance and call close() on that method to stop receiving message temporarily from the topic. But I am not sure how to get it.
Following is my configuration. Kindly let me know how to modify the configuration to perform this.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms.xsd">
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"
p:userName="admin"
p:password="admin"
p:brokerURL="tcp://127.0.0.1:61616"
primary="true"
></bean>
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer" p:durableSubscriptionName="gxaa-durable1" p:clientId="gxaa-client1">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="adiTopic"/>
<property name="messageListener" ref="adiListener"/>
</bean>
<bean id="configTemplate" class="org.springframework.jms.core.JmsTemplate"
p:connectionFactory-ref="connectionFactory"
p:defaultDestination-ref="adiTopic" primary="true"
p:pubSubDomain="true">
</bean>
<bean id="adiTopic" class="org.apache.activemq.command.ActiveMQTopic" p:physicalName="gcaa.adi.topic"></bean>
<bean id="adiListener" class="com.gcaa.asset.manager.impl.AdiListener"></bean>
why not calling DefaultMessageListenerContainer.stop(); to stop the container and consumers ?
you can inject jmsContainer to another bean and close it when you want and call start() later.
all messages sent to the broker when your durable consumer is offline will be stored until it reconnect.
to make subscription durables you need to add this to jmsContainer bean
<property name="subscriptionDurable" value="true" />
<property name="cacheLevel" value="1" />
you can add a subscriptionName or the class name of the specified message listener will be used.
You can add a clientID to the connectionFactory
<property name="clientID" value="${jms.clientId}" />
or use
<bean class="org.springframework.jms.connection.SingleConnectionFactory"
id="singleConnectionFactory">
<constructor-arg
ref="connectionFactory" />
<property name="reconnectOnException" value="true" />
<property name="clientId" value="${jms.clientId}" />
</bean>
and update jmsContainer
<bean id="jmsContainer"
class="org.springframework.jms.listener.DefaultMessageListenerContainer"
p:durableSubscriptionName="gxaa-durable1" p:clientId="gxaa-client1">
<property name="connectionFactory" ref="singleConnectionFactory" />
<property name="destination" ref="adiTopic" />
<property name="messageListener" ref="adiListener" />
<property name="subscriptionDurable" value="true" />
<property name="cacheLevel" value="1" />
</bean>
UPDATE :
if your adiListener implements org.springframework.jms.listener.SessionAwareMessageListener it have to define method onMessage(M message, Session session) and when you have the session you can call javax.jms.Session.unsubscribe(String subscriptionName)
subscriptionName is defined above and can be injected to this bean or the class name of the specified message listener can be used.

Fanout exchange behaving as Direct exchange in Spring AMQP

I am facing an issue while using RabbitMQ in the fanout exchange which due to some unknown reason is behaving like a direct exchange.
I am using a following binding and queue configuration
<bean id="testfanout"
class="com.test">
<constructor-arg name="exchange" ref="test" />
<constructor-arg name="routingKey" value="test" />
<constructor-arg name="queue" value="testQ" />
<constructor-arg name="template">
<bean class="org.springframework.amqp.rabbit.core.RabbitTemplate">
<constructor-arg ref="connectionFactory" />
</bean>
</constructor-arg>
<constructor-arg value="true"/>
</bean>
<rabbit:fanout-exchange name="test" id="test">
<rabbit:bindings>
<rabbit:binding queue="test"/>
</rabbit:bindings>
</rabbit:fanout-exchange>
Now we have a same code listening to same testQ on two different VM's but somehow message is send to one VM listener using the round robin algorithm
Sender code
channel = ...
RabbitTemplate template = null;
if(channel != null){
template = channel.getTemplate();
if(template != null){
template.setQueue(channel.getQueue());
template.setExchange(channel.getExchange().getName());
template.convertAndSend(channel.getRoutingKey(), txtMsg);
The routing key is ignored for a fanout exchange.
Are you sure it's actually a fanout exchange in rabbitmq? I don't see a RabbitAdmin in your configuration (which would attempt to declare the exchange and binding).
Look at your exchange in the RabbitMQ UI and check the type/bindings.

Configuring Consumer Cancellation in RabbitMQ

We are using an 2-Node active-active RabbitMQ cluster with mirrored queue. With the mirroring policy being :
"policies":[{"vhost":"/","name":"ha-all","pattern":"","apply->to":"all","definition":{"ha-mode":"all","ha-sync-mode":"automatic"},"priority":0}]
Versions : RabbitMQ 3.5.4, Erlang 17.4 , spring-amqp/spring-rabbit :1.4.5.RELEASE
Now,we are trying to achieve consumer cancellation,as mentioned in Highly Available Queues.
However,since we have not used channel,we can't use {{basicConsumer}} method as given in the above link.
How do I set,"x-cancel-on-ha-failover" to true in the configuration,itself?
With the beans xml being thus :
<rabbit:connection-factory id="connectionFactory"
addresses="localhost:5672"
username="guest"
password="guest"
channel-cache-size="5" />
<!-- CREATE THE JsonMessageConverter BEAN -->
<bean id="jsonMessageConverter" class="org.springframework.amqp.support.converter.JsonMessageConverter" />
<!-- Spring AMQP Template -->
<rabbit:template id="amqpTemplate" connection-factory="connectionFactory" retry-template="retryTemplate" message-converter="jsonMessageConverter" />
<!-- in case connection is broken then Retry based on the below policy -->
<bean id="retryTemplate" class="org.springframework.retry.support.RetryTemplate">
<property name="backOffPolicy">
<bean class="org.springframework.retry.backoff.ExponentialBackOffPolicy">
<property name="initialInterval" value="500" />
<property name="multiplier" value="2" />
<property name="maxInterval" value="30000" />
</bean>
</property>
</bean>
<rabbit:queue name="testQueue" durable="true">
<rabbit:queue-arguments>
<entry key="x-max-priority">
<value type="java.lang.Integer">10</value>
</entry>
</rabbit:queue-arguments>
</rabbit:queue>
<bean id="messsageConsumer" class="consumer.RabbitConsumer">
</bean>
<rabbit:listener-container
connection-factory="connectionFactory" concurrency="5" max-concurrency="5" message-converter="jsonMessageConverter">
<rabbit:listener queues="testQueue" ref="messsageConsumer" />
</rabbit:listener-container>
The <rabbit:listener-container> actually populates a SimpleMessageListenerContainer bean on background. And the last one supports public void setConsumerArguments(Map<String, Object> args) on the matter.
So, to fix your requirements you just need to build the raw SimpleMessageListenerContainer <bean> for your messsageConsumer.
Meanwhile you are fixing that for your application, I'd ask you for the JIRA regarding adding <consumer-arguments> component. And we may be able to address it with the current GA deadline.

Receiving RabbitMQ message from one channel, set the messageId in the transformer and send it to other channel using Spring Integration

I'm new to RabbitMQ and Spring Integration.
I have a use case to consume JSON message from a channel, convert it to an object. One of the field that I need to set in the object is the message Id(delivery.getEnvelope().getDeliveryTag()) of the message that we receive from rabbitMQ which we need for ack handling after all the business logic.
How to do it using spring integration?
Here is my xml configuration.
<bean id="devRabbitmqConnectionFactory" class="com.rabbitmq.client.ConnectionFactory">
<property name="brokerURL" value="#{props[rabbitmq_inputjms_url]}" />
<property name="redeliveryPolicy" ref="redeliveryPolicy" />
</bean>
<bean id="devJMSCachingConnectionFactory"
class="org.springframework.amqp.rabbit.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="devRabbitmqConnectionFactory" />
<property name="sessionCacheSize" value="10" />
<property name="cacheProducers" value="false" />
</bean>
<int-jms:channel id="devJMSChannel" acknowledge="transacted"
connection-factory="devJMSCachingConnectionFactory" message-driven="false"
queue-name="devJMSChannel">
</int-jms:channel>
<bean id="redeliveryPolicy" class="org.apache.activemq.RedeliveryPolicy">
<property name="initialRedeliveryDelay" value="5000" />
<property name="maximumRedeliveries" value="5" />
</bean>
<int:transformer id="devObjectTransformer" input-channel="devJMSChannel" ref="devService" method="readEventFromRabbitMQ"
output-channel="devPacketChannel">
<int:poller fixed-rate="10" task-executor="devObjectTransformerExecutor" />
</int:transformer>
The transformer method "readEventFromRabbitMQ" gets the message String from msg.getPayload() converts it into object and sends it to the output channel. But not sure how to get the message Id in the transformer class. Can somebody help me with this?
public List<DevEventRecord> readEventFromRabbitMQ(Message<EventsDetail> msg){
DevEventRecord[] eventRecords=null;
EventsDetail expEvent = null;
long receivedTime =System.currentTimeMillis();
int packetId = -1;
try{
monitorBean.incrementDeviceExceptionPacketCount();
expEvent = msg.getPayload();
LogUtil.debug("readExceptionEvent :: consumed JMS Q "+expEvent);
eventRecords = dispatchPacket(expEvent);
}
catch(ProcessingException pe){
notifyAck(expEvent.getUniqueId(),,,,);
}
catch(Exception ex){
notifyAck(expEvent.getUniqueId(),,,,);
LogUtil.error("Exception occured while reading object in readEvent , "+ex.toString());
}
return getEventRecordList(eventRecords);
}
The deliveryTag is presented as message header after an <int-amqp:inbound-channel-adapter> under the key AmqpHeaders.DELIVERY_TAG.
I don't understand why you mix AMQP and JMS, but anyway those channel implementations don't populate headers from received message. It is out of their responcibity.
Please, use <int-amqp:inbound-channel-adapter> and here is a sample how to ack message manually using deliveryTag header.