Apache camel: Multiple executions with onCompletion mode="BeforeConsumer" - apache-camel

Sorry for my english, I'll make my best effort for write this.
I have a xml file like this:
<routes>
<route id="id1">
<from uri="timer://foo?fixedRate=true&period=60s"/>
<onCompletion mode="BeforeConsumer">
<log message="finish"/>
</onCompletion>
<setBody>
<simple>Probando 1</simple>
</setBody>
<log message="working1"/>
<to uri="direct:test1"/>
</route>
<route id="id2">
<from uri="direct:test1"/>
<setBody>
<simple>Test2</simple>
</setBody>
<log message="working2"/>
<to uri="direct:test2"/>
</route>
<route id="id3">
<from uri="direct:test2"/>
<setBody>
<simple>Test3</simple>
</setBody>
<log message="working3"/>
</route>
</routes>
if remove mode="BeforeConsumer" from the first route i get one "finish" in the log, but if i use the xml with mode="BeforeConsumer" i get the message 3 times (one per each route). Is that the behavior expected?
I need that the onCompletion code runs one time but i need too the mode="BeforeConsumer"

Related

Multi File Download Not Working in Apache Camel

I am using quartz and poll enrich to download multiple files at once , one of the configuration used is localWorkDirectory=/tmp/sftp_tmp/ . I see in logs that says
org.apache.camel.component.file.remote.SftpConsumer.processExchange - About to process file: RemoteFile[filename] . But it randomly sometimes places the file in /tmp/sftp_tmp/ instead of the at final destination c:/cameldata at times. I am using camel version 2.24.1
<route id="sftp-read">
<from uri="quartz2://sftp?stateful=true&trigger.repeatInterval=3s"/>
<pollEnrich><spel>file:c:/cameldata?include=SFTPRECFILE&noop=true&idempotent=false&readLock=markerFile</spel></pollEnrich>
<to uri="seda:startSftpExecutor?waitForTaskToComplete=Always&timeout=-1"/>
</route>
<route id="sftp-executor">
<from uri="seda:startSftpExecutor" />
<pollEnrich><spel>{{sftpUrl}}&sendEmptyMessageWhenIdle=true&binary=false&include=BR-.*&maximumReconnectAttempts=3&noop=true&maxMessagesPerPoll=1&localWorkDirectory=/tmp/sftp_tmp/&stepwise=false</spel></pollEnrich>
<choice>
<when>
<simple>${in.body} != null</simple>
<to uri="file:c:/cameldata?fileName=${file:onlyname}&tempPrefix=.tmp"/>
</when>
<otherwise>
<setBody><constant></constant></setBody>
<log loggingLevel="INFO" message="file(s) downloaded successfully" />
<to uri="file:c:/cameldata?fileName=LOADFILE" />
</otherwise>
</choice>
</route>

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.

How to create a timer based camel polling route?

i created a route that is supposed to read from an OPC-UA endpoint. The read operation should be performed every second, based on a timer. Every example i found shows that the route can only have one from item. My route looks like this:
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
<route id="opctorest">
<from uri="timer://simpleTimer?period=1000"/>
<log message="Triggered Route: opctorest: Sensorreading body: ${body}"/>
<to uri="milo-client:tcp://0.0.0.0:4840/freeopcua/server?nodeId=2&namespaceUri=http://examples.freeopcua.github.io"/>
<convertBodyTo type="java.lang.String"/>
<to uri="stream:out"/>
</route>
</camelContext>
When i deploy the route, it gets called every second, but it writes to the endpoint, since the call is declared in a to element. How can i turn this into a read? I could not find a solution so far. Thanks!
Use the .enrich() to turn it into a read when you want to read in the middle of a route.
http://camel.apache.org/content-enricher.html
For your example something similar to (not tested):
<camelContext xmlns="http://camel.apache.org/schema/blueprint">
<route id="opctorest">
<from uri="timer://simpleTimer?period=1000"/>
<log message="Triggered Route: opctorest: Sensorreading body: ${body}"/>
<enrich uri="milo-client:tcp://0.0.0.0:4840/freeopcua/server?nodeId=2&namespaceUri=http://examples.freeopcua.github.io"/>
<convertBodyTo type="java.lang.String"/>
<to uri="stream:out"/>
</route>
</camelContext>

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>

How to process the same message sequentially in Camel?

I am new to Camel and I am trying to do the following.
I want to process the same message twice. I have to transform both messages and process one message first and then second only if the first one is sucessfully executed (I have a condition).
I tried to use a multicast first. Then I transform the messages in each route. The first one (the operation_DC) only sends a successfull message to the second one (the operation_AC) only if it is sucessfull. The second operation contains an aggregation that waits until it times out for both messages. I only want to process the messaged from the multicast and discard the other.
<route id="t_operation_ME">
<from uri="direct:operation_ME" id="t.direct.aggregatedService"></from>
<setHeader headerName="id">
<simple>exchangeId</simple>
</setHeader>
<multicast parallelProcessing="true" strategyRef="defaultAggregationStrategy" stopOnException="true" onPrepareRef="cleanHeader" parallelAggregate="false" completionPredicate="">
<to uri="direct:operation_DC"></to>
<to uri="direct:operation_AC"></to>
</multicast>
</route>
<route id="direct:operation_DC">
<from uri="direct:operation_DC" />
<log message="ENTER DC"></log>
<to uri="xslt:{{depasse:core.transformation.xml.path}}client/t/Operation_toDC_request.xsl" id="t.dc.transform.productos" />
<to uri="activemq:QCIn" id="t.dc.qcin.queue.send"></to>
<log message="EXIT DC ${body}"></log>
<choice>
<when>
<xpath>//Data/Status[. = 'OK']</xpath>
<log message="SEND TO AC"></log>
<to uri="direct:operation_AC"></to>
</when>
</choice>
</route>
<route id="direct:operation_AC">
<from uri="direct:operation_AC" />
<log message="ENTER AC"></log>
<aggregate completionTimeout="20000" completionSize="2" discardOnCompletionTimeout="true" strategyRef="tAggregationStrategy">
<correlationExpression>
<simple>header.id</simple>
</correlationExpression>
<log message="ENTER AGG AC ${body}"></log>
<to uri="xslt:{{depasse:core.transformation.xml.path}}client/t/Operation_toAC_request.xsl" id="t.ac.transform.productos" />
<to uri="activemq:QCIn" id="t.ac.qcin.queue.send"></to>
<log message="EXIT AC ${body}"></log>
</aggregate>
<log message="END AC\n${body}"></log>
</route>
The thing is that when I log "EXIT AC" and "END AC" the message is different. This means that while in the server I am watching the process correctly, the AC client receives an incorrect message.
Multicast send copy of messages always to every endpoint between tags. If you want to call route operation_AC only after operation_DC run successfully, you shouldn't use multicast, but you should save your body at the beginning of operation_DC, for example using property and Camel Simple:
<from uri="direct:operation_DC" />
<setProperty propertyName="body">
<simple>${body}</simple>
</setProperty>
and before calling operation_AC set body to bring message body from the beginning:
<setBody>
<simple>${property.body}</simple>
</setBody>
<to uri="direct:operation_AC"/>

Resources