I'm using Camel 4.0.0-RC2 and following the documentation on binding beans to a RouteTemplate. I have a simple route template to consume from a configurable fromEndpointUri
and pass a configurable foo
to my fooConsumer
:
routeTemplate("myTemplate")
// Define the parameters for the route template.
.templateParameter ("fromEndpointUri")
.templateBean ("foo", Foo.class)
// Define the route.
.from ("{{fromEndpointUri}}")
.bean (fooConsumer, "processFoo(${body}, #{{foo}})");
The route I create using the template is simply:
templatedRoute("myTemplate")
.routeId ("myTemplateRoute")
.parameter ("fromEndpointUri", "direct:test")
.bean ("foo", Foo.class, ignored -> new Foo());
When sending a message through this route, an error is thrown No type converter available to convert from type: java.lang.String to the required type: com.example.Foo
. The relevant stack trace is:
org.apache.camel.NoTypeConversionAvailableException: No type converter available to convert from type: java.lang.String to the required type: com.example.Foo
at org.apache.camel.impl.converter.CoreTypeConverterRegistry.mandatoryConvertTo(CoreTypeConverterRegistry.java:279) ~[camel-base-4.0.0-RC2.jar:4.0.0-RC2]
at org.apache.camel.component.bean.MethodInfo$ParameterExpression.evaluateParameterValue(MethodInfo.java:717) ~[camel-bean-4.0.0-RC2.jar:4.0.0-RC2]
... 94 more
Wrapped by: org.apache.camel.component.bean.ParameterBindingException: Error during parameter binding on method: public java.util.List com.example.FooConsumer.processFoo(java.lang.String,com.example.Foo) at parameter #1 with type: class com.example.Foo with value type: class java.lang.String and value: #foo-1
at org.apache.camel.component.bean.MethodInfo$ParameterExpression.evaluateParameterValue(MethodInfo.java:728) ~[camel-bean-4.0.0-RC2.jar:4.0.0-RC2]
at org.apache.camel.component.bean.MethodInfo$ParameterExpression.evaluateParameterExpressions(MethodInfo.java:618) ~[camel-bean-4.0.0-RC2.jar:4.0.0-RC2]
at org.apache.camel.component.bean.MethodInfo$ParameterExpression.evaluate(MethodInfo.java:591) ~[camel-bean-4.0.0-RC2.jar:4.0.0-RC2]
at org.apache.camel.component.bean.MethodInfo.initializeArguments(MethodInfo.java:262) ~[camel-bean-4.0.0-RC2.jar:4.0.0-RC2]
at org.apache.camel.component.bean.MethodInfo.createMethodInvocation(MethodInfo.java:270) ~[camel-bean-4.0.0-RC2.jar:4.0.0-RC2]
at org.apache.camel.component.bean.BeanInfo.createInvocation(BeanInfo.java:266) ~[camel-bean-4.0.0-RC2.jar:4.0.0-RC2]
at org.apache.camel.component.bean.AbstractBeanProcessor.process(AbstractBeanProcessor.java:128) ~[camel-bean-4.0.0-RC2.jar:4.0.0-RC2]
at org.apache.camel.component.bean.BeanProcessor.process(BeanProcessor.java:81) ~[camel-bean-4.0.0-RC2.jar:4.0.0-RC2]
...
How do I pass the route's instance of Foo
to my FooConsumer
? Some variations I have tried are:
Variation | Error |
---|---|
.bean(fooConsumer, "processFoo(${body}, {{foo}})") |
No type converter available to convert from type: java.lang.String to the required type: com.example.Foo |
.to("bean:fooConsumer?method=processFoo(${body},#{{foo}})") |
No type converter available to convert from type: java.lang.String to the required type: com.example.Foo |
.to("bean:fooConsumer?method=processFoo(${body},{{foo}})") |
No type converter available to convert from type: java.lang.String to the required type: com.example.Foo |
.bean(fooConsumer, "processFoo(${body}, ${bean:#{{foo}}})") |
No bean could be found in the registry for: #foo-1 |
.bean(fooConsumer, "processFoo(${body}, ${bean:{{foo}}})") |
No bean could be found in the registry for: foo-1 |
Surely it's possible to do this since the examples show passing a myClient
bean to a aws2-s3
endpoint using the #{{myClient}}
syntax.
I've found one way of using these route templates like I'm trying to here, but it doesn't feel very clean. Perhaps I'm trying to use these in a way they aren't intended to be used. Anyhow, to get it to work, I created a inner ServiceActivator
class in the class defining the route template:
private class ServiceActivator {
private final Foo foo;
public ServiceActivator(final RouteTemplateContext routeTemplateContext) {
this.foo = (
routeTemplateContext
.getLocalBeanRepository ()
.lookupByNameAndType ("foo", Foo.class)
);
}
@Handler
public void processFoo(@Body final String body) {
// do something
}
}
Then, I updated the route template to:
foo
because having the definition makes Camel attempt to create a default value for it.ServiceActivator
on route creation.ServiceActivator
in the bean
definition of the route.routeTemplate("myTemplate")
// Define the parameters for the route template
.templateParameter ("fromEndpointUri")
.templateBean ("serviceActivator, ServiceActivator.class, ServiceActivator::new)
// Define the route.
.from("{{fromEndpointUri}}")
.bean("{{serviceActivator}}")
And left my templated route as:
templatedRoute("myTemplate")
.routeId ("myTemplateRoute")
.parameter ("fromEndpointUri", "direct:test")
.bean ("foo", Foo.class, ignored -> new Foo());
Again, it seems almost like a hack rather than using this feature of a Camel in a way that it was intended to be used. From what I could tell stepping through the code, the route template parameters and beans are only available at the time of route creation, so this was a way of capturing all of that information during route creation to be used in later executions of the route.