Skip to content

CAMEL-23338: added OpenSearchClient as parameter to pass custom instance#24320

Open
renjth-81 wants to merge 6 commits into
apache:mainfrom
renjth-81:opensearch-custom-openSearchClient
Open

CAMEL-23338: added OpenSearchClient as parameter to pass custom instance#24320
renjth-81 wants to merge 6 commits into
apache:mainfrom
renjth-81:opensearch-custom-openSearchClient

Conversation

@renjth-81

@renjth-81 renjth-81 commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Description

Added OpenSearchClient as a parameter to allow custom OpenSearchClient instance to be passed.

Target

  • [ ✓] I checked that the commit is targeting the correct branch (Camel 4 uses the main branch)

Tracking

  • If this is a large change, bug fix, or code improvement, I checked there is a JIRA issue filed for the change (usually before you start working on it).

Apache Camel coding standards and style

  • [ ✓] I checked that each commit in the pull request has a meaningful subject line and body.
  • [ ✓] I have run mvn clean install -DskipTests locally from root folder and I have committed all auto-generated changes.

AI-assisted contributions

  • [ x] If this PR includes AI-generated code, commits have proper co-authorship attribution (e.g., Co-authored-by trailers) and the PR description identifies the AI tool used.

@github-actions

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@davsclaus

Copy link
Copy Markdown
Contributor

you need to also rebuild camel-catalog, and some code in component and endpoint dsl

@davsclaus

Copy link
Copy Markdown
Contributor

There are uncommitted changes
HEAD detached at pull/24320/merge
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git restore ..." to discard changes in working directory)
modified: catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/opensearch.json
modified: dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/OpensearchComponentBuilderFactory.java
modified: dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/OpensearchEndpointBuilderFactory.java

@oscerd oscerd left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution, @renjth-81 — being able to pass a custom OpenSearchClient is a useful addition, and the generated catalog/DSL files are all correctly regenerated. However, there's a blocking correctness problem in the current implementation that CI is also catching, so I'm requesting changes.

Blocker: the cached transport captures a null RestClient → NPE on every exchange

OpensearchProducer now builds and caches openSearchClient in its constructor:

  • the constructor sets this.client = endpoint.getClient(); then this.openSearchClient = createDefaultOpenSearchClient();
  • createDefaultOpenSearchClient() builds new OpenSearchClient(new RestClientTransport(client, ...)), but only calls startClient() when configuration.isDisconnect() && client == null.

In the standard configuration (a RestClient built from hostAddresses, no autowired client), endpoint.getClient() is null at construction time — the component creates the endpoint with a null client by default (OpensearchComponent.java:91), and the real RestClient is created lazily later in doStart() → startClient() (OpensearchProducer.java, the double-checked-locking block around lines 452–467). disconnect defaults to false (OpensearchConfiguration.java:60), so createDefaultOpenSearchClient() does not start the client and ends up wrapping a RestClientTransport around a null RestClient.

process() then does OpenSearchTransport transport = openSearchClient._transport(); and reuses that cached transport, so every exchange dereferences the null RestClient:

java.lang.NullPointerException: Cannot invoke
  "org.opensearch.client.RestClient.performRequestAsync(...)" because "this.restClient" is null

This is exactly what CI is failing on — build (17, false) and build (25, false) are both red, with OpensearchPingIT.testPing and all OpensearchBulkIT.* failing with this NPE. The user-supplied-client path (endpoint.getOpenSearchClient() != null) works, but the default path — which all of the component's own integration tests exercise — is broken.

Suggested direction

Resolve the transport lazily rather than in the constructor: keep using the user-supplied OpenSearchClient when one is provided, but for the default case build the transport in process() / after doStart() once client is non-null (as the original code did). Please also keep the disconnect=true semantics in mind — the old process() rebuilt the transport per call and cleanup() closes and nulls the client after each exchange (OpensearchProducer.java, around lines 437–445), so a single cached transport would also break disconnect mode after the first exchange.

