Request only messaging with direct-vm in camel routes - apache-camel

How can we process direct-vm asynchronously?
I have the following Camel routes definition:
<route id="routeA">
<from uri="activemq:queue:queueA" />
<to uri="direct-vm:someProcessing" />
<to uri="direct-vm:processAsync" />
</route>
<route id="routeB">
<from uri="direct-vm:processAsync">
<threads executorServiceRef="someRef">
<inOnly uri="direct-vm:timeTakingRoute" />
<threads>
<route>
When queue consumer consumes message and sends to routeB and calls direct-vm:timeTakingRoute using threads DSL, caller thread which is queueA still waits until thread created using thread DSL completes.
How can we process this asynchronously (Caller thread should not wait until thread created using thread DSL completes)?

Direct component is designed to be synchronous http://camel.apache.org/direct-vm.html
Please try to use seda instead:
<route id="routeA">
<from uri="activemq:queue:queueA" />
<to uri="direct-vm:someProcessing" />
<inOnly uri="seda:processAsync" />
</route>
<route id="routeB">
<from uri="seda:processAsync" />
<to uri="direct-vm:timeTakingRoute" />
</route>

You can either choose seda or vm component.
When using seda, note that queues are only visible within a it's CamelContext.
If you want inter-communication between CamelContext, use vm component, which is an extension to seda component.
Read more: seda vm

Related

How can I make only one instance of an application receive each AMQP topic message (consumer group behaviour)

I am using Apache Camel's AMQP component to listen to messages from an ActiveMQ Artemis topic.
This application is run on Kubernetes with two replicas.
I've configured a durable subscription with a unique clientId per pod and a common subscription name:
<route autoStartup=true" id="myRoute">
<from id="_amqp_topic" uri="amqp:topic:xxx?connectionFactory=#amqpCF&disableReplyTo=true&transacted=false&subscriptionDurable=true&clientId={{container-id}}&durableSubscriptionName=eventSubscription"/>
<log loggingLevel="INFO" message="Received event: ${body}"/>
...
</route>
The problem is both pods receive the message, when only one of them should. I'm trying to achieve something similar to Kafka's consumer groups, where only one member of the group receives each message.
If you want only one subscriber to receive the message then the subscribers must share the same subscription. Therefore, you need to:
set subscriptionShared=true in your _amqp_topic uri
use the same client ID (i.e. don't use {{container-id}})
For example:
<route autoStartup=true" id="myRoute">
<from id="_amqp_topic" uri="amqp:topic:xxx?connectionFactory=#amqpCF&disableReplyTo=true&transacted=false&subscriptionDurable=true&clientId=myClientID&durableSubscriptionName=eventSubscription&subscriptionShared=true"/>
<log loggingLevel="INFO" message="Received event: ${body}"/>
...
</route>
Another option would simply to use a queue instead of a topic, e.g.:
<route autoStartup=true" id="myRoute">
<from id="_amqp_queue" uri="amqp:queue:xxx?connectionFactory=#amqpCF&disableReplyTo=true&transacted=false"/>
<log loggingLevel="INFO" message="Received event: ${body}"/>
...
</route>

Calling route after previous one finishes in camel context

I'm writing one camel application using blueprint.I have two routes which calls same class beans but for different case(Handled that in class based on route id).I want to start second route only when first route completes it's execution(sequentially instead of parallel).So is there any way to do the same.Following is my code-
<camelContext id="test"
xmlns="http://camel.apache.org/schema/blueprint">
<route id="1">
<from uri="timer"/>
<to uri="bean:test"/>
</route>
<route id="2">
<from uri="timer"/>
<to uri="bean:test"/>
</route>
</camelContext>
Thanks
Routes are started when camel context is bootstrapped. You're probably looking for the exchange to be routed to "route2" after it's been processed by "route1"
That sounds like the same route. You have a few options for reusing a bean while implementing different behavior. The easiest one IMO from where you are is using different methods:
class TestBean {
void test1(){}
void test2(){}
}
Then changing your route config:
<route id="1">
<from uri="timer"/>
<to uri="bean:test?method=test1"/>
<to uri="bean:test?method=test2"/>
</route>
Of course you can change to make these be different beans...

How to add scheduler and custome checks around camel file processing

Before explaining my problem I would state here that I am completely new to camel file processing. I have a requirement to read the file from a directory do some processing and delete them. This was a very high level requirement and I am able to achieve this using camel. But now I've got some new requirements as stated below. Need help on that.
Create this application as a job and trigger it by reading another directory where some specific files would be dropped other wise it should kicked of by its own every 15-20 minutes.
Before triggering the actual application make sure that the directory has some specific number of files present (say 25 files)
If all files are present - execute a method to create a unique tracking ID for all these 25 files. If I have a unique ID how can I make it available through multiple routes?
As of now I have tried implementing routepolicy but since I have never used it earlier I need some guidance so that I can go ahead with this.
1. Separe your route logic from route triggering
<route id="TriggerFromFile">
<from uri="file:triggerFolder" />
<log message="Triggered from file" />
<to uri="direct:startLogic" />
<route>
<route id="TriggerFromTimer">
<from uri="timer:triggerTimer?period=15m" />
<log message="Triggered from timer" />
<to uri="direct:startLogic" />
</route>
<route id="Logic>
<from uri="direct:startLogic" />
<to uri="..." />
</route>
2. Count the number of files and use that as a filter
Define a bean that counts the number of files in the dir, set that number
as body and validate using a filter.
<route id="TriggerFromFile">
<from uri="file:triggerFolder" />
<log message="Triggered from file" />
<to uri="direct:countFile" />
<route>
<route id="TriggerFromTimer">
<from uri="timer:triggerTimer?period=15m" />
<log message="Triggered from timer" />
<to uri="direct:countFile" />
</route>
<route id="FileCount">
<from uri="direct:countFile" />
<to uri="bean:countFilesInDir" />
<log message="There are ${body} files the directory" />
<filter>
<simple>${body} >= 25</simple>
<to uri="direct:startLogic" />
</filter>
</route>
<route id="Logic">
<from uri="direct:startLogic" />
<to uri="..." />
</route>
3. Set a Header to your Exchange's message before sending it to other routes
When Camel sends an Exchange between routes, headers and properties are copied.
Calculate a unique id in some way (concatenate file names, md5 of content, file modification timestamp....) and set it in a Header.
An header can hold any java object.

rabbitMQ message forwarding from one queue to another

Could you please help me to resolve the problem I am facing?
When forwarding message from one queue to another ( no message seems to be published ) ,
steps:
timer publish current date to queue 'inbox'- this part working
from 'inbox' to 'outbox' - this is not working
from 'outbox' to print console
Follows the spring xml.
<route>
<camel:from
uri="rabbitmq://localhost:5672/outBox?sername=guest&password=guest" />
<camel:to uri="stream:out" />
</route>
<route>
<camel:from
uri="rabbitmq://localhost:5672/inbox?username=guest&password=guest" />
<camel:to
uri="rabbitmq://localhost:5672/outBox?username=guest&password=guest" />
</route>
<route>
<camel:from uri="timer:foo?period=10" />
<setBody>
<simple>${body}Message at ${date:now:yyyy-MM-dd HH:mm:ss}</simple>
</setBody>
<to
uri="rabbitmq://localhost:5672/inbox?username=guest&password=guest" />
</route>
According to http://camel.apache.org/rabbitmq.html the URI format is
rabbitmq://hostname[:port]/exchangeName?[options]
Thus inbox in your code is not queue name but exchange.
Use queue= option to specify the queue

camel when/otherwise for activemq and seda

I have a route where I'd like to compute if "from" is activemq or something else. I found replaceFromWith, but it seems to be used for testing only. I tried using camel's choice when/otherwise to switch between "from activemq" and "from seda", but that errors out as invalid syntax. I'm looking for something that would do the following:
<route id="doPost">
<choice>
<when>
<groovy>exchange.getIn().getHeader("sometest") != null</groovy>
<from uri="activemq:doPost?..."/>
</when>
<otherwise>
<from uri="seda:doPost?....."/>
</otherwise>
</choice>
Thanks in advance, appreciate the help.
You cannot dynamically select a consumer based on the exchange, the exchange wont exist before consuming it using the <from .../> tag.
Instead, you can construct a route for each consumer, normalize the exchange you receive from different types of consumers, and forward them to a common route for processing.
<route id="activemqConsumerRoute">
<from uri="activemq:doPost?..."/>
<!-- normalize the exchange to be understandable by the next route -->
<to uri="direct:commonProcessingRoute" />
</route>
<route id="sedaConsumerRoute">
<from uri="seda:doPost?....."/>
<!-- normalize the exchange to be understandable by the next route -->
<to uri="direct:commonProcessingRoute" />
</route>
<route id="commonProccessingRoute">
<from uri="direct:commonProcessingRoute" />
<!-- whatever business logic you have -->
</route>

Resources