[
https://issues.apache.org/jira/browse/CAMEL-26310?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Work on CAMEL-26310 started by shashank.
----------------------------------------
> camel-core - a component created on first use can be used by another thread
> before it has started (endpoints created on a component that is still
> starting)
> -----------------------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-26310
> URL: https://issues.apache.org/jira/browse/CAMEL-26310
> Project: Camel
> Issue Type: Bug
> Components: camel-core
> Reporter: shashank
> Assignee: shashank
> Priority: Major
>
> A component that is created on first use after CamelContext has started is
> started by the thread that created it, after {{components.computeIfAbsent}}
> has returned ({{AbstractCamelContext.getComponent}}, camel-base-engine, main
> c536c99caaaa). Another thread that asks for the same component meanwhile gets
> it from the map before it has started ({{created}} is false for that thread,
> so it does not start it), and {{doGetEndpoint}} goes on to
> {{component.createEndpoint(...)}} and starts the new endpoint on a component
> that is still starting (or not started at all yet).
> A component that sets up in {{doStart}} what its endpoints use then fails for
> that thread. For example {{VertxHttpComponent.doStart}} creates its managed
> {{Vertx}}, and {{VertxHttpEndpoint.doStart}} creates its {{WebClient}} from
> {{getComponent().getVertx()}}: the second thread gets
> {{WebClient.create(null, ...)}}. The first sends to a new scheme after
> startup are usually made from several threads at once (a ProducerTemplate in
> a web service, a toD, recipient list, routing slip or enrich with a scheme
> that is only known at runtime), so they fail intermittently with
> {{ResolveEndpointFailedException}} or a NullPointerException from the
> component.
> CAMEL-14891 (3.3.0, toD) and CAMEL-20230 (4.4.0, enrich and pollEnrich) work
> around it by resolving the component of a uri with a fixed scheme when the
> route starts; the race in {{getComponent}} remains for the other paths (a
> {{ProducerTemplate}}, recipient list, routing slip, or a toD / enrich whose
> scheme is only known at runtime). It also happens while CamelContext is
> starting: the consumers of the routes that have already started can send
> while the later routes start.
> Reproduced on main (latches, no sleeps): a component bound in the registry
> whose {{doStart}} waits on a latch and creates a client its
> {{createEndpoint}} uses; after context start, thread 1 calls
> {{context.getEndpoint("slow:a")}} (creates and starts the component, held in
> {{doStart}}), then thread 2 calls {{context.getEndpoint("slow:b")}}: thread 2
> creates its endpoint while the component is {{Starting}} and fails
> ({{ResolveEndpointFailedException}} ... {{Cannot invoke "Object.hashCode()"
> because "this.client" is null}}). The same from a
> {{StartupListener.onCamelContextStarting}} (CamelContext starting).
> *Fix*: when {{getComponent}} returns a component that another thread has just
> created and that has not started yet or is starting (CamelContext started or
> starting, {{autoStart}}), it starts it with {{ServiceHelper.startService}}:
> the service lock of the component makes it wait while the creating thread is
> in {{doStart}}, and it is a no-op once started. If the creating thread failed
> to start it, the waiting thread tries to start it once more and gets an
> exception or a started component. A component that has been stopped (such as
> by {{removeComponent}}) or that failed to start before is returned as it is,
> as before. The component is still started outside {{computeIfAbsent}} (the
> deadlock the comment there mentions).
> *Tests*: new {{ComponentStartedBeforeUseTest}} (camel-core; latches, no
> sleeps; a component whose {{doStart}} waits on a latch and then creates the
> client its {{createEndpoint}} uses):
> {{testOtherThreadWaitsForTheComponentToStart}} (after CamelContext has
> started) and
> {{testOtherThreadWaitsForTheComponentToStartWhileCamelContextStarts}} (from
> {{StartupListener.onCamelContextStarting}}) fail on main
> ({{ResolveEndpointFailedException}} caused by a null client) and pass with
> the fix; {{testStoppedComponentIsNotStartedAgain}} is a control that passes
> on both. camel-management component, endpoint and CamelContext tests: 94, 0
> failures.
> *How it was found*: by reading the check-then-act uses of
> {{ConcurrentHashMap}} in camel-base-engine ({{computeIfAbsent}} with a side
> effect after it returns); reproduced on HotSpot with the test above.
> *Affected*: main, camel-4.22.x, camel-4.18.x and camel-4.14.x have the same
> {{getComponent}} (read, not run).
> _Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)