Federico Mariani created CAMEL-24067:
----------------------------------------

             Summary: camel-spring - SpringInjector does not inject 
CamelContext into CamelContextAware beans created via factory method
                 Key: CAMEL-24067
                 URL: https://issues.apache.org/jira/browse/CAMEL-24067
             Project: Camel
          Issue Type: Bug
          Components: camel-spring
    Affects Versions: 4.21.0
            Reporter: Federico Mariani


Core {{DefaultInjector.newInstance(type, factoryClass, factoryMethod)}} calls 
{{CamelContextAware.trySetCamelContext(answer, camelContext)}} after invoking 
the factory method. {{SpringInjector}} (the injector used by every 
{{SpringCamelContext}}, i.e. all camel-spring / camel-spring-boot applications) 
has the same factory-method code but *without* the {{trySetCamelContext}} call.

As a result, a {{CamelContextAware}} bean created through a factory method - 
e.g. the property-binding syntax {{#class:com.foo.MyBean#createMyBean}} on an 
endpoint/component option - gets the {{CamelContext}} injected on a plain 
{{CamelContext}} but *not* when running in Spring. Same route, different 
behavior depending on runtime.

Two smaller defects in the same method could be fixed together:
* when the factory method exists but is not {{public static}}, the method 
silently returns {{null}} instead of raising an error (same silent-null exists 
in core, but core at least documents the contract via the same code path)
* the guard {{fm.getReturnType() != Void.class}} tests the boxed {{Void}} 
wrapper; a {{void}} factory method has return type {{void.class}} 
({{Void.TYPE}}), so the check never excludes anything (this quirk is shared 
with {{DefaultInjector}} and could be fixed in both)

Reproducer (verified on 4.22.0-SNAPSHOT: the {{DefaultCamelContext}} test 
passes, the {{SpringCamelContext}} test fails; dependencies: camel-core, 
camel-spring, junit-jupiter):

{code:java}
package org.apache.camel.spring.repro;

import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
import org.apache.camel.impl.DefaultCamelContext;
import org.apache.camel.spring.SpringCamelContext;
import org.junit.jupiter.api.Test;
import 
org.springframework.context.annotation.AnnotationConfigApplicationContext;

import static org.junit.jupiter.api.Assertions.assertNotNull;

public class SpringInjectorFactoryMethodTest {

    @Test
    void factoryMethodBeanGetsCamelContextWithDefaultInjector() throws 
Exception {
        try (DefaultCamelContext camel = new DefaultCamelContext()) {
            camel.start();
            MyBean bean = camel.getInjector().newInstance(MyBean.class, 
"createMyBean");
            assertNotNull(bean);
            // passes: DefaultInjector calls 
CamelContextAware.trySetCamelContext
            assertNotNull(bean.getCamelContext());
        }
    }

    @Test
    void factoryMethodBeanGetsCamelContextWithSpringInjector() throws Exception 
{
        try (AnnotationConfigApplicationContext appCtx = new 
AnnotationConfigApplicationContext()) {
            appCtx.refresh();
            SpringCamelContext camel = new SpringCamelContext(appCtx);
            camel.start();
            try {
                MyBean bean = camel.getInjector().newInstance(MyBean.class, 
"createMyBean");
                assertNotNull(bean);
                // EXPECTED: same behavior as DefaultInjector
                // ACTUAL: fails, SpringInjector skips CamelContextAware 
injection
                assertNotNull(bean.getCamelContext());
            } finally {
                camel.stop();
            }
        }
    }

    public static class MyBean implements CamelContextAware {
        private CamelContext camelContext;

        public static MyBean createMyBean() {
            return new MyBean();
        }

        @Override
        public CamelContext getCamelContext() {
            return camelContext;
        }

        @Override
        public void setCamelContext(CamelContext camelContext) {
            this.camelContext = camelContext;
        }
    }
}
{code}

Suggested fix: mirror {{DefaultInjector}} - after {{answer = type.cast(obj);}} 
add {{CamelContextAware.trySetCamelContext(answer, camelContext)}}. 
{{SpringInjector}} currently has no {{CamelContext}} reference, but it is 
always created from {{SpringCamelContext.createInjector()}}, which can pass 
{{this}} (or the {{ApplicationContext}} lookup can be used).

_This issue was found during an AI-assisted code review of the camel-spring 
component. Reported by Claude Code on behalf of Federico Mariani. The 
reproducers were executed and verified against 4.22.0-SNAPSHOT (main) before 
filing._



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to