Add aws-secretsmanager-r2dbc #14
eddumelendez
started this conversation in
Ideas
Replies: 1 comment
-
Hi! what are you think about this? I have two alternative ways for this:
<dependency>
<!-- docs of config https://docs.awspring.io/spring-cloud-aws/docs/current/reference/html/index.html#integrating-your-spring-cloud-application-with-the-aws-secrets-manager -->
<!-- list of config https://docs.awspring.io/spring-cloud-aws/docs/current/reference/html/appendix.html -->
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-starter-aws-secrets-manager-config</artifactId>
</dependency>
<dependency>
<!-- docs of config https://docs.awspring.io/spring-cloud-aws/docs/current/reference/html/index.html#integrating-your-spring-cloud-application-with-the-aws-parameter-store -->
<!-- list of config https://docs.awspring.io/spring-cloud-aws/docs/current/reference/html/appendix.html -->
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-starter-aws-parameter-store-config</artifactId>
</dependency>
import io.r2dbc.pool.ConnectionPool;
import io.r2dbc.pool.ConnectionPoolConfiguration;
import io.r2dbc.spi.Connection;
import io.r2dbc.spi.ConnectionFactory;
import io.r2dbc.spi.ConnectionFactoryMetadata;
import lombok.Getter;
import lombok.SneakyThrows;
import org.reactivestreams.Publisher;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryOptionsBuilderCustomizer;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcAutoConfiguration;
import org.springframework.boot.autoconfigure.r2dbc.R2dbcProperties;
import org.springframework.boot.context.properties.PropertyMapper;
import org.springframework.cglib.core.ReflectUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Import;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.lang.reflect.Method;
import static lombok.AccessLevel.PROTECTED;
@Component
@Getter(PROTECTED)
@Import(R2dbcAutoConfiguration.class)
@SuppressWarnings("JavadocReference")
public class AwsR2dbcConnectionFactoryBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware {
private ApplicationContext ctx;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ConnectionPool) {
return wrapPool((ConnectionPool) bean);
} else if (bean instanceof ConnectionFactory) {
return wrapFactory((ConnectionFactory) bean);
}
return bean;
}
/**
* Logic based on {@link org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryConfigurations.PoolConfiguration}
*/
protected ConnectionPool wrapPool(ConnectionPool bean) {
var connectionFactory = wrapFactory(bean.unwrap());
var pool = getCtx().getBean(R2dbcProperties.class).getPool();
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
ConnectionPoolConfiguration.Builder builder = ConnectionPoolConfiguration.builder(connectionFactory);
map.from(pool.getMaxIdleTime()).to(builder::maxIdleTime);
map.from(pool.getMaxLifeTime()).to(builder::maxLifeTime);
map.from(pool.getMaxAcquireTime()).to(builder::maxAcquireTime);
map.from(pool.getMaxCreateConnectionTime()).to(builder::maxCreateConnectionTime);
map.from(pool.getInitialSize()).to(builder::initialSize);
map.from(pool.getMaxSize()).to(builder::maxSize);
map.from(pool.getValidationQuery()).whenHasText().to(builder::validationQuery);
map.from(pool.getValidationDepth()).to(builder::validationDepth);
return new ConnectionPool(builder.build());
}
protected ConnectionFactory wrapFactory(ConnectionFactory bean) {
return new AwsSecretManagerConnectionFactory(bean, getCtx());
}
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.ctx = ctx;
}
@Getter(PROTECTED)
public static class AwsSecretManagerConnectionFactory implements ConnectionFactory {
//TODO make this class accessible????
protected static final String BASE_CONNECTION_FACTORY =
"org.springframework.boot.autoconfigure.r2dbc.ConnectionFactoryConfigurations" +
"$GenericConfiguration.connectionFactory(" +
"org.springframework.boot.autoconfigure.r2dbc.R2dbcProperties," +
"org.springframework.core.io.ResourceLoader," +
"org.springframework.beans.factory.ObjectProvider" +
")";
//TODO make sure threadsafe
private ConnectionFactory current;
private final ApplicationContext ctx;
private final Method factoryMethod;
private final Object factoryOwnerInstance;
public AwsSecretManagerConnectionFactory(ConnectionFactory current, ApplicationContext ctx) {
this.current = current;
this.ctx = ctx;
// ATTENTION! for library experience extract this creator to self customizer bean!
factoryMethod = ReflectUtils.findMethod(BASE_CONNECTION_FACTORY, ctx.getClassLoader());
factoryMethod.trySetAccessible();
factoryOwnerInstance = ReflectUtils.newInstance(factoryMethod.getDeclaringClass());
}
@Override
public Publisher<? extends Connection> create() {
var publisher = getCurrent().create();
if (publisher instanceof Mono) {
return ((Mono<? extends Connection>) publisher).onErrorResume(this::createFallback);
} else if (publisher instanceof Flux) {
return ((Flux<? extends Connection>) publisher).onErrorResume(this::createFallback);
}
return publisher;
}
private <P extends Publisher<? extends Connection>> P createFallback(Throwable throwable) {
//TODO do this only for Auth exceptions
current = recreate();
//TODO make it retryable
return (P) create();
}
@Override
public ConnectionFactoryMetadata getMetadata() {
return getCurrent().getMetadata();
}
public ConnectionFactory unwrap() {
return getCurrent();
}
@SneakyThrows
protected ConnectionFactory recreate() {
var properties = getCtx().getBean(R2dbcProperties.class);
var customizers = getCtx().getBeanProvider(ConnectionFactoryOptionsBuilderCustomizer.class);
return (ConnectionFactory) getFactoryMethod().invoke(getFactoryOwnerInstance(), properties, getCtx(), customizers);
}
@Override
public String toString() {
return getClass().getSimpleName() + "[" + getCurrent().toString() + "]";
}
}
} |
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
-
Similar to aws-secretsmanager-jdbc, a new project can be created to support secretsmanager for r2dbc.
Beta Was this translation helpful? Give feedback.
All reactions