Reputation: 381
I'm having a problem with Apache Camel that I can't understand. I have this issue with JBoss Fuse 6.3.0, which bundles Apache Camel 2.17.0.redhat-630224.
I have a simple route: it downloads files from an FTP server, transforms them into POJOs (this part works), then aggregates them into a single POJO which is marshalled and saved to a file.
In JBoss Developer Studio, I test this by doing "Run as... > local Camel context". Behind the scenes, this simply runs mvn clean package org.apache.camel:camel-maven-plugin:run
. Whether I do it from the IDE, or manually in my terminal, the route works fine.
However, when I build an OSGi bundle (with mvn clean install
) which I then deploy into JBoss Fuse (Apache Karaf), the application deploys successfully and the download/transform parts works fine, but then the aggregation fails.
The aggregation is handled by a custom class that implements org.apache.camel.processor.aggregate.AggregationStrategy
(documented here). The problem I have is that the newExchange
parameter I receive always have a null body. Now, the oldExchange
being null the first time is expected, but the newExchange
's body? (edit: the correlation expression is a simple constant, since all POJOs are aggregated together)
Even weirder: if I modify the route to marshall my POJOs just before the aggregator, I receive a String with the expected data. This proves (I think!) that the transformations work as expected. Also, Fuse's logs show no error messages (neither at deploy time nor at runtime). This looks a lot like a configuration or dependency issue, but for the life of me I can't find any similar issue reported anywhere.
Has anyone ever seen something similar before? Or at least, do you have any tips as to what could be the problem's source?
Edit: here's the relevant part of the route:
<choice>
// one <when> per file which produces a POJO
<when id="_when_some_xml">
<simple>${file:onlyname} == 'something.xml'</simple>
<to id="_to2" uri="ref:transform_something_xml"/>
</when>
</choice>
// if I add a marshalling here, I receive non-null exchanges in the aggregator... but they're strings and not the POJOs I want.
<aggregate completionSize="12" id="_aggregate_things"
strategyMethodAllowNull="true" strategyRef="MyAggregator">
<correlationExpression>
<constant trim="false">true</constant>
</correlationExpression>
<log id="_log_things_aggregated" message="Data aggregated."/>
<convertBodyTo id="_convertBodyTo_anotherClass" type="net.j11e.mypackage.MyClass"/>
// [...] next: marshal and save to file
Note: I tried with strategyMethodAllowNull="false", didn't change a thing.
And here's the Aggregator:
public class EpgAggregator implements AggregationStrategy {
@Override
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
// first message being aggregated: no oldExchange, simply keep the message
if (oldExchange == null) {
System.out.println("Old exchange is null");
return newExchange;
}
if (newExchange.getIn().getBody(MyClass.class) == null) {
System.out.println("newExchange body is null");
}
// ...
The second if
triggers every time, even for the first aggregation, if I remove the return
in the first if
.
Edit Ok, so thanks to noMad17n's comment below, I had a breakthrough: the problem has to do with class loading.
When I got the newExchanges
's body without specifying a class (Object newBody = newExchange.getIn().getBody();
), the result was not null, but I couldn't cast it as MyClass
: I got a java.lang.ClassCastException: net.j11e.MyClass cannot be cast to net.j11e.MyClass
.
Reading about how OSGi can lead to multiple classloaders loading the same class, I renamed MyClass
to MyOtherClass
and after a reboot (??), everything worked. However, after uninstalling my bundle and reinstalling it, the problem is back.
osgi:find-class MyClass
returns two bundles: mine and dozer-osgi, which is (I guess) logical since MyClass instances are produced by a dozer transformation.
Ok, so maybe I should not uninstall and reinstall bundles very often but use osgi:update
, osgi:refresh
, or whatever. But still, there should be a way to make this work? Something else than uninstalling my bundle, refreshing/updating dozer, stopping/restarting Fuse, and reinstalling my bundle, hoping that one of the aforementioned operations somehow makes the correct classes be loaded?
Upvotes: 0
Views: 543
Reputation: 381
For those who might encounter this issue in the future, here's a recap:
getBody
return null (getMandatoryBody
would return an exception, etc.)osgi:find-class MyClass
. This will return your bundle... and another.osgi:list | grep thebundle
) and refreshing it (osgi:refresh 123
). You can also refresh the bundle from Fuse's web UI (hawtio): OSGi > bundles > your bundle > the refresh button at the top of the page (next to the start, stop, update, and uninstall buttons).That's more a mitigation than a proper solution to this issue. The real solution would probably involve fixing the package import/export rules or something, but this is beyond my current skills.
Fair warning, too: sometimes, refreshing dozer-osgi apparently wasn't enough. MyClass was not imported by it anymore (osgi:find-class MyClass
would not return dozer-osgi), but I still had my NullPointerException
problem. In these rare occurrences, I had to restart Fuse. I don't know why these few cases happened.
Upvotes: 1