Skip to content

Commit

Permalink
Amazon Services - Use synthetic beans to push builders to producers
Browse files Browse the repository at this point in the history
This simplifies things a lot and will also avoid race conditions.
  • Loading branch information
gsmet committed Jun 10, 2021
1 parent b0b4550 commit ef6d0d8
Show file tree
Hide file tree
Showing 28 changed files with 333 additions and 659 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import java.util.Optional;
import java.util.function.Function;

import javax.enterprise.context.ApplicationScoped;

import org.jboss.jandex.DotName;
import org.jboss.jandex.Type;

Expand All @@ -18,6 +20,7 @@
import io.quarkus.amazon.common.runtime.SyncHttpClientBuildTimeConfig;
import io.quarkus.amazon.common.runtime.SyncHttpClientConfig;
import io.quarkus.arc.deployment.BeanRegistrationPhaseBuildItem;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.arc.processor.BuildExtension;
import io.quarkus.arc.processor.InjectionPointInfo;
import io.quarkus.deployment.Feature;
Expand All @@ -26,7 +29,6 @@
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.runtime.RuntimeValue;
import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder;
import software.amazon.awssdk.core.SdkClient;
import software.amazon.awssdk.http.SdkHttpClient;
import software.amazon.awssdk.http.async.SdkAsyncHttpClient;

Expand Down Expand Up @@ -148,68 +150,57 @@ protected void createNettyAsyncTransportBuilder(List<AmazonClientBuildItem> amaz
});
}

protected void createClientBuilders(List<AmazonClientSyncTransportBuildItem> syncClientBuilders,
List<AmazonClientAsyncTransportBuildItem> asyncClientBuilders,
BuildProducer<AmazonClientBuilderBuildItem> builderProducer,
Function<RuntimeValue<SdkHttpClient.Builder>, RuntimeValue<AwsClientBuilder>> syncFunc,
Function<RuntimeValue<SdkAsyncHttpClient.Builder>, RuntimeValue<AwsClientBuilder>> asyncFunc) {
protected void createClientBuilders(
AmazonClientRecorder recorder,
RuntimeValue<AwsConfig> awsConfigRuntime,
RuntimeValue<SdkConfig> sdkConfigRuntime,
SdkBuildTimeConfig sdkBuildConfig,
List<AmazonClientSyncTransportBuildItem> amazonClientSyncTransports,
List<AmazonClientAsyncTransportBuildItem> amazonClientAsyncTransports,
Class<?> syncClientBuilderClass,
Function<RuntimeValue<SdkHttpClient.Builder>, RuntimeValue<AwsClientBuilder>> syncClientBuilderFunction,
Class<?> asyncClientBuilderClass,
Function<RuntimeValue<SdkAsyncHttpClient.Builder>, RuntimeValue<AwsClientBuilder>> asyncClientBuilderFunction,
BuildProducer<SyntheticBeanBuildItem> syntheticBeans) {
String configName = configName();

Optional<RuntimeValue<SdkHttpClient.Builder>> syncClientBuilder = syncClientBuilders.stream()
Optional<RuntimeValue<SdkHttpClient.Builder>> syncSdkHttpClientBuilder = amazonClientSyncTransports.stream()
.filter(c -> configName.equals(c.getAwsClientName()))
.map(c -> c.getClientBuilder())
.findFirst();
Optional<RuntimeValue<SdkAsyncHttpClient.Builder>> asyncClientBuilder = asyncClientBuilders.stream()
Optional<RuntimeValue<SdkAsyncHttpClient.Builder>> asyncSdkAsyncHttpClientBuilder = amazonClientAsyncTransports.stream()
.filter(c -> configName.equals(c.getAwsClientName()))
.map(c -> c.getClientBuilder())
.findFirst();

if (!syncClientBuilder.isPresent() && !asyncClientBuilder.isPresent()) {
if (!syncSdkHttpClientBuilder.isPresent() && !asyncSdkAsyncHttpClientBuilder.isPresent()) {
return;
}

builderProducer.produce(new AmazonClientBuilderBuildItem(configName,
syncClientBuilder.isPresent() ? syncFunc.apply(syncClientBuilder.get()) : null,
asyncClientBuilder.isPresent() ? asyncFunc.apply(asyncClientBuilder.get()) : null));
}

protected void buildClients(List<AmazonClientBuilderConfiguredBuildItem> configuredClients,
Function<RuntimeValue<? extends AwsClientBuilder>, RuntimeValue<? extends SdkClient>> syncClient,
Function<RuntimeValue<? extends AwsClientBuilder>, RuntimeValue<? extends SdkClient>> asyncClient) {

for (AmazonClientBuilderConfiguredBuildItem client : configuredClients) {
if (configName().equals(client.getAwsClientName())) {
if (client.getSyncBuilder() != null) {
syncClient.apply(client.getSyncBuilder());
}
if (client.getAsyncBuilder() != null) {
asyncClient.apply(client.getAsyncBuilder());
}
}
RuntimeValue<AwsClientBuilder> syncClientBuilder = syncSdkHttpClientBuilder.isPresent()
? syncClientBuilderFunction.apply(syncSdkHttpClientBuilder.get())
: null;
RuntimeValue<AwsClientBuilder> asyncClientBuilder = asyncSdkAsyncHttpClientBuilder.isPresent()
? asyncClientBuilderFunction.apply(asyncSdkAsyncHttpClientBuilder.get())
: null;

if (syncClientBuilder != null) {
syncClientBuilder = recorder.configure(syncClientBuilder, awsConfigRuntime, sdkConfigRuntime,
sdkBuildConfig, configName());
syntheticBeans.produce(SyntheticBeanBuildItem.configure(syncClientBuilderClass)
.setRuntimeInit()
.scope(ApplicationScoped.class)
.runtimeValue(syncClientBuilder)
.done());
}
if (asyncClientBuilder != null) {
asyncClientBuilder = recorder.configure(asyncClientBuilder, awsConfigRuntime, sdkConfigRuntime,
sdkBuildConfig, configName());
syntheticBeans.produce(SyntheticBeanBuildItem.configure(asyncClientBuilderClass)
.setRuntimeInit()
.scope(ApplicationScoped.class)
.runtimeValue(asyncClientBuilder)
.done());
}
}

protected void initClientBuilders(List<AmazonClientBuilderBuildItem> clients, AmazonClientRecorder recorder,
RuntimeValue<AwsConfig> awsConfigRuntime,
RuntimeValue<SdkConfig> sdkConfigRuntime, SdkBuildTimeConfig sdkBuildConfig,
BuildProducer<AmazonClientBuilderConfiguredBuildItem> producer) {
Optional<AmazonClientBuilderBuildItem> matchingClientBuilderBuildItem = clients.stream()
.filter(c -> c.getAwsClientName().equals(configName()))
.findAny();

matchingClientBuilderBuildItem.ifPresent(client -> {
RuntimeValue<? extends AwsClientBuilder> syncBuilder = null;
RuntimeValue<? extends AwsClientBuilder> asyncBuilder = null;

if (client.getSyncBuilder() != null) {
syncBuilder = recorder.configure(client.getSyncBuilder(), awsConfigRuntime, sdkConfigRuntime,
sdkBuildConfig, configName());
}
if (client.getAsyncBuilder() != null) {
asyncBuilder = recorder.configure(client.getAsyncBuilder(), awsConfigRuntime, sdkConfigRuntime,
sdkBuildConfig, configName());
}
producer.produce(new AmazonClientBuilderConfiguredBuildItem(configName(), syncBuilder, asyncBuilder));
});
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
import io.quarkus.amazon.common.deployment.AbstractAmazonServiceProcessor;
import io.quarkus.amazon.common.deployment.AmazonClientAsyncTransportBuildItem;
import io.quarkus.amazon.common.deployment.AmazonClientBuildItem;
import io.quarkus.amazon.common.deployment.AmazonClientBuilderBuildItem;
import io.quarkus.amazon.common.deployment.AmazonClientBuilderConfiguredBuildItem;
import io.quarkus.amazon.common.deployment.AmazonClientInterceptorsPathBuildItem;
import io.quarkus.amazon.common.deployment.AmazonClientSyncTransportBuildItem;
import io.quarkus.amazon.common.deployment.AmazonHttpClients;
Expand All @@ -21,19 +19,20 @@
import io.quarkus.amazon.dynamodb.runtime.DynamodbConfig;
import io.quarkus.amazon.dynamodb.runtime.DynamodbRecorder;
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.BeanContainerBuildItem;
import io.quarkus.arc.deployment.BeanRegistrationPhaseBuildItem;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.deployment.Feature;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.annotations.ExecutionTime;
import io.quarkus.deployment.annotations.Record;
import io.quarkus.deployment.builditem.ExtensionSslNativeSupportBuildItem;
import io.quarkus.deployment.builditem.FeatureBuildItem;
import io.quarkus.deployment.builditem.ShutdownContextBuildItem;
import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem;
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClientBuilder;
import software.amazon.awssdk.services.dynamodb.DynamoDbClient;
import software.amazon.awssdk.services.dynamodb.DynamoDbClientBuilder;

public class DynamodbProcessor extends AbstractAmazonServiceProcessor {

Expand Down Expand Up @@ -128,34 +127,23 @@ void setupNettyAsyncTransport(List<AmazonClientBuildItem> amazonClients, Dynamod

@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
void createClientBuilders(List<AmazonClientSyncTransportBuildItem> syncTransports,
List<AmazonClientAsyncTransportBuildItem> asyncTransports, DynamodbRecorder recorder,
DynamodbConfig runtimeConfig, BuildProducer<AmazonClientBuilderBuildItem> builderProducer) {

createClientBuilders(syncTransports, asyncTransports, builderProducer,
(syncTransport) -> recorder.createSyncBuilder(runtimeConfig, syncTransport),
(asyncTransport) -> recorder.createAsyncBuilder(runtimeConfig, asyncTransport));
}

@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
void configureClient(List<AmazonClientBuilderBuildItem> clients, DynamodbRecorder recorder,
void createClientBuilders(DynamodbRecorder recorder,
AmazonClientRecorder commonRecorder,
DynamodbConfig runtimeConfig,
BuildProducer<AmazonClientBuilderConfiguredBuildItem> producer) {

initClientBuilders(clients, commonRecorder, recorder.getAwsConfig(runtimeConfig), recorder.getSdkConfig(runtimeConfig),
buildTimeConfig.sdk, producer);
}

@BuildStep
@Record(ExecutionTime.RUNTIME_INIT)
void buildClients(List<AmazonClientBuilderConfiguredBuildItem> configuredClients, DynamodbRecorder recorder,
BeanContainerBuildItem beanContainer,
ShutdownContextBuildItem shutdown) {

buildClients(configuredClients,
(syncBuilder) -> recorder.buildClient(syncBuilder, beanContainer.getValue(), shutdown),
(asyncBuilder) -> recorder.buildAsyncClient(asyncBuilder, beanContainer.getValue(), shutdown));
List<AmazonClientSyncTransportBuildItem> syncTransports,
List<AmazonClientAsyncTransportBuildItem> asyncTransports,
BuildProducer<SyntheticBeanBuildItem> syntheticBeans) {

createClientBuilders(commonRecorder,
recorder.getAwsConfig(runtimeConfig),
recorder.getSdkConfig(runtimeConfig),
buildTimeConfig.sdk,
syncTransports,
asyncTransports,
DynamoDbClientBuilder.class,
(syncTransport) -> recorder.createSyncBuilder(runtimeConfig, syncTransport),
DynamoDbAsyncClientBuilder.class,
(asyncTransport) -> recorder.createAsyncBuilder(runtimeConfig, asyncTransport),
syntheticBeans);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import javax.annotation.PreDestroy;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Instance;
import javax.enterprise.inject.Produces;

import software.amazon.awssdk.services.dynamodb.DynamoDbAsyncClient;
Expand All @@ -12,41 +13,40 @@
@ApplicationScoped
public class DynamodbClientProducer {

private volatile DynamoDbClientBuilder syncConfiguredBuilder;
private volatile DynamoDbAsyncClientBuilder asyncConfiguredBuilder;
private final DynamoDbClient syncClient;
private final DynamoDbAsyncClient asyncClient;

private DynamoDbClient client;
private DynamoDbAsyncClient asyncClient;
DynamodbClientProducer(Instance<DynamoDbClientBuilder> syncClientBuilderInstance,
Instance<DynamoDbAsyncClientBuilder> asyncClientBuilderInstance) {
this.syncClient = syncClientBuilderInstance.isResolvable() ? syncClientBuilderInstance.get().build() : null;
this.asyncClient = asyncClientBuilderInstance.isResolvable() ? asyncClientBuilderInstance.get().build() : null;
}

@Produces
@ApplicationScoped
public DynamoDbClient client() {
client = syncConfiguredBuilder.build();
return client;
if (syncClient == null) {
throw new IllegalStateException("The DynamoDbClient is required but has not been detected/configured.");
}
return syncClient;
}

@Produces
@ApplicationScoped
public DynamoDbAsyncClient asyncClient() {
asyncClient = asyncConfiguredBuilder.build();
if (asyncClient == null) {
throw new IllegalStateException("The DynamoDbAsyncClient is required but has not been detected/configured.");
}
return asyncClient;
}

@PreDestroy
public void destroy() {
if (client != null) {
client.close();
if (syncClient != null) {
syncClient.close();
}
if (asyncClient != null) {
asyncClient.close();
}
}

public void setSyncConfiguredBuilder(DynamoDbClientBuilder syncConfiguredBuilder) {
this.syncConfiguredBuilder = syncConfiguredBuilder;
}

public void setAsyncConfiguredBuilder(DynamoDbAsyncClientBuilder asyncConfiguredBuilder) {
this.asyncConfiguredBuilder = asyncConfiguredBuilder;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
import io.quarkus.amazon.common.runtime.NettyHttpClientConfig;
import io.quarkus.amazon.common.runtime.SdkConfig;
import io.quarkus.amazon.common.runtime.SyncHttpClientConfig;
import io.quarkus.arc.runtime.BeanContainer;
import io.quarkus.runtime.RuntimeValue;
import io.quarkus.runtime.ShutdownContext;
import io.quarkus.runtime.annotations.Recorder;
import software.amazon.awssdk.awscore.client.builder.AwsClientBuilder;
import software.amazon.awssdk.http.SdkHttpClient;
Expand Down Expand Up @@ -56,22 +54,4 @@ public RuntimeValue<AwsClientBuilder> createAsyncBuilder(DynamodbConfig config,
}
return new RuntimeValue<>(builder);
}

public RuntimeValue<DynamoDbClient> buildClient(RuntimeValue<? extends AwsClientBuilder> builder,
BeanContainer beanContainer,
ShutdownContext shutdown) {
DynamodbClientProducer producer = beanContainer.instance(DynamodbClientProducer.class);
producer.setSyncConfiguredBuilder((DynamoDbClientBuilder) builder.getValue());
shutdown.addShutdownTask(producer::destroy);
return new RuntimeValue<>(producer.client());
}

public RuntimeValue<DynamoDbAsyncClient> buildAsyncClient(RuntimeValue<? extends AwsClientBuilder> builder,
BeanContainer beanContainer,
ShutdownContext shutdown) {
DynamodbClientProducer producer = beanContainer.instance(DynamodbClientProducer.class);
producer.setAsyncConfiguredBuilder((DynamoDbAsyncClientBuilder) builder.getValue());
shutdown.addShutdownTask(producer::destroy);
return new RuntimeValue<>(producer.asyncClient());
}
}
Loading

0 comments on commit ef6d0d8

Please sign in to comment.