package io.datakernel.cube; import io.datakernel.codegen.DefiningClassLoader; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; public class CubeClassLoaderCache implements CubeClassLoaderCacheMBean { static final class Key { final Set<String> attributes; final Set<String> measures; final Set<String> filterDimensions; Key(Set<String> attributes, Set<String> measures, Set<String> filterDimensions) { this.attributes = attributes; this.measures = measures; this.filterDimensions = filterDimensions; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Key that = (Key) o; if (!attributes.equals(that.attributes)) return false; if (!measures.equals(that.measures)) return false; if (!filterDimensions.equals(that.filterDimensions)) return false; return true; } @Override public int hashCode() { int result = attributes.hashCode(); result = 31 * result + measures.hashCode(); result = 31 * result + filterDimensions.hashCode(); return result; } @Override public String toString() { return "{" + attributes + ", " + measures + ", " + filterDimensions + '}'; } } private final DefiningClassLoader rootClassLoader; private final LinkedHashMap<Key, DefiningClassLoader> cache = new LinkedHashMap<Key, DefiningClassLoader>(16, 0.75f, false) { protected boolean removeEldestEntry(Map.Entry<Key, DefiningClassLoader> eldest) { return size() > targetCacheKeys; } }; private int targetCacheKeys; // JMX private int cacheRequests; private int cacheMisses; private CubeClassLoaderCache(DefiningClassLoader rootClassLoader, int targetCacheKeys) { this.rootClassLoader = rootClassLoader; this.targetCacheKeys = targetCacheKeys; } public static CubeClassLoaderCache create(DefiningClassLoader root, int cacheSize) { return new CubeClassLoaderCache(root, cacheSize); } public synchronized DefiningClassLoader getOrCreate(Key key) { cacheRequests++; DefiningClassLoader classLoader = cache.get(key); if (classLoader == null) { cacheMisses++; classLoader = DefiningClassLoader.create(rootClassLoader); cache.put(key, classLoader); } return classLoader; } // JMX @Override synchronized public void clear() { cache.clear(); } @Override public int getTargetCacheKeys() { return targetCacheKeys; } @Override public void setTargetCacheKeys(int targetCacheKeys) { this.targetCacheKeys = targetCacheKeys; } @Override synchronized public int getDefinedClassesCount() { int result = 0; for (DefiningClassLoader classLoader : cache.values()) { result += classLoader.getDefinedClassesCount(); } return result; } @Override synchronized public int getDefinedClassesCountMaxPerKey() { int result = 0; for (DefiningClassLoader classLoader : cache.values()) { result = Math.max(result, classLoader.getDefinedClassesCount()); } return result; } @Override public int getCacheKeys() { return targetCacheKeys; } @Override public int getCacheRequests() { return cacheRequests; } @Override public int getCacheMisses() { return cacheMisses; } @Override public Map<String, String> getCacheContents() { Map<String, String> map; synchronized (this) { map = new LinkedHashMap<>(cache.size()); } for (Map.Entry<Key, DefiningClassLoader> entry : cache.entrySet()) { map.put(entry.getKey().toString(), entry.getValue().toString()); } return map; } }