Apache Camel 聚合在 IDE 中和在 JBoss Fuse 中部署时的不同行为
Different behavior of Apache Camel aggregation in IDE and when deployed in JBoss Fuse
我在使用 Apache Camel 时遇到了一个我无法理解的问题。 JBoss Fuse 6.3.0 有这个问题,它捆绑了 Apache Camel 2.17.0.redhat-630224.
我有一个简单的路线:它从 FTP 服务器下载文件,将它们转换成 POJO(这部分有效),然后将它们聚合成一个 POJO,然后将其编组并保存到一个文件中。
在 JBoss Developer Studio 中,我通过 "Run as... > local Camel context" 进行测试。在幕后,这只是运行 mvn clean package org.apache.camel:camel-maven-plugin:run
。无论我是从 IDE 执行此操作,还是在我的终端中手动执行此操作,路由都可以正常工作。
但是,当我构建一个 OSGi 包(使用 mvn clean install
)然后部署到 JBoss Fuse (Apache Karaf) 时,应用程序部署成功并且 download/transform 部分工作很好,但是聚合失败了。
聚合由实现 org.apache.camel.processor.aggregate.AggregationStrategy
的自定义 class 处理(已记录 here)。我遇到的问题是我收到的 newExchange
参数总是有一个空主体。现在,预计 oldExchange
第一次为空,但 newExchange
的正文? (编辑:相关表达式是一个简单的常量,因为所有 POJO 都聚合在一起)
更奇怪:如果我修改路由以在聚合器之前编组我的 POJO,我会收到一个包含预期数据的字符串。这证明(我认为!)转换按预期工作。此外,Fuse 的日志没有显示任何错误消息(无论是在部署时还是在运行时)。这看起来很像配置或依赖性问题,但就我的生活而言,我在任何地方都找不到任何类似的问题。
有没有人见过类似的东西?或者至少,关于问题的根源,您有什么建议吗?
编辑:这是路线的相关部分:
<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
注意:我尝试使用 strategyMethodAllowNull="false",没有任何改变。
这是聚合器:
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");
}
// ...
第二个 if
每次都会触发,即使是第一次聚合,如果我删除第一个 if
中的 return
。
编辑
好的,感谢 noMad17n 在下面的评论,我有了一个突破:问题与 class 加载有关。
当我在没有指定 class (Object newBody = newExchange.getIn().getBody();
) 的情况下得到 newExchanges
的正文时,结果不为空,但我无法将其转换为 MyClass
:我得到了一个 java.lang.ClassCastException: net.j11e.MyClass cannot be cast to net.j11e.MyClass
.
阅读有关 OSGi 如何导致多个 class 加载程序加载相同 class 的信息,我将 MyClass
重命名为 MyOtherClass
并在重新启动后 (??),一切正常。但是,卸载我的包并重新安装后,问题又回来了。
osgi:find-class MyClass
returns 两个包:我的和 dozer-osgi,这是(我猜)合乎逻辑的,因为 MyClass 实例是由推土机转换产生的。
好的,所以也许我不应该经常卸载和重新安装捆绑包,而应该使用 osgi:update
、osgi:refresh
或其他任何方式。但是,应该有办法使这项工作正常进行吗?除了卸载我的包、refreshing/updating dozer、stopping/restarting Fuse 和重新安装我的包之外,还希望上述操作之一以某种方式加载正确的 classes 吗?
对于那些将来可能会遇到此问题的人,这里有一个回顾:
- 问题是由于您的包导出的包的旧版本仍在被另一个包使用(在我的例子中是 dozer-osgi)。在这里,这会导致对 MyClass 的转换失败,从而使
getBody
return 为空(getMandatoryBody
会 return 一个异常,等等)
- 要确定导致问题的包,请使用命令
osgi:find-class MyClass
。这将 return 你的包...和另一个。
- 通过查找捆绑包 ID (
osgi:list | grep thebundle
) 并刷新它 (osgi:refresh 123
) 来刷新该捆绑包。您还可以从 Fuse 的网站 UI (hawtio) 刷新捆绑包:OSGi > 捆绑包 > 您的捆绑包 > 页面顶部的刷新按钮(在开始、停止、更新和卸载按钮旁边)。
这与其说是解决此问题的正确解决方案,不如说是一种缓解措施。真正的解决方案可能涉及修复包 import/export 规则或其他内容,但这超出了我目前的技能范围。
公平警告:有时,刷新 dozer-osgi 显然是不够的。它不再导入 MyClass(osgi:find-class MyClass
不会 return dozer-osgi),但我仍然遇到 NullPointerException
问题。在这些罕见的情况下,我不得不重新启动 Fuse。我不知道为什么会发生这几起案件。
我在使用 Apache Camel 时遇到了一个我无法理解的问题。 JBoss Fuse 6.3.0 有这个问题,它捆绑了 Apache Camel 2.17.0.redhat-630224.
我有一个简单的路线:它从 FTP 服务器下载文件,将它们转换成 POJO(这部分有效),然后将它们聚合成一个 POJO,然后将其编组并保存到一个文件中。
在 JBoss Developer Studio 中,我通过 "Run as... > local Camel context" 进行测试。在幕后,这只是运行 mvn clean package org.apache.camel:camel-maven-plugin:run
。无论我是从 IDE 执行此操作,还是在我的终端中手动执行此操作,路由都可以正常工作。
但是,当我构建一个 OSGi 包(使用 mvn clean install
)然后部署到 JBoss Fuse (Apache Karaf) 时,应用程序部署成功并且 download/transform 部分工作很好,但是聚合失败了。
聚合由实现 org.apache.camel.processor.aggregate.AggregationStrategy
的自定义 class 处理(已记录 here)。我遇到的问题是我收到的 newExchange
参数总是有一个空主体。现在,预计 oldExchange
第一次为空,但 newExchange
的正文? (编辑:相关表达式是一个简单的常量,因为所有 POJO 都聚合在一起)
更奇怪:如果我修改路由以在聚合器之前编组我的 POJO,我会收到一个包含预期数据的字符串。这证明(我认为!)转换按预期工作。此外,Fuse 的日志没有显示任何错误消息(无论是在部署时还是在运行时)。这看起来很像配置或依赖性问题,但就我的生活而言,我在任何地方都找不到任何类似的问题。
有没有人见过类似的东西?或者至少,关于问题的根源,您有什么建议吗?
编辑:这是路线的相关部分:
<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
注意:我尝试使用 strategyMethodAllowNull="false",没有任何改变。
这是聚合器:
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");
}
// ...
第二个 if
每次都会触发,即使是第一次聚合,如果我删除第一个 if
中的 return
。
编辑 好的,感谢 noMad17n 在下面的评论,我有了一个突破:问题与 class 加载有关。
当我在没有指定 class (Object newBody = newExchange.getIn().getBody();
) 的情况下得到 newExchanges
的正文时,结果不为空,但我无法将其转换为 MyClass
:我得到了一个 java.lang.ClassCastException: net.j11e.MyClass cannot be cast to net.j11e.MyClass
.
阅读有关 OSGi 如何导致多个 class 加载程序加载相同 class 的信息,我将 MyClass
重命名为 MyOtherClass
并在重新启动后 (??),一切正常。但是,卸载我的包并重新安装后,问题又回来了。
osgi:find-class MyClass
returns 两个包:我的和 dozer-osgi,这是(我猜)合乎逻辑的,因为 MyClass 实例是由推土机转换产生的。
好的,所以也许我不应该经常卸载和重新安装捆绑包,而应该使用 osgi:update
、osgi:refresh
或其他任何方式。但是,应该有办法使这项工作正常进行吗?除了卸载我的包、refreshing/updating dozer、stopping/restarting Fuse 和重新安装我的包之外,还希望上述操作之一以某种方式加载正确的 classes 吗?
对于那些将来可能会遇到此问题的人,这里有一个回顾:
- 问题是由于您的包导出的包的旧版本仍在被另一个包使用(在我的例子中是 dozer-osgi)。在这里,这会导致对 MyClass 的转换失败,从而使
getBody
return 为空(getMandatoryBody
会 return 一个异常,等等) - 要确定导致问题的包,请使用命令
osgi:find-class MyClass
。这将 return 你的包...和另一个。 - 通过查找捆绑包 ID (
osgi:list | grep thebundle
) 并刷新它 (osgi:refresh 123
) 来刷新该捆绑包。您还可以从 Fuse 的网站 UI (hawtio) 刷新捆绑包:OSGi > 捆绑包 > 您的捆绑包 > 页面顶部的刷新按钮(在开始、停止、更新和卸载按钮旁边)。
这与其说是解决此问题的正确解决方案,不如说是一种缓解措施。真正的解决方案可能涉及修复包 import/export 规则或其他内容,但这超出了我目前的技能范围。
公平警告:有时,刷新 dozer-osgi 显然是不够的。它不再导入 MyClass(osgi:find-class MyClass
不会 return dozer-osgi),但我仍然遇到 NullPointerException
问题。在这些罕见的情况下,我不得不重新启动 Fuse。我不知道为什么会发生这几起案件。