Redelivery delay settings doesn't work on ActiveMQXAConnectionFactory - apache-camel

I've been trying to configure a jms endpoint (activemq 5.7) in my camel context (camel 2.8) so it should use redelivery of messages on rollback. Unfortunately, it doesn't work as I've expected: it returns back a message to a queue, but a consumer takes the message in despite of specified delay parameters immediately after that.
My configuration is the following:
...
<bean id="jmstx" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="configuration" ref="jmsTxConfig" />
</bean>
<bean id="jmsTxConfig" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory" ref="jmsXaPoolConnectionFactory"/>
<property name="transactionManager" ref="osgiPlatformTransactionManager"/>
<property name="transacted" value="false"/>
<property name="cacheLevelName" value="CACHE_NONE"/>
<property name="concurrentConsumers" value="${jms.concurrentConsumers}" />
</bean>
<bean id="jmsXaPoolConnectionFactory" class="org.apache.activemq.pool.XaPooledConnectionFactory">
<property name="maxConnections" value="${jms.maxConnections}" />
<property name="connectionFactory" ref="jmsXaConnectionFactory" />
<property name="transactionManager" ref="osgiJtaTransactionManager" />
</bean>
<bean id="jmsXaConnectionFactory" class="org.apache.activemq.ActiveMQXAConnectionFactory">
<property name="brokerURL" value="${jms.broker.url}"/>
<property name="redeliveryPolicy">
<bean class="org.apache.activemq.RedeliveryPolicy">
<property name="maximumRedeliveries" value="-1"/>
<property name="initialRedeliveryDelay" value="2000" />
<property name="redeliveryDelay" value="5000" />
</bean>
</property>
</bean>
A small sample that demonstrates how I'm using the endpoint:
<route id="main-route">
<from uri="jmstx:queue:my-queue" />
<to uri="direct:subroute" />
...
<!-- some logic -->
</route>
<route id="subroute">
<from uri="direct:subroute" />
<transacted ref="PROPAGATION_MANDATORY"/>
...
<!-- Rollback on some condition -->
<rollback/>
...
</route>
Does anyone knows why it happens?
Thank you.

Yes, I encountered same problem.
After hacking the ActiveMQ source code, and adding some logging information, I found some evidences and googled the solution.
Just like the answer said:
https://developer.jboss.org/thread/266172
After setting cacheLevel to CACHE_CONSUMER on the activemq component, solved the problem.
ex:
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory" ref="jmsPooledConnectionFactory" />
<property name="concurrentConsumers" value="1"/>
<property name="transacted" value="true"/>
<property name="cacheLevelName" value="CACHE_CONSUMER"/>
<property name="acknowledgementModeName" value="SESSION_TRANSACTED"/>
</bean>

Related

Camel client acknowledge mode is not working

I'm creating a Spring Boot application using Apache Camel to connect to ActiveMQ and IBM MQ. I'm using the JMS component to connect the MQ. I'm using the acknowledgement as CLIENT_ACKNOWLEDGE so any exception or application crash message should retain in MQ. But when I start the application and as soon as the message received in MQ it's getting cleared. Below is my configuration
<bean class="org.springframework.jms.core.JmsTemplate" id="jmsTemplate">
<property name="connectionFactory" ref="connectionFactory" />
<property name="receiveTimeout" value="1000" />
</bean>
<bean id="connectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="jmsConnectionFactory" />
<property name="cacheConsumers" value="true" />
<property name="reconnectOnException" value="true" />
<property name="sessionCacheSize" value="100"/>
</bean>
<bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
</bean>
<bean id="config" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="transacted" value="true"/>
<property name="cacheLevelName" value="CACHE_CONSUMER"/>
<property name="acknowledgementModeName" value="CLIENT_ACKNOWLEDGE"/>
</bean>
<bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="configuration" ref="config" />
</bean>
Please help me what's need to be added to achieve the functionality.

How to stop consuming messages in a camel route when destination unavailable

I'm bridging IBM and ActiveMQ queues with Camel.
Everything works ok, but when destination becomes unvailable because of network problem all the processed messages get lost.
Camel should stop consuming from origin if destination is unavailable and start again when it becomes available, but I don't find correct pattern/configuration to do that.
I'm configuring routes like following one from IBM MQ to IBM MQ, but I'm looking for a way to solve any combination of IBM and ActiveMQ.
<bean id="lsywmq" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory">
<bean class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="transportType" value="1" />
<property name="hostName" value="${mq_lsy_hostname}" />
<property name="port" value="${mq_lsy_port}" />
<property name="queueManager" value="${mq_lsy_queuemanager}" />
<property name="channel" value="${mq_lsy_channel}" />
<property name="useConnectionPooling" value="true" />
</bean>
</property>
</bean>
<bean id="wmq" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory">
<bean class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="transportType" value="1" />
<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="useConnectionPooling" value="true" />
</bean>
</property>
</bean>
<camelContext xmlns="http://camel.apache.org/schema/blueprint"
allowUseOriginalMessage="false" >
<route id="EFF2AVIO">
<from uri="lsywmq:queue:{{mq_lsy_queue}}"/>
<to uri="wmq:queue:{{mq_queue}}"/>
</route>
</camelContext>
I would like to get it working as a pipeline, if it get stuck at destination, all the pipe remains stucked.
You need to set the transacted property of the JMS connectionFactory to true.
This won't actually stop your consumer, but you'll get the Guaranteed Delivery EIP pattern implemented, so your messages wont't be lost any more.
From now on you could also provide redelivery policy for the ActiveMQ connectionFactory, something that will be relevant for you, e.g. a long redeliveryDelay
You may use JMSConfiguration object to configure JMS options like this
<bean id="wmq" class="org.apache.camel.component.jms.JmsComponent">
<property name="configuration" ref="jmsConfig"/>
</bean>
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="transacted" value="true"/>
<property name="connectionFactory" ref="myConnectionFactory"/>
</bean>
<bean id="myConnectionFactory" class="com.ibm.mq.jms.MQQueueConnectionFactory">
<property name="transportType" value="1" />
<property name="hostName" value="${mq_lsy_hostname}" />
<property name="port" value="${mq_lsy_port}" />
<property name="queueManager" value="${mq_lsy_queuemanager}" />
<property name="channel" value="${mq_lsy_channel}" />
<property name="useConnectionPooling" value="true" />
</bean>

How to make my transaction commit in ActiveMQ, when my process is done successfully at the client side

<route>
<from
uri="jms:queue:inBox?concurrentConsumers=20" />
<camel:process ref="processor1" />
<transacted/>
<camel:process ref="Processor2" />
</route>
once my Processor 2 is successful then the inbox queue has to de-queue the message from activeMQ. Currently it is dequeuing the messages at the JMS Endpoint.
<bean id="jmsTransactionManager"
class="org.springframework.jms.connection.JmsTransactionManager">
<property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>
<bean id="jmsConnectionFactory"
class="com.mypackage.EncryptionAwareActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
<property name="userName" value="admin" />
<property name="password" value="#PASSWORD#" />
</bean>
<bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory"
init-method="start" destroy-method="stop">
<property name="maxConnections" value="20" />
<property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory" ref="pooledConnectionFactory" />
<property name="concurrentConsumers" value="50" />
<property name="deliveryPersistent" value="true" />
<property name="transacted" value="true" />
<property name="transactionManager" ref="jmsTransactionManager" />
</bean>
<bean id="jms" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="configuration" ref="jmsConfig" />
</bean>
So I'm using this configuration like this and setting transacted as true And also i have used JMS transnational manager.
It's not enough to just add <transacted/> to your route. You also need to configure your JMS component to support transactions.
I suggest you read the documentation thoroughly.
Below is a relevant code example from the link:
<bean id="jmstx" class="org.apache.camel.component.jms.JmsComponent">
<property name="configuration" ref="jmsConfig" />
</bean>
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory" ref="jmsConnectionFactory"/>
<property name="transactionManager" ref="jmsTransactionManager"/>
<property name="transacted" value="true"/>
<property name="cacheLevelName" value="CACHE_CONNECTION"/>
</bean>
<bean id="jmsTransactionManager" class="org.springframework.jms.connection.JmsTransactionManager">
<property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>
<bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://broker1?brokerConfig=xbean:tutorial/activemq.xml"/>
</bean>

JMS Transaction simple example

I try to implement a simple jms transaction test. I want to check that my message is left in my ActiveMQ broker's queue after an exception occurs in my route.
In this test, the exception occurs after a new message is posted in the queue but the message is not in the queue any longer as I was expecting. What I'am missing ? blueprint code below
<bean id="jmstx" class="org.apache.camel.component.jms.JmsComponent">
<property name="configuration" ref="jmsConfig" />
</bean>
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory" ref="jmsConnectionFactory" />
<property name="transactionManager" ref="jmsTransactionManager" />
<property name="transacted" value="true" />
<property name="cacheLevelName" value="CACHE_CONNECTION" />
</bean>
<bean id="jmsTransactionManager"
class="org.springframework.jms.connection.JmsTransactionManager">
<property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>
<bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://localhost:61616" />
<property name="userName" value="smix" />
<property name="password" value="smix" />
<property name="redeliveryPolicy">
<bean class="org.apache.activemq.RedeliveryPolicy">
<property name="maximumRedeliveries" value="0" />
</bean>
</property>
</bean>
<bean id="myEx" class="java.lang.Exception" />
<camelContext id="ctx_m27" xmlns="http://camel.apache.org/schema/blueprint">
<route id="rt_m27_01" trace="false" autoStartup="true">
<from uri="jmstx:queue:Q.m27_IN" />
<throwException ref="myEx" />
<to uri="jmstx:queue:Q.m27_OUT" />
</route>
</camelContext>
You have configured the broker to not use redelivery, eg
<property name="maximumRedeliveries" value="0" />
So when the transaction fails, the message is not redelivered, and then the broker moves the message to its dead letter queue (DLQ). So the message is there.
You can read more about the DLQ in ActiveMQ here: http://activemq.apache.org/message-redelivery-and-dlq-handling.html
Thanks to Claus answer, I've changed redeliveryPolicy to :
<property name="redeliveryPolicy">
<bean class="org.apache.activemq.RedeliveryPolicy">
<property name="maximumRedeliveries" value="-1" />
<property name="initialRedeliveryDelay" value="2000" />
<property name="redeliveryDelay" value="60000" />
<property name="useExponentialBackOff" value="false"/>
</bean>
</property>
In fact, cacheLevelName parameter need also to be changed to :
<property name="cacheLevelName" value="CACHE_CONSUMER" />
Default value (CACHE_AUTO), CACHE_NONE and CACHE_SESSION causes strangely redelivery delay to 0.

Aries Blueprint in Karaf - Can a blueprint reference an external properties file

I am using an ActiveMQ blueprint to setup a JMS Connection Pool.
I also use Camel to service some functionality.
I use the org.apache.camel.spring.spi.BridgePropertyPlaceholderConfigurer to allow the use of an external properties file in setting up the camel-context file.
Is there a similar type functionality using blueprints?
So basically, I want to replace ${server.address} with a property I get from a property file in the configuration below:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.0.0"
xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0"
xmlns:amq="http://activemq.apache.org/schema/core">
<bean id="activemqConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL"
value="nio://${server.address}" />
</bean>
<bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory">
<property name="maxConnections" value="8" />
<property name="connectionFactory" ref="activemqConnectionFactory" />
</bean>
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory" ref="pooledConnectionFactory" />
<property name="concurrentConsumers" value="5" />
</bean>
<bean id="resourceManager" class="org.apache.activemq.pool.ActiveMQResourceManager"
init-method="recoverResource">
<property name="transactionManager" ref="transactionManager" />
<property name="connectionFactory" ref="activemqConnectionFactory" />
<property name="resourceName" value="activemq.localhost" />
</bean>
<bean id="xaConnectionFactory" class="org.apache.activemq.ActiveMQXAConnectionFactory">
<argument value="nio://${server.address}" />
</bean>
<bean id="connectionFactory" class="org.fusesource.jms.pool.JcaPooledConnectionFactory"
init-method="start" destroy-method="stop">
<property name="connectionFactory" ref="pooledConnectionFactory" />
<property name="name" value="activemq" />
</bean>
<reference id="transactionManager" interface="javax.transaction.TransactionManager" />
<service ref="pooledConnectionFactory" interface="javax.jms.ConnectionFactory">
<service-properties>
<entry key="name" value="localhost" />
</service-properties>
</service>
</blueprint>
You can use System properties and/or configuration via the config admin:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
xmlns:ext="http://aries.apache.org/blueprint/xmlns/blueprint-ext/v1.0.0">
<!-- Allow the use of system properties -->
<ext:property-placeholder placeholder-prefix="$[" placeholder-suffix="]" />
<!--
config admin properties from etc/com.example.config.cfg
-->
<cm:property-placeholder persistent-id="com.example.config" update-strategy="reload">
<cm:default-properties>
<cm:property name="configDir" value="$[karaf.base]/my-config" />
</cm:default-properties>
</cm:property-placeholder>
<bean id="config" class="com.example.Config">
<property name="rootDir" value="${configDir}" />
<property name="someSysProp" value="$[someSysProp]" />
</bean>
</blueprint>
The "ext:property-placeholder" element allows you to use system properties (like karaf.base in the example) via the defined placeholder pre- and suffix, but this is optional. If you only need your own configuration you can provide it via a file in etc/
etc/com.example.config.cfg and reference it via the persistence-id.

Resources