Restlet with Same Path and Different Verbs - apache-camel

I have the following interesting situation. I have one path with three verbs: GET, DELETE, POST. They correspond to three routes in Camel context. My observation is that if the three routes are in the same Camel Context, every works well. But if the routes are in different camel contexts, only one of them works. So far, I noticed that DELETE wworks and the two others stop working. My example context is below:
<camel:camelContext id="get-test" autoStartup="true">
<camel:route>
<camel:from uri="restlet:/path?restletMethod=DELETE"></camel:from>
<camel:transform>
<camel:constant>Hi Delete</camel:constant>
</camel:transform>
</camel:route>
<camel:route>
<camel:from uri="restlet:/path?restletMethod=GET"></camel:from>
<camel:transform>
<camel:constant>Hi Get</camel:constant>
</camel:transform>
</camel:route>
<camel:route>
<camel:from uri="restlet:/path?restletMethod=POST"></camel:from>
<camel:transform>
<camel:constant>Hi Post</camel:constant>
</camel:transform>
</camel:route>
</camel:camelContext>
So, the above is the working scenario. The scenario that does not work is below with three different contexts:
<camel:camelContext id="delete-test" autoStartup="true">
<camel:route>
<camel:from uri="restlet:/path?restletMethod=DELETE"></camel:from>
<camel:transform>
<camel:constant>Hi Delete</camel:constant>
</camel:transform>
</camel:route>
</camel:camelContext>
<camel:camelContext id="get-test" autoStartup="true">
<camel:route>
<camel:from uri="restlet:/path?restletMethod=GET"></camel:from>
<camel:transform>
<camel:constant>Hi Get</camel:constant>
</camel:transform>
</camel:route>
</camel:camelContext>
<camel:camelContext id="post-test" autoStartup="true">
<camel:route>
<camel:from uri="restlet:/path?restletMethod=POST"></camel:from>
<camel:transform>
<camel:constant>Hi Post</camel:constant>
</camel:transform>
</camel:route>
</camel:camelContext>
Maybe I am missing something in the camel spec that forbid this kind of configuration?

Yes this is not supported. The logic that selects the route to process the message only uses the context path as part of the logic.
Not sure how easy it would be to add restletMethod as well as part of that selection logic. Feel free to log a JIRA ticket, and dive into the code to contribute. We love contributions:
http://camel.apache.org/contributing

Related

camel: use header value in multiple routes

Is it possible to set a header in one route and then use it again later in another route in the same context?
For example, I have a route that sets a header as so:
<setHeader headerName="clientId">
<xpath>/Alarm/clientid/text()</xpath>
</setHeader>
The route uses Dead Letter Channel to send the message to an error route in the same context when delivery fails and I would like to use the clientId header I defined above in the message the error route sends, but currently calling the header returns nothing (an empty string I assume).
It should work , I am using camel 2.15.1 ,check the code below , if you run it you should see the header
<bean id="mybean" class="java.lang.Exception" />
<camel:camelContext xmlns="http://camel.apache.org/schema/spring"
trace="false">
<camel:errorHandler id="deadLetterErrorHandler"
type="DeadLetterChannel" deadLetterUri="direct:b">
</camel:errorHandler>
<camel:route>
<camel:from uri="timer:foo?repeatCount=1" />
<camel:setHeader headerName="myheader">
<camel:simple>Sundar</camel:simple>
</camel:setHeader>
<camel:to uri="direct:a" />
</camel:route>
<camel:route errorHandlerRef="deadLetterErrorHandler">
<camel:from uri="direct:a" />
<camel:throwException ref="mybean"></camel:throwException>
</camel:route>
<camel:route>
<camel:from uri="direct:b" />
<camel:log message="${in.header.myheader}"/>
</camel:route>
</camel:camelContext>
It should print a log like below
24 Feb 2016 19:09:47,707 route3 INFO Sundar

Not seeing header or property in Camel route

I have the following Camel context XML. I set a header named MediaType. But, when I set a breakpoint in RenamerProcessor I don't see the header (I've also tried using setProperty with the same results. Being very new to Camel, I've found several examples that make it seem like the below should work.
What is wrong?
<camel:route>
<camel:from uri="file://c:/CamelTVInput" />
<camel:setHeader headerName="MediaType">
<camel:constant>T</camel:constant>
</camel:setHeader>
<camel:to uri="file://c:/CamelReadyToRename" />
</camel:route>
<camel:route>
<camel:from uri="file://c:/CamelReadyToRename?move=//c:/CamelBackup" />
<camel:process ref="RenamerProcessor" />
<camel:to uri="file://c:/CamelOutput" />
</camel:route>
You cannot transfer headers using files. eg when you write to a file, then its only the message body that is written as the file content.
But this is component specific if headers is part of the protocol, eg JMS, HTTP support headers.
If you want to keep files then use something else, Camel has some internal components like seda / direct.

Reading Camel Constant From Property file

I am trying to read time delay from property file .
have defined in my property file :
time_inMilis=15000
I have configured my camel context xml to be :
<bean id="property" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>file:/D:/Develop/resources/my.properties
</value>
</property>
</bean>
<camel:camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<propertyPlaceholder id="properties" location="file:/D:/Develop/resources/my.properties"/>
<camel:route id="delayQueue">
<camel:from uri="seda:queue1" />
<delay asyncDelayed="true">
<constant>${time_inMilis}</constant>
</delay>
<camel:to uri="seda:queue2" />
</camel:route>
</camel:camelContext>
camel do not throw any error but it seems that it ignores ${time_inMilis} and set 0 for my delay time.
What is the right way to read the delay constant from my property file ?
First, it would be enough just to use camel:propertyPlaceholder instead of declaring bean property.
Second mistake is that you are using Constant instead of Simple expression when trying to read your time_inMilis property value.
Third, when trying to get value of you property, you should specifically tell Camel that your are looking at properties.
If your context defines propertiesPlaceholder like this:
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<propertyPlaceholder id="props" location="classpath:/org/smp/eip/sample.properties"/>
<package>org.apache.camel.example.spring</package>
</camelContext>
them with java DSL you'll be able to read the textProeprty value like this
from("file:src/data?noop=true")
.transform().simple("Text read from properties: ${properties:textProperty}")
.bean(new SomeBean());
Using Spring DSL from your original post, the correct way of reading property would be:
<camel:route id="delayQueue">
<camel:from uri="seda:queue1" />
<delay asyncDelayed="true">
<simple>${properties:time_inMilis}</simple>
</delay>
<camel:to uri="seda:queue2" />
</camel:route>

how to write concise apache camel xml

