TenantAwareCacheResolver.java

package com.tradecloud.cache;


import com.hazelcast.config.*;
import com.hazelcast.spring.cache.HazelcastCacheManager;
import com.tradecloud.authentication.Client;
import com.tradecloud.authentication.MultiTenantUtil;
import com.tradecloud.config.MyEvictionListener;
import com.tradecloud.repository.UserClientMappingRepository;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.CacheOperationInvocationContext;
import org.springframework.cache.interceptor.CacheResolver;
import org.springframework.stereotype.Component;

import java.util.*;

@Component("tenantAwareCacheResolver")
public class TenantAwareCacheResolver implements CacheResolver {

    private static final Logger log = Logger.getLogger(TenantAwareCacheResolver.class);
    @Autowired
    private CacheManager cacheManager;

    @Autowired
    private UserClientMappingRepository userClientMappingLookupService;

    private Set<String> configured = new HashSet();

    @Override
    public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
        String tenant = MultiTenantUtil.getActiveClient();

        Collection<String> baseNames = context.getOperation().getCacheNames();
        if (baseNames == null || baseNames.isEmpty()) {
            return Collections.emptyList();
        }

        java.util.List<Cache> caches = new java.util.ArrayList<>(baseNames.size());

        for (String baseName : baseNames) {
            String cacheName = tenant + "_" + baseName;
            Cache cache = cacheManager.getCache(cacheName);

            if (cache == null) {
                throw new IllegalStateException("No cache found with name: " + cacheName);
            }

            configureCache(tenant, cacheName);
            caches.add(cache);
        }

        return caches;
    }

    private void configureCache(String tenant, String cacheName) {
        if (tenant != null && !configured.contains(cacheName)) {
            Client client = userClientMappingLookupService.findClientByCode(tenant);

            Map<String, String> connectionProperties = client.getConnectionProperties();
            if (connectionProperties != null && !connectionProperties.isEmpty()) {
                MapConfig currentConfig = ((HazelcastCacheManager) cacheManager).getHazelcastInstance().getConfig().getMapConfig(cacheName);
                currentConfig = overrideConfig(currentConfig, connectionProperties);
                ((HazelcastCacheManager) cacheManager).getHazelcastInstance().getConfig().addMapConfig(currentConfig);
            }
            configured.add(cacheName);
        }
    }

    public MapConfig overrideConfig(MapConfig mapConfig, Map<String, String> connectionProperties) {
        if (connectionProperties != null && !connectionProperties.isEmpty()) {
            EntryListenerConfig entryListenerConfig = new EntryListenerConfig();
            entryListenerConfig.setImplementation(new MyEvictionListener());
            entryListenerConfig.setLocal(false); // Set to true if you only want to hear about local partition events
            mapConfig.addEntryListenerConfig(entryListenerConfig);
            EvictionConfig evictionConfig = mapConfig.setBackupCount(valueOrDefault("backup-count", connectionProperties, 1))
                    .setTimeToLiveSeconds(valueOrDefault("time-to-live", connectionProperties, 0))
                    .setMaxIdleSeconds(valueOrDefault("max-idle-seconds", connectionProperties, 0))
                    .setReadBackupData(valueOrDefault("read-backup-data", connectionProperties, true))
                    .setStatisticsEnabled(valueOrDefault("statistics-enabled", connectionProperties, true))
                    .setPerEntryStatsEnabled(valueOrDefault("per-entry-stats-enabled", connectionProperties, true))
                    .setMergePolicyConfig(new MergePolicyConfig("com.hazelcast.spi.merge.PassThroughMergePolicy", MergePolicyConfig.DEFAULT_BATCH_SIZE))
                    .getEvictionConfig();

            //use default or general max size unless there is a named value
            int maxSize = valueOrDefault("max-size", connectionProperties, 10000);
            int maxSizeNamed = valueOrDefault(mapConfig.getName().replace("*_", ""),
                    connectionProperties, 10000);
            if (maxSize != maxSizeNamed)
                evictionConfig.setSize(maxSizeNamed);
            else
                evictionConfig.setSize(maxSize);

            evictionConfig.setEvictionPolicy(valueOrDefault("eviction-policy", connectionProperties, EvictionPolicy.LFU));

            //specific case: if not found(max-size-policy) do not configure
            if (connectionProperties.get("max-size-policy") != null)
                evictionConfig.setMaxSizePolicy(valueOrDefault("max-size-policy", connectionProperties));

            log.debug("Override Map: " + mapConfig.getName() + " | TTL: " + mapConfig.getTimeToLiveSeconds() + " seconds");
        }
        return mapConfig;
    }

    private static int valueOrDefault(String key, Map<String, String> map, int defaultValue) {
        key = liteConfigKey(key);
        if (map.get(key) != null) {
            return Integer.parseInt(map.get(key));
        }
        return defaultValue;
    }

    private static String valueOrDefault(String key, Map<String, String> map, String defaultValue) {
        key = liteConfigKey(key);
        if (map.get(key) != null) {
            return map.get(key);
        }
        return defaultValue;
    }

    private static MaxSizePolicy valueOrDefault(String key, Map<String, String> map) {
        key = liteConfigKey(key);
        if (map.get(key) != null) {
            return MaxSizePolicy.valueOf(map.get(key));
        }
        return null;
    }

    private static EvictionPolicy valueOrDefault(String key, Map<String, String> map, EvictionPolicy defaultPolicy) {
        key = liteConfigKey(key);
        if (map.get(key) != null) {
            return EvictionPolicy.valueOf(map.get(key));
        }
        return defaultPolicy;
    }

    private static boolean valueOrDefault(String key, Map<String, String> map, boolean defaultValue) {
        key = liteConfigKey(key);
        if (map.get(key) != null) {
            return Boolean.parseBoolean(map.get(key));
        }
        return defaultValue;
    }

    /**
     *
     * @param key
     * @return the worker need it's own set of config
     */
    private static String liteConfigKey(String key) {
        if (System.getenv("hazelcast_role") != null && System.getenv("hazelcast_role").equals("WORKER")) {
            return key + "_lite";
        }
        return key;
    }

    public void setCacheManager(CacheManager cacheManager) {
        this.cacheManager = cacheManager;
    }
}