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

io.lettuce.core.protocol.RedisHandshakeHandler Maven / Gradle / Ivy

Go to download

Advanced and thread-safe Java Redis client for synchronous, asynchronous, and reactive usage. Supports Cluster, Sentinel, Pipelining, Auto-Reconnect, Codecs and much more.

The newest version!
package io.lettuce.core.protocol;

import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.TimeUnit;

import io.lettuce.core.RedisCommandTimeoutException;
import io.lettuce.core.RedisConnectionException;
import io.lettuce.core.internal.ExceptionFactory;
import io.lettuce.core.resource.ClientResources;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.Timeout;

/**
 * Handler to initialize a Redis Connection using a {@link ConnectionInitializer}.
 *
 * @author Mark Paluch
 * @since 6.0
 */
public class RedisHandshakeHandler extends ChannelInboundHandlerAdapter {

    private final ConnectionInitializer connectionInitializer;

    private final ClientResources clientResources;

    private final Duration initializeTimeout;

    private final CompletableFuture handshakeFuture = new CompletableFuture<>();

    private volatile boolean timedOut = false;

    public RedisHandshakeHandler(ConnectionInitializer connectionInitializer, ClientResources clientResources,
            Duration initializeTimeout) {
        this.connectionInitializer = connectionInitializer;
        this.clientResources = clientResources;
        this.initializeTimeout = initializeTimeout;
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {

        Runnable timeoutGuard = () -> {

            timedOut = true;
            if (handshakeFuture.isDone()) {
                return;
            }

            fail(ctx, new RedisCommandTimeoutException(
                    "Connection initialization timed out after " + ExceptionFactory.formatTimeout(initializeTimeout)));
        };

        Timeout timeoutHandle = clientResources.timer().newTimeout(t -> {

            if (clientResources.eventExecutorGroup().isShuttingDown()) {
                timeoutGuard.run();
                return;
            }

            clientResources.eventExecutorGroup().submit(timeoutGuard);
        }, initializeTimeout.toNanos(), TimeUnit.NANOSECONDS);

        handshakeFuture.thenAccept(ignore -> {
            timeoutHandle.cancel();
        });

        super.channelRegistered(ctx);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {

        if (shouldFail()) {
            fail(ctx, new RedisConnectionException("Connection closed prematurely"));
        }

        super.channelInactive(ctx);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) {

        CompletionStage future = connectionInitializer.initialize(ctx.channel());

        future.whenComplete((ignore, throwable) -> {

            if (throwable != null) {

                if (shouldFail()) {
                    fail(ctx, throwable);
                }
            } else {
                ctx.fireChannelActive();
                succeed();
            }
        });
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {

        if (shouldFail()) {
            fail(ctx, cause);
        }

        super.exceptionCaught(ctx, cause);
    }

    /**
     * Complete the handshake future successfully.
     */
    protected void succeed() {
        handshakeFuture.complete(null);
    }

    /**
     * Complete the handshake future with an error and close the channel..
     */
    protected void fail(ChannelHandlerContext ctx, Throwable cause) {

        ctx.close().addListener(closeFuture -> {
            handshakeFuture.completeExceptionally(cause);
        });
    }

    /**
     * @return future to synchronize channel initialization. Returns a new future for every reconnect.
     */
    public CompletionStage channelInitialized() {
        return handshakeFuture;
    }

    private boolean shouldFail() {
        return !handshakeFuture.isDone() && !timedOut;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy