[
https://issues.apache.org/jira/browse/CAMEL-25288?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Claus Ibsen resolved CAMEL-25288.
---------------------------------
Resolution: Fixed
Fixed via https://github.com/apache/camel/pull/27320
> camel-dapr - the consumers close the configured or autowired Dapr clients
> when they stop, and the configuration consumer fails to start in a route with
> a NullPointerException
> ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-25288
> URL: https://issues.apache.org/jira/browse/CAMEL-25288
> Project: Camel
> Issue Type: Bug
> Components: camel-dapr
> Reporter: shashank
> Assignee: shashank
> Priority: Major
> Fix For: 4.23.0
>
>
> The {{client}}, {{previewClient}} and {{workflowClient}} options are
> autowired ({{@Metadata(autowired = true)}}): a single {{DaprClient}} /
> {{DaprPreviewClient}} bean in the registry (for example the beans of the Dapr
> Spring Boot starter) is used by every dapr endpoint. The consumers close
> clients that they do not own:
> h3. 1. DaprPubSubConsumer closes a configured or autowired preview client
> {code:java}
> protected void doStop() throws Exception {
> if (subscription != null) {
> subscription.close();
> }
> if (client != null) {
> client.close(); // also the configured / autowired previewClient
> }
> ...
> {code}
> After the route is stopped (or restarted), every other dapr endpoint using
> that client fails (the gRPC channel is shut down), and the route itself
> cannot subscribe again: a route restart creates a new consumer, which gets
> the same closed client from the configuration.
> h3. 2. DaprConfigurationConsumer closes the client of its endpoint, and never
> ends its subscription
> {code:java}
> protected void doStop() throws Exception {
> if (client != null) {
> client.unsubscribeConfiguration(subscriptionId, configStore); // a
> Mono that is never subscribed: does nothing
> client.close(); //
> the endpoint's client
> }
> ...
> {code}
> The client is {{endpoint.getClient()}}: the configured / autowired
> {{DaprClient}} shared by all dapr endpoints, or the client that the endpoint
> created for all its producers and consumers. The {{Flux}} subscription is
> never disposed and the unsubscribe {{Mono}} is never subscribed, so the
> subscription is not ended at Dapr when the client is not closed. The
> subscription id was only recorded from a response without items.
> h3. 3. DaprConfigurationConsumer cannot start in a route
> The consumer takes {{endpoint.getClient()}} in its constructor, but the
> endpoint creates (or takes the configured) client in {{doStart}}, and a route
> creates its consumer before it starts the endpoint. In a route the consumer
> has no client and the route fails to start:
> {noformat}
> FailedToStartRoute Failed to start route: configuration because: Cannot invoke
> "io.dapr.client.DaprClient.subscribeConfiguration(io.dapr.client.domain.SubscribeConfigurationRequest)"
> because "this.client" is null
> {noformat}
> The existing unit test builds the consumer with a mocked endpoint whose
> client is already set, so it did not show this.
> h3. Reproduction (unit tests, no Dapr sidecar)
> {{DaprConsumerSharedClientTest}}: a {{CamelTestSupport}} test with mocked
> {{DaprClient}}, {{DaprPreviewClient}} and {{DaprWorkflowClient}} beans in the
> registry (autowired), a {{dapr:pubSub}} route and a {{dapr:configuration}}
> route.
> * Stop and start the pub/sub route: on main {{previewClient.close()}} is
> called ({{NeverWantedButInvoked: daprPreviewClient.close()}}).
> * Start the configuration route: on main it fails with the
> {{NullPointerException}} above. With only the client taken in {{doStart}},
> stopping it does not end the subscription ({{expected: <true> but was:
> <false>}}), does not unsubscribe and closes the shared client.
> * The existing {{DaprPubSubConsumerTest}} / {{DaprConfigurationConsumerTest}}
> asserted {{close()}} on the configured / endpoint client; they now assert
> that it is not closed (and that the configuration consumer unsubscribes with
> the subscription id).
> h3. Proposed fix
> Ownership as in the other components (for example the AWS components close
> only the clients that they created):
> * {{DaprPubSubConsumer}} closes only a preview client that it created (none
> configured); then a restart creates a new one.
> * {{DaprConfigurationConsumer}} takes the endpoint's client in {{doStart}},
> records the subscription id from every response, and on stop disposes its
> subscription and unsubscribes (blocking up to 10 seconds, a failure is
> logged), without closing the client. Both are needed with Dapr Java SDK
> 1.18.1: its {{Flux}} does not cancel the gRPC stream when it is disposed, so
> the explicit {{unsubscribeConfiguration}} is what ends the subscription at
> the sidecar (the sidecar logs that this call is deprecated in favour of
> disconnecting).
> * {{DaprEndpoint}} closes the {{DaprClient}}, {{DaprPreviewClient}} and
> {{DaprWorkflowClient}} that it created in {{doStart}} when it stops (as
> {{Sqs2Endpoint}} does), never the configured or autowired ones; it creates
> new ones when it is started again.
> No upgrade note: a configured or autowired client was closed by Camel and now
> stays open for its owner (Spring or the application), as for the other
> components.
> {{DaprEndpointClientOwnershipTest}} (2): the endpoint closes the clients it
> created (Mockito {{mockConstruction}} of {{DaprClientBuilder}} /
> {{DaprWorkflowClient}}; fails without the endpoint change: {{Wanted but not
> invoked: daprClient.close()}}) and never the configured ones, which it uses
> again after a restart.
> With the fix the camel-dapr unit tests pass (56).
> Affected: 4.14.x, 4.18.x and main (same code).
> Duplicate check (2026-10-03): JIRA component camel-dapr / text "dapr" (9
> issues): CAMEL-25274 (ours, acknowledgement), CAMEL-23645 (open, move to the
> new Topic API), feature issues. GitHub pull requests
> "DaprConfigurationConsumer", "camel-dapr close client": none.
> _Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)