Camel - Using property in Spring DSL - spring

I'm trying to set some properties in my process method but I'm not able to figure out that how to use those properties in xml, like I can use header values in xml easily by using syntax : ${in.header.myKey}
Here's my code :
<route>
<from uri="activemq:queue:start.queue" />
<to uri="stream:out" />
<process ref="jsonProcessor"></process>
<to uri="bean:validateInputIdentifiers?method=validation(${in.property.SourceMap}, ${in.property.DestinationMap})" />
</route>
Here in.property.SourceMap is Unknown function. What is the correct way?
Would be great if it is something similar to header. Also I want to use property only and not header since values of header may not persists later in my routes.
Here's process method code:
#Override
public void process(Exchange exchange) throws Exception {
List<Map<String, String>> body = exchange.getIn().getBody(List.class);
Map<String, String> sourceMap = body.get(0);
Map<String, String> destinationMap = body.get(1);
exchange.setProperty("SourceMap", sourceMap);
exchange.setProperty("DestinationMap", destinationMap);
}
Kindly provide the solution.

There could be multiple solution combinations for your problem.
Sample Property Key and Value.
<cm:property name="app.user" value="PROD008"/>
In Route if u want to set header with property value. Use below code snippet.
<setHeader headerName="password">
<simple>${properties:app.user}</simple>
</setHeader>
If you want to use property, you can use below snippet.
<to uri="{{some.endpoint}}"/>
For your example: if Properties are SourceMap and DestinationMap you can use any of below.
1. <to uri="bean:validateInputIdentifiers?method=validation(${property.SourceMap}, ${property.DestinationMap})" />
2. <to uri="bean:validateInputIdentifiers?method=validation({{SourceMap}},{{DestinationMap}})" />
If you want to use header instead of property then use below code snippet.
<to uri="bean:validateInputIdentifiers?method=validation(${header.SourceMap}, ${header.DestinationMap})" />

After hit and trial I got the working solution:
<route>
<from uri="activemq:queue:start.queue" />
<to uri="stream:out" />
<process ref="jsonProcessor"></process>
<to uri="bean:validateInputIdentifiers?method=validation(${property.SourceMap}, ${property.DestinationMap})" />
</route>

Related

Testing apache camel routes

I have an application build using Apache Camel 2.15.3. And I'm wiring the routes using spring-xml for dependency injection. I'm having a hard time understanding how to write automatic test for my routes. For example I might have the routes:
<onException id="Exception">
<exception>java.lang.Exception</exception>
<handled>
<constant>true</constant>
</handled>
<to uri="direct:fear"/>
</onException>
<route id="happyStory">
<from uri="direct:inTheBeginning"/>
<to uri="bean:enchantedKingdom?method=warn" />
<to uri="bean:fluffykins" />
</route>
<route id="scaryStory">
<from uri="direct:fear"/>
<onException>
<exception>java.lang.Exception</exception>
<handled>
<constant>true</constant>
</handled>
</onException>
<to uri="bean:monster"/>
<choice>
<when>
<simple>${header.succesfullywarned}</simple>
<to uri="bean:enchantedKingdom?method=hide"/>
</when>
<otherwise>
<to uri="bean:enchantedKingdom?method=panic" />
</otherwise>
</choice>
</route>
And I wan't to be able to say that when the bean method warn is called then the header "succesfullywarned" should be set in the message and then when the bean fluffykins is called there should be a exception that causes the message to get sent to "scaryStory" and in this case I wan't to assert that the bean method hide is called.
This is roughly my test class setup:
#RunWith(CamelSpringJUnit4ClassRunner.class)
#ContextConfiguration({"/META-INF/spring/route-
stories.xml","/META-INF/spring/beans.xml"})
#MockEndpointsAndSkip("(bean:fluffykins|bean:monster|bean:enchantedKingdom?method=warn|bean:enchantedKingdom?method=hide|bean:enchantedKingdom?method=panic)")
public class StoryHappyRouteTest extends CamelSpringTestSupport {
private String url = "direct:inTheBeginning";
#Autowired
private ApplicationContext applicationContext;
#Override
protected AbstractApplicationContext createApplicationContext() {
return (AbstractApplicationContext)applicationContext;
}
#Test
public void test(){
MockEndpoint warn = getMockEndpoint("mock:bean:enchantedKingdom?method=warn");
MockEndpoint fluffy = getMockEndpoint("mock:bean:fluffykins");
MockEndpoint monster = getMockEndpoint("mock:bean:monster");
MockEndpoint hide = getMockEndpoint("mock:bean:enchantedKingdom?method=hide");
MockEndpoint panic = getMockEndpoint("mock:bean:enchantedKingdom?method=panic");
fluffy.whenAnyExchangeReceived(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
System.out.println("Bunny!");
throw new NullPointerException();
}
});
template.sendBody(url,"");
warn.assertExchangeReceived(0);
fluffy.assertExchangeReceived(0);
monster.assertExchangeReceived(0);
panic.assertExchangeReceived(0);
}
}
I've read the chapter on testing in the first edition of Camel in action and look around in the manual (http://camel.apache.org/testing.html) but I don't understand how to apply it in my situation. In the above test everything works except where I have bean with multiple methods so I have an uri that contains "?method=methodname", and for some reason this makes it not work. I don't get an error or but the mock is not used and instead the actual beans are called.
Is it not possible to do what I wan't to do? I can change the test setup in any way, but it is a given that the routes and the beans are defined in the spring-xml files.
I've taught about mocking the beans themselves and not the endpoints but the only way I can think of doing that is creating a "imposter-beans.xml" file where all the beans are defined, that points to stubbclasses that extend every class used in the routes. But that feels like an elaborate and wrong approach.
You can write code what to do when the mock receives a message. This is covered in the book in section 6.2.6, where you can use methods like whenAnyExchangeReceived or whenExchangeReceived, and in those inlined processors you can set the header, or throw an exception etc. See for example listing 6.9.

how to inject the route into camel FileFilter

Is there a way to inject the route into camel FileFilter ?
I have this camel route :
<route id="mislaka-bituch">
<from
uri="ftp://{{mislaka_bituch_in_path}}?filter=#fileFilter&readLock=none&delete=true&moveFailed=.error&sortBy=file:modified" />
<convertBodyTo type="java.lang.String" />
<to uri="bean:redirectService?method=analyzeMislaka" />
<toD uri="ftp://${exchangeProperty[targetPath]}" />
</route>
And in my FileFilter I have the method :
public boolean accept(GenericFile<T> file)
But how do i get the route info (name for example) in this class ?
Yes as said in the comments, you cannot get the id of the route etc, in the FileFilter method, you can only get file information from the GenericFile.

Apache Camel -- Reference type constants in Spring DSL

I'm trying to define a publish operation to Hazelcast topic using Spring DSL
<from uri="direct:inbound" />
<onCompletion>
<log message="onCompletion:- ${body}" />
<setHeader headerName="${type:org.apache.camel.component.hazelcast.HazelcastConstants.OPERATION}">
<simple>${type:org.apache.camel.component.hazelcast.HazelcastConstants.PUBLISH_OPERATION}</simple>
</setHeader>
<to uri="hazelcast:topic:foo" />
</onCompletion>
<log message="${body}" />
The above route works, but I have to use long SIMPLE scripts like "${type:org.apache.camel.component.hazelcast.HazelcastConstants.OPERATION}" to ref a constant value. Is there any simpler or short form for this?
I tried to define a spring bean for HazelcastConstants class and ref it through SIMPLE scripts as below but it's not working with MethodNotFoundException "Method with name: OPERATION not found on bean"
<bean id="hazelcastConstants" class="org.apache.camel.component.hazelcast.HazelcastConstants" />
... ...
<simple>${bean:hazelcastConstants.OPERATION}</simple>
Your bean workaround would work, if you defined a bean contained a method returning the constant in question, e.g.:
public class ContantRetriever() {
public String getHazelCastOperation() {
return org.apache.camel.component.hazelcast.HazelcastConstants.PUBLISH_OPERATION;
}
}
Your Spring context:
<bean id="hazelcastConstants" class="yourpackage.ContantRetriever"/>
<simple>${bean:hazelcastConstants.getHazelCastOperation}</simple>
If that is no good for you, I am afraid you are stuck with the long form of accessing constants.

CamelJdbcColumnNames header missed

