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

io.lettuce.core.pubsub.StatefulRedisPubSubConnectionImpl 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!
/*
 * Copyright 2011-Present, Redis Ltd. and Contributors
 * All rights reserved.
 *
 * Licensed under the MIT License.
 *
 * This file contains contributions from third-party contributors
 * licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package io.lettuce.core.pubsub;

import java.lang.reflect.Array;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import io.lettuce.core.RedisChannelWriter;
import io.lettuce.core.RedisCommandExecutionException;
import io.lettuce.core.RedisFuture;
import io.lettuce.core.StatefulRedisConnectionImpl;
import io.lettuce.core.codec.RedisCodec;
import io.lettuce.core.protocol.ConnectionWatchdog;
import io.lettuce.core.pubsub.api.async.RedisPubSubAsyncCommands;
import io.lettuce.core.pubsub.api.reactive.RedisPubSubReactiveCommands;
import io.lettuce.core.pubsub.api.sync.RedisPubSubCommands;
import io.netty.util.internal.logging.InternalLoggerFactory;

import static io.lettuce.core.ClientOptions.DEFAULT_JSON_PARSER;

/**
 * An thread-safe pub/sub connection to a Redis server. Multiple threads may share one {@link StatefulRedisPubSubConnectionImpl}
 *
 * A {@link ConnectionWatchdog} monitors each connection and reconnects automatically until {@link #close} is called. All
 * pending commands will be (re)sent after successful reconnection.
 *
 * @param  Key type.
 * @param  Value type.
 * @author Mark Paluch
 */
public class StatefulRedisPubSubConnectionImpl extends StatefulRedisConnectionImpl
        implements StatefulRedisPubSubConnection {

    private final PubSubEndpoint endpoint;

    /**
     * Initialize a new connection.
     *
     * @param endpoint the {@link PubSubEndpoint}
     * @param writer the writer used to write commands
     * @param codec Codec used to encode/decode keys and values.
     * @param timeout Maximum time to wait for a response.
     */
    public StatefulRedisPubSubConnectionImpl(PubSubEndpoint endpoint, RedisChannelWriter writer, RedisCodec codec,
            Duration timeout) {

        super(writer, endpoint, codec, timeout, DEFAULT_JSON_PARSER);
        this.endpoint = endpoint;
        endpoint.setConnectionState(getConnectionState());
    }

    /**
     * Add a new listener.
     *
     * @param listener Listener.
     */
    @Override
    public void addListener(RedisPubSubListener listener) {
        endpoint.addListener(listener);
    }

    /**
     * Remove an existing listener.
     *
     * @param listener Listener.
     */
    @Override
    public void removeListener(RedisPubSubListener listener) {
        endpoint.removeListener(listener);
    }

    @Override
    public RedisPubSubAsyncCommands async() {
        return (RedisPubSubAsyncCommands) async;
    }

    @Override
    protected RedisPubSubAsyncCommandsImpl newRedisAsyncCommandsImpl() {
        return new RedisPubSubAsyncCommandsImpl<>(this, codec);
    }

    @Override
    public RedisPubSubCommands sync() {
        return (RedisPubSubCommands) sync;
    }

    @Override
    protected RedisPubSubCommands newRedisSyncCommandsImpl() {
        return syncHandler(async(), RedisPubSubCommands.class);
    }

    @Override
    public RedisPubSubReactiveCommands reactive() {
        return (RedisPubSubReactiveCommands) reactive;
    }

    @Override
    protected RedisPubSubReactiveCommandsImpl newRedisReactiveCommandsImpl() {
        return new RedisPubSubReactiveCommandsImpl<>(this, codec);
    }

    /**
     * Re-subscribe to all previously subscribed channels and patterns.
     *
     * @return list of the futures of the {@literal subscribe} and {@literal psubscribe} commands.
     */
    protected List> resubscribe() {

        List> result = new ArrayList<>();

        if (endpoint.hasChannelSubscriptions()) {
            result.add(async().subscribe(toArray(endpoint.getChannels())));
        }

        if (endpoint.hasShardChannelSubscriptions()) {
            result.add(async().ssubscribe(toArray(endpoint.getShardChannels())));
        }

        if (endpoint.hasPatternSubscriptions()) {
            result.add(async().psubscribe(toArray(endpoint.getPatterns())));
        }

        return result;
    }

    @SuppressWarnings("unchecked")
    private  T[] toArray(Collection c) {
        Class cls = (Class) c.iterator().next().getClass();
        T[] array = (T[]) Array.newInstance(cls, c.size());
        return c.toArray(array);
    }

    @Override
    public void activated() {
        super.activated();
        for (RedisFuture command : resubscribe()) {
            command.exceptionally(throwable -> {
                if (throwable instanceof RedisCommandExecutionException) {
                    InternalLoggerFactory.getInstance(getClass()).warn("Re-subscribe failed: " + command.getError());
                }
                return null;
            });
        }
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy