
com.aerospike.mapper.tools.ReactiveAeroMapper Maven / Gradle / Ivy
package com.aerospike.mapper.tools;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Function;
import javax.validation.constraints.NotNull;
import com.aerospike.client.AerospikeException;
import com.aerospike.client.Bin;
import com.aerospike.client.Key;
import com.aerospike.client.Operation;
import com.aerospike.client.Value;
import com.aerospike.client.policy.BatchPolicy;
import com.aerospike.client.policy.Policy;
import com.aerospike.client.policy.QueryPolicy;
import com.aerospike.client.policy.RecordExistsAction;
import com.aerospike.client.policy.ScanPolicy;
import com.aerospike.client.policy.WritePolicy;
import com.aerospike.client.query.Filter;
import com.aerospike.client.query.KeyRecord;
import com.aerospike.client.query.Statement;
import com.aerospike.client.reactor.IAerospikeReactorClient;
import com.aerospike.mapper.tools.converters.MappingConverter;
import com.aerospike.mapper.tools.utils.MapperUtils;
import com.aerospike.mapper.tools.virtuallist.ReactiveVirtualList;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
public class ReactiveAeroMapper implements IReactiveAeroMapper {
private final IAerospikeReactorClient reactorClient;
private final IAeroMapper aeroMapper;
private final MappingConverter mappingConverter;
/**
* Create a new Builder to instantiate the AeroMapper.
* @author tfaulkes
*
*/
public static class Builder extends AbstractBuilder {
public Builder(IAerospikeReactorClient reactorClient) {
super(new ReactiveAeroMapper(reactorClient));
ClassCache.getInstance().setReactiveDefaultPolicies(reactorClient);
}
}
private ReactiveAeroMapper(@NotNull IAerospikeReactorClient reactorClient) {
this.reactorClient = reactorClient;
this.aeroMapper = new AeroMapper.Builder(reactorClient.getAerospikeClient()).build();
this.mappingConverter = new MappingConverter(this, reactorClient.getAerospikeClient());
}
@Override
public Flux save(@NotNull T... objects) {
return Flux.fromStream(Arrays.stream(objects))
.flatMap(this::save);
}
@Override
public Mono save(@NotNull T object, String... binNames) {
return save(null, object, RecordExistsAction.REPLACE, binNames);
}
@Override
public Mono save(@NotNull WritePolicy writePolicy, @NotNull T object, String... binNames) {
return save(writePolicy, object, null, binNames);
}
@SuppressWarnings("unchecked")
private Mono save(WritePolicy writePolicy, @NotNull T object, RecordExistsAction recordExistsAction, String[] binNames) {
Class clazz = (Class) object.getClass();
ClassCacheEntry entry = MapperUtils.getEntryAndValidateNamespace(clazz, this);
if (writePolicy == null) {
writePolicy = new WritePolicy(entry.getWritePolicy());
if (recordExistsAction != null) {
writePolicy.recordExistsAction = recordExistsAction;
}
// #132 -- Only override the TTL / send key if the policy was not passed in.
Integer ttl = entry.getTtl();
Boolean sendKey = entry.getSendKey();
if (ttl != null) {
writePolicy.expiration = ttl;
}
if (sendKey != null) {
writePolicy.sendKey = sendKey;
}
}
String set = entry.getSetName();
if ("".equals(set)) {
// Use the null set
set = null;
}
Key key = new Key(entry.getNamespace(), set, Value.get(entry.getKey(object)));
Bin[] bins = entry.getBins(object, writePolicy.recordExistsAction != RecordExistsAction.REPLACE, binNames);
return reactorClient
.put(writePolicy, key, bins)
.map(docKey -> object)
.onErrorMap(this::translateError);
}
@Override
public Mono update(@NotNull T object, String... binNames) {
return save(null, object, RecordExistsAction.UPDATE, binNames);
}
@Override
public Mono readFromDigest(@NotNull Class clazz, @NotNull byte[] digest) {
return this.readFromDigest(clazz, digest, true);
}
@Override
public Mono readFromDigest(@NotNull Class clazz, @NotNull byte[] digest, boolean resolveDependencies) throws AerospikeException {
ClassCacheEntry entry = MapperUtils.getEntryAndValidateNamespace(clazz, this);
Key key = new Key(entry.getNamespace(), digest, entry.getSetName(), null);
return this.read(null, clazz, key, entry, resolveDependencies);
}
@Override
public Mono readFromDigest(Policy readPolicy, @NotNull Class clazz, @NotNull byte[] digest) {
return this.readFromDigest(readPolicy, clazz, digest, true);
}
@Override
public Mono readFromDigest(Policy readPolicy, @NotNull Class clazz, @NotNull byte[] digest, boolean resolveDependencies) {
ClassCacheEntry entry = MapperUtils.getEntryAndValidateNamespace(clazz, this);
Key key = new Key(entry.getNamespace(), digest, entry.getSetName(), null);
return this.read(readPolicy, clazz, key, entry, resolveDependencies);
}
@Override
public Mono read(@NotNull Class clazz, @NotNull Object userKey) {
return this.read(clazz, userKey, true);
}
@Override
public Mono read(@NotNull Class clazz, @NotNull Object userKey, boolean resolveDependencies) {
ClassCacheEntry entry = MapperUtils.getEntryAndValidateNamespace(clazz, this);
String set = entry.getSetName();
Key key = new Key(entry.getNamespace(), set, Value.get(entry.translateKeyToAerospikeKey(userKey)));
return read(null, clazz, key, entry, resolveDependencies);
}
@Override
public Mono read(Policy readPolicy, @NotNull Class clazz, @NotNull Object userKey) {
return this.read(readPolicy, clazz, userKey, true);
}
@Override
public Mono read(Policy readPolicy, @NotNull Class clazz, @NotNull Object userKey, boolean resolveDependencies) {
ClassCacheEntry entry = MapperUtils.getEntryAndValidateNamespace(clazz, this);
String set = entry.getSetName();
Key key = new Key(entry.getNamespace(), set, Value.get(entry.translateKeyToAerospikeKey(userKey)));
return read(readPolicy, clazz, key, entry, resolveDependencies);
}
@Override
public Flux read(@NotNull Class clazz, @NotNull Object[] userKeys) {
return read(null, clazz, userKeys);
}
@Override
public Flux read(BatchPolicy batchPolicy, @NotNull Class clazz, @NotNull Object[] userKeys) {
return read(null, clazz, userKeys, (Operation[]) null);
}
@Override
public Flux read(@NotNull Class clazz, @NotNull Object[] userKeys, Operation... operations) {
return read(null, clazz, userKeys, operations);
}
@Override
public Flux read(BatchPolicy batchPolicy, @NotNull Class clazz, @NotNull Object[] userKeys, Operation... operations) {
ClassCacheEntry entry = MapperUtils.getEntryAndValidateNamespace(clazz, this);
String set = entry.getSetName();
Key[] keys = new Key[userKeys.length];
for (int i = 0; i < userKeys.length; i++) {
if (userKeys[i] == null) {
throw new AerospikeException("Cannot pass null to object " + i + " in multi-read call");
} else {
keys[i] = new Key(entry.getNamespace(), set, Value.get(entry.translateKeyToAerospikeKey(userKeys[i])));
}
}
return readBatch(batchPolicy, clazz, keys, entry, operations);
}
private Mono read(Policy readPolicy, @NotNull Class clazz, @NotNull Key key, @NotNull ClassCacheEntry entry, boolean resolveDependencies) {
if (readPolicy == null) {
readPolicy = entry.getReadPolicy();
}
return reactorClient
.get(readPolicy, key)
.filter(keyRecord -> Objects.nonNull(keyRecord.record))
.map(keyRecord -> {
try {
ThreadLocalKeySaver.save(key);
return mappingConverter.convertToObject(clazz, keyRecord.record, entry, resolveDependencies);
} catch (ReflectiveOperationException e) {
throw new AerospikeException(e);
} finally {
ThreadLocalKeySaver.clear();
}
});
}
private Flux readBatch(BatchPolicy batchPolicy, @NotNull Class clazz, @NotNull Key[] keys,
@NotNull ClassCacheEntry entry, Operation... operations) {
if (batchPolicy == null) {
batchPolicy = entry.getBatchPolicy();
}
Flux keyRecordFlux;
if (operations != null && operations.length > 0) {
keyRecordFlux = reactorClient
.getFlux(batchPolicy, keys, operations);
} else {
keyRecordFlux = reactorClient
.getFlux(batchPolicy, keys);
}
return keyRecordFlux.filter(keyRecord -> Objects.nonNull(keyRecord.record))
.map(keyRecord -> {
try {
ThreadLocalKeySaver.save(keyRecord.key);
return mappingConverter.convertToObject(clazz, keyRecord.record, entry, true);
} catch (ReflectiveOperationException e) {
throw new AerospikeException(e);
} finally {
ThreadLocalKeySaver.clear();
}
});
}
@Override
public Mono delete(@NotNull Class clazz, @NotNull Object userKey) {
return delete(null, clazz, userKey);
}
@Override
public Mono delete(WritePolicy writePolicy, @NotNull Class clazz, @NotNull Object userKey) {
ClassCacheEntry entry = MapperUtils.getEntryAndValidateNamespace(clazz, this);
Object asKey = entry.translateKeyToAerospikeKey(userKey);
if (writePolicy == null) {
writePolicy = entry.getWritePolicy();
if (entry.getDurableDelete() != null) {
// Clone the write policy so we're not changing the original one
writePolicy = new WritePolicy(writePolicy);
writePolicy.durableDelete = entry.getDurableDelete();
}
}
Key key = new Key(entry.getNamespace(), entry.getSetName(), Value.get(asKey));
return reactorClient
.delete(writePolicy, key)
.map(k -> true);
}
@Override
public Mono delete(@NotNull Object object) {
return this.delete((WritePolicy) null, object);
}
@Override
public Mono delete(WritePolicy writePolicy, @NotNull Object object) {
ClassCacheEntry> entry = MapperUtils.getEntryAndValidateNamespace(object.getClass(), this);
Key key = new Key(entry.getNamespace(), entry.getSetName(), Value.get(entry.getKey(object)));
if (writePolicy == null) {
writePolicy = entry.getWritePolicy();
if (entry.getDurableDelete() != null) {
writePolicy = new WritePolicy(writePolicy);
writePolicy.durableDelete = entry.getDurableDelete();
}
}
return reactorClient
.delete(writePolicy, key)
.map(k -> true);
}
@Override
public Mono find(@NotNull Class clazz, Function function) throws AerospikeException {
return Mono.fromCallable(() -> {
asMapper().find(clazz, function);
return null;
});
}
@Override
public Flux scan(@NotNull Class clazz) {
return scan(null, clazz);
}
@Override
public Flux scan(ScanPolicy policy, @NotNull Class clazz) {
return scan(policy, clazz, -1);
}
@Override
public Flux scan(@NotNull Class clazz, int recordsPerSecond) {
return scan(null, clazz, recordsPerSecond);
}
@Override
public Flux scan(ScanPolicy policy, @NotNull Class clazz, int recordsPerSecond) {
ClassCacheEntry entry = MapperUtils.getEntryAndValidateNamespace(clazz, this);
if (policy == null) {
policy = entry.getScanPolicy();
}
if (recordsPerSecond >= 0) {
// Ensure the underlying rate on the policy does not change
policy = new ScanPolicy(policy);
policy.recordsPerSecond = recordsPerSecond;
}
String namespace = entry.getNamespace();
String setName = entry.getSetName();
return reactorClient.scanAll(policy, namespace, setName)
.map(keyRecord -> getMappingConverter().convertToObject(clazz, keyRecord.record));
}
@Override
public Flux query(@NotNull Class clazz, Filter filter) {
return query(null, clazz, filter);
}
@Override
public Flux query(QueryPolicy policy, @NotNull Class clazz, Filter filter) {
ClassCacheEntry entry = MapperUtils.getEntryAndValidateNamespace(clazz, this);
if (policy == null) {
policy = entry.getQueryPolicy();
}
Statement statement = new Statement();
statement.setFilter(filter);
statement.setNamespace(entry.getNamespace());
statement.setSetName(entry.getSetName());
return reactorClient.query(policy, statement)
.map(keyRecord -> getMappingConverter().convertToObject(clazz, keyRecord.record));
}
@Override
public ReactiveVirtualList asBackedList(@NotNull Object object, @NotNull String binName, Class elementClazz) {
return new ReactiveVirtualList<>(this, object, binName, elementClazz);
}
@Override
public ReactiveVirtualList asBackedList(@NotNull Class> owningClazz, @NotNull Object key, @NotNull String binName, Class elementClazz) {
return new ReactiveVirtualList<>(this, owningClazz, key, binName, elementClazz);
}
@Override
public IAerospikeReactorClient getReactorClient() {
return reactorClient;
}
@Override
public MappingConverter getMappingConverter() {
return mappingConverter;
}
@Override
public IAeroMapper asMapper() {
return aeroMapper;
}
@Override
public Policy getReadPolicy(Class> clazz) {
return getPolicyByClassAndType(clazz, ClassCache.PolicyType.READ);
}
@Override
public WritePolicy getWritePolicy(Class> clazz) {
return (WritePolicy) getPolicyByClassAndType(clazz, ClassCache.PolicyType.WRITE);
}
@Override
public BatchPolicy getBatchPolicy(Class> clazz) {
return (BatchPolicy) getPolicyByClassAndType(clazz, ClassCache.PolicyType.BATCH);
}
@Override
public ScanPolicy getScanPolicy(Class> clazz) {
return (ScanPolicy) getPolicyByClassAndType(clazz, ClassCache.PolicyType.SCAN);
}
@Override
public QueryPolicy getQueryPolicy(Class> clazz) {
return (QueryPolicy) getPolicyByClassAndType(clazz, ClassCache.PolicyType.QUERY);
}
private Policy getPolicyByClassAndType(Class> clazz, ClassCache.PolicyType policyType) {
ClassCacheEntry> entry = ClassCache.getInstance().loadClass(clazz, this);
switch (policyType) {
case READ:
return entry == null ? reactorClient.getReadPolicyDefault() : entry.getReadPolicy();
case WRITE:
return entry == null ? reactorClient.getWritePolicyDefault() : entry.getWritePolicy();
case BATCH:
return entry == null ? reactorClient.getBatchPolicyDefault() : entry.getBatchPolicy();
case SCAN:
return entry == null ? reactorClient.getScanPolicyDefault() : entry.getScanPolicy();
case QUERY:
return entry == null ? reactorClient.getQueryPolicyDefault() : entry.getQueryPolicy();
default:
throw new UnsupportedOperationException("Provided unsupported policy.");
}
}
private Throwable translateError(Throwable e) {
if (e instanceof AerospikeException) {
return translateError(e);
}
return e;
}
@Override
public Mono getNamespace(Class clazz) {
ClassCacheEntry entry = MapperUtils.getEntryAndValidateNamespace(clazz, this);
return entry == null ? null : Mono.just(entry.getNamespace());
}
@Override
public Mono getSet(Class clazz) {
ClassCacheEntry> entry = ClassCache.getInstance().loadClass(clazz, this);
return entry == null ? null : Mono.just(entry.getSetName());
}
@Override
public Mono
© 2015 - 2025 Weber Informatics LLC | Privacy Policy