io.tarantool.driver.core.AbstractTarantoolClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of cartridge-driver Show documentation
Show all versions of cartridge-driver Show documentation
Tarantool Cartridge driver for Tarantool versions 1.10+ based on Netty framework
package io.tarantool.driver.core;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelOption;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.tarantool.driver.TarantoolVersion;
import io.tarantool.driver.api.CallResult;
import io.tarantool.driver.api.MultiValueCallResult;
import io.tarantool.driver.api.SingleValueCallResult;
import io.tarantool.driver.api.TarantoolClient;
import io.tarantool.driver.api.TarantoolClientConfig;
import io.tarantool.driver.api.TarantoolResult;
import io.tarantool.driver.api.connection.ConnectionSelectionStrategyFactory;
import io.tarantool.driver.api.connection.TarantoolConnection;
import io.tarantool.driver.api.connection.TarantoolConnectionListeners;
import io.tarantool.driver.api.metadata.TarantoolMetadataOperations;
import io.tarantool.driver.api.metadata.TarantoolMetadataProvider;
import io.tarantool.driver.api.metadata.TarantoolSpaceMetadata;
import io.tarantool.driver.api.space.TarantoolSpaceOperations;
import io.tarantool.driver.core.connection.TarantoolConnectionFactory;
import io.tarantool.driver.core.connection.TarantoolConnectionManager;
import io.tarantool.driver.core.metadata.SpacesMetadataProvider;
import io.tarantool.driver.core.metadata.TarantoolMetadata;
import io.tarantool.driver.exceptions.TarantoolClientException;
import io.tarantool.driver.exceptions.TarantoolSpaceNotFoundException;
import io.tarantool.driver.mappers.CallResultMapper;
import io.tarantool.driver.mappers.MessagePackMapper;
import io.tarantool.driver.mappers.MessagePackObjectMapper;
import io.tarantool.driver.mappers.MessagePackValueMapper;
import io.tarantool.driver.mappers.converters.ValueConverter;
import io.tarantool.driver.mappers.factories.ResultMapperFactoryFactory;
import io.tarantool.driver.mappers.factories.ResultMapperFactoryFactoryImpl;
import io.tarantool.driver.protocol.Packable;
import io.tarantool.driver.protocol.TarantoolProtocolException;
import io.tarantool.driver.protocol.requests.TarantoolCallRequest;
import io.tarantool.driver.protocol.requests.TarantoolEvalRequest;
import io.tarantool.driver.utils.Assert;
import org.msgpack.value.Value;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
/**
* Basic Tarantool client implementation. Subclasses must provide the connection manager.
*
* @param target tuple type
* @param target tuple collection type
* @author Alexey Kuzin
*/
public abstract class AbstractTarantoolClient>
implements TarantoolClient {
private final NioEventLoopGroup eventLoopGroup;
private final TarantoolClientConfig config;
private final Bootstrap bootstrap;
private final TarantoolConnectionFactory connectionFactory;
private final TarantoolConnectionListeners listeners;
private final AtomicReference metadataHolder = new AtomicReference<>();
private final ResultMapperFactoryFactoryImpl mapperFactoryFactory;
private final SpacesMetadataProvider metadataProvider;
private final ScheduledExecutorService timeoutScheduler;
private TarantoolConnectionManager connectionManager;
/**
* Create a client.
*
* @param config the client configuration
* @see TarantoolClientConfig
*/
public AbstractTarantoolClient(TarantoolClientConfig config) {
this(config, new TarantoolConnectionListeners());
}
/**
* Create a client, specifying the connection established event listeners.
*
* @param config the client configuration
* @param selectionStrategyFactory instantiates strategies which provide the algorithm of selecting connections
* from the connection pool for performing the next request
* @param listeners connection established event listeners
* @see TarantoolClientConfig
* @deprecated
*/
protected AbstractTarantoolClient(
TarantoolClientConfig config,
ConnectionSelectionStrategyFactory selectionStrategyFactory,
TarantoolConnectionListeners listeners) {
this(config, listeners);
}
/**
* Create a client, specifying the connection established event listeners.
*
* @param config the client configuration
* @param listeners connection established event listeners
* @see TarantoolClientConfig
*/
public AbstractTarantoolClient(TarantoolClientConfig config, TarantoolConnectionListeners listeners) {
Assert.notNull(config, "Tarantool client config must not be null");
Assert.notNull(listeners, "Tarantool connection listeners must not be null");
this.config = config;
this.mapperFactoryFactory = new ResultMapperFactoryFactoryImpl();
this.eventLoopGroup = new NioEventLoopGroup(config.getEventLoopThreadsNumber());
this.bootstrap = new Bootstrap()
.group(eventLoopGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_REUSEADDR, true)
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.TCP_NODELAY, true)
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, config.getConnectTimeout());
this.timeoutScheduler =
Executors.newSingleThreadScheduledExecutor(new TarantoolDaemonThreadFactory("tarantool-timeout"));
this.connectionFactory = new TarantoolConnectionFactory(config, this.bootstrap, this.timeoutScheduler);
this.listeners = listeners;
this.metadataProvider = new SpacesMetadataProvider(this, config.getMessagePackMapper());
}
/**
* Provides a connection manager for Tarantool server connections
*
* @param config contains Tarantool client configuration options
* @param connectionFactory provides helper methods for connection instantiation
* @param listeners listeners which will be invoked once all connections are established
* @return connection manager
*/
protected abstract TarantoolConnectionManager connectionManager(
TarantoolClientConfig config,
TarantoolConnectionFactory connectionFactory,
TarantoolConnectionListeners listeners);
private TarantoolConnectionManager connectionManager() {
if (this.connectionManager == null) {
synchronized (this) {
if (this.connectionManager == null) {
this.connectionManager = connectionManager(config, connectionFactory, listeners);
}
}
}
return this.connectionManager;
}
@Override
public boolean refresh() {
return connectionManager().refresh();
}
@Override
public TarantoolVersion getVersion() throws TarantoolClientException {
try {
return connectionManager().getConnection().thenApply(TarantoolConnection::getVersion).get();
} catch (InterruptedException e) {
throw new TarantoolClientException(e);
} catch (ExecutionException e) {
throw new TarantoolClientException(e.getCause());
}
}
@Override
public TarantoolSpaceOperations space(String spaceName) throws TarantoolClientException {
Assert.hasText(spaceName, "Space name must not be null or empty");
TarantoolMetadataOperations metadata = this.metadata();
Optional meta = metadata.getSpaceByName(spaceName);
if (!meta.isPresent()) {
throw new TarantoolSpaceNotFoundException(spaceName);
}
return spaceOperations(config, connectionManager(), metadata, meta.get());
}
@Override
public TarantoolSpaceOperations space(int spaceId) throws TarantoolClientException {
Assert.state(spaceId > 0, "Space ID must be greater than 0");
TarantoolMetadataOperations metadata = this.metadata();
Optional meta = metadata.getSpaceById(spaceId);
if (!meta.isPresent()) {
throw new TarantoolSpaceNotFoundException(spaceId);
}
return spaceOperations(config, connectionManager(), metadata, meta.get());
}
/**
* Creates a space API implementation instance for the specified space
*
* @param config Tarantool client configuration
* @param connectionManager configured internal connection manager
* @param metadata metadata operations
* @param spaceMetadata current space metadata
* @return space API implementation instance
*/
protected abstract TarantoolSpaceOperations spaceOperations(
TarantoolClientConfig config,
TarantoolConnectionManager connectionManager,
TarantoolMetadataOperations metadata,
TarantoolSpaceMetadata spaceMetadata);
@Override
public TarantoolMetadataOperations metadata() throws TarantoolClientException {
if (metadataHolder.get() == null) {
this.metadataHolder.compareAndSet(null, new TarantoolMetadata(metadataProvider()));
}
return metadataHolder.get();
}
@Override
public TarantoolMetadataProvider metadataProvider() {
return metadataProvider;
}
@Override
public CompletableFuture> call(String functionName) throws TarantoolClientException {
return call(functionName, Collections.emptyList());
}
@Override
public CompletableFuture> call(String functionName, Object... arguments)
throws TarantoolClientException {
return call(functionName, Arrays.asList(arguments));
}
@Override
public CompletableFuture> call(String functionName, List> arguments)
throws TarantoolClientException {
return call(functionName, arguments, config.getMessagePackMapper());
}
@Override
public CompletableFuture> call(String functionName, List> arguments, MessagePackMapper mapper)
throws TarantoolClientException {
return makeRequest(functionName, arguments, mapper, mapper);
}
@Override
public CompletableFuture> call(String functionName, Class tupleClass)
throws TarantoolClientException {
return call(functionName, Collections.emptyList(), tupleClass);
}
@Override
public CompletableFuture> call(
String functionName,
CallResultMapper, SingleValueCallResult>> resultMapper)
throws TarantoolClientException {
return call(functionName, Collections.emptyList(), resultMapper);
}
@Override
public CompletableFuture> call(String functionName, List> arguments, Class tupleClass)
throws TarantoolClientException {
return call(functionName, arguments, config.getMessagePackMapper(), tupleClass);
}
@Override
public CompletableFuture> call(
String functionName,
List> arguments,
CallResultMapper, SingleValueCallResult>> resultMapper)
throws TarantoolClientException {
return call(functionName, arguments, config.getMessagePackMapper(), resultMapper);
}
@Override
public CompletableFuture> call(
String functionName,
List> arguments,
MessagePackObjectMapper argumentsMapper,
Class tupleClass)
throws TarantoolClientException {
return call(functionName, arguments, argumentsMapper,
mapperFactoryFactory.getTarantoolResultMapper(config.getMessagePackMapper(), tupleClass));
}
@Override
public CompletableFuture> call(
String functionName,
List> arguments,
MessagePackObjectMapper argumentsMapper,
CallResultMapper, SingleValueCallResult>> resultMapper)
throws TarantoolClientException {
return callForSingleResult(functionName, arguments, argumentsMapper, resultMapper);
}
@Override
public CompletableFuture callForSingleResult(
String functionName,
List> arguments,
Class resultClass)
throws TarantoolClientException {
return callForSingleResult(functionName, arguments, config.getMessagePackMapper(), resultClass);
}
@Override
public CompletableFuture callForSingleResult(
String functionName,
List> arguments,
ValueConverter valueConverter)
throws TarantoolClientException {
return callForSingleResult(functionName, arguments, config.getMessagePackMapper(), valueConverter);
}
@Override
public CompletableFuture callForSingleResult(
String functionName,
List> arguments,
CallResultMapper> resultMapper) throws TarantoolClientException {
return callForSingleResult(functionName, arguments, config.getMessagePackMapper(), resultMapper);
}
@Override
public CompletableFuture callForSingleResult(String functionName, Class resultClass)
throws TarantoolClientException {
return callForSingleResult(functionName, Collections.emptyList(), resultClass);
}
@Override
public CompletableFuture callForSingleResult(String functionName, ValueConverter valueConverter)
throws TarantoolClientException {
return callForSingleResult(functionName, Collections.emptyList(), valueConverter);
}
@Override
public CompletableFuture callForSingleResult(
String functionName,
CallResultMapper> resultMapper) throws TarantoolClientException {
return callForSingleResult(functionName, Collections.emptyList(), resultMapper);
}
@Override
public CompletableFuture callForSingleResult(
String functionName,
List> arguments,
MessagePackObjectMapper argumentsMapper,
Class resultClass)
throws TarantoolClientException {
return callForSingleResult(functionName, arguments, argumentsMapper,
mapperFactoryFactory.getDefaultSingleValueMapper(config.getMessagePackMapper(), resultClass));
}
@Override
public CompletableFuture callForSingleResult(
String functionName,
List> arguments,
MessagePackObjectMapper argumentsMapper,
ValueConverter valueConverter)
throws TarantoolClientException {
return callForSingleResult(functionName, arguments, argumentsMapper,
mapperFactoryFactory.getSingleValueResultMapper(valueConverter));
}
@Override
public CompletableFuture callForSingleResult(
String functionName,
List> arguments,
MessagePackObjectMapper argumentsMapper,
CallResultMapper> resultMapper)
throws TarantoolClientException {
return makeRequestForSingleResult(functionName, arguments, argumentsMapper, resultMapper)
.thenApply(CallResult::value);
}
@Override
public > CompletableFuture callForMultiResult(
String functionName,
List> arguments,
Supplier resultContainerSupplier,
Class resultClass)
throws TarantoolClientException {
return callForMultiResult(
functionName, arguments, config.getMessagePackMapper(), resultContainerSupplier, resultClass);
}
@Override
public > CompletableFuture callForMultiResult(
String functionName,
List> arguments,
Supplier resultContainerSupplier,
ValueConverter valueConverter) throws TarantoolClientException {
return callForMultiResult(functionName, arguments, config.getMessagePackMapper(),
resultContainerSupplier, valueConverter);
}
@Override
public > CompletableFuture callForMultiResult(
String functionName,
List> arguments,
CallResultMapper> resultMapper) throws TarantoolClientException {
return callForMultiResult(functionName, arguments, config.getMessagePackMapper(), resultMapper);
}
@Override
public > CompletableFuture callForMultiResult(
String functionName,
Supplier resultContainerSupplier,
Class resultClass)
throws TarantoolClientException {
return callForMultiResult(functionName, Collections.emptyList(), resultContainerSupplier, resultClass);
}
@Override
public > CompletableFuture callForMultiResult(
String functionName,
Supplier resultContainerSupplier,
ValueConverter valueConverter)
throws TarantoolClientException {
return callForMultiResult(functionName, Collections.emptyList(), resultContainerSupplier, valueConverter);
}
@Override
public > CompletableFuture callForMultiResult(
String functionName,
CallResultMapper> resultMapper)
throws TarantoolClientException {
return callForMultiResult(functionName, Collections.emptyList(), resultMapper);
}
@Override
public > CompletableFuture callForMultiResult(
String functionName,
List> arguments,
MessagePackObjectMapper argumentsMapper,
Supplier resultContainerSupplier,
Class resultClass)
throws TarantoolClientException {
return callForMultiResult(functionName, arguments, argumentsMapper,
mapperFactoryFactory.getDefaultMultiValueMapper(config.getMessagePackMapper(), resultClass));
}
@Override
public > CompletableFuture callForMultiResult(
String functionName,
List> arguments,
MessagePackObjectMapper argumentsMapper,
Supplier resultContainerSupplier,
ValueConverter valueConverter)
throws TarantoolClientException {
return callForMultiResult(functionName, arguments, argumentsMapper,
mapperFactoryFactory.getMultiValueResultMapper(resultContainerSupplier, valueConverter));
}
@Override
public > CompletableFuture callForMultiResult(
String functionName,
List> arguments,
MessagePackObjectMapper argumentsMapper,
CallResultMapper> resultMapper)
throws TarantoolClientException {
return makeRequestForMultiResult(functionName, arguments, argumentsMapper, resultMapper)
.thenApply(CallResult::value);
}
private CompletableFuture> makeRequestForSingleResult(
String functionName,
List> arguments,
MessagePackObjectMapper argumentsMapper,
CallResultMapper> resultMapper) {
return makeRequest(functionName, arguments, argumentsMapper, resultMapper);
}
private > CompletableFuture> makeRequestForMultiResult(
String functionName,
List> arguments,
MessagePackObjectMapper argumentsMapper,
CallResultMapper> resultMapper) {
return makeRequest(functionName, arguments, argumentsMapper, resultMapper);
}
private CompletableFuture makeRequest(
String functionName,
List> arguments,
MessagePackObjectMapper argumentsMapper,
MessagePackValueMapper resultMapper)
throws TarantoolClientException {
try {
TarantoolCallRequest.Builder builder = new TarantoolCallRequest.Builder()
.withFunctionName(functionName);
if (arguments.size() > 0) {
builder.withArguments(arguments);
}
TarantoolCallRequest request = builder.build(argumentsMapper);
return connectionManager().getConnection().thenCompose(c -> c.sendRequest(request, resultMapper));
} catch (TarantoolProtocolException e) {
throw new TarantoolClientException(e);
}
}
@Override
public CompletableFuture> eval(String expression) throws TarantoolClientException {
return eval(expression, Collections.emptyList());
}
@Override
public CompletableFuture> eval(String expression, List> arguments)
throws TarantoolClientException {
return eval(expression, arguments, config.getMessagePackMapper());
}
@Override
public CompletableFuture> eval(String expression, MessagePackValueMapper resultMapper)
throws TarantoolClientException {
return eval(expression, Collections.emptyList(), resultMapper);
}
@Override
public CompletableFuture> eval(String expression, List> arguments, MessagePackValueMapper resultMapper)
throws TarantoolClientException {
return eval(expression, arguments, config.getMessagePackMapper(), resultMapper);
}
@Override
public CompletableFuture> eval(
String expression,
List> arguments,
MessagePackObjectMapper argumentsMapper,
MessagePackValueMapper resultMapper) throws TarantoolClientException {
try {
TarantoolEvalRequest request = new TarantoolEvalRequest.Builder()
.withExpression(expression)
.withArguments(arguments)
.build(argumentsMapper);
return connectionManager().getConnection().thenCompose(c -> c.sendRequest(request, resultMapper));
} catch (TarantoolProtocolException e) {
throw new TarantoolClientException(e);
}
}
@Override
public TarantoolClientConfig getConfig() {
return config;
}
protected Bootstrap getBootstrap() {
return bootstrap;
}
@Override
public void close() throws Exception {
try {
connectionManager().close();
timeoutScheduler.shutdownNow();
} finally {
eventLoopGroup.shutdownGracefully();
}
}
@Override
public TarantoolConnectionListeners getConnectionListeners() {
return listeners;
}
@Override
public ResultMapperFactoryFactory getResultMapperFactoryFactory() {
return mapperFactoryFactory;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy