Camel Content Based Routing - apache-camel

I'm new to camel, I want write a camel route in such a way that if the upper bean method returns "hi" then I have to call another route. But that is not happening in the below code. Please let me know the solution.
Here's my code :
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jaxws="http://cxf.apache.org/jaxws"
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd
http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route id="firstRoute">
<from uri="activemq:queue:test.queue" />
<doTry>
<to uri="bean:myBean?method=appendCamel(1,hell)" />
<log message="TEST LOG" />
<when>
<xpath>${in.body} = 'hi'</xpath>
<to uri="stream:out" />
</when>
<doCatch>
<exception>java.lang.Exception</exception>
</doCatch>
</doTry>
<to uri="stream:out" />
<to uri="bean:myBean?method=appendCamel2(34)" />
<to uri="stream:out" />
<to uri="direct:nextRoute" />
</route>
<route>
<from uri="direct:nextRoute" />
<to uri="stream:out" />
</route>
</camelContext>
<bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="vm://localhost?broker.persistent=false" />
</bean>
<bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="connectionFactory" ref="connectionFactory" />
</bean>
<bean id="myBean" class="Camel.CamelHelloWorldSid.MyBean" />

You should use the filter EIP: http://camel.apache.org/message-filter.html
<filter>
<simple>${in.body} == 'hi'</simple>
<to uri="direct:nextRoute" />
</filter>
If you need more than one predicate, then you need to use the content based router: http://camel.apache.org/content-based-router.html (which is like if .. elseif ... elseif ... else in Java)

<camelContext xmlns="http://camel.apache.org/schema/spring">
<route id="firstRoute">
<from uri="activemq:queue:test.queue" />
<doTry>
<to uri="bean:myBean?method=appendCamel(1,hell)" />
<log message="TEST LOG" />
<when>
<simple>${in.body} == 'hi'</simple>
<to uri="direct:nextRoute" />
</when>
<doCatch>
<exception>java.lang.Exception</exception>
</doCatch>
</doTry>
<to uri="stream:out" />
<to uri="bean:myBean?method=appendCamel2(34)" />
<to uri="stream:out" />
<to uri="direct:nextRoute" />
</route>
<route>
<from uri="direct:nextRoute" />
<to uri="stream:out" />
</route>
</camelContext>

Related

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>

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.

Redelivery delay settings doesn't work on ActiveMQXAConnectionFactory

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>

Log4net SmtpAppender configuration to receive chunks of log

I would like to save everything in a log file and receive mails containing 512 messages per mail.
RollingFileAppender works fine. But in emails I receive sometimes 1 message in mail, sometimes 4 messages (not the same ones).
I tried MemoryAppender too. There I have also 1-2 messages in queue, nothing more.
I tried "lossy" SmtpAppender, the result is same. What should I change?
My configuration is here
<log4net>
<appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
<file value="c:\temp\log-" />
<bufferSize value="0" />
<appendToFile value="true" />
<rollingStyle value="Composite" />
<param name="DatePattern" value="yyyy.MM.dd'.log'" />
<maxSizeRollBackups value="-1" />
<maximumFileSize value="10MB" />
<staticLogFileName value="false" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %-5level - %message%newline" />
</layout>
</appender>
<appender name="SmtpAppender" type="log4net.Appender.SmtpAppender">
<bufferSize value="512" />
<lossy value="false" />
<authentication value="Basic" />
<to value="test#test.com" />
<from value="test#test.com" />
<username value="test#test.com" />
<password value="xxxxx" />
<subject value="My subject" />
<smtpHost value="mail.test.com" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%date %-5level - %message%newline" />
</layout>
</appender>
<root>
<level value="ALL" />
<appender-ref ref="RollingLogFileAppender" />
<appender-ref ref="SmtpAppender" />
</root>
</log4net>
The following appender config works for me (I get 6 log messages at a time):
<appender name="SmtpAppender" type="log4net.Appender.SmtpAppender">
<to value="name#server.com" />
<from value="name#server.com" />
<subject value="Log Email" />
<smtpHost value="email.test.com" />
<bufferSize value="5" />
<lossy value="false" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%property{log4net:HostName} :: %date %level %logger [%thread] - %message%newline" />
</layout>
</appender>
Do your log messages have multiple lines perhaps?

Resources