All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.tarantool.driver.AbstractTarantoolClient Maven / Gradle / Ivy

Go to download

Tarantool Cartridge driver for Tarantool versions 1.10+ based on Netty framework

There is a newer version: 0.14.0
Show newest version
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;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy