Dynamic Spring xml - spring

Im trying to implement a load balancer using Apache Camel and Spring.
To do so, one has to configure the target servers in a spring.xml like this:
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="localhost:8000"/>
<loadBalance>
<roundRobin/>
<to uri="localhost:8080"/>
<to uri="localhost:8081"/>
<to uri="localhost:8082"/>
</loadBalance>
</route>
</camelContext>
Now, is there a way to read in all target servers from a properties file?
Just so one could change them without editing the spring.xml...

As your describe, I think the recipient-list(http://camel.apache.org/recipient-list.html) can solve your problem.
You can decide which uri to be sent in the java code, things like read servers from a properties file can be easily done.

There are multiple choices available to do that:
http://camel.apache.org/recipient-list.html
http://camel.apache.org/dynamic-router.html
Starting from Camel 2.16 you can also use "dynamic to" (http://camel.apache.org/message-endpoint.html)

Related

JUnit test for AWS S3 Camel XML DSL route in Spring boot

I have configured an XML camel route as mentioned below
<route>
<from uri="sftp://testuser#localhost?password=test&delete=true" />
<setHeader name="CamelAwsS3Key">
<simple>${in.header.camelFileName}</simple>
</setHeader>
<to uri="aws-s3://myTestBucket?accessKey=******&secretKey=RAW(******)&deleteAfterWrite=false&region=AP_SOUTH_1" />
</route>
I ahve been trying to create test cases for the same. contextLoads() tries to access the s3 localtion in real time which i want to prevent.
Saw there are solutions with LocalStack, is there any other way i can test a scenario without other libraries?

How to use dynamic values in URI endpoints in apache camel XML configuration

I am using apache camel to create routes between endpoints where through one URI (API Gateway) running on Tomcat on one port, I am mapping to another URI running on Tomcat on different domain and port.
<bean id="hostnameVerifier" class="org.apache.http.conn.ssl.AllowAllHostnameVerifier" />
...
<camel:sslContextParameters id="ssl">
<camel:keyManagers keyPassword="password">
<camel:keyStore ... />
</camel:keyManagers>
<camel:trustManagers>
<camel:keyStore ... />
</camel:trustManagers>
</camel:sslContextParameters>
....
<rest path="/MyService" consumes="application/json" produces="application/json">
<post uri="/login">
<description>Authenticate User</description>
<route streamCache="true">
<to
uri="https4://domain-b:9000/Auth/user/login?bridgeEndpoint=true&sslContextParametersRef=ssl&x509HostnameVerifier=hostnameVerifier" />
</route>
</post>
...
</rest>
Now as far as I am hardcoding the domain-b in my to endpoints, things are working fine. Problem comes when I have to dynamically fill that value from an input from some configuration file.
This is how I am trying to achieve the same -
<bean id="properties" class="org.apache.camel.component.properties.PropertiesComponent">
<property name="location" value="classpath:${LOCATION_PATH}propsfile.properties"/>
</bean>
The name of the properties key is "domain", now in my end points defintion I am writing the same as -
<to
uri="https4://${properties.domain}:9000/Auth/user/login?bridgeEndpoint=true&sslContextParametersRef=ssl&x509HostnameVerifier=hostnameVerifier" />
Basically after loading the properties in a bean named properties, I am trying to replace the domain-b with ${properties.domain} or #{properties.domain}, but does not seem to be working.
If anyone can suggest, in XML based config only, how can I read the URL domain from the properties file, that will be really awesome.
-AJ
You have to use property place holder to achieve dynamic uri the way you want.
For example:
<camelContext ...>
<propertyPlaceholder id="properties" location="YOUR_PROPERTY_FILE_LOCATION"/>
</camelContext>
And then try with
<to uri="https4://{{properties.domain}}:9000/.......>
Note: When you are configuring your property file using spring bean "PropertiesComponent", you have to use camel Property component inside your camel route to achieve dynamic value loading.
Since Camel 2.16
We can use
.from("file://path")
.toD("file://folder/${file:onlyname}")
We can use file

Camel Exceptions not being logged

I'm using Camel 2.10.2 and I have the following configuration:
<camelContext id="camelContext" trace="true" xmlns="http://camel.apache.org/schema/spring">
<errorHandler type="LoggingErrorHandler" level="INFO" logName="my.logger" id="webserviceLoggingHandler"/>
<route id="webService" errorHandlerRef="webserviceLoggingHandler">
<from uri="direct:webService" />
<to uri="spring-ws:{{webservice.url}}?messageFactory=#messageFactory&messageSender=#messageSender" />
<onException>
<exception>java.lang.Exception</exception>
<to uri="log:my.logger?level=INFO"/>
</onException>
</route>
<route id="validate">
<from uri="activemq:validate" />
<to uri="direct:webService" />
</route>
</camelContext>
I would expect this to log to the console. Instead what I'm finding is that exceptions (for example IOExceptions like certificate errors) are just being consumed and I really need them to be logged.
Debugging the code I can see that the SpringDefaultErrorHandler is being used and this is delegating to the LoggingExceptionHandler which has an injected CamelLogger which has a NOPLogger injected. This means that nothing is being logged at all.
After reading through the docs I am unsure if I need to implement a specific error handler and relevant onException handling, or if I should instead just use the log component, or a mix of the two as I have above?
Any guidance gratefully received.
many Thanks
OK, I found the problem. My camel setup was fine. We recently upgraded from Camel 2.6.0 to 2.10.2. Reading the camel docs I found that from camel 2.7 slf4j was used for logging instead of commons-logging. I already had the log4j and slf4j jars but was missing the slf4j-log4j binding jar. This is what was causing all logging to go to an NOPLogger. Once I dropped the jar in it all works fine.
Thanks

Using apache camel xml route, how can I set a customized fileName from a JMS message?

With the java JMS API, I got from a DB an array of bytes and then I'm sending it to an ActiveMQ as a javax.jms.BytesMessage. After that with camel I want to put the file on a location,
I have this route in camel:
<route>
<from uri="activemq:queue.fileOuput"/>
<convertBodyTo type="java.nio.ByteBuffer"/>
<to uri="file://C:/output/"/>
</route>
But my problem is that my file in c:\output\ directory, I got the file with the message id as the file name, like
queue-preVerificacion-fileoutput-ID-jmachine-57401-1347652410053-0-1-1-1-1
but I want to put the name I have in the database, like MyFile.xml.
I have tried to set a message property like fileName and file:name, and also I saw in the apache document that I need to put a header "org.apache.camel.file.name", but with jms I don't know how to do it.
So my question is how can I put a customized name in the camel route?
Thanks to all.
Just place the file name in the jms message (as a string property).
// Something like this if you send the message using plain java/jms:
msg.setStringProperty("filename","MyFile.xml");
..//Send msg
Then you can do something like this in camel
<to uri="file://C:/output/?fileName=${header.filename}"/>
you just need to set the "CamelFileName" header value (based on a message header, etc)
<route>
<from uri="activemq:queue.fileOuput"/>
<convertBodyTo type="java.nio.ByteBuffer"/>
<setHeader headerName="CamelFileName">
<constant>${header.fileName}</constant>
</setHeader>
<to uri="file://C:/output/"/>
</route>
I think "org.apache.camel.file.name" is for camel 1.x , in the 2.x version CamelFileName worked fine. But I wanted a more dynamic file name, name based on the content. This example using a processor worked well ( camel 2.18 )
<route>
<from uri="MQ:MY_Q_NAME" />
<process ref="MyMessageProcessor"/>
<to uri="file://E:\OUTPUT" />
</route>
Inside the Processor :
exchange.getIn().setHeader(Exchange.FILE_NAME, myFileName);

Multiple camel route processing same file

I have 10 routes defined with 2 of them listed here. The other 8 are identical but the from endpoint is a different dir. Each route picks up a file that is located in the dir and is processed by one groovy class. When one file is dropped into one of the dir it works fine, but when a file is dropped into each of the dir's at the same time each thread seems to go haywire. I do receive logging messages that I have picked up each file, but then it seems like each thread uses one process "beginProcess" because its like all my variables defined in groovy are being changed as each file gets picked up. Im not sure if this makes any sence, but can someone tell me that what I am doing below is legal? Basically can multiple routes call one class and if so whether I am doing that correct? If it is then at least I would know that it has to be in my groovy class(but seeing that it works with one file suggest that its not here, but not assuming that at this point). Thanks much!
<camel:camelContext id="myId"
xmlns="http://camel.apache.org/schema/spring">
<route>
<from
uri="file://directoryStructure1/?move=archive&sortBy=ignoreCase:file:name&readLock=markerFile&readLockCheckInterval=5000&readLockTimeout=10m&maxMessagesPerPoll=1" />
<process ref="beginProcess"></process>
</route>
<route>
<from
uri="file://directoryStructure2/?move=archive&sortBy=ignoreCase:file:name&readLock=markerFile&readLockCheckInterval=5000&readLockTimeout=10m&maxMessagesPerPoll=1" />
<process ref="beginProcess"></process>
</route>
</camel:camelContext>
<bean id="beginProcess" class="package.groovy.class"> </bean>
Seems like you are using a singleton (single instance) object of your class since that would be default in spring when you define "beginProcess".
That is fine, if the implementaiton of "package.groovy.class" is thread-safe, which seems to be the case, it is not.
Basically, there are a few ways to fix this.
1) Never process concurrent. It is easy to achieve with the SEDA transport that can stack up files on a queue while another file is processed.
<route>
<from
uri="file://directoryStructure1/?move=archive&sortBy=ignoreCase:file:name&readLock=markerFile&readLockCheckInterval=5000&readLockTimeout=10m&maxMessagesPerPoll=1" />
<to uri="seda:process"/>
</route>
<route>
<from
uri="file://directoryStructure2/?move=archive&sortBy=ignoreCase:file:name&readLock=markerFile&readLockCheckInterval=5000&readLockTimeout=10m&maxMessagesPerPoll=1" />
<to uri="seda:process"/>
</route>
<route>
<from uri="seda:process"/>
<process ref="beginProcess"/>
</route>
2) Create a new bean every time the beginProcess is called:
<bean id="beginProcess" class="package.groovy.class" scope="prototype"/>
A good page about spring bean scoping is found here.
3) Make your package.groovy.class thread safe. That is, no fields and variables should be kept as a state or, the state should be possible to share between concurrent requests and be synchronized.

Resources