Camel REST to Controlbus - apache-camel

I am exposing a REST endpoint to control Camel Routes:
<get uri="/camel/route/{id}" produces="text/plain">
<description>stop a camel route by its ID</description>
<param name="id" type="path" description="Route ID" dataType="string"/>
<param name="action" type="query" description="Action to take" dataType="string"/>
<responseMessage message="OK" code="200" />
<to uri="controlbus:route?routeId=${header.id}&action=${header.action}"/>
</get>
But the control bus seems fail to resolve ID and Action as it log as:
ControlBusProducer - ControlBus task done [${header.action} route ${header.id}] with result -> void
How do I resolve REST path and query parameters in the route?

Expressions such as ${header.action} are not evaluated in <to> processors. If you are using a recent version of Camel use <toD> instead. If that's not available in your version, <recipientList> can do the job.
http://camel.apache.org/how-to-use-a-dynamic-uri-in-to.html

Related

How to use properties with the SimpleRegistry in Apache Camel (Spring XML)

I want to use a SimpleRegistry to store properties (as global variables). The property is changed with setProperty in a route with a jms endpoint. The camel documentation changed last week and has many dead links, also the Registry page. I did not found any samples that describe the use of the simpleRegistry.
I used the camel-example-servlet-tomcat as base. I do not use Fuse or the patched camel wildfly, because is to huge for our simple module.
<beans .... >
.
.
.
<bean id="simpleRegistry" class="org.apache.camel.support.SimpleRegistry" />
<camelContext xmlns="http://camel.apache.org/schema/spring">
<propertyPlaceholder id="properties" location="ref:simpleRegistry" />
<route id="storeConfig">
<from id="myTopic" uri="jms:topic:myTopic?selector=Configuration %3D 'xyz'" />
<log id="printHeader2" message="Received header: ${headers}" />
<log id="logToken" message="Received token: ${headers[myToken]}" />
<setProperty id="setMyToken" name="myProperty">
<simple>${headers[myToken]}</simple>
</setProperty>
</route>
<route id="externalIncomingDataRoute">
<from uri="servlet:hello" />
<transform>
<simple>The Token is: {{myProperty}}</simple>
</transform>
</route>
</camelContext>
</beans>
With the camel context deined like above, I got a java.io.FileNotFoundException Properties simpleRegistry not found in registry.
When I use <propertyPlaceholder id="properties" location="classpath:test.properties" /> and create a test.properties file, everything works fine but I cannot change the property. The operation in the setProperty tag is ignored.
The reason why I need a global variable is, I send a dynamic configuration (the myToken) via a jms topic to the camel context. A single route should store this configuration globaly. If an other route is called via an rest component, this route need the token to make a choice.
Alternatively you can achieve the same result following the below approach which uses the PropertiesComponent
<bean id="applicationProperties" class="java.util.Properties"/>
<bean id="properties" class="org.apache.camel.component.properties.PropertiesComponent">
<property name="location" value="classpath:application.properties"/>
<property name="overrideProperties" ref="applicationProperties" />
</bean>
Define the property place holder in the camel context:
<propertyPlaceholder id="propertiesRef" location="ref:applicationProperties" />
Set a property as shown below :
<bean ref="applicationProperties" method="setProperty(token, 'Test'})" />
And to fetch the property : ${properties:token}
OK, there are multiple subjects in your question.
You write you want to use Camel SimpleRegistry, but you obviously have a Spring application.
If you got Spring available, the Camel Registry automatically uses the Spring bean registry. The Camel Registry is just a thin wrapper or provider interface that uses whenever possible an available registry of another framework.
The Camel SimpleRegistry is only used when nothing else is available. This is basically an in-memory registry based on a Map.
You want to set an application property with <setProperty>.
<setProperty> sets an Exchange property, NOT an application property. With this you can save values in the Exchange of a message.
You want to use "global variables".
You could perhaps use a Spring singleton bean that is a Map. You could then autowire it where you need it, it would be like an application wide available map.
However, think twice why you need this kind of variable. This could also be a symptom of a design problem.

Spring Boot Camel Test

I am writing a test for Camel using Spring boot.
Below is configuration on the test class
#RunWith(CamelSpringBootRunner.class)
#SpringBootApplication
#ComponentScan(basePackages = ["example.test"])
#UseAdviceWith
#BootstrapWith(SpringBootTestContextBootstrapper)
#DirtiesContext
class RouteTest {
private static final Logger LOGGER = LoggerFactory.getLogger(RouteTest.class)
#Autowired ModelCamelContext camelContext
#Test
void "flow"() {
camelContext.getRouteDefinition(route.routeId).adviceWith(camelContext, new AdviceWithRouteBuilder() {
#Override
void configure() throws Exception {
}
}
LOGGER.info("IN TEST: ******* Camel Status: "+camelContext.getStatus())
}
I expect camel should not be started. But when I run the test it is already started.
I noticed that CamelSpringBootRunner does start camel context in CamelSpringBootExecutionListener.
How do I force not to start the camel context.
In the latest version of camel there is an option for autoStartup of camel. You an achieve what you want by adding autoStartup option.
For example the route below is configured autoStartup=false to prevent Camel starting when Spring starts.
<camelContext id="myCamel" xmlns="http://camel.apache.org/schema/spring" autoStartup="false">
<route>
<from uri="direct:start"/>
<to uri="mock:result"/>
</route>
</camelContext>
You can manually start Camel later by invoking its start method as shown below:
ApplicationContext ac = ...
SpringCamelContext camel = (SpringCamelContext) ac.getBean("myCamel");
// now start Camel manually
camel.start();
If you are using older version of camel then autoStartup option will not work try using shouldStartContext instead.
Sometimes starting camel after setting shouldStartContext doesn't work so I have put the work around in below example. Try this :
setting shouldStartContext manually before starting the context from the code:
((SpringCamelContext)camelContext).setShouldStartContext(true);
camelContext.start();
Example context:
<camel:camelContext id="ids.camel.context" shouldStartContext="false">
<!-- Queue endpoints. -->
<camel:endpoint id="defaultInQueue" uri="jms:queue:${default.in.queue.name}"/>
<camel:endpoint id="defaultOutQueue" uri="jms:queue:${default.out.queue.name}"/>
<!-- Route to send messages to IDS -->
<camel:route id="out" trace="true">
<camel:from uri="direct:sender"/>
<!-- Do not expect answer (fire and forget) -->
<camel:setExchangePattern pattern="InOnly"/>
<camel:to ref="defaultOutQueue"/>
</camel:route>
<!-- Route to receive -->
<camel:route id ="in" trace="true">
<camel:from ref="defaultInQueue"/>
<camel:to uri="bean:defaultTextAdapter?method=onMessage"/>
</camel:route>
</camel:camelContext>
Using Camel 2.20.1 solved the issue

Camel - Why the setProperty changes the header as well?

I've imagined, that setProperty change the header as well, and I don't know why.
<setProperty propertyName="A"><constant>AAA</constant></setProperty>
<log message="HA: ${headers.A}" />
<log message="PA: ${exchangeProperty[A]}" />
Both logs print AAA. Camel version 2.17.3, Spring version 4.3.2.RELEASE.
How should I use the setProperty?
As answered by Claus Ibsen:
This is working as Camel was designed with the header/property
expressions in the DSL.
A header lookup will fallback as property.
Source: Simple message header.XXX and exchange property.XXX the same?

Apache Camel - Delay when exception occurs

I have written a camel route that polls a file folder, picks up request, checks for memory consumption on server (in a java file). If its below threshold it drops the request on a JMS queue otherwise it throws an exception and picks it again for processing.
What i need to do is that when exception is thrown i need to delay processing for a configurable amount of time say 15 mins. This will give some time for server to stabilize instead of keeping it polling unnecessarily.
I am using errorHandler mechanism of camel however it doesnt seem to work. Camle keeps on picking up the request without any delay. Please help with this issue.
Below is the bundle context snapshot:
<camel:onException>
<camel:exception>java.lang.Exception</camel:exception>
<camel:redeliveryPolicy backOffMultiplier="500" />
<camel:log message="Default error handler was called"></camel:log>
</camel:onException>
<camel:route>
<!-- Reading from REST url -->
<camel:from uri="<my url>" />
<!-- If else loop -->
<camel:choice>
<camel:when>
<camel:xpath>Some path</camel:xpath>
<!-- Parsing OrderNumber and OrderVersion-->
<camel:log message="Recieved request ${headers.OrderNumber}-${headers.OrderVersion}.xml"/>
<camel:setHeader headerName="OrderNumber">
<xpath>Some path</xpath>
</camel:setHeader>
<camel:setHeader headerName="OrderVersion">
<camel:xpath>Some path</camel:xpath>
</camel:setHeader>
<!-- Request being put in file folder -->
<to
uri="file:data/inbox?fileName=${header.OrderNumber}-${header.OrderVersion}.xml"
pattern="InOut" />
</camel:when>
<!-- For all other requests put on queue -->
<camel:otherwise>
<camel:log message="Request ${headers.OrderNumber}-${headers.OrderVersion}.xml directly sent to queue"/>
<to uri="my queue"
pattern="InOut" />
</camel:otherwise>
</camel:choice>
</camel:route>
<camel:route errorHandlerRef="errorHandler">
<!-- Route to put message from folder to JMS queue if memory consumption is below limit-->
<camel:from uri="file:data/inbox"/>
<camel:process ref="checkMemoryConsumption"/>
<camel:convertBodyTo type="String" />
<camel:log message="Sucessfully processing service order ${headers.OrderNumber}-${headers.OrderVersion}.xml"/>
<to uri="my queue"
pattern="InOut" />
</camel:route>
Did you try redeliveryDelay of redeliveryPolicyProfile?
Below policy profile will retry 3 times with the delay of 1000mS between each try.
<redeliveryPolicyProfile id="myRedeliveryProfile"
maximumRedeliveries="3"
redeliveryDelay="1000" allowRedeliveryWhileStopping="false"
retryAttemptedLogLevel="INFO" />
Read more on here
By making below modification in my bundle context (bundle context displayed above), re-delivery policy kicked in and seems to do the trick.

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.

Resources