org.redisson.spring.data.connection.RedissonReactiveSubscription Maven / Gradle / Ivy
/**
* Copyright (c) 2013-2020 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.spring.data.connection;
import java.nio.ByteBuffer;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
import org.redisson.api.RFuture;
import org.redisson.client.BaseRedisPubSubListener;
import org.redisson.client.ChannelName;
import org.redisson.client.codec.ByteArrayCodec;
import org.redisson.client.codec.Codec;
import org.redisson.client.protocol.pubsub.PubSubType;
import org.redisson.connection.ConnectionManager;
import org.redisson.misc.CountableListener;
import org.redisson.misc.RedissonPromise;
import org.redisson.pubsub.PubSubConnectionEntry;
import org.redisson.pubsub.PublishSubscribeService;
import org.springframework.data.redis.connection.ReactiveSubscription;
import reactor.core.Disposable;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
/**
*
* @author Nikita Koksharov
*
*/
public class RedissonReactiveSubscription implements ReactiveSubscription {
private final Map channels = new ConcurrentHashMap();
private final Map patterns = new ConcurrentHashMap();
private final PublishSubscribeService subscribeService;
public RedissonReactiveSubscription(ConnectionManager connectionManager) {
this.subscribeService = connectionManager.getSubscribeService();
}
@Override
public Mono subscribe(ByteBuffer... channels) {
RedissonPromise result = new RedissonPromise();
CountableListener listener = new CountableListener(result, null, channels.length);
for (ByteBuffer channel : channels) {
RFuture f = subscribeService.subscribe(ByteArrayCodec.INSTANCE, toChannelName(channel));
f.onComplete((res, e) -> RedissonReactiveSubscription.this.channels.put(channel, res));
f.onComplete(listener);
}
return Mono.fromFuture(result);
}
protected ChannelName toChannelName(ByteBuffer channel) {
return new ChannelName(RedissonBaseReactive.toByteArray(channel));
}
@Override
public Mono pSubscribe(ByteBuffer... patterns) {
RedissonPromise result = new RedissonPromise();
CountableListener listener = new CountableListener(result, null, patterns.length);
for (ByteBuffer channel : patterns) {
RFuture f = subscribeService.psubscribe(toChannelName(channel), ByteArrayCodec.INSTANCE);
f.onComplete((res, e) -> RedissonReactiveSubscription.this.patterns.put(channel, res));
f.onComplete(listener);
}
return Mono.fromFuture(result);
}
@Override
public Mono unsubscribe() {
return unsubscribe(channels.keySet().toArray(new ByteBuffer[channels.size()]));
}
@Override
public Mono unsubscribe(ByteBuffer... channels) {
RedissonPromise result = new RedissonPromise();
CountableListener listener = new CountableListener(result, null, channels.length);
for (ByteBuffer channel : channels) {
RFuture f = subscribeService.unsubscribe(toChannelName(channel), PubSubType.UNSUBSCRIBE);
f.onComplete(listener);
}
return Mono.fromFuture(result);
}
@Override
public Mono pUnsubscribe() {
return unsubscribe(patterns.keySet().toArray(new ByteBuffer[patterns.size()]));
}
@Override
public Mono pUnsubscribe(ByteBuffer... patterns) {
RedissonPromise result = new RedissonPromise();
CountableListener listener = new CountableListener(result, null, patterns.length);
for (ByteBuffer channel : patterns) {
RFuture f = subscribeService.unsubscribe(toChannelName(channel), PubSubType.PUNSUBSCRIBE);
f.onComplete(listener);
}
return Mono.fromFuture(result);
}
@Override
public Set getChannels() {
return channels.keySet();
}
@Override
public Set getPatterns() {
return patterns.keySet();
}
private final AtomicReference>> flux = new AtomicReference<>();
private volatile Disposable disposable;
@Override
public Flux> receive() {
if (flux.get() != null) {
return flux.get();
}
Flux> f = Flux.>create(emitter -> {
emitter.onRequest(n -> {
AtomicLong counter = new AtomicLong(n);
BaseRedisPubSubListener listener = new BaseRedisPubSubListener() {
@Override
public void onPatternMessage(CharSequence pattern, CharSequence channel, Object message) {
emitter.next(new PatternMessage<>(ByteBuffer.wrap(pattern.toString().getBytes()),
ByteBuffer.wrap(channel.toString().getBytes()), ByteBuffer.wrap((byte[])message)));
if (counter.decrementAndGet() == 0) {
disposable.dispose();
emitter.complete();
}
}
@Override
public void onMessage(CharSequence channel, Object msg) {
emitter.next(new ChannelMessage<>(ByteBuffer.wrap(channel.toString().getBytes()), ByteBuffer.wrap((byte[])msg)));
if (counter.decrementAndGet() == 0) {
disposable.dispose();
emitter.complete();
}
}
};
disposable = () -> {
for (Entry entry : channels.entrySet()) {
entry.getValue().removeListener(toChannelName(entry.getKey()), listener);
}
for (Entry entry : patterns.entrySet()) {
entry.getValue().removeListener(toChannelName(entry.getKey()), listener);
}
};
for (Entry entry : channels.entrySet()) {
entry.getValue().addListener(toChannelName(entry.getKey()), listener);
}
for (Entry entry : patterns.entrySet()) {
entry.getValue().addListener(toChannelName(entry.getKey()), listener);
}
emitter.onDispose(disposable);
});
});
if (flux.compareAndSet(null, f)) {
return f;
}
return flux.get();
}
@Override
public Mono cancel() {
return unsubscribe().then(pUnsubscribe()).then(Mono.fromRunnable(() -> {
if (disposable != null) {
disposable.dispose();
}
}));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy