[
https://issues.apache.org/jira/browse/CAMEL-24351?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Henrik updated CAMEL-24351:
---------------------------
Description:
On 4.21.0 a Spring Boot application can start with every platform-http route
reporting itself started, while every request to them returns 404 from Spring's
{{{}BasicErrorController{}}}:
{code:json}
{"status":404,"error":"Not Found","path":"/myroute"}
{code}
Camel logs nothing for these requests because no route is invoked, and the app
looks healthy: startup shows {{{}Started myroute (rest://get:/myroute){}}},
non-HTTP routes work, health probes stay green. Whether an instance is affected
is decided at startup and fixed for its lifetime, so restarts appear to fix it
at random.
h3. Cause
{{SpringBootPlatformHttpAutoConfiguration}} declares the mapping bean {{{}@Bean
@Lazy{}}}. {{CamelRequestHandlerMapping}} registers itself as a
{{PlatformHttpListener}} in its constructor, and
{{PlatformHttpComponent.addHttpEndpoint()}} notifies only listeners that
already exist. A lazy bean is created on first demand, which is not ordered
against CamelContext startup: if Camel starts before anything resolves
{{HandlerMapping}} beans, all endpoints are registered before the mapping
exists and its registry stays empty forever. It is silent because
{{addPlatformHttpListener()}} does not replay {{getHttpEndpoints()}} to a new
listener, and {{addHttpEndpoint()}} logs listener exceptions at WARN only.
h3. Why 4.21.0
Eager singletons are created in {{{}preInstantiateSingletons(){}}}, always
before {{finishRefresh()}} starts Camel, so the listener could not miss a
registration.
||Version||Annotation||Instantiation||
|4.18.3|none|eager|
|4.20.0|@DependsOn|eager|
|4.21.0|@Lazy|lazy|
Verified from the constant pool of the released class in each starter jar.
{{@Lazy}} arrived in commit 264db4c12e0 ("Fix compilation error in camel-aws-s3
(#1783)"), which replaced an {{ObjectProvider<CamelContext>}} parameter, used
to defer the lookup and avoid a circular dependency, with direct injection plus
{{{}@Lazy{}}}.
Applications with a separate management port are not affected: the management
child context initialises its DispatcherServlet during startup, which
instantiates the parent's mapping before Camel starts. Deterministic check:
{{{}getBeanDefinition("platformHttpEngineRequestMapping").isLazyInit(){}}}.
h3. Fix
Drop {{@Lazy}} and resolve the CamelContext from an {{ObjectProvider}} inside
the method, restoring eager instantiation while keeping the circular dependency
avoidance:
{code:java}
@Bean
public CamelRequestHandlerMapping platformHttpEngineRequestMapping(
PlatformHttpEngine engine, ObjectProvider<CamelContext> camelContextProvider) {
CamelContext camelContext = camelContextProvider.getObject();
PlatformHttpComponent component = camelContext.getComponent("platform-http",
PlatformHttpComponent.class);
return new CamelRequestHandlerMapping(component, engine);
}
{code}
h3. Workaround
{{{}spring.mvc.servlet.load-on-startup=1{}}}: Tomcat's
{{performDeferredLoadOnStartup()}} runs inside {{{}onRefresh(){}}}, before the
{{ContextRefreshedEvent}} that starts Camel.
PR: [https://github.com/apache/camel-spring-boot/pull/1874]
was:
On 4.21.0 a Spring Boot application can start with every platform-http route
reporting itself started, while every request to them returns 404 from Spring's
\{{BasicErrorController}}:
{code:json}
{"status":404,"error":"Not Found","path":"/myroute"}
{code}
Camel logs nothing for these requests because no route is invoked, and the app
looks healthy: startup shows {{{}Started myroute (rest://get:/myroute){}}},
non-HTTP routes work, health probes stay green. Whether an instance is affected
is decided at startup and fixed for its lifetime, so restarts appear to fix it
at random.
h3. Cause
{{SpringBootPlatformHttpAutoConfiguration}} declares the mapping bean {{{}@Bean
@Lazy{}}}. {{CamelRequestHandlerMapping}} registers itself as a
{{PlatformHttpListener}} in its constructor, and
{{PlatformHttpComponent.addHttpEndpoint()}} notifies only listeners that
already exist. A lazy bean is created on first demand, which is not ordered
against CamelContext startup: if Camel starts before anything resolves
{{HandlerMapping}} beans, all endpoints are registered before the mapping
exists and its registry stays empty forever. It is silent because
{{addPlatformHttpListener()}} does not replay {{getHttpEndpoints()}} to a new
listener, and {{addHttpEndpoint()}} logs listener exceptions at WARN only.
h3. Why 4.21.0
Eager singletons are created in {{{}preInstantiateSingletons(){}}}, always
before {{finishRefresh()}} starts Camel, so the listener could not miss a
registration.
||Version||Annotation||Instantiation||
|4.18.3|none|eager|
|4.20.0|@DependsOn|eager|
|4.21.0|@Lazy|lazy|
Verified from the constant pool of the released class in each starter jar.
{{@Lazy}} arrived in commit 264db4c12e0 ("Fix compilation error in camel-aws-s3
(#1783)"), which replaced an {{ObjectProvider<CamelContext>}} parameter, used
to defer the lookup and avoid a circular dependency, with direct injection plus
{{{}@Lazy{}}}.
Applications with a separate management port are not affected: the management
child context initialises its DispatcherServlet during startup, which
instantiates the parent's mapping before Camel starts. Deterministic check:
{{{}getBeanDefinition("platformHttpEngineRequestMapping").isLazyInit(){}}}.
h3. Fix
Drop {{@Lazy}} and resolve the CamelContext from an {{ObjectProvider}} inside
the method, restoring eager instantiation while keeping the circular dependency
avoidance:
{code:java}
@Bean
public CamelRequestHandlerMapping platformHttpEngineRequestMapping(
PlatformHttpEngine engine, ObjectProvider<CamelContext> camelContextProvider) {
CamelContext camelContext = camelContextProvider.getObject();
PlatformHttpComponent component = camelContext.getComponent("platform-http",
PlatformHttpComponent.class);
return new CamelRequestHandlerMapping(component, engine);
}
{code}
h3. Workaround
{{{}spring.mvc.servlet.load-on-startup=1{}}}: Tomcat's
{{performDeferredLoadOnStartup()}} runs inside {{{}onRefresh(){}}}, before the
{{ContextRefreshedEvent}} that starts Camel.
PR: [https://github.com/apache/camel-spring-boot/pull/1874]
> camel-platform-http-starter: CamelRequestHandlerMapping is @Lazy, so every
> platform-http endpoint can silently return 404
> ---------------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-24351
> URL: https://issues.apache.org/jira/browse/CAMEL-24351
> Project: Camel
> Issue Type: Bug
> Components: camel-platform-http
> Affects Versions: 4.21.0
> Reporter: Henrik
> Priority: Major
> Labels: regression, spring-boot
> Attachments:
> 0001-CAMEL-24351-camel-platform-http-starter-the-request-.patch
>
>
> On 4.21.0 a Spring Boot application can start with every platform-http route
> reporting itself started, while every request to them returns 404 from
> Spring's {{{}BasicErrorController{}}}:
> {code:json}
> {"status":404,"error":"Not Found","path":"/myroute"}
> {code}
> Camel logs nothing for these requests because no route is invoked, and the
> app looks healthy: startup shows {{{}Started myroute
> (rest://get:/myroute){}}}, non-HTTP routes work, health probes stay green.
> Whether an instance is affected is decided at startup and fixed for its
> lifetime, so restarts appear to fix it at random.
> h3. Cause
> {{SpringBootPlatformHttpAutoConfiguration}} declares the mapping bean
> {{{}@Bean @Lazy{}}}. {{CamelRequestHandlerMapping}} registers itself as a
> {{PlatformHttpListener}} in its constructor, and
> {{PlatformHttpComponent.addHttpEndpoint()}} notifies only listeners that
> already exist. A lazy bean is created on first demand, which is not ordered
> against CamelContext startup: if Camel starts before anything resolves
> {{HandlerMapping}} beans, all endpoints are registered before the mapping
> exists and its registry stays empty forever. It is silent because
> {{addPlatformHttpListener()}} does not replay {{getHttpEndpoints()}} to a new
> listener, and {{addHttpEndpoint()}} logs listener exceptions at WARN only.
> h3. Why 4.21.0
> Eager singletons are created in {{{}preInstantiateSingletons(){}}}, always
> before {{finishRefresh()}} starts Camel, so the listener could not miss a
> registration.
> ||Version||Annotation||Instantiation||
> |4.18.3|none|eager|
> |4.20.0|@DependsOn|eager|
> |4.21.0|@Lazy|lazy|
> Verified from the constant pool of the released class in each starter jar.
> {{@Lazy}} arrived in commit 264db4c12e0 ("Fix compilation error in
> camel-aws-s3 (#1783)"), which replaced an {{ObjectProvider<CamelContext>}}
> parameter, used to defer the lookup and avoid a circular dependency, with
> direct injection plus {{{}@Lazy{}}}.
> Applications with a separate management port are not affected: the management
> child context initialises its DispatcherServlet during startup, which
> instantiates the parent's mapping before Camel starts. Deterministic check:
> {{{}getBeanDefinition("platformHttpEngineRequestMapping").isLazyInit(){}}}.
> h3. Fix
> Drop {{@Lazy}} and resolve the CamelContext from an {{ObjectProvider}} inside
> the method, restoring eager instantiation while keeping the circular
> dependency avoidance:
> {code:java}
> @Bean
> public CamelRequestHandlerMapping platformHttpEngineRequestMapping(
> PlatformHttpEngine engine, ObjectProvider<CamelContext> camelContextProvider)
> {
> CamelContext camelContext = camelContextProvider.getObject();
> PlatformHttpComponent component = camelContext.getComponent("platform-http",
> PlatformHttpComponent.class);
> return new CamelRequestHandlerMapping(component, engine);
> }
> {code}
> h3. Workaround
> {{{}spring.mvc.servlet.load-on-startup=1{}}}: Tomcat's
> {{performDeferredLoadOnStartup()}} runs inside {{{}onRefresh(){}}}, before
> the {{ContextRefreshedEvent}} that starts Camel.
> PR: [https://github.com/apache/camel-spring-boot/pull/1874]
--
This message was sent by Atlassian Jira
(v8.20.10#820010)