Skip to content

Commit

Permalink
Ability to specify different HTTP clients (#267)
Browse files Browse the repository at this point in the history
* Ability to specify different HTTP clients

* Ability to specify different HTTP clients

* prevent setting both http builder and client

* fixed license and style issues

* added sample configuration
  • Loading branch information
musketyr authored Oct 25, 2024
1 parent 4842207 commit af4fe2a
Show file tree
Hide file tree
Showing 27 changed files with 494 additions and 33 deletions.
32 changes: 32 additions & 0 deletions docs/guide/src/docs/asciidoc/aws.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,35 @@ aws:
endpoint: http://localhost:8000
----

The same service can also be configured with different https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/http-configuration.html[HTTP client] settings when using AWS SDK 2.x.:

[source,yaml]
----
aws:
dynamodb:
# can be url-connection or aws-crt, apache is the default
client: url-connection
# can be aws-crt, netty is the default
async-client: aws-crt
----

The client libraries must be added to the classpath. For example for Gradle add the following dependencies:

[source,groovy]
----
dependencies {
runtimeOnly "software.amazon.awssdk:aws-crt-client:$awsSdk2Version"
runtimeOnly "software.amazon.awssdk:url-connection-client:$awsSdk2Version"
runtimeOnly "software.amazon.awssdk:netty-nio-client:$awsSdk2Version"
runtimeOnly "software.amazon.awssdk:apache-client:$awsSdk2Version"
}
----

The particular client builder can be then configured using `BeanCreatedEventListener` beans:

[source,java,indent=0,options="nowrap"]
.NettyClientCustomizer
----
include::{root-dir}/subprojects/micronaut-amazon-awssdk-core/src/test/groovy/com/agorapulse/micronaut/amazon/awssdk/core/client/NettyClientCustomizer.java[tags=customizer]
----

Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ dependencies {
api "software.amazon.awssdk:sts:$project.awsSdk2Version"
api "software.amazon.awssdk:aws-query-protocol:$project.awsSdk2Version"

// HTTP clients
api "software.amazon.awssdk:aws-crt-client:$project.awsSdk2Version"
api "software.amazon.awssdk:url-connection-client:$project.awsSdk2Version"
api "software.amazon.awssdk:netty-nio-client:$project.awsSdk2Version"
api "software.amazon.awssdk:apache-client:$project.awsSdk2Version"

api group: 'com.amazonaws', name: 'amazon-dax-client', version: '1.0.230341.0'
api group: 'com.amazonaws', name: 'aws-lambda-java-events', version: awsLambdaEventsVersion
api group: 'com.fasterxml.jackson.dataformat', name: 'jackson-dataformat-cbor', version: project['jackson.datatype.version']
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package com.agorapulse.micronaut.amazon.awssdk.cloudwatch;

import com.agorapulse.micronaut.amazon.awssdk.core.client.ClientBuilderProvider;
import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.Factory;
import io.micronaut.context.annotation.Requires;
Expand Down Expand Up @@ -44,10 +45,11 @@ public class CloudWatchFactory {
CloudWatchClient cloudWatch(
AwsCredentialsProvider credentialsProvider,
AwsRegionProvider awsRegionProvider,
ClientBuilderProvider builderProvider,
CloudWatchConfiguration configuration
) {
return configuration
.configure(CloudWatchClient.builder().credentialsProvider(credentialsProvider), awsRegionProvider)
.configure(CloudWatchClient.builder().credentialsProvider(credentialsProvider), awsRegionProvider, builderProvider, Optional.empty())
.build();
}

Expand All @@ -58,11 +60,11 @@ CloudWatchAsyncClient cloudWatchAsync(
AwsCredentialsProvider credentialsProvider,
AwsRegionProvider awsRegionProvider,
CloudWatchConfiguration configuration,
ClientBuilderProvider builderProvider,
Optional<SdkAsyncHttpClient> httpClient
) {
CloudWatchAsyncClientBuilder builder = configuration
.configure(CloudWatchAsyncClient.builder().credentialsProvider(credentialsProvider), awsRegionProvider);
httpClient.ifPresent(builder::httpClient);
.configure(CloudWatchAsyncClient.builder().credentialsProvider(credentialsProvider), awsRegionProvider, builderProvider, httpClient);
return builder.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
*/
package com.agorapulse.micronaut.amazon.awssdk.cloudwatchlogs;

import com.agorapulse.micronaut.amazon.awssdk.core.client.ClientBuilderProvider;
import io.micronaut.aws.sdk.v2.service.cloudwatchlogs.CloudwatchLogsClientFactory;
import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.Factory;
Expand Down Expand Up @@ -47,10 +48,11 @@ public class CloudWatchLogsFactory {
CloudWatchLogsClient cloudWatchLogs(
AwsCredentialsProvider credentialsProvider,
AwsRegionProvider awsRegionProvider,
ClientBuilderProvider builderProvider,
CloudWatchLogsConfiguration configuration
) {
return configuration
.configure(CloudWatchLogsClient.builder().credentialsProvider(credentialsProvider), awsRegionProvider)
.configure(CloudWatchLogsClient.builder().credentialsProvider(credentialsProvider), awsRegionProvider, builderProvider, Optional.empty())
.build();
}

Expand All @@ -61,12 +63,12 @@ CloudWatchLogsClient cloudWatchLogs(
CloudWatchLogsAsyncClient cloudWatchAsync(
AwsCredentialsProvider credentialsProvider,
AwsRegionProvider awsRegionProvider,
ClientBuilderProvider builderProvider,
CloudWatchLogsConfiguration configuration,
Optional<SdkAsyncHttpClient> httpClient
) {
CloudWatchLogsAsyncClientBuilder builder = configuration
.configure(CloudWatchLogsAsyncClient.builder().credentialsProvider(credentialsProvider), awsRegionProvider);
httpClient.ifPresent(builder::httpClient);
.configure(CloudWatchLogsAsyncClient.builder().credentialsProvider(credentialsProvider), awsRegionProvider, builderProvider, httpClient);
return builder.build();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,15 @@
*/
dependencies {
api 'software.amazon.awssdk:aws-core'

// HTTP clients
compileOnly "software.amazon.awssdk:aws-crt-client:$project.awsSdk2Version"
compileOnly "software.amazon.awssdk:url-connection-client:$project.awsSdk2Version"
compileOnly "software.amazon.awssdk:netty-nio-client:$project.awsSdk2Version"
compileOnly "software.amazon.awssdk:apache-client:$project.awsSdk2Version"

testImplementation "software.amazon.awssdk:aws-crt-client:$project.awsSdk2Version"
testImplementation "software.amazon.awssdk:url-connection-client:$project.awsSdk2Version"
testImplementation "software.amazon.awssdk:netty-nio-client:$project.awsSdk2Version"
testImplementation "software.amazon.awssdk:apache-client:$project.awsSdk2Version"
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,25 @@ public void setEndpoint(String endpoint) {
this.endpoint = endpoint;
}

public String getClient() {
return client;
}

public void setClient(String client) {
this.client = client;
}

public String getAsyncClient() {
return asyncClient;
}

public void setAsyncClient(String asyncClient) {
this.asyncClient = asyncClient;
}

@Nullable private String region;
@Nullable private String endpoint;
@Nullable private String client;
@Nullable private String asyncClient;

}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,12 @@
*/
package com.agorapulse.micronaut.amazon.awssdk.core;

import com.agorapulse.micronaut.amazon.awssdk.core.client.ClientBuilderProvider;
import software.amazon.awssdk.awscore.client.builder.AwsAsyncClientBuilder;
import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder;
import software.amazon.awssdk.awscore.presigner.SdkPresigner;
import software.amazon.awssdk.core.client.builder.SdkSyncClientBuilder;
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.regions.providers.AwsRegionProvider;

Expand All @@ -32,7 +36,11 @@ public interface RegionAndEndpointConfiguration {

String getEndpoint();

default <C, B extends AwsClientBuilder<B, C>> B configure(B builder, AwsRegionProvider awsRegionProvider) {
String getClient();

String getAsyncClient();

default <C, B extends AwsClientBuilder<B, C>> B configure(B builder, AwsRegionProvider awsRegionProvider, ClientBuilderProvider builderProvider, Optional<SdkAsyncHttpClient> httpClient) {
builder.region(Optional.ofNullable(getRegion()).map(Region::of).orElseGet(awsRegionProvider::getRegion));

if (getEndpoint() != null) {
Expand All @@ -43,6 +51,16 @@ default <C, B extends AwsClientBuilder<B, C>> B configure(B builder, AwsRegionPr
}
}

if (getClient() != null && builder instanceof SdkSyncClientBuilder<?, ?> clientBuilder) {
builderProvider.findHttpClientBuilder(getClient()).ifPresent(clientBuilder::httpClientBuilder);
}

if (getAsyncClient() != null && builder instanceof AwsAsyncClientBuilder<?, ?> clientBuilder) {
builderProvider.findAsyncHttpClientBuilder(getClient()).ifPresent(clientBuilder::httpClientBuilder);
} else if (httpClient.isPresent() && builder instanceof AwsAsyncClientBuilder<?, ?> clientBuilder) {
clientBuilder.httpClient(httpClient.get());
}

return builder;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright 2018-2024 Agorapulse.
*
* Licensed 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
*
* https://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 com.agorapulse.micronaut.amazon.awssdk.core.client;

import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.Factory;
import io.micronaut.context.annotation.Requires;
import jakarta.inject.Named;
import jakarta.inject.Singleton;
import software.amazon.awssdk.http.apache.ApacheHttpClient;

@Factory
@Requires(classes = ApacheHttpClient.class)
public class ApacheHttpClientBuilderFactory {

@Bean
@Singleton
@Named("apache")
public ApacheHttpClient.Builder awsCrtHttpClientBuilder() {
return ApacheHttpClient.builder();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright 2018-2024 Agorapulse.
*
* Licensed 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
*
* https://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 com.agorapulse.micronaut.amazon.awssdk.core.client;

import io.micronaut.context.annotation.Bean;
import io.micronaut.context.annotation.Factory;
import io.micronaut.context.annotation.Requires;
import jakarta.inject.Named;
import jakarta.inject.Singleton;
import software.amazon.awssdk.http.crt.AwsCrtAsyncHttpClient;
import software.amazon.awssdk.http.crt.AwsCrtHttpClient;

@Factory
@Requires(classes = {AwsCrtHttpClient.class, AwsCrtAsyncHttpClient.class})
public class AwsCrtHttpClientBuilderFactory {

@Bean
@Singleton
@Named("aws-crt")
public AwsCrtAsyncHttpClient.Builder awsCrtHttpAsyncClientBuilder() {
return AwsCrtAsyncHttpClient.builder();
}

@Bean
@Singleton
@Named("aws-crt")
public AwsCrtHttpClient.Builder awsCrtHttpClientBuilder() {
return AwsCrtHttpClient.builder();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright 2018-2024 Agorapulse.
*
* Licensed 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
*
* https://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 com.agorapulse.micronaut.amazon.awssdk.core.client;

import software.amazon.awssdk.http.SdkHttpClient;
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;

import java.util.Optional;

public interface ClientBuilderProvider {

String APACHE = "apache";
String AWS_CRT = "aws-crt";
String URL_CONNECTION = "url-connection";
String NETTY = "netty";

<B extends SdkHttpClient.Builder<B>> Optional<SdkHttpClient.Builder<B>> findHttpClientBuilder(String implementation);

<B extends SdkAsyncHttpClient.Builder<B>> Optional<SdkAsyncHttpClient.Builder<B>> findAsyncHttpClientBuilder(String implementation);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* Copyright 2018-2024 Agorapulse.
*
* Licensed 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
*
* https://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 com.agorapulse.micronaut.amazon.awssdk.core.client;

import io.micronaut.context.annotation.Bean;
import software.amazon.awssdk.http.SdkHttpClient;
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;

import java.util.Map;
import java.util.Optional;

@Bean
public class DefaultClientBuilderProvider implements ClientBuilderProvider {

private final Map<String, SdkHttpClient.Builder<?>> httpClientBuilders;
private final Map<String, SdkAsyncHttpClient.Builder<?>> httpAsyncClientBuilders;

public DefaultClientBuilderProvider(Map<String, SdkHttpClient.Builder<?>> httpClientBuilders, Map<String, SdkAsyncHttpClient.Builder<?>> httpAsyncClientBuilders) {
this.httpClientBuilders = httpClientBuilders;
this.httpAsyncClientBuilders = httpAsyncClientBuilders;
}

@Override
public <B extends SdkHttpClient.Builder<B>> Optional<SdkHttpClient.Builder<B>> findHttpClientBuilder(String implementation) {
return Optional.ofNullable((SdkHttpClient.Builder<B>) httpClientBuilders.get(implementation));
}

@Override
public <B extends SdkAsyncHttpClient.Builder<B>> Optional<SdkAsyncHttpClient.Builder<B>> findAsyncHttpClientBuilder(String implementation) {
return Optional.ofNullable((SdkAsyncHttpClient.Builder<B>) httpAsyncClientBuilders.get(implementation));
}

}
Loading

0 comments on commit af4fe2a

Please sign in to comment.