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

org.redisson.client.RedisPubSubConnection Maven / Gradle / Ivy

Go to download

Easy Redis Java client and Real-Time Data Platform. Valkey compatible. Sync/Async/RxJava3/Reactive API. Client side caching. Over 50 Redis based Java objects and services: JCache API, Apache Tomcat, Hibernate, Spring, Set, Multimap, SortedSet, Map, List, Queue, Deque, Semaphore, Lock, AtomicLong, Map Reduce, Bloom filter, Scheduler, RPC

There is a newer version: 3.45.1
Show newest version
/**
 * Copyright (c) 2013-2021 Nikita Koksharov
 *
 * 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
 *
 *    http://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 org.redisson.client;

import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.FutureListener;
import org.redisson.client.codec.Codec;
import org.redisson.client.protocol.CommandData;
import org.redisson.client.protocol.RedisCommand;
import org.redisson.client.protocol.RedisCommands;
import org.redisson.client.protocol.decoder.MultiDecoder;
import org.redisson.client.protocol.pubsub.*;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;

/**
 * 
 * @author Nikita Koksharov
 *
 */
public class RedisPubSubConnection extends RedisConnection {

    final Queue> listeners = new ConcurrentLinkedQueue>();
    final Map channels = new ConcurrentHashMap<>();
    final Map patternChannels = new ConcurrentHashMap<>();
    final Set unsubscibedChannels = new HashSet();
    final Set punsubscibedChannels = new HashSet();

    public RedisPubSubConnection(RedisClient redisClient, Channel channel, CompletableFuture connectionPromise) {
        super(redisClient, channel, connectionPromise);
    }

    public void addListener(RedisPubSubListener listener) {
        listeners.add((RedisPubSubListener) listener);
    }

    public void removeListener(RedisPubSubListener listener) {
        listeners.remove(listener);
    }

    public void onMessage(PubSubStatusMessage message) {
        for (RedisPubSubListener redisPubSubListener : listeners) {
            redisPubSubListener.onStatus(message.getType(), message.getChannel());
        }
    }

    public void onMessage(PubSubMessage message) {
        for (RedisPubSubListener redisPubSubListener : listeners) {
            redisPubSubListener.onMessage(message.getChannel(), message.getValue());
        }
    }

    public void onMessage(PubSubPatternMessage message) {
        for (RedisPubSubListener redisPubSubListener : listeners) {
            redisPubSubListener.onPatternMessage(message.getPattern(), message.getChannel(), message.getValue());
        }
    }

    public ChannelFuture subscribe(Codec codec, ChannelName... channels) {
        for (ChannelName ch : channels) {
            this.channels.put(ch, codec);
        }
        return async(new PubSubMessageDecoder(codec.getValueDecoder()), RedisCommands.SUBSCRIBE, channels);
    }

    public ChannelFuture psubscribe(Codec codec, ChannelName... channels) {
        for (ChannelName ch : channels) {
            patternChannels.put(ch, codec);
        }
        return async(new PubSubPatternMessageDecoder(codec.getValueDecoder()), RedisCommands.PSUBSCRIBE, channels);
    }

    public ChannelFuture unsubscribe(ChannelName... channels) {
        synchronized (this) {
            for (ChannelName ch : channels) {
                this.channels.remove(ch);
                unsubscibedChannels.add(ch);
            }
        }
        ChannelFuture future = async((MultiDecoder) null, RedisCommands.UNSUBSCRIBE, channels);
        future.addListener(new FutureListener() {
            @Override
            public void operationComplete(Future future) throws Exception {
                if (!future.isSuccess()) {
                    for (ChannelName channel : channels) {
                        removeDisconnectListener(channel);
                        onMessage(new PubSubStatusMessage(PubSubType.UNSUBSCRIBE, channel));
                    }
                }
            }
        });
        return future;
    }
    
    public void removeDisconnectListener(ChannelName channel) {
        synchronized (this) {
            unsubscibedChannels.remove(channel);
            punsubscibedChannels.remove(channel);
        }
    }
    
    @Override
    public void fireDisconnected() {
        super.fireDisconnected();
        
        Set channels = new HashSet();
        Set pchannels = new HashSet();
        synchronized (this) {
            channels.addAll(unsubscibedChannels);
            pchannels.addAll(punsubscibedChannels);
        }
        for (ChannelName channel : channels) {
            onMessage(new PubSubStatusMessage(PubSubType.UNSUBSCRIBE, channel));
        }
        for (ChannelName channel : pchannels) {
            onMessage(new PubSubStatusMessage(PubSubType.PUNSUBSCRIBE, channel));
        }
    }
    
    public ChannelFuture punsubscribe(ChannelName... channels) {
        synchronized (this) {
            for (ChannelName ch : channels) {
                patternChannels.remove(ch);
                punsubscibedChannels.add(ch);
            }
        }
        ChannelFuture future = async((MultiDecoder) null, RedisCommands.PUNSUBSCRIBE, channels);
        future.addListener(new FutureListener() {
            @Override
            public void operationComplete(Future future) throws Exception {
                if (!future.isSuccess()) {
                    for (ChannelName channel : channels) {
                        removeDisconnectListener(channel);
                        onMessage(new PubSubStatusMessage(PubSubType.PUNSUBSCRIBE, channel));
                    }
                }
            }
        });
        return future;
    }

    private  ChannelFuture async(MultiDecoder messageDecoder, RedisCommand command, Object... params) {
        CompletableFuture promise = new CompletableFuture<>();
        return channel.writeAndFlush(new CommandData<>(promise, messageDecoder, null, command, params));
    }

    public Map getChannels() {
        return Collections.unmodifiableMap(channels);
    }

    public Map getPatternChannels() {
        return Collections.unmodifiableMap(patternChannels);
    }

}