骆驼豆组件调用@Named / @Dependent bean的缓存实例

时间:2018-06-29 20:23:10

标签: apache-camel

在我们的应用程序中,我们在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-cdijboss-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
    }
}

1 个答案:

答案 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");
}