在我们的应用程序中,我们在JBoss EAP 7.1环境中将Apache Camel与camel-cdi
组件一起使用。将Apache Camel升级到实际版本后,该应用程序开始在并行执行中表现不正确。
我发现,bean组件总是调用相同的实例。据我了解,具有@Dependent
范围的bean应该始终是每次CDI查找的新鲜实例。
我尝试了端点参数cache=false
,该参数应为默认值,但行为保持不变。还尝试指定@Dependent
,它也应该是默认值。
附加MCVE,在Apache Camel 2.20.0
及更高版本上失败。适用于2.19.5
及更高版本。完全可复制的项目on Github。
@ApplicationScoped
@Startup
@ContextName("cdi-context")
public class MainRouteBuilder extends RouteBuilder {
public void configure() throws Exception {
from("timer:test")
.to("bean:someDependentBean?cache=false");
}
}
@Named
//@Dependent //Dependent is default
public class SomeDependentBean implements Processor {
private int numOfInvocations = 0;
private static Logger log = LoggerFactory.getLogger(SomeDependentBean.class);
public void process(Exchange exchange) throws Exception {
log.info("This is: "+toString());
numOfInvocations++;
if (numOfInvocations!=1){
throw new IllegalStateException(numOfInvocations+"!=1");
} else {
log.info("OK");
}
}
}
我可以在应用程序中做些什么来更改此行为并使用Apache Camel的实际版本吗?
编辑:
删除标签camel-cdi
和jboss-weld
。我创建了单元测试,以模拟这种情况,而无需依赖于camel-cdi和Weld。该测试包含测试JndiRegistry#lookup
的断言,该断言返回正确的实例。我相信,根据该测试,问题出在bean组件本身中。版本> = {2.20.0
失败,并通过<= 2.19.5
public class CamelDependentTest extends CamelTestSupport {
private Context context;
private JndiRegistry registry;
@Override
protected RoutesBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
@Override
public void configure() throws Exception {
from("direct:in")
.to("bean:something?cache=false");
}
};
}
@Override
protected JndiRegistry createRegistry() throws Exception {
JndiRegistry registry = super.createRegistry();
registry.bind("something", new SomeDependentBean());
this.context = registry.getContext();
this.registry = registry;
return registry;
}
@Test
public void testFreshBeanInContext() throws Exception{
SomeDependentBean originalInstance = registry.lookup("something", SomeDependentBean.class);
template.sendBody("direct:in",null);
context.unbind("something");
context.bind("something", new SomeDependentBean()); //Bind new instance to Context
Assert.assertNotSame(registry.lookup("something"), originalInstance); //Passes, the issue is not in JndiRegistry.
template.sendBody("direct:in",null); //fails, uses cached instance of SameDependentBean
}
}
答案 0 :(得分:1)
根据CAMEL-12610被假定为Processor
是单例范围。在版本2.20.0
中引入了此行为。不要实现Processor
接口,而是将可调用方法注释为@Handler
。
替换
@Named
public class SomeDependentBean implements Processor {
public void process(Exchange exchange) throws Exception {
}
}
使用
@Named
public class SomeDependentBean {
@Handler
public void process(Exchange exchange) throws Exception {
}
}
如果您不能像我那样负担得起,因为这破坏了我们应用扩展的行为,那么我已经实现了简单的组件。该组件没有缓存,可以直接从注册表中调用Processor
。
CdiEndpoint类
public class CdiEndpoint extends ProcessorEndpoint {
private String beanName;
protected CdiEndpoint(String endpointUri, Component component) {
super(endpointUri, component);
}
public void setBeanName(String beanName) {
this.beanName = beanName;
}
@Override
protected void onExchange(Exchange exchange) throws Exception {
Object target = getCamelContext().getRegistry().lookupByName(beanName);
Processor processor = getCamelContext().getTypeConverter().tryConvertTo(Processor.class, target);
if (processor != null){
processor.process(exchange);
} else {
throw new RuntimeException("CDI bean "+beanName+" not found");
}
}
}
CdiComponent类
public class CdiComponent extends DefaultComponent {
@Override
protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception {
CdiEndpoint endpoint = new CdiEndpoint(uri, this);
endpoint.setBeanName(remaining);
return endpoint;
}
}
用法
public void configure() throws Exception {
getContext().addComponent("cdi", new CdiComponent());
from("direct:in")
.to("cdi:something");
}