I try to use the jdbc component from camel. I found the documentation here: http://camel.apache.org/jdbc.html.
It works well as the result is available from the database but there is no header in the queued answer called CamelJdbcColumnNames as mentioned in the documentation.All i can see is CamelJdbcRowCount. My camel version is 2.15.1.Do i have to turn a switch to enable this?
Here is an extract of my spring-config.xml:
<bean id="ds" class="org.springframework.jdbc.datasource.SimpleDriverDataSource">
<property name="driverClass" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:#myhost:1521:mydbsid"/>
<property name="username" value="myuser"/>
<property name="password" value="mypass"/>
</bean>
<route id="route db">
<from uri="file://data/inbox" />
<to uri="jdbc:ds" />
<to uri="jms:sqlret" />
</route>
EDIT:
To exclude the jms i added a Processor. With this i want to debug the message header. This is a new extract of my spring-config.xml:
<bean id="jdbccheck" class="mypackage.JdbcCheck"></bean>
<route id="route db">
<from uri="file://data/inbox" />
<to uri="jdbc:ds" />
<process ref="jdbccheck"/>
<to uri="jms:sqlret" />
</route>
The Processor code:
public class JdbcCheck implements Processor {
private static final Logger LOG = Logger.getLogger(JdbcCheck.class.getName());
#Override
public void process(Exchange exchange) throws Exception {
LOG.info(exchange.getIn().getHeaders().toString());
}
}
The log message:
{breadcrumbId=ID-chris-HP-50597-1429955241877-0-1, CamelFileAbsolute=false, CamelFileAbsolutePath=C:\daten\chris\source\netbeans\GbLuna\data\inbox2\in.sql, CamelFileContentType=null, CamelFileLastModified=1429953640254, CamelFileLength=36, CamelFileName=in.sql, CamelFileNameConsumed=in.sql, CamelFileNameOnly=in.sql, CamelFileParent=data\inbox2, CamelFilePath=data\inbox2\in.sql, CamelFileRelativePath=in.sql, CamelJdbcRowCount=837}
The last var-/value pair is CamelJdbcRowCount=837 which seems to me that it works somehow. But for further processing i want to deal with the column names. So: how to get CamelJdbcColumnNames?
Ah okay got it now, its because you send the data to a JMS endpoint. And JMS specification only support a number of data types for JMS headers/properties. And that is usually String, numbers and primitive types.
You can read more about this on the Camel JMS documentation page, and from the JMS spec/javadoc.
The column names header is stores as a header of Java collection type and that is not supported by JMS.
http://camel.apache.org/jms
If you enable the Camel tracer you should be able to see the header before the message is routed to the JMS endpoint: http://camel.apache.org/tracer

Apache Camel: Weave does not work in Split. Why?

I have the following XML DSL context definition in spring:
<beans>
<camelContext>
<route>
<from uri="direct:foo"/>
<split parallelProcessing="true">
<simple>${body}</simple>
<to uri="direct:bar"/>
</split>
</route>
</camelContext>
</beans>
In my test I'm trying to weave the direct:bar endpoint like so:
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
weaveByToString(".*direct:bar.*").replace().to("mock:bar");
}
});
This successfully works. But when route is starts an exception is thrown saying org.apache.camel.NoSuchBeanException: No bean could be found in the registry for: direct:bar.
Why?
May be camel does not support weaving inside split?
Note: Everything works just fine with following XML:
<beans>
<camelContext>
<route>
<from uri="direct:dummy"/>
<to uri="direct:bar"/>
</route>
<route>
<from uri="direct:foo"/>
<split parallelProcessing="true">
<simple>${body}</simple>
<to uri="direct:dummy"/>
</split>
</route>
</camelContext>
</beans>
I could not reproduce your error with the use case described using Camel 2.7. Here is my test that pass:
#Test
public void test() throws Exception {
context.getRouteDefinitions().get(0).adviceWith(context, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
weaveByToString(".*direct:bar.*").replace().to("mock:bar");
}
});
MockEndpoint mock = getMockEndpoint("mock:bar");
mock.expectedMessageCount(1);
template.sendBody("direct:foo", "this is a test");
assertMockEndpointsSatisfied();
}
Starting the route using camel:run also start without throwing a NoSuchBeanException.

Resources