Компонент Camel Bean вызывает кэшированный экземпляр @Named / @Dependent bean - PullRequest
0 голосов
/ 29 июня 2018

В нашем приложении мы используем Apache Camel с компонентом camel-cdi в среде JBoss EAP 7.1. После обновления Apache Camel до актуальной версии приложение стало некорректно работать при параллельном выполнении.

Я обнаружил, что компонент компонента всегда вызывает один и тот же экземпляр. Насколько я понимаю, bean-компонент с областью действия @Dependent всегда должен быть свежим экземпляром для каждого поиска CDI.

Я пробовал параметр конечной точки cache=false, который должен быть значением по умолчанию, но поведение остается прежним. Также попытался указать @Dependent, что также должно быть по умолчанию.

Присоединение MCVE, которое не работает на Apache Camel 2.20.0 и новее. Хорошо работает с 2.19.5 и старше. Полностью воспроизводимый проект на 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?

EDIT:

Удаление тегов camel-cdi и jboss-weld. Я создал модульный тест, чтобы смоделировать эту ситуацию без зависимостей от camel-cdi и Weld. Этот тест содержит утверждение для проверки JndiRegistry#lookup, который возвращает правильный экземпляр. В соответствии с этим тестом, я считаю, проблема заключается в самом компоненте бобов. Сбой с версией> = 2.20.0 и передачей с <= <code>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 голосов
/ 08 июля 2018

Согласно 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");
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...