This is an automated email from the ASF dual-hosted git repository.
oscerd pushed a commit to branch camel-4.22.x
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-4.22.x by this push:
new 6c8fe7302dc8 [backport camel-4.22.x] CAMEL-26343: camel-aws - set the
AWS SDK HTTP client explicitly instead of the classpath lookup (#28564)
6c8fe7302dc8 is described below
commit 6c8fe7302dc8c76d17a0011ef6bbd518d4229462
Author: Andrea Cosentino <[email protected]>
AuthorDate: Thu Oct 29 15:16:36 2026 +0100
[backport camel-4.22.x] CAMEL-26343: camel-aws - set the AWS SDK HTTP
client explicitly instead of the classpath lookup (#28564)
AwsClientBuilderUtil set an HTTP client on the SDK client builder only
with a proxy or trustAllCertificates. Otherwise the AWS SDK looked the
client up with ServiceLoader, and that lookup fails as a whole when one
registration cannot be loaded, as when a Kafka Connect plugin sees the
apache-client registration of the worker's own AWS SDK
("ApacheSdkHttpService not a subtype").
Set Apache5HttpClient (sync) and NettyNioAsyncHttpClient (async), the
implementations the lookup selects from Camel's dependencies, unless the
AWS SDK setting software.amazon.awssdk.http.service.impl (or
software.amazon.awssdk.http.async.service.impl) names one.
Signed-off-by: Andrea Cosentino <[email protected]>
Co-authored-by: Claude Opus 5.5 <[email protected]>
---
components/camel-aws/camel-aws-common/pom.xml | 6 +
.../component/aws/common/AwsClientBuilderUtil.java | 24 ++-
.../aws/common/AwsClientBuilderUtilTest.java | 162 +++++++++++++++++++++
.../software.amazon.awssdk.http.SdkHttpService | 19 +++
...re.amazon.awssdk.http.async.SdkAsyncHttpService | 19 +++
5 files changed, 228 insertions(+), 2 deletions(-)
diff --git a/components/camel-aws/camel-aws-common/pom.xml
b/components/camel-aws/camel-aws-common/pom.xml
index 7bfc97803226..61cf1e5f42e0 100644
--- a/components/camel-aws/camel-aws-common/pom.xml
+++ b/components/camel-aws/camel-aws-common/pom.xml
@@ -72,6 +72,12 @@
<artifactId>netty-nio-client</artifactId>
<version>${aws-java-sdk2-version}</version>
</dependency>
+ <dependency>
+ <groupId>software.amazon.awssdk</groupId>
+ <artifactId>sts</artifactId>
+ <version>${aws-java-sdk2-version}</version>
+ <scope>test</scope>
+ </dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
diff --git
a/components/camel-aws/camel-aws-common/src/main/java/org/apache/camel/component/aws/common/AwsClientBuilderUtil.java
b/components/camel-aws/camel-aws-common/src/main/java/org/apache/camel/component/aws/common/AwsClientBuilderUtil.java
index b2f678d8a1cf..210987a30ba9 100644
---
a/components/camel-aws/camel-aws-common/src/main/java/org/apache/camel/component/aws/common/AwsClientBuilderUtil.java
+++
b/components/camel-aws/camel-aws-common/src/main/java/org/apache/camel/component/aws/common/AwsClientBuilderUtil.java
@@ -31,6 +31,7 @@ import
software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder;
import software.amazon.awssdk.core.SdkClient;
+import software.amazon.awssdk.core.SdkSystemSetting;
import software.amazon.awssdk.core.client.builder.SdkAsyncClientBuilder;
import software.amazon.awssdk.core.client.builder.SdkSyncClientBuilder;
import software.amazon.awssdk.http.SdkHttpClient;
@@ -76,7 +77,7 @@ public final class AwsClientBuilderUtil {
Apache5HttpClient.Builder httpClientBuilder = null;
boolean httpClientConfigured = false;
- // 1. Configure proxy
+ // 1. Configure proxy, or else the default HTTP client
if (ObjectHelper.isNotEmpty(config.getProxyHost())
&& ObjectHelper.isNotEmpty(config.getProxyPort())) {
LOG.trace("Configuring proxy: {}:{}", config.getProxyHost(),
config.getProxyPort());
@@ -87,6 +88,9 @@ public final class AwsClientBuilderUtil {
.build();
httpClientBuilder =
Apache5HttpClient.builder().proxyConfiguration(proxyConfig);
httpClientConfigured = true;
+ } else if
(useDefaultHttpClient(SdkSystemSetting.SYNC_HTTP_SERVICE_IMPL)) {
+ httpClientBuilder = Apache5HttpClient.builder();
+ httpClientConfigured = true;
}
// 2. Configure credentials
@@ -167,7 +171,7 @@ public final class AwsClientBuilderUtil {
NettyNioAsyncHttpClient.Builder httpClientBuilder = null;
boolean httpClientConfigured = false;
- // 1. Configure proxy
+ // 1. Configure proxy, or else the default HTTP client
if (ObjectHelper.isNotEmpty(config.getProxyHost())
&& ObjectHelper.isNotEmpty(config.getProxyPort())) {
LOG.trace("Configuring async proxy: {}:{}", config.getProxyHost(),
config.getProxyPort());
@@ -179,6 +183,9 @@ public final class AwsClientBuilderUtil {
.build();
httpClientBuilder =
NettyNioAsyncHttpClient.builder().proxyConfiguration(proxyConfig);
httpClientConfigured = true;
+ } else if
(useDefaultHttpClient(SdkSystemSetting.ASYNC_HTTP_SERVICE_IMPL)) {
+ httpClientBuilder = NettyNioAsyncHttpClient.builder();
+ httpClientConfigured = true;
}
// 2. Configure credentials
@@ -238,6 +245,19 @@ public final class AwsClientBuilderUtil {
return buildAsyncClient(config, builderSupplier, null);
}
+ /**
+ * Whether to set the default HTTP client on the client builder: the one
the AWS SDK would select itself. Without an
+ * HTTP client on the builder, the SDK looks up every implementation
registered on the classpath, and fails if any
+ * of them cannot be loaded, for example when a Kafka Connect plugin sees
the registrations of the AWS SDK of the
+ * worker. When the given AWS SDK setting names an implementation, the
choice is left to the SDK.
+ *
+ * @param implSetting the AWS SDK setting that names the sync or async
HTTP client implementation
+ * @return true to set the default HTTP client, false when the
setting names an implementation
+ */
+ private static boolean useDefaultHttpClient(SdkSystemSetting implSetting) {
+ return implSetting.getStringValue().isEmpty();
+ }
+
/**
* Resolve the appropriate credentials provider based on configuration.
* <p>
diff --git
a/components/camel-aws/camel-aws-common/src/test/java/org/apache/camel/component/aws/common/AwsClientBuilderUtilTest.java
b/components/camel-aws/camel-aws-common/src/test/java/org/apache/camel/component/aws/common/AwsClientBuilderUtilTest.java
new file mode 100644
index 000000000000..e0943bdaa68a
--- /dev/null
+++
b/components/camel-aws/camel-aws-common/src/test/java/org/apache/camel/component/aws/common/AwsClientBuilderUtilTest.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.aws.common;
+
+import java.util.ServiceConfigurationError;
+
+import org.junit.jupiter.api.Test;
+import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
+import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
+import software.amazon.awssdk.core.Protocol;
+import software.amazon.awssdk.core.SdkSystemSetting;
+import software.amazon.awssdk.core.exception.SdkClientException;
+import software.amazon.awssdk.regions.Region;
+import software.amazon.awssdk.services.sts.StsAsyncClient;
+import software.amazon.awssdk.services.sts.StsClient;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+/**
+ * The test resources register {@link ForeignHttpService} as a sync and an
async HTTP client implementation of the AWS
+ * SDK, which it is not. The SDK lookup of the HTTP client on the classpath
then fails, as when a Kafka Connect plugin
+ * sees the registrations of the AWS SDK of the worker.
+ */
+class AwsClientBuilderUtilTest {
+
+ private static final AwsCommonConfiguration CONFIGURATION = new
StaticCredentialsConfiguration();
+
+ @Test
+ void sdkLookupFailsOnTheForeignRegistration() {
+ StaticCredentialsProvider credentials
+ =
StaticCredentialsProvider.create(AwsBasicCredentials.create("accessKey",
"secretKey"));
+
+ assertThatThrownBy(() ->
StsClient.builder().region(Region.US_EAST_1).credentialsProvider(credentials).build())
+ .isInstanceOf(ServiceConfigurationError.class)
+ .hasMessageContaining("not a subtype");
+ assertThatThrownBy(() ->
StsAsyncClient.builder().region(Region.US_EAST_1).credentialsProvider(credentials).build())
+ .isInstanceOf(ServiceConfigurationError.class)
+ .hasMessageContaining("not a subtype");
+ }
+
+ @Test
+ void buildClientDoesNotUseTheSdkLookup() {
+ try (StsClient client =
AwsClientBuilderUtil.buildClient(CONFIGURATION, StsClient::builder)) {
+ assertThat(client).isNotNull();
+ }
+ }
+
+ @Test
+ void buildAsyncClientDoesNotUseTheSdkLookup() {
+ try (StsAsyncClient client =
AwsClientBuilderUtil.buildAsyncClient(CONFIGURATION, StsAsyncClient::builder)) {
+ assertThat(client).isNotNull();
+ }
+ }
+
+ @Test
+ void sdkSettingLeavesTheChoiceToTheSdk() {
+ // the SDK fails to load the implementation the setting names, so
Camel did not set its own HTTP client
+ String property = SdkSystemSetting.SYNC_HTTP_SERVICE_IMPL.property();
+ System.setProperty(property,
"org.apache.camel.component.aws.common.MissingHttpService");
+ try {
+ assertThatThrownBy(() ->
AwsClientBuilderUtil.buildClient(CONFIGURATION, StsClient::builder))
+ .isInstanceOf(SdkClientException.class)
+ .hasMessageContaining(property);
+ } finally {
+ System.clearProperty(property);
+ }
+ }
+
+ /**
+ * Registered in the test resources as an SdkHttpService and an
SdkAsyncHttpService, without being either.
+ */
+ public static class ForeignHttpService {
+ }
+
+ private static final class StaticCredentialsConfiguration implements
AwsCommonConfiguration {
+
+ @Override
+ public String getAccessKey() {
+ return "accessKey";
+ }
+
+ @Override
+ public String getSecretKey() {
+ return "secretKey";
+ }
+
+ @Override
+ public String getSessionToken() {
+ return null;
+ }
+
+ @Override
+ public boolean isUseDefaultCredentialsProvider() {
+ return false;
+ }
+
+ @Override
+ public boolean isUseProfileCredentialsProvider() {
+ return false;
+ }
+
+ @Override
+ public boolean isUseSessionCredentials() {
+ return false;
+ }
+
+ @Override
+ public String getProfileCredentialsName() {
+ return null;
+ }
+
+ @Override
+ public Protocol getProxyProtocol() {
+ return null;
+ }
+
+ @Override
+ public String getProxyHost() {
+ return null;
+ }
+
+ @Override
+ public Integer getProxyPort() {
+ return null;
+ }
+
+ @Override
+ public String getRegion() {
+ return Region.US_EAST_1.id();
+ }
+
+ @Override
+ public boolean isOverrideEndpoint() {
+ return false;
+ }
+
+ @Override
+ public String getUriEndpointOverride() {
+ return null;
+ }
+
+ @Override
+ public boolean isTrustAllCertificates() {
+ return false;
+ }
+ }
+}
diff --git
a/components/camel-aws/camel-aws-common/src/test/resources/META-INF/services/software.amazon.awssdk.http.SdkHttpService
b/components/camel-aws/camel-aws-common/src/test/resources/META-INF/services/software.amazon.awssdk.http.SdkHttpService
new file mode 100644
index 000000000000..366f708cf2a4
--- /dev/null
+++
b/components/camel-aws/camel-aws-common/src/test/resources/META-INF/services/software.amazon.awssdk.http.SdkHttpService
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Not an HTTP client implementation: the AWS SDK lookup fails on it, see
AwsClientBuilderUtilTest
+org.apache.camel.component.aws.common.AwsClientBuilderUtilTest$ForeignHttpService
diff --git
a/components/camel-aws/camel-aws-common/src/test/resources/META-INF/services/software.amazon.awssdk.http.async.SdkAsyncHttpService
b/components/camel-aws/camel-aws-common/src/test/resources/META-INF/services/software.amazon.awssdk.http.async.SdkAsyncHttpService
new file mode 100644
index 000000000000..366f708cf2a4
--- /dev/null
+++
b/components/camel-aws/camel-aws-common/src/test/resources/META-INF/services/software.amazon.awssdk.http.async.SdkAsyncHttpService
@@ -0,0 +1,19 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# Not an HTTP client implementation: the AWS SDK lookup fails on it, see
AwsClientBuilderUtilTest
+org.apache.camel.component.aws.common.AwsClientBuilderUtilTest$ForeignHttpService