I am new to Apache Camel. I have setup my application to use Apache Camel using XML based configuration. My configuration contains multiple routes which have similar set of steps. I was trying to find if there a way to place common or repeating parts of these different routes in one place and refer them from routes instead of repeating them again and again ?
for e.g. in my below camel route configuration, route 2 is repeating few steps from route 1. So is there a way to extract the common steps of the route 1 and route 2 and then refer extracted part from route 1 and 2?
<context:property-placeholder location="classpath:quartz.properties" />
<context:component-scan base-package="com"></context:component-scan>
<camel:route>
<camel:from uri="quartz://deadlines/SDGWD?cron=15+34+14+?+*+MON-SUN+*" />
<camel:onCompletion>
<camel:to uri="seda:checkAnyPendingDeadlines"/>
</camel:onCompletion>
<camel:to uri="bean:sdgwdNotifier" />
<camel:choice>
<camel:when>
<camel:method ref="deadlineHandler" method="canProcessDeadline" />
<camel:bean ref="deadlineHandler" method="prepareDeadline" />
<camel:bean ref="sdgwdProcessor" method="initiateMessageProcessing" />
<camel:bean ref="schedulerXdrTransformer" method="marshall" />
<camel:to uri="wmq:SU.SES" />
<camel:bean ref="sdgwdProcessor" method="waitForAcknowledgment" />
<camel:bean ref="sdgwdProcessor" method="afterMessageProcessed" />
<camel:bean ref="deadlineHandler" method="onDeadlineProcessingCompletion" />
</camel:when>
<camel:otherwise>
<camel:bean ref="deadlineHandler" method="enqueDeadline" />
</camel:otherwise>
</camel:choice>
</camel:route>
<camel:route>
<camel:from uri ="seda:checkAnyPendingDeadlines"/>
<camel:onCompletion>
<camel:to uri ="seda:checkAnyPendingDeadlines"/>
</camel:onCompletion>
<camel:to uri="bean:deadlineHandler?method=getNextProcessableDeadline" />
<camel:choice>
<camel:when>
<camel:method ref="deadlineHandler" method="canProcessDeadline" />
<camel:bean ref="deadlineHandler" method="prepareDeadline" />
<camel:choice>
<camel:when>
<camel:simple>${body.deadline} == ${type:settlementcontrol.scheduler.model.Deadline.SDGW} </camel:simple>
<camel:bean ref="sdgwdProcessor" method="initiateMessageProcessing" />
<camel:bean ref="schedulerXdrTransformer" method="marshall" />
<camel:to uri="wmq:SU.SES" />
<camel:bean ref="sdgwdProcessor" method="waitForAcknowledgment" />
<camel:bean ref="sdgwdProcessor" method="afterMessageProcessed" />
<camel:bean ref="deadlineHandler" method="onDeadlineProcessingCompletion" />
</camel:when>
</camel:choice>
</camel:when>
<camel:otherwise>
<camel:bean ref="deadlineHandler" method="enqueDeadline" />
</camel:otherwise>
</camel:choice>
</camel:route>
Thanks,
Vaibhav
Is common route, which contains repeating part of your flow, sufficient? If so, then create something like this:
<camel:route id="myCommonPartOfFlow">
<camel:from uri="direct-vm:common-in"/>
[common part]
</camel:route>
You can invoke your sub (myCommonPartOfFlow) route from your main routes now:
<camel:to uri="direct-vm:common-in/>
You can either use a direct component to create subroutes that factor out common parts or split your routes so that the common routes have their own route and then other routes can send messages to the common route. For example, if you have two routes that do the following processes "Process A" -> "Process B" -> "Process C" and "Process D" -> "Process B" -> "Process E" then you can separate Process B into its own route and do the following:
from("jms:queue:processB") - "Process C" -> end()
Process A -> set Header "JMSReplyTo", jms:queue:processC -> to("jms:queue:processB")
Process C -> set Header "JMSReplyTo", jms:queue:processE -> to("jms:queue:processB")
The for the sake of brevity I used Java DSL but the same can be done with XML DSL. I also used "process A" and the like to abstract what could be multiple steps in the route. The concepts of linking the routes is the same no matter what the process is. The advantage to this approach is that it allows federation of "process B" to handle the extra load and distribute processing around your infrastructure. Once you go to Asynchrounous programming, your capabilities rise quite a bit.

How do I process a zip file with Camel using the Spring DSL?

I want to monitor a directory for zip files and then individually process the files in the zip file using Camel and the Spring DSL. Is something like the following possible?
<camel:route>
<camel:from uri="file:/src/Path/"/>
<camel:split streaming="true">
<zipSplit/>
<camel:to uri="bean:fileProcessor?method=processStream"/>
</camel:split>
</camel:route>
Ok so I found that the following works for a zip file containing one file but the question is how can a zip file containing more than one file be processed?
<bean id="zipFileDataFormat" class="org.apache.camel.dataformat.zipfile.ZipFileDataFormat"/>
<camel:camelContext>
<camel:contextScan/>
<camel:route>
<camel:from uri="file:///C:/testSrc/?delay=6000&noop=true&idempotent=false"/>
<camel:unmarshal ref="zipFileDataFormat"/>
<camel:to uri="file:///C:/testDst"/>
</camel:route>
</camel:camelContext>
Note that the file is not being sent to a bean for processing just unzipped into another directory.
This works. Note the log element will give you the details.
<bean id="zipFileDataFormat" class="org.apache.camel.dataformat.zipfile.ZipFileDataFormat">
<property name="usingIterator" value="true"/>
</bean>
<camel:camelContext>
<camel:contextScan/>
<camel:route id="unzipMultipleFiles">
<camel:from uri="file:///C:/testSrc/?delay=30000&noop=true&idempotent=false"/>
<camel:unmarshal ref="zipFileDataFormat"/>
<camel:split streaming="true">
<camel:simple>${body}</camel:simple>
<camel:to uri="log:org.apache.camel?level=INFO&showAll=true&multiline=true"/>
<camel:to uri="file:///C:/testDst"/>
</camel:split>
</camel:route>
</camel:camelContext>

Resources