io.tarantool.driver.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;
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.api.TarantoolClient;
import io.tarantool.driver.api.TarantoolResult;
import io.tarantool.driver.api.TarantoolTupleFactory;
import io.tarantool.driver.api.space.TarantoolSpace;
import io.tarantool.driver.api.space.TarantoolSpaceOperations;
import io.tarantool.driver.core.TarantoolConnectionFactory;
import io.tarantool.driver.core.TarantoolConnectionListeners;
import io.tarantool.driver.core.TarantoolConnectionManager;
import io.tarantool.driver.exceptions.TarantoolClientException;
import io.tarantool.driver.exceptions.TarantoolSpaceNotFoundException;
import io.tarantool.driver.mappers.MessagePackMapper;
import io.tarantool.driver.mappers.MessagePackObjectMapper;
import io.tarantool.driver.mappers.MessagePackValueMapper;
import io.tarantool.driver.mappers.TarantoolCallResultMapper;
import io.tarantool.driver.mappers.TarantoolCallResultMapperFactory;
import io.tarantool.driver.mappers.ValueConverter;
import io.tarantool.driver.metadata.TarantoolMetadata;
import io.tarantool.driver.metadata.TarantoolMetadataOperations;
import io.tarantool.driver.metadata.TarantoolSpaceMetadata;
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.ArrayValue;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.atomic.AtomicReference;
/**
* Basic Tarantool client implementation. Subclasses must provide the connection manager.
*
* @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 connectionManagerHolder = new AtomicReference<>();
private final AtomicReference metadataHolder = new AtomicReference<>();
private final TarantoolCallResultMapperFactory mapperFactory;
private final DefaultTarantoolTupleFactory tupleFactory;
/**
* Create a client.
* @param config the client configuration
* @see TarantoolClientConfig
*/
protected AbstractTarantoolClient(TarantoolClientConfig config) {
this(config, new TarantoolConnectionListeners());
}
/**
* Create a client, specifying the connection established event listeners.
* @param listeners connection established event listeners
* @param config the client configuration
* @see TarantoolClientConfig
*/
protected 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.mapperFactory = new TarantoolCallResultMapperFactory(config.getMessagePackMapper());
this.eventLoopGroup = new NioEventLoopGroup();
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.connectionFactory = new TarantoolConnectionFactory(config, getBootstrap());
listeners.add(connection -> {
try {
return metadata().refresh().thenApply(v -> connection);
} catch (Throwable e) {
throw new CompletionException(e);
}
});
this.listeners = listeners;
this.tupleFactory = new DefaultTarantoolTupleFactory(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.connectionManagerHolder.get() == null) {
this.connectionManagerHolder.compareAndSet(null, connectionManager(config, connectionFactory, listeners));
}
return connectionManagerHolder.get();
}
@Override
public TarantoolVersion getVersion() throws TarantoolClientException {
return connectionManager().getConnection().getVersion();
}
@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 new TarantoolSpace(config, connectionManager(), meta.get(), metadata);
}
@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 new TarantoolSpace(config, connectionManager(), meta.get(), metadata());
}
@Override
public TarantoolMetadataOperations metadata() throws TarantoolClientException {
if (metadataHolder.get() == null) {
this.metadataHolder.compareAndSet(null, new TarantoolMetadata(config, connectionManager()));
}
return metadataHolder.get();
}
@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 {
try {
TarantoolCallRequest.Builder builder = new TarantoolCallRequest.Builder()
.withFunctionName(functionName);
if (arguments.size() > 0) {
builder.withArguments(arguments);
}
TarantoolCallRequest request = builder.build(mapper);
return connectionManager().getConnection().sendRequest(request, mapper);
} catch (TarantoolProtocolException e) {
throw new TarantoolClientException(e);
}
}
@Override
public CompletableFuture> call(String functionName, Class tupleClass)
throws TarantoolClientException {
return call(functionName, getConverter(tupleClass));
}
@Override
public CompletableFuture> call(String functionName,
ValueConverter tupleMapper)
throws TarantoolClientException {
return call(functionName, Collections.emptyList(), tupleMapper);
}
@Override
public CompletableFuture> call(String functionName, List> arguments, Class tupleClass)
throws TarantoolClientException {
return call(functionName, arguments, config.getMessagePackMapper(), getConverter(tupleClass));
}
@Override
public CompletableFuture> call(String functionName,
List> arguments,
ValueConverter tupleMapper)
throws TarantoolClientException {
return call(functionName, arguments, config.getMessagePackMapper(), tupleMapper);
}
@Override
public CompletableFuture> call(String functionName,
List> arguments,
MessagePackObjectMapper argumentsMapper,
Class tupleClass)
throws TarantoolClientException {
ValueConverter converter = getConverter(tupleClass);
return call(functionName, arguments, argumentsMapper, mapperFactory.withConverter(tupleClass, converter));
}
@Override
public CompletableFuture> call(String functionName,
List> arguments,
MessagePackObjectMapper argumentsMapper,
ValueConverter tupleMapper)
throws TarantoolClientException {
return call(functionName, arguments, argumentsMapper, mapperFactory.withConverter(tupleMapper));
}
@Override
public CompletableFuture> call(String functionName,
List> arguments,
MessagePackObjectMapper argumentsMapper,
TarantoolCallResultMapper 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().sendRequest(request, resultMapper);
} catch (TarantoolProtocolException e) {
throw new TarantoolClientException(e);
}
}
private ValueConverter getConverter(Class tupleClass) {
Optional> converter =
config.getMessagePackMapper().getValueConverter(ArrayValue.class, tupleClass);
if (!converter.isPresent()) {
throw new TarantoolClientException("No ArrayValue converter for type " + tupleClass + " is present");
}
return converter.get();
}
@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().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();
} finally {
try {
eventLoopGroup.shutdownGracefully().sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public TarantoolConnectionListeners getListeners() {
return listeners;
}
@Override
public TarantoolTupleFactory getTarantoolTupleFactory() {
return tupleFactory;
}
}