Passing parameter to bean's method in camel blueprint - apache-camel

I'm trying to call bean's public method by passing parameter .But i'm unable to run it.Here is my sample blueprint code(I have written from clause but here i'm pasting only necessary code)-
<bean id="ProcessorRef" class="com.healthedge.customer.THC.extractor.ProcessorClass">
<to uri="bean:ProcessorRef" />
Processor class-
public class ProcessorClass{
public String whatAmI(String str) {
return "I am "+str;
}
}
Now in above example how can I invoke whatAmI method with parameter from blueprint?TIA

You can do it by calling directly the method you want
<bean id="ProcessorRef" class="com.healthedge.customer.THC.extractor.ProcessorClass">
<bean ref="ProcessorRef" method="whatAmI('your_parameter_here')" />

If the value you want to pass to whatAmI method is in header, you can do like this.
<to uri="bean:ProcessorRef?method=whatAmI(${header.xyz})" />
If its a constant string, you can put the string directly in place of ${header.xyz}
Another option is to modify your whatAmI method to
public class ProcessorClass{
public String whatAmI(Exchange exchange) {
// exchange has many methods, with which you can access headers and body.
}
}
In such a case you can write the route like this
<to uri="bean:ProcessorRef?method=whatAmI" />
Personally, I would prefer second option, because it gives you access to complete exchange object, which would have all the details.

Related

Not able to update Camel exchange property

I am setting a property of camel exchange in route 1. I am trying to update the same in the second route inside a splitter. but in the second iteration of splitter i am getting the original value that I set in the route 1 instead of the new updated value. Below is the sample i am trying..
<route handleFault="true" streamCache="true" id="route1">
<from uri="cxfrs://bean://test?synchronous=true"/>
<bean ref="testBean" method="setMyProperty"/>// setting initial value for property
<to uri="direct:directCall"/>
</route>
<route handleFault="true" streamCache="true" id="route2">
<from uri="direct:directcall"/>
<log message="Inside Second call..."/>
<split>
<jsonpath>some Json path</jsonpath>
<bean ref="formatConvertor" method ="convertLHMToJSON(${body})"/>
<split>
<jsonpath>some json path</jsonpath>
<bean ref="PropertySetter" method ="setProperty"/> //I am setting new value in this method
</split>
</split>
Inside the beans:
public void setMyProperty(Exchange exchange) {
exchange.setProperty("testProp", "hello");
}
public void setProperty(Exchange exchange) {
sysout(exchange.getProperty("testProp").toString())//this always prints "hello"
String x=exchange.getProperty("testProp")+some other value;// in all iterations of split I am getting 'hello' as the value of the property instead of new value
exchange.setProperty("testProp", x);
sysout(exchange.getProperty("testProp").toString())// this line prints the new value
}
Why the property is not updated? Even i tried setting in headers. same result. Thank you.
You must use a split-aggregate pattern and copy the property value during each iteration of the split from the old exchange to the new exchange because every time when the split iteration happens a new exchange is created from the source message (only carrying the properties & headers set before the split)
recover headers value after split apache camel

Camel JAXB marshalling return XML object type?

I am sending a Java object to producer endpoint and waiting for the marshalled XML object. I tried changing it to Node object/ File object but it is giving ClassCastException.
So took the xmlObj in an object class type. What should be the correct class to capture the response?
public class ClientEight {
#Produce(uri = "direct:invoice")
ProducerTemplate template;
public static void main(String args[]) throws InterruptedException {
AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("resources/camel-configTen.xml");
InvoiceXml invoice = new InvoiceXml("fdf3443", 3454, 435345.44 f, "hfhfddfdg"); //any java object we are passing
ClientEight client = (ClientEight) ctx.getBean("client");
Object xmlObj = client.template.requestBody(invoice);
System.out.println(xmlObj);
}
}
Above is a client code which you are using to send the Java object to a producer endpoint and since you are using template.requestBody, you are getting back the object returned.
<camel:camelContext>
<camel:dataFormats>
<!-- path to jaxb annotated class -->
<camel:jaxb id="invoiceJaxb" contextPath="com.java.bean"
prettyPrint="true" />
</camel:dataFormats>
<camel:route>
<camel:from uri="direct:invoice" />
<camel:marshal ref="invoiceJaxb" />
<camel:log message=" ${body}" />
<camel:to uri="file://src/resources?fileName=One.xml"/>
</camel:route>
</camel:camelContext>
the unmarshall processor return a stream, not a single object. In camel, more generally, if you want a specific type, you shouldn't get directly a body as an object, but use the various methods to convert the body into that type.
Try :
Document document = client.template.requestBody(invoice, org.w3c.dom.Document.class);

Camel Blueprint Testing - how can I dynamically replace/proxy a remote service referenced in Blueprint in order to prevent use of the real service?

I have the following scenario:
I have an OSGI bundle that has a service reference defined in the blueprint XML that references an interface in a remote bundle, and a bean that uses one of the impl's methods to populate a Properties object.
Relevant snippet from Bundle #1's XML (the consumer):
...
<!-- other bean definitions, namespace stuff, etc -->
<!-- reference to the fetching service -->
<reference id="fetchingService" interface="company.path.to.fetching.bundle.FetchingService" />
<!-- bean to hold the actual Properties object: the getConfigProperties method is one of the overridden interface methods -->
<bean id="fetchedProperties" class="java.util.Properties" factory-ref="fetchingService" factory-method="getProperties" />
<camelContext id="contextThatNeedsProperties" xmlns="http://camel.apache.org/schema/blueprint">
<propertyPlaceholder id="properties" location="ref:fetchedProperties" />
...
<!-- the rest of the context stuff - routes and so on -->
</camelContext>
Remote Bundle's blueprint.xml:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:camel="http://camel.apache.org/schema/blueprint"
xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
xsi:schemaLocation="
http://www.osgi.org/xmlns/blueprint/v1.0.0 http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd">
<cm:property-placeholder id="config-properties" persistent-id="company.path.configfetcher" />
<bean id="fetchingService" class="company.path.to.fetching.bundle.impl.FetchingServiceImpl" scope="singleton" init-method="createLoader" depends-on="config-properties">
<property name="environment" value="${environment}" />
<property name="pathToRestService" value="${restPath}" />
</bean>
<service ref="fetchingService" interface="company.path.to.fetching.bundle.FetchingService" />
<!-- END TESTING -->
</blueprint>
From Impl Class:
public synchronized Properties getProperties() {
if(!IS_RUNNING) {
// timer task that regularly calls the REST api to check for updates
timer.schedule(updateTimerTask, 0, pollInterval);
IS_RUNNING = true;
}
//Map<String, Properties> to return matching object if it's there
if(PROPERTIES_BY_KEY.containsKey(environment)) {
return PROPERTIES_BY_KEY.get(environment);
}
/* if nothing, return an empty Properties object - if this is the case, then whatever bundle is relying on these
* properties is going to fail and we'll see it in the logs
*/
return new Properties();
}
The issue:
I have a test class (extending CamelBlueprintTestSupport) and there are a lot of moving parts such that I can't really change the order of things. Unfortunately, the properties bean method gets called before the CamelContext is started. Not that big a deal because in the test environment there is no config file to read the necessary properties from so the retrieval fails and we get back an empty properties object [note: we're overriding the properties component with fakes since it's not that class being tested], but in a perfect world, I'd like to be able to do two things:
1) replace the service with a new Impl()
2) intercept calls to the getProperties method OR tie the bean to the new service so that the calls return the properties from the fake impl
Thoughts?
Edit #1:
Here's one of the things I'm doing as a workaround right now:
try {
ServiceReference sr = this.getBundleContext().getServiceReference(FetchingService.class);
if(sr != null) {
((FetchingServiceImpl)this.getBundleContext().getService(sr)).setEnvironment(env);
((FetchingServiceImpl)this.getBundleContext().getService(sr)).setPath(path);
}
} catch(Exception e) {
log.error("Error getting Fetching service: {}", e.getMessage());
}
The biggest problem here is that I have to wait until the createCamelContext is called for a BundleContext to exist; therefore, the getProperties call has already happened once. As I said, since in the testing environment no config for the FetchingService class exists to provide the environment and path strings, that first call will fail (resulting in an empty Properties object). The second time around, the code above has set the properties in the impl class and we're off to the races. This is not a question about something that isn't working. Rather, it is about a better, more elegant solution that can be applied in other scenarios.
Oh, and for clarification before anyone asks, the point of this service is so that we don't have to have a .cfg file for every OSGI bundle deployed to our Servicemix instance - this central service will go and fetch the configs that the other bundles need and the only .cfg file that need exist is for the Fetcher.
Other pertinent details:
Camel 2.13.2 - wish it was 2.14 because they've added more property-placeholder tools to that version that would probably make this easier
Servicemix - 5.3.1
Have you tried overriding CamelBlueprintTestSupport's addServicesOnStartup in your test (see "Adding services on startup" http://camel.apache.org/blueprint-testing.html)?
In your case something like:
#Override
protected void addServicesOnStartup(Map<String, KeyValueHolder<Object, Dictionary>> services) {
services.put(FetchingService.class.getName(), asService(new FetchServiceImpl(), null));
}

jboss fuse split List property

I have a proxy, which take a RequestBean as argument, which contains a list property and I need to split this list property using split EIP.
I tried
<split streaming="true">
<simple>${body}</simple>
<process ref="requestHeaderProcessor" />
</split>
My Complete route is
<route id="httpBridge">
<from uri="cxf:bean:splitterOperation?dataFormat=POJO" />
<split streaming="true">
<simple>${body}</simple>
<bean ref="requestHeaderProcessor" method="process" />
</split>
<to uri="cxf:bean:realService" />
</route>
My Proxy Service method signature is
public List<ResponseBean> splitList(List<RequestContent> requestBean);
ResponseBean.java
ResponseBean {
private String name;
}
RequestBean.java
RequestBean {
private String list;
}
The processor is not receiving individual RequestContent, I want the processor to receive RequestContent individually.
tried printing following line and
System.out.println(exchange.getIn().getBody().getClass().getName());
and got java.util.ArrayList. So its 100% Iteratable.
But, when I print the
System.out.println(exchange.getIn().getBody());
I am getting
[webservice.RequestContent#10128f3, webservice.RequestContent#1277137]
Which is list of all the RequestContent.
But, why am I seeing the list in the bean, the Exchange must contain only one RequestContent according to split definition (Since, it processes sequentially).
Where am I making mistake. or is this the way it works. How can I make sure it splits the content?
How to achieve this?
Whatever this method returns is what is used for splitting
<simple>${body.requestBean.requestContent}</simple>
So make sure that is a List or array, or can be iterated

Do we need #InOnly annotation or <setExchangePattern pattern="InOnly"/> if we are using sendBody method of ProducerTemplate?

For POJO producing, it is mentioned in Camel docs that InOut is the default.
But, if we are using the sendBody() of ProducerTemplate, is there any need of setting #InOnly also as in the example below.
public class MyBean {
#Produce(uri = "direct:hello")
private ProducerTemplate producerTemplate;
#InOnly
void someInOnlyMethod()
{
...
producerTemplate.sendBody("mystr");
...
}
}
Similarly in the example below, if direct:hello gets a message from the above MyBean object configured without the #InOnly annotation, do we need the setExchangePattern element?
<route>
<from uri="direct:hello"/>
<setExchangePattern pattern="InOnly"/>
<to uri="mock:result"/>
</route>
1)
No when you use the producer template then the MEP is set accordingly to the method you use on the template. eg all send methods is InOnly, and all request methods is InOut. Though some methods has a pattern parameter where you can specify the MEP.
2)
No you often dont have to set/change the MEP. Though in situations where you send a message to a endpoint which supports both InOnly or InOut (and behaves differently) you may want to set the MEP explicit to your needs.
See about these EIPs for details
http://camel.apache.org/event-message.html
http://camel.apache.org/request-reply.html
for example a JMS endpoint can do InOnly (send only to a queue) or InOut (do request/reply over JMS) etc.

Resources