Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
se.fortnox.reactivewizard.db.DbProxy Maven / Gradle / Ivy
package se.fortnox.reactivewizard.db;
import com.fasterxml.jackson.core.type.TypeReference;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Scheduler;
import reactor.core.scheduler.Schedulers;
import se.fortnox.reactivewizard.db.config.DatabaseConfig;
import se.fortnox.reactivewizard.db.paging.PagingOutput;
import se.fortnox.reactivewizard.db.statement.DbStatementFactory;
import se.fortnox.reactivewizard.db.statement.DbStatementFactoryFactory;
import se.fortnox.reactivewizard.db.transactions.ConnectionScheduler;
import se.fortnox.reactivewizard.json.JsonSerializerFactory;
import se.fortnox.reactivewizard.metrics.Metrics;
import se.fortnox.reactivewizard.util.DebugUtil;
import se.fortnox.reactivewizard.util.ReflectionUtil;
import javax.annotation.Nullable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import static java.text.MessageFormat.format;
@Singleton
public class DbProxy implements InvocationHandler {
private static final TypeReference OBJECT_ARRAY_TYPE_REFERENCE = new TypeReference<>() {
};
private final DbStatementFactoryFactory dbStatementFactoryFactory;
private final Scheduler scheduler;
protected final Map statementFactories;
private final ConnectionScheduler connectionScheduler;
protected final Function paramSerializer;
private final DatabaseConfig databaseConfig;
@Inject
public DbProxy(DatabaseConfig databaseConfig,
@Nullable ConnectionProvider connectionProvider,
DbStatementFactoryFactory dbStatementFactoryFactory,
JsonSerializerFactory jsonSerializerFactory
) {
this(databaseConfig,
threadPool(databaseConfig.getPoolSize()),
connectionProvider,
dbStatementFactoryFactory,
jsonSerializerFactory);
}
public DbProxy(DatabaseConfig databaseConfig,
Scheduler scheduler,
ConnectionProvider connectionProvider,
DbStatementFactoryFactory dbStatementFactoryFactory,
JsonSerializerFactory jsonSerializerFactory
) {
this(databaseConfig, scheduler, connectionProvider, dbStatementFactoryFactory,
jsonSerializerFactory.createStringSerializer(OBJECT_ARRAY_TYPE_REFERENCE),
new ConcurrentHashMap<>());
}
public DbProxy(DatabaseConfig databaseConfig, ConnectionProvider connectionProvider) {
this(databaseConfig,
Schedulers.boundedElastic(),
connectionProvider,
new DbStatementFactoryFactory(),
new JsonSerializerFactory());
}
protected DbProxy(DatabaseConfig databaseConfig,
Scheduler scheduler,
ConnectionProvider connectionProvider,
DbStatementFactoryFactory dbStatementFactoryFactory,
Function paramSerializer,
Map statementFactories
) {
this.scheduler = scheduler;
this.dbStatementFactoryFactory = dbStatementFactoryFactory;
this.paramSerializer = paramSerializer;
this.databaseConfig = databaseConfig;
this.statementFactories = statementFactories;
this.connectionScheduler = new ConnectionScheduler(connectionProvider, scheduler);
}
private static Scheduler threadPool(int poolSize) {
if (poolSize == -1) {
return Schedulers.boundedElastic();
}
return Schedulers.newBoundedElastic(10, Integer.MAX_VALUE, "DbProxy");
}
/**
* Create proxy from interface.
*
* @param daoInterface the interface
* @param the type of the interface
* @return the proxy
*/
public T create(Class daoInterface) {
return (T) Proxy.newProxyInstance(daoInterface.getClassLoader(),
new Class[]{daoInterface},
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
ReactiveStatementFactory reactiveStatementFactory = statementFactories.get(method);
if (reactiveStatementFactory == null || DebugUtil.IS_DEBUG) {
if (DebugUtil.IS_DEBUG) {
// Need to get the actual interface method in order to get updated annotations
method = ReflectionUtil.getRedefinedMethod(method);
}
DbStatementFactory statementFactory = dbStatementFactoryFactory.createStatementFactory(method);
PagingOutput pagingOutput = new PagingOutput(method);
reactiveStatementFactory = new ReactiveStatementFactory(
statementFactory,
pagingOutput,
createMetrics(method),
databaseConfig,
converterFromPublisher(method),
method);
statementFactories.put(method, reactiveStatementFactory);
}
return reactiveStatementFactory.create(args, connectionScheduler);
}
private static Function converterFromPublisher(Method method) {
Class> returnType = method.getReturnType();
if (Flux.class.isAssignableFrom(returnType)) {
return flux -> flux;
} else if (Mono.class.isAssignableFrom(returnType)) {
return Mono::from;
} else {
throw new IllegalArgumentException(String.format("DAO method %s::%s must return a Flux or Mono. Found %s",
method.getDeclaringClass().getName(),
method.getName(),
method.getReturnType().getName()));
}
}
private Metrics createMetrics(Method method) {
String type = method.isAnnotationPresent(Query.class) ? "query" : "update";
String metricsName = format(
"DAO_type:{0}_method:{1}.{2}_{3}",
type, method.getDeclaringClass().getName(), method.getName(), method.getParameterCount());
return Metrics.get(metricsName);
}
public DbProxy usingConnectionProvider(ConnectionProvider connectionProvider) {
return new DbProxy(databaseConfig, scheduler, connectionProvider, dbStatementFactoryFactory, paramSerializer, statementFactories);
}
public DbProxy usingConnectionProvider(ConnectionProvider connectionProvider, DatabaseConfig databaseConfig) {
return new DbProxy(databaseConfig, scheduler, connectionProvider, dbStatementFactoryFactory, paramSerializer, statementFactories);
}
public DbProxy usingConnectionProvider(ConnectionProvider newConnectionProvider, Scheduler newScheduler) {
return new DbProxy(databaseConfig, newScheduler, newConnectionProvider, dbStatementFactoryFactory, paramSerializer, statementFactories);
}
public DatabaseConfig getDatabaseConfig() {
return databaseConfig;
}
}