Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.springframework.data.redis.core.DefaultReactiveStreamOperations Maven / Gradle / Ivy
Go to download
This OSGi bundle wraps ${pkgArtifactId} ${pkgVersion} jar files.
/*
* Copyright 2018-2020 the original author or authors.
*
* 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 org.springframework.data.redis.core;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.Function;
import org.reactivestreams.Publisher;
import org.springframework.core.convert.ConversionService;
import org.springframework.data.domain.Range;
import org.springframework.data.redis.connection.ReactiveStreamCommands;
import org.springframework.data.redis.connection.RedisZSetCommands.Limit;
import org.springframework.data.redis.connection.stream.ByteBufferRecord;
import org.springframework.data.redis.connection.stream.Consumer;
import org.springframework.data.redis.connection.stream.MapRecord;
import org.springframework.data.redis.connection.stream.PendingMessages;
import org.springframework.data.redis.connection.stream.PendingMessagesSummary;
import org.springframework.data.redis.connection.stream.ReadOffset;
import org.springframework.data.redis.connection.stream.Record;
import org.springframework.data.redis.connection.stream.RecordId;
import org.springframework.data.redis.connection.stream.StreamInfo.XInfoConsumer;
import org.springframework.data.redis.connection.stream.StreamInfo.XInfoGroup;
import org.springframework.data.redis.connection.stream.StreamInfo.XInfoStream;
import org.springframework.data.redis.connection.stream.StreamOffset;
import org.springframework.data.redis.connection.stream.StreamReadOptions;
import org.springframework.data.redis.hash.HashMapper;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Default implementation of {@link ReactiveStreamOperations}.
*
* @author Mark Paluch
* @author Christoph Strobl
* @since 2.2
*/
class DefaultReactiveStreamOperations implements ReactiveStreamOperations {
private final ReactiveRedisTemplate template;
private final RedisSerializationContext serializationContext;
private final StreamObjectMapper objectMapper;
DefaultReactiveStreamOperations(ReactiveRedisTemplate template,
RedisSerializationContext serializationContext,
@Nullable HashMapper hashMapper) {
this.template = template;
this.serializationContext = serializationContext;
this.objectMapper = new StreamObjectMapper(hashMapper) {
@Override
protected HashMapper doGetHashMapper(ConversionService conversionService, Class targetType) {
if (objectMapper.isSimpleType(targetType) || ClassUtils.isAssignable(ByteBuffer.class, targetType)) {
return new HashMapper() {
@Override
public Map toHash(Object object) {
Object key = "payload";
Object value = object;
if (serializationContext.getHashKeySerializationPair() == null) {
key = key.toString().getBytes(StandardCharsets.UTF_8);
}
if (serializationContext.getHashValueSerializationPair() == null) {
value = conversionService.convert(value, byte[].class);
}
return Collections.singletonMap(key, value);
}
@Override
public Object fromHash(Map hash) {
Object value = hash.values().iterator().next();
if (ClassUtils.isAssignableValue(targetType, value)) {
return value;
}
HV deserialized = deserializeHashValue((ByteBuffer) value);
if (ClassUtils.isAssignableValue(targetType, deserialized)) {
return value;
}
return conversionService.convert(deserialized, targetType);
}
};
}
return super.doGetHashMapper(conversionService, targetType);
}
};
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveStreamOperations#acknowledge(java.lang.Object, java.lang.String, java.lang.String[])
*/
@Override
public Mono acknowledge(K key, String group, RecordId... recordIds) {
Assert.notNull(key, "Key must not be null!");
Assert.hasText(group, "Group must not be null or empty!");
Assert.notNull(recordIds, "MessageIds must not be null!");
Assert.notEmpty(recordIds, "MessageIds must not be empty!");
return createMono(connection -> connection.xAck(rawKey(key), group, recordIds));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveStreamOperations#add(java.lang.Object, java.util.Map)
*/
@Override
public Mono add(Record record) {
Assert.notNull(record.getStream(), "Key must not be null!");
Assert.notNull(record.getValue(), "Body must not be null!");
MapRecord input = StreamObjectMapper.toMapRecord(this, record);
return createMono(connection -> connection.xAdd(serializeRecord(input)));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveStreamOperations#delete(java.lang.Object, java.lang.String[])
*/
@Override
public Mono delete(K key, RecordId... recordIds) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(recordIds, "MessageIds must not be null!");
return createMono(connection -> connection.xDel(rawKey(key), recordIds));
}
@Override
public Mono createGroup(K key, ReadOffset readOffset, String group) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(readOffset, "ReadOffset must not be null!");
Assert.notNull(group, "Group must not be null!");
return createMono(connection -> connection.xGroupCreate(rawKey(key), group, readOffset, true));
}
@Override
public Mono deleteConsumer(K key, Consumer consumer) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(consumer, "Consumer must not be null!");
return createMono(connection -> connection.xGroupDelConsumer(rawKey(key), consumer));
}
@Override
public Mono destroyGroup(K key, String group) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(group, "Group must not be null!");
return createMono(connection -> connection.xGroupDestroy(rawKey(key), group));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveStreamOperations#consumers(java.lang.Object)
*/
@Override
public Flux consumers(K key, String group) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(group, "Group must not be null!");
return createFlux(connection -> connection.xInfoConsumers(rawKey(key), group));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveStreamOperations#info(java.lang.Object)
*/
@Override
public Mono info(K key) {
Assert.notNull(key, "Key must not be null!");
return createMono(connection -> connection.xInfo(rawKey(key)));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveStreamOperations#groups(java.lang.Object)
*/
@Override
public Flux groups(K key) {
Assert.notNull(key, "Key must not be null!");
return createFlux(connection -> connection.xInfoGroups(rawKey(key)));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.StreamOperations#pending(java.lang.Object, java.lang.String, org.springframework.data.domain.Range, java.lang.Long)
*/
@Override
public Mono pending(K key, String group, Range range, long count) {
ByteBuffer rawKey = rawKey(key);
return createMono(connection -> connection.xPending(rawKey, group, range, count));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.StreamOperations#pending(java.lang.Object, org.springframework.data.redis.connection.stream.Consumer, org.springframework.data.domain.Range, java.lang.Long)
*/
@Override
public Mono pending(K key, Consumer consumer, Range range, long count) {
ByteBuffer rawKey = rawKey(key);
return createMono(connection -> connection.xPending(rawKey, consumer, range, count));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.StreamOperations#pending(java.lang.Object, java.lang.String)
*/
@Override
public Mono pending(K key, String group) {
ByteBuffer rawKey = rawKey(key);
return createMono(connection -> connection.xPending(rawKey, group));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveStreamOperations#size(java.lang.Object)
*/
@Override
public Mono size(K key) {
Assert.notNull(key, "Key must not be null!");
return createMono(connection -> connection.xLen(rawKey(key)));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveStreamOperations#range(java.lang.Object, org.springframework.data.domain.Range, org.springframework.data.redis.connection.RedisZSetCommands.Limit)
*/
@Override
public Flux> range(K key, Range range, Limit limit) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(range, "Range must not be null!");
Assert.notNull(limit, "Limit must not be null!");
return createFlux(connection -> connection.xRange(rawKey(key), range, limit).map(this::deserializeRecord));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveStreamOperations#read(org.springframework.data.redis.connection.RedisStreamCommands.StreamReadOptions, org.springframework.data.redis.connection.RedisStreamCommands.StreamOffset[])
*/
@Override
public Flux> read(StreamReadOptions readOptions, StreamOffset... streams) {
Assert.notNull(readOptions, "StreamReadOptions must not be null!");
Assert.notNull(streams, "Streams must not be null!");
return createFlux(connection -> {
StreamOffset[] streamOffsets = rawStreamOffsets(streams);
return connection.xRead(readOptions, streamOffsets).map(this::deserializeRecord);
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveStreamOperations#read(org.springframework.data.redis.connection.RedisStreamCommands.Consumer, org.springframework.data.redis.connection.RedisStreamCommands.StreamReadOptions, org.springframework.data.redis.connection.RedisStreamCommands.StreamOffset[])
*/
@Override
public Flux> read(Consumer consumer, StreamReadOptions readOptions, StreamOffset... streams) {
Assert.notNull(consumer, "Consumer must not be null!");
Assert.notNull(readOptions, "StreamReadOptions must not be null!");
Assert.notNull(streams, "Streams must not be null!");
return createFlux(connection -> {
StreamOffset[] streamOffsets = rawStreamOffsets(streams);
return connection.xReadGroup(consumer, readOptions, streamOffsets).map(this::deserializeRecord);
});
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveStreamOperations#reverseRange(java.lang.Object, org.springframework.data.domain.Range, org.springframework.data.redis.connection.RedisZSetCommands.Limit)
*/
@Override
public Flux> reverseRange(K key, Range range, Limit limit) {
Assert.notNull(key, "Key must not be null!");
Assert.notNull(range, "Range must not be null!");
Assert.notNull(limit, "Limit must not be null!");
return createFlux(connection -> connection.xRevRange(rawKey(key), range, limit).map(this::deserializeRecord));
}
/*
* (non-Javadoc)
* @see org.springframework.data.redis.core.ReactiveStreamOperations#trim(java.lang.Object, long)
*/
@Override
public Mono trim(K key, long count) {
Assert.notNull(key, "Key must not be null!");
return createMono(connection -> connection.xTrim(rawKey(key), count));
}
@Override
public HashMapper getHashMapper(Class targetType) {
return objectMapper.getHashMapper(targetType);
}
@SuppressWarnings("unchecked")
private StreamOffset[] rawStreamOffsets(StreamOffset[] streams) {
return Arrays.stream(streams).map(it -> StreamOffset.create(rawKey(it.getKey()), it.getOffset()))
.toArray(StreamOffset[]::new);
}
private Mono createMono(Function> function) {
Assert.notNull(function, "Function must not be null!");
return template.createMono(connection -> function.apply(connection.streamCommands()));
}
private Flux createFlux(Function> function) {
Assert.notNull(function, "Function must not be null!");
return template.createFlux(connection -> function.apply(connection.streamCommands()));
}
private ByteBuffer rawKey(K key) {
return serializationContext.getKeySerializationPair().write(key);
}
@SuppressWarnings("unchecked")
private ByteBuffer rawHashKey(HK key) {
try {
return serializationContext.getHashKeySerializationPair().write(key);
} catch (IllegalStateException e) {}
return ByteBuffer.wrap(objectMapper.getConversionService().convert(key, byte[].class));
}
@SuppressWarnings("unchecked")
private ByteBuffer rawValue(HV value) {
try {
return serializationContext.getHashValueSerializationPair().write(value);
} catch (IllegalStateException e) {}
return ByteBuffer.wrap(objectMapper.getConversionService().convert(value, byte[].class));
}
@SuppressWarnings("unchecked")
private HK readHashKey(ByteBuffer buffer) {
return (HK) serializationContext.getHashKeySerializationPair().getReader().read(buffer);
}
private K readKey(ByteBuffer buffer) {
return serializationContext.getKeySerializationPair().read(buffer);
}
@SuppressWarnings("unchecked")
private HV deserializeHashValue(ByteBuffer buffer) {
return (HV) serializationContext.getHashValueSerializationPair().read(buffer);
}
private MapRecord deserializeRecord(ByteBufferRecord record) {
return record.map(it -> it.mapEntries(this::deserializeRecordFields).withStreamKey(readKey(record.getStream())));
}
private Entry deserializeRecordFields(Entry it) {
return Collections.singletonMap(readHashKey(it.getKey()), deserializeHashValue(it.getValue())).entrySet().iterator()
.next();
}
private ByteBufferRecord serializeRecord(MapRecord record) {
return ByteBufferRecord
.of(record.map(it -> it.mapEntries(this::serializeRecordFields).withStreamKey(rawKey(record.getStream()))));
}
private Entry serializeRecordFields(Entry it) {
return Collections.singletonMap(rawHashKey(it.getKey()), rawValue(it.getValue())).entrySet().iterator().next();
}
}