Other items

  • Tests: please add a test proving a custom OpenSearchClient instance is actually honored at runtime — the new option currently has no coverage, and the existing integration tests regressed.
  • Jira: apache/camel changes should be tracked by a CAMEL-XXXXX issue with the commit/PR title prefixed accordingly. Could you create one (component camel-opensearch) and update the title? The added missing files commit subject could also be made more descriptive.

For context: this PR currently shows as approved, but that review was on the very first commit (62b5af1), before the generated-files commit and before CI ran — so it predates the failures above.

Thanks again for working on this — happy to re-review once the transport init is made lazy and CI is green.


Reviewed with Claude Code on behalf of Andrea Cosentino (@oscerd). This review was generated by an AI agent and may contain inaccuracies; please verify all suggestions before applying.

@davsclaus

Copy link
Copy Markdown
Contributor

creating a new client should ideally not be done in constructors but in doInit or doStart

@renjth-81 renjth-81 changed the title added OpenSearchClient as parameter to pass custom instance CAMEL-23338 - added OpenSearchClient as parameter to pass custom instance Jul 2, 2026
@renjth-81 renjth-81 changed the title CAMEL-23338 - added OpenSearchClient as parameter to pass custom instance CAMEL-23338: added OpenSearchClient as parameter to pass custom instance Jul 2, 2026
…nt' into opensearch-custom-openSearchClient
@renjth-81 renjth-81 requested a review from oscerd July 2, 2026 11:07
@renjth-81

renjth-81 commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

@davsclaus @oscerd I have pushed the fixes and integration test. Please could you review

@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • catalog/camel-catalog
  • components/camel-opensearch
  • dsl/camel-componentdsl
  • dsl/camel-endpointdsl

🔬 Scalpel shadow comparison — Scalpel: 12 tested, 26 compile-only — current: 38 all tested

Maveniverse Scalpel detected 38 affected modules (current approach: 38).

Skip-tests mode would test 12 modules (4 direct + 8 downstream), skip tests for 26 (generated code, meta-modules)

Modules Scalpel would test (12)
  • camel-catalog
  • camel-componentdsl
  • camel-endpointdsl
  • camel-jbang-mcp
  • camel-jbang-plugin-mcp
  • camel-jbang-plugin-route-parser
  • camel-jbang-plugin-tui
  • camel-jbang-plugin-validate
  • camel-launcher-container
  • camel-opensearch
  • camel-yaml-dsl-validator
  • camel-yaml-dsl-validator-maven-plugin
Modules with tests skipped (26)
  • apache-camel
  • camel-allcomponents
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-csimple-maven-plugin
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • docs
  • dummy-component

ℹ️ Shadow mode — Scalpel observes but does not affect test execution. Learn more

All tested modules (38 modules)
  • Camel :: All Components Sync point
  • Camel :: Assembly
  • Camel :: Catalog :: CSimple Maven Plugin (deprecated)
  • Camel :: Catalog :: Camel Catalog
  • Camel :: Catalog :: Camel Report Maven Plugin
  • Camel :: Catalog :: Camel Route Parser
  • Camel :: Catalog :: Console
  • Camel :: Catalog :: Dummy Component
  • Camel :: Catalog :: Lucene (deprecated)
  • Camel :: Catalog :: Maven
  • Camel :: Catalog :: Suggest
  • Camel :: Component DSL
  • Camel :: Coverage
  • Camel :: Docs
  • Camel :: Endpoint DSL
  • Camel :: Endpoint DSL :: Support
  • Camel :: Integration Tests
  • Camel :: JBang :: Core
  • Camel :: JBang :: Integration tests
  • Camel :: JBang :: MCP
  • Camel :: JBang :: Main
  • Camel :: JBang :: Plugin :: Edit
  • Camel :: JBang :: Plugin :: Generate
  • Camel :: JBang :: Plugin :: Kubernetes
  • Camel :: JBang :: Plugin :: MCP
  • Camel :: JBang :: Plugin :: Route Parser
  • Camel :: JBang :: Plugin :: TUI
  • Camel :: JBang :: Plugin :: Testing
  • Camel :: JBang :: Plugin :: Validate
  • Camel :: Kamelet Main
  • Camel :: Launcher
  • Camel :: Launcher :: Container
  • Camel :: OpenSearch Java API Client
  • Camel :: YAML DSL
  • Camel :: YAML DSL :: Deserializers
  • Camel :: YAML DSL :: Maven Plugins
  • Camel :: YAML DSL :: Validator
  • Camel :: YAML DSL :: Validator Maven Plugin

⚙️ View full build and test results

@davsclaus davsclaus left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for addressing the NPE blocker from the previous review, @renjth-81 — the lazy transport resolution in process() is the right approach. However, there are a couple of remaining issues that need fixing before this can be merged.

Blocker: Missing Mockito dependency

The new test CustomOpensearchClientPingIT.java uses Mockito.mock(), Mockito.when(), and RETURNS_SMART_NULLS, but components/camel-opensearch/pom.xml does not declare mockito-core as a test dependency. No transitive path brings it in either (camel-test-junit6, camel-test-infra-core, and camel-test-infra-opensearch do not depend on Mockito). The module will fail to compile.

Please add to pom.xml:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>${mockito-version}</version>
    <scope>test</scope>
</dependency>

Bug: Transport closed on user-provided client when disconnect=true

In OpensearchProducer.cleanup(), IOHelper.close(ctx.transport()) is called when disconnect=true. For the default path this is safe — a fresh RestClientTransport is created per process() call. But when a custom OpenSearchClient is provided, ctx.transport() is openSearchClient._transport() — the user's own transport. Closing it will corrupt the user's client for future use.

The test exercises this path (disconnect=true + custom client), but since the mock's close() is a no-op, the bug is masked.

Suggested fix: skip closing the transport and client in cleanup() when a custom OpenSearchClient was provided.

Minor: doStart() logs spurious warning with custom client

doStart() unconditionally calls startClient() when disconnect=false. When a user provides only an OpenSearchClient (no hostAddresses), startClient() logs: "Incorrect ip address and port parameters settings for OpenSearch cluster". Consider skipping startClient() when a custom OpenSearchClient is set.

Minor: Commit hygiene

  • Per project conventions, commits should be prefixed with CAMEL-23338:.
  • There's a merge commit and duplicate commit messages — please squash/clean up before merge.
  • A spurious blank line was added in OpensearchProducer.java between Integer from = message.getHeader(...) and if (from == null) — please remove.

Note: this review does not replace specialized AI review tools (CodeRabbit, Sourcery) or static analysis (SonarCloud).

This review was generated by an AI agent and may contain inaccuracies. Please verify all suggestions before applying.


import org.apache.camel.builder.RouteBuilder;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing dependency: mockito-core is not declared as a test dependency in components/camel-opensearch/pom.xml and is not transitively available. This import (and all Mockito usage in this file) will cause a compilation failure.

Please add to pom.xml:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>${mockito-version}</version>
    <scope>test</scope>
</dependency>

final ObjectMapper mapper = new ObjectMapper();
mapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
OpenSearchTransport transport = new RestClientTransport(client, new JacksonJsonpMapper(mapper));
// 2. Index and type will be set by:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Transport lifecycle bug with disconnect=true: When a custom OpenSearchClient is provided and disconnect=true, cleanup() (line ~438) calls IOHelper.close(ctx.transport()). In this branch, transport is openSearchClient._transport() — the user's own transport. Closing it will corrupt the user's client.

The default path creates a fresh RestClientTransport per call, so closing it is safe. But the custom client's transport should not be closed by the component.

Suggested fix: pass a flag into ActionContext indicating whether the transport is user-owned, and skip IOHelper.close(ctx.transport()) in cleanup() when it is.

@@ -188,6 +195,7 @@ public boolean process(Exchange exchange, AsyncCallback callback) {
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: this blank line was not present before and is not needed — please remove to keep the diff clean.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants