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

com.aerospike.mapper.tools.ClassCache Maven / Gradle / Ivy

package com.aerospike.mapper.tools;

import java.util.HashMap;
import java.util.Map;

import javax.validation.constraints.NotNull;

import com.aerospike.client.AerospikeException;
import com.aerospike.client.IAerospikeClient;
import com.aerospike.client.policy.BatchPolicy;
import com.aerospike.client.policy.Policy;
import com.aerospike.client.policy.QueryPolicy;
import com.aerospike.client.policy.ScanPolicy;
import com.aerospike.client.policy.WritePolicy;
import com.aerospike.client.reactor.IAerospikeReactorClient;
import com.aerospike.mapper.exceptions.NotAnnotatedClass;
import com.aerospike.mapper.tools.configuration.ClassConfig;
import com.aerospike.mapper.tools.configuration.Configuration;
import com.aerospike.mapper.tools.utils.TypeUtils;

public class ClassCache {
    private static final ClassCache instance = new ClassCache();

    public static ClassCache getInstance() {
        return instance;
    }

    enum PolicyType {
        READ,
        WRITE,
        BATCH,
        SCAN,
        QUERY
    }

    private final Map, ClassCacheEntry> cacheMap = new HashMap<>();
    private final Map classesConfig = new HashMap<>();
    private final Map defaultPolicies = new HashMap<>();
    private final Map storedNameToCacheEntry = new HashMap<>();
    private final Map, Policy>> childrenPolicies = new HashMap<>();
    private final Map, Policy>> specificPolicies = new HashMap<>();

    private ClassCache() {
        for (PolicyType thisType : PolicyType.values()) {
            this.childrenPolicies.put(thisType, new HashMap<>());
            this.specificPolicies.put(thisType, new HashMap<>());
        }
    }

    public  ClassCacheEntry loadClass(@NotNull Class clazz, IBaseAeroMapper mapper) {
        if (clazz.isPrimitive() || clazz.equals(Object.class) || clazz.equals(String.class) || clazz.equals(Character.class) || Number.class.isAssignableFrom(clazz)) {
            return null;
        }
        ClassCacheEntry entry = cacheMap.get(clazz);
        if (entry == null) {
            try {
                // Construct a class cache entry. This must be done in 2 steps, one creating the entry and the other finalizing construction of
                // it. This is to cater for classes  which recursively refer to themselves, such as
                // 	public static class A {
                //      @AerospikeKey
                //      public int id;
                //      public A a;
                //  }
                entry = new ClassCacheEntry<>(clazz, mapper, getClassConfig(clazz),
                        determinePolicy(clazz, PolicyType.READ),
                        (WritePolicy) determinePolicy(clazz, PolicyType.WRITE),
                        (BatchPolicy) determinePolicy(clazz, PolicyType.BATCH),
                        (QueryPolicy) determinePolicy(clazz, PolicyType.QUERY),
                        (ScanPolicy) determinePolicy(clazz, PolicyType.SCAN));
            } catch (NotAnnotatedClass nae) {
                return null;
            }
            cacheMap.put(clazz, entry);
            try {
                entry.construct();
            } catch (IllegalArgumentException iae) {
                cacheMap.remove(clazz);
                return null;
            }
        }
        return entry;
    }

    // package visibility
    void setStoredName(@NotNull ClassCacheEntry entry, @NotNull String name) {
        ClassCacheEntry existingEntry = storedNameToCacheEntry.get(name);
        if (existingEntry != null && !(existingEntry.equals(entry))) {
            String errorMessage = String.format("Stored name of \"%s\" is used for both %s and %s",
                    name, existingEntry.getUnderlyingClass().getName(), entry.getUnderlyingClass().getName());
            throw new AerospikeException(errorMessage);
        } else {
            storedNameToCacheEntry.put(name, entry);
        }
    }

    public ClassCacheEntry getCacheEntryFromStoredName(@NotNull String name) {
        return storedNameToCacheEntry.get(name);
    }

    void setDefaultPolicies(IAerospikeClient client) {
        if (client != null) {
            this.defaultPolicies.put(PolicyType.READ, client.getReadPolicyDefault());
            this.defaultPolicies.put(PolicyType.WRITE, client.getWritePolicyDefault());
            this.defaultPolicies.put(PolicyType.BATCH, client.getBatchPolicyDefault());
            this.defaultPolicies.put(PolicyType.QUERY, client.getQueryPolicyDefault());
            this.defaultPolicies.put(PolicyType.SCAN, client.getScanPolicyDefault());
        }
    }

    void setReactiveDefaultPolicies(IAerospikeReactorClient reactorClient) {
        this.defaultPolicies.put(PolicyType.READ, reactorClient.getReadPolicyDefault());
        this.defaultPolicies.put(PolicyType.WRITE, reactorClient.getWritePolicyDefault());
        this.defaultPolicies.put(PolicyType.BATCH, reactorClient.getBatchPolicyDefault());
        this.defaultPolicies.put(PolicyType.QUERY, reactorClient.getQueryPolicyDefault());
        this.defaultPolicies.put(PolicyType.SCAN, reactorClient.getScanPolicyDefault());
    }

    void setDefaultPolicy(PolicyType policyType, Policy policy) {
        this.defaultPolicies.put(policyType, policy);
    }

    void setChildrenPolicy(PolicyType policyType, Class parentClass, Policy policy) {
        this.childrenPolicies.get(policyType).put(parentClass, policy);
    }

    void setSpecificPolicy(PolicyType policyType, Class parentClass, Policy policy) {
        this.childrenPolicies.get(policyType).put(parentClass, policy);
    }

    public boolean hasClass(Class clazz) {
        return cacheMap.containsKey(clazz);
    }

    private Policy determinePolicy(@NotNull Class clazz, @NotNull PolicyType policyType) {
        // Specific classes have the highest precedence
        Policy result = specificPolicies.get(policyType).get(clazz);
        if (result != null) {
            return result;
        }
        // Otherwise, iterate up class hierarchy looking for the policy.
        Class thisClass = clazz;
        while (thisClass != null) {
            Policy aPolicy = childrenPolicies.get(policyType).get(thisClass);
            if (aPolicy != null) {
                return aPolicy;
            }
            thisClass = thisClass.getSuperclass();
        }
        // To get here, must have nothing specified, use the default
        return this.defaultPolicies.get(policyType);
    }

    /**
     * This method is typically only used for testing
     */
    public void clear() {
        this.cacheMap.clear();
        this.classesConfig.clear();
        TypeUtils.clear();
        this.storedNameToCacheEntry.clear();
    }

    public void addConfiguration(@NotNull Configuration configuration) {
        for (ClassConfig thisConfig : configuration.getClasses()) {
            classesConfig.put(thisConfig.getClassName(), thisConfig);
        }
    }

    public ClassConfig getClassConfig(String className) {
        return classesConfig.get(className);
    }

    public ClassConfig getClassConfig(Class clazz) {
        return classesConfig.get(clazz.getName());
    }

    public boolean hasClassConfig(String className) {
        return classesConfig.containsKey(className);
    }

    public boolean hasClassConfig(Class clazz) {
        return classesConfig.containsKey(clazz.getName());
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy