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

com.aerospike.mapper.tools.converters.MappingConverter Maven / Gradle / Ivy

package com.aerospike.mapper.tools.converters;

import com.aerospike.client.*;
import com.aerospike.client.policy.BatchPolicy;
import com.aerospike.mapper.tools.*;

import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;

public class MappingConverter {

    private final IBaseAeroMapper mapper;
    private final IAerospikeClient aerospikeClient;

    public MappingConverter(IBaseAeroMapper mapper, IAerospikeClient aerospikeClient) {
        this.mapper = mapper;
        this.aerospikeClient = aerospikeClient;
    }

    /**
     * Translate a Java object to an Aerospike format object. Note that this could potentially have performance issues as
     * the type information of the passed object must be determined on every call.
     * @param obj A given Java object.
     * @return An Aerospike format object.
     */
    public Object translateToAerospike(Object obj) {
        if (obj == null) {
            return null;
        }
        TypeMapper thisMapper = TypeUtils.getMapper(obj.getClass(), TypeUtils.AnnotatedType.getDefaultAnnotateType(), mapper);
        return thisMapper == null ? obj : thisMapper.toAerospikeFormat(obj);
    }

    /**
     * Translate an Aerospike object to a Java object. Note that this could potentially have performance issues as
     * the type information of the passed object must be determined on every call.
     * @param obj A given Java object.
     * @return An Aerospike format object.
     */
    @SuppressWarnings("unchecked")
    public  T translateFromAerospike(@NotNull Object obj, @NotNull Class expectedClazz) {
        TypeMapper thisMapper = TypeUtils.getMapper(expectedClazz, TypeUtils.AnnotatedType.getDefaultAnnotateType(), mapper);
        T result = (T)(thisMapper == null ? obj : thisMapper.fromAerospikeFormat(obj));
        resolveDependencies(ClassCache.getInstance().loadClass(expectedClazz, mapper));
        return result;
    }

    // --------------------------------------------------------------------------------------------------
    // The following are convenience methods to convert objects to / from lists / maps / records in case
    // it is needed to perform this operation manually. They will not be needed in most use cases.
    // --------------------------------------------------------------------------------------------------
    /**
     * Given a record loaded from Aerospike and a class type, attempt to convert the record to
     * an instance of the passed class.
     * @param clazz The class type to convert the Aerospike record to.
     * @param record The Aerospike record to convert.
     * @return A virtual list.
     * @throws AerospikeException an AerospikeException will be thrown in case of an encountering a ReflectiveOperationException.
     */
    public  T convertToObject(Class clazz, Record record) {
        try {
            return convertToObject(clazz, record, null);
        } catch (ReflectiveOperationException e) {
            throw new AerospikeException(e);
        }
    }

    public  T convertToObject(Class clazz, Record record, ClassCacheEntry entry) throws ReflectiveOperationException {
        return this.convertToObject(clazz, record, entry, true);
    }

    /**
     * This method should not be used, it is public only to allow mappers to see it.
     */
    public  T convertToObject(Class clazz, Record record, ClassCacheEntry entry, boolean resolveDependencies) throws ReflectiveOperationException {
        if (entry == null) {
            entry = ClassCache.getInstance().loadClass(clazz, mapper);
        }
        T result = entry.constructAndHydrate(record);
        if (resolveDependencies) {
            resolveDependencies(entry);
        }
        return result;
    }

    public  T convertToObject(Class clazz, List record) {
        return this.convertToObject(clazz, record, true);
    }

    /**
     * This method should not be used, it is public only to allow mappers to see it.
     */
    public  T convertToObject(Class clazz, List record, boolean resolveDependencies) {
        try {
            ClassCacheEntry entry = ClassCache.getInstance().loadClass(clazz, mapper);
            T result;
            result = clazz.getConstructor().newInstance();
            entry.hydrateFromList(record, result);
            if (resolveDependencies) {
                resolveDependencies(entry);
            }
            return result;
        } catch (ReflectiveOperationException e) {
            throw new AerospikeException(e);
        }
    }

    public  List convertToList(@NotNull T instance) {
        ClassCacheEntry entry = (ClassCacheEntry) ClassCache.getInstance().loadClass(instance.getClass(), mapper);
        return entry.getList(instance, false, false);
    }

    public  T convertToObject(Class clazz, Map record) {
        try {
            ClassCacheEntry entry = ClassCache.getInstance().loadClass(clazz, mapper);
            T result = clazz.getConstructor().newInstance();
            entry.hydrateFromMap(record, result);
            return result;
        } catch (ReflectiveOperationException e) {
            throw new AerospikeException(e);
        }
    }

    public  Map convertToMap(@NotNull T instance) {
        ClassCacheEntry entry = (ClassCacheEntry) ClassCache.getInstance().loadClass(instance.getClass(), mapper);
        return entry.getMap(instance, false);
    }

    /**
     * If an object refers to other objects (eg A has a list of B via references), then reading the object will populate the
     * ids. If configured to do so, these objects can be loaded via a batch load and populated back into the references which
     * contain them. This method performs this batch load, translating the records to objects and mapping them back to the
     * references.
     * 

* These loaded child objects can themselves have other references to other objects, so we iterate through this until * the list of deferred objects is empty. The deferred objects are stored in a

ThreadLocalData
 list, so are thread safe
     * @param parentEntity - the ClassCacheEntry of the parent entity. This is used to get the batch policy to use.
     */
    public void resolveDependencies(ClassCacheEntry parentEntity) {
        List deferredObjects = DeferredObjectLoader.getAndClear();

        if (deferredObjects.size() == 0) {
            return;
        }

        BatchPolicy batchPolicy = parentEntity == null ? aerospikeClient.getBatchPolicyDefault() : parentEntity.getBatchPolicy();
        BatchPolicy batchPolicyClone = new BatchPolicy(batchPolicy);

        while (!deferredObjects.isEmpty()) {
            int size = deferredObjects.size();

            ClassCacheEntry[] classCaches = new ClassCacheEntry[size];
            Key[] keys = new Key[size];

            for (int i = 0; i < size; i++) {
                DeferredObjectLoader.DeferredObjectSetter thisObjectSetter = deferredObjects.get(i);
                DeferredObjectLoader.DeferredObject deferredObject = thisObjectSetter.getObject();
                Class clazz = deferredObject.getType();
                ClassCacheEntry entry = MapperUtils.getEntryAndValidateNamespace(clazz, mapper);
                classCaches[i] = entry;

                if (deferredObject.isDigest()) {
                    keys[i] = new Key(entry.getNamespace(), (byte[])deferredObject.getKey(), entry.getSetName(), null);
                }
                else {
                    keys[i] = new Key(entry.getNamespace(), entry.getSetName(), Value.get(entry.translateKeyToAerospikeKey(deferredObject.getKey())));
                }
            }

            // Load the data
            if (keys.length <= 2) {
                // Just single-thread these keys for speed
                batchPolicyClone.maxConcurrentThreads = 1;
            }
            else {
                batchPolicyClone.maxConcurrentThreads = batchPolicy.maxConcurrentThreads;
            }
            Record[] records = aerospikeClient.get(batchPolicyClone, keys);

            for (int i = 0; i < size; i++) {
                DeferredObjectLoader.DeferredObjectSetter thisObjectSetter = deferredObjects.get(i);
                try {
                    ThreadLocalKeySaver.save(keys[i]);
                    Object result = records[i] == null ? null : convertToObject((Class) thisObjectSetter.getObject().getType(), records[i], classCaches[i], false);
                    thisObjectSetter.getSetter().setValue(result);
                } catch (ReflectiveOperationException e) {
                    throw new AerospikeException(e);
                } finally {
                    ThreadLocalKeySaver.clear();
                }
            }
            deferredObjects = DeferredObjectLoader.getAndClear();
        }
    }
}