package com.lambdaworks.redis.pubsub;
import static com.lambdaworks.redis.protocol.CommandType.*;
import com.lambdaworks.redis.protocol.Command;
import rx.Observable;
import rx.Subscriber;
import com.lambdaworks.redis.RedisReactiveCommandsImpl;
import com.lambdaworks.redis.api.rx.Success;
import com.lambdaworks.redis.codec.RedisCodec;
import com.lambdaworks.redis.protocol.CommandArgs;
import com.lambdaworks.redis.pubsub.api.rx.ChannelMessage;
import com.lambdaworks.redis.pubsub.api.rx.PatternMessage;
import com.lambdaworks.redis.pubsub.api.rx.RedisPubSubReactiveCommands;
import java.util.Map;
/**
* A reactive and thread-safe API for a Redis pub/sub connection.
*
* @param <K> Key type.
* @param <V> Value type.
* @author Mark Paluch
*/
public class RedisPubSubReactiveCommandsImpl<K, V> extends RedisReactiveCommandsImpl<K, V> implements
RedisPubSubReactiveCommands<K, V> {
private PubSubCommandBuilder<K, V> commandBuilder;
/**
* Initialize a new connection.
*
* @param connection the connection .
* @param codec Codec used to encode/decode keys and values.
*/
public RedisPubSubReactiveCommandsImpl(StatefulRedisPubSubConnection<K, V> connection, RedisCodec<K, V> codec) {
super(connection, codec);
this.connection = connection;
this.commandBuilder = new PubSubCommandBuilder<>(codec);
}
/**
* Add a new listener.
*
* @param listener Listener.
*/
@Override
public void addListener(RedisPubSubListener<K, V> listener) {
getStatefulConnection().addListener(listener);
}
@Override
public Observable<PatternMessage<K, V>> observePatterns() {
SubscriptionPubSubListener<K, V, PatternMessage<K, V>> listener = new SubscriptionPubSubListener<K, V, PatternMessage<K, V>>() {
@Override
public void message(K pattern, K channel, V message) {
if (subscriber == null) {
return;
}
if (subscriber.isUnsubscribed()) {
subscriber.onCompleted();
removeListener(this);
subscriber = null;
return;
}
subscriber.onNext(new PatternMessage<>(pattern, channel, message));
}
};
return Observable.create(new PubSubObservable<>(listener));
}
@Override
public Observable<ChannelMessage<K, V>> observeChannels() {
SubscriptionPubSubListener<K, V, ChannelMessage<K, V>> listener = new SubscriptionPubSubListener<K, V, ChannelMessage<K, V>>() {
@Override
public void message(K channel, V message) {
if (subscriber == null) {
return;
}
if (subscriber.isUnsubscribed()) {
subscriber.onCompleted();
removeListener(this);
subscriber = null;
return;
}
subscriber.onNext(new ChannelMessage<>(channel, message));
}
};
return Observable.create(new PubSubObservable<>(listener));
}
/**
* Remove an existing listener.
*
* @param listener Listener.
*/
@Override
public void removeListener(RedisPubSubListener<K, V> listener) {
getStatefulConnection().removeListener(listener);
}
@Override
public Observable<Success> psubscribe(K... patterns) {
return getSuccessObservable(createObservable(() -> commandBuilder.psubscribe(patterns)));
}
@Override
public Observable<Success> punsubscribe(K... patterns) {
return getSuccessObservable(createObservable(() -> commandBuilder.punsubscribe(patterns)));
}
@Override
public Observable<Success> subscribe(K... channels) {
return getSuccessObservable(createObservable(() -> commandBuilder.subscribe(channels)));
}
@Override
public Observable<Success> unsubscribe(K... channels) {
return getSuccessObservable(createObservable(() -> commandBuilder.unsubscribe(channels)));
}
@Override
public Observable<Long> publish(K channel, V message) {
return createObservable(() -> commandBuilder.publish(channel, message));
}
@Override
public Observable<K> pubsubChannels(K channel) {
return createDissolvingObservable(() -> commandBuilder.pubsubChannels(channel));
}
@Override
public Observable<Map<K, Long>> pubsubNumsub(K... channels) {
return createObservable(() -> commandBuilder.pubsubNumsub(channels));
}
@Override
@SuppressWarnings("unchecked")
public StatefulRedisPubSubConnection<K, V> getStatefulConnection() {
return (StatefulRedisPubSubConnection<K, V>) super.getStatefulConnection();
}
private class PubSubObservable<T> implements Observable.OnSubscribe<T> {
private SubscriptionPubSubListener<K, V, T> listener;
public PubSubObservable(SubscriptionPubSubListener<K, V, T> listener) {
this.listener = listener;
}
@Override
public void call(Subscriber<? super T> subscriber) {
listener.activate(subscriber);
subscriber.onStart();
addListener(listener);
}
}
private static class SubscriptionPubSubListener<K, V, T> extends RedisPubSubAdapter<K, V> {
protected Subscriber<? super T> subscriber;
public void activate(Subscriber<? super T> subscriber) {
this.subscriber = subscriber;
}
}
}