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