package com.bagri.server.hazelcast.impl; import static com.bagri.core.server.api.CacheConstants.*; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.bagri.client.hazelcast.impl.IdGeneratorImpl; import com.bagri.core.model.Path; import com.bagri.core.server.api.ModelManagement; import com.bagri.core.server.api.impl.ModelManagementBase; import com.bagri.support.idgen.IdGenerator; import com.hazelcast.core.EntryEvent; import com.hazelcast.core.EntryListener; import com.hazelcast.core.HazelcastInstance; import com.hazelcast.core.IAtomicLong; import com.hazelcast.core.IMap; import com.hazelcast.core.MapEvent; import com.hazelcast.core.ReplicatedMap; import com.hazelcast.map.listener.EntryAddedListener; import com.hazelcast.map.listener.EntryRemovedListener; import com.hazelcast.map.listener.EntryUpdatedListener; import com.hazelcast.map.listener.MapClearedListener; import com.hazelcast.map.listener.MapEvictedListener; import com.hazelcast.query.Predicate; import com.hazelcast.query.Predicates; import com.hazelcast.query.impl.predicates.RegexPredicate; public class ModelManagementImpl extends ModelManagementBase implements ModelManagement { protected IMap<String, Path> pathCache; private IdGenerator<Long> pathGen; private ConcurrentMap<Integer, Path> cachePath = new ConcurrentHashMap<>(); private ConcurrentMap<String, Set<Path>> cacheType = new ConcurrentHashMap<>(); public ModelManagementImpl() { super(); } public ModelManagementImpl(HazelcastInstance hzInstance) { super(); initialize(hzInstance); } private void initialize(HazelcastInstance hzInstance) { //pathCache = hzInstance.getReplicatedMap(CN_XDM_PATH_DICT); pathCache = hzInstance.getMap(CN_XDM_PATH_DICT); pathGen = new IdGeneratorImpl(hzInstance.getAtomicLong(SQN_PATH)); // init listeners here //pathCache.addEntryListener(new PathCacheListener()); //, true); pathCache.addEntryListener(new PathEntryListener(), true); } protected Map<String, Path> getPathCache() { return pathCache; } protected IdGenerator<Long> getPathGen() { return pathGen; } public void setPathCache(IMap<String, Path> pathCache) { this.pathCache = pathCache; } public void setPathGen(IAtomicLong pathGen) { this.pathGen = new IdGeneratorImpl(pathGen); } private Path getPathInternal(int pathId) { Predicate<String, Path> f = Predicates.equal("pathId", pathId); Collection<Path> entries = pathCache.values(f); if (entries.isEmpty()) { return null; } // check size > 1 ?? return entries.iterator().next(); } @Override public Path getPath(int pathId) { Path result = cachePath.get(pathId); if (result == null) { result = getPathInternal(pathId); if (result != null) { cachePath.putIfAbsent(pathId, result); } } return result; } private Collection<Path> getTypePathsInternal(String root) { Predicate<String, Path> f = Predicates.equal("root", root); Collection<Path> entries = pathCache.values(f); if (entries.isEmpty()) { return entries; } // check size > 1 ?? List<Path> result = new ArrayList<Path>(entries); //Collections.sort(result); //if (logger.isTraceEnabled()) { // logger.trace("getTypePath; returning {} for type {}", result, typeId); //} return result; } @Override public Collection<Path> getTypePaths(String root) { Collection<Path> result = cacheType.get(root); // TODO: think why the result is empty? happens from ModelManagementImplTest only? if (result == null || result.isEmpty()) { result = getTypePathsInternal(root); if (result != null) { Set<Path> paths = new HashSet<>(result); paths = new HashSet<>(); cacheType.putIfAbsent(root, paths); } } return result; } @Override protected Set<Map.Entry<String, Path>> getTypedPathEntries(String root) { Predicate<String, Path> f = Predicates.equal("root", root); Set<Map.Entry<String, Path>> entries = pathCache.entrySet(f); return entries; } @Override protected Set<Map.Entry<String, Path>> getTypedPathWithRegex(String regex, String root) { regex = regex.replaceAll("\\{", Matcher.quoteReplacement("\\{")); regex = regex.replaceAll("\\}", Matcher.quoteReplacement("\\}")); Predicate<String, Path> filter = new RegexPredicate("path", regex); if (root != null) { filter = Predicates.and(filter, Predicates.equal("root", root)); } Set<Map.Entry<String, Path>> entries = pathCache.entrySet(filter); return entries; } //@Override @SuppressWarnings({ "unchecked", "rawtypes" }) protected <K> boolean lock(Map<K, ?> cache, K key) { try { return ((IMap) cache).tryLock(key, timeout, TimeUnit.MILLISECONDS); } catch (InterruptedException ex) { logger.error("Interrupted on lock", ex); return false; } } //@Override @SuppressWarnings({ "unchecked", "rawtypes" }) protected <K> void unlock(Map<K, ?> cache, K key) { ((IMap) cache).unlock(key); } @Override protected <K, V> V putIfAbsent(Map<K, V> map, K key, V value) { IMap<K, V> cache = (IMap<K, V>) map; V val2 = cache.putIfAbsent(key, value); //V val2 = cache.put(key, value); if (val2 == null) { return value; } logger.debug("putIfAbsent; got collision on cache: {}, key: {}; returning: {}", cache.getName(), key, val2); return val2; } @Override public void updatePath(Path path) { String pathKey = getPathKey(path.getRoot(), path.getPath()); ((IMap<String, Path>) getPathCache()).set(pathKey, path); } private class PathEntryListener implements EntryListener<String, Path> { @Override public void entryAdded(EntryEvent<String, Path> event) { Path path = event.getValue(); cachePath.putIfAbsent(path.getPathId(), path); Set<Path> paths = cacheType.get(path.getRoot()); if (paths == null) { paths = new HashSet<>(); Set<Path> paths2 = cacheType.putIfAbsent(path.getRoot(), paths); if (paths2 != null) { paths = paths2; } } paths.add(path); } @Override public void entryUpdated(EntryEvent<String, Path> event) { Path path = event.getValue(); cachePath.put(path.getPathId(), path); Set<Path> paths = cacheType.get(path.getRoot()); if (paths == null) { paths = new HashSet<>(); Set<Path> paths2 = cacheType.putIfAbsent(path.getRoot(), paths); if (paths2 != null) { paths = paths2; } } paths.add(path); } @Override public void entryRemoved(EntryEvent<String, Path> event) { Path path = event.getValue(); cachePath.remove(path.getPathId()); Set<Path> paths = cacheType.get(path.getRoot()); if (paths != null) { paths.remove(path); } } @Override public void entryEvicted(EntryEvent<String, Path> event) { // TODO Auto-generated method stub } @Override public void mapCleared(MapEvent event) { cachePath.clear(); cacheType.clear(); } @Override public void mapEvicted(MapEvent event) { // don't think we have to clear everything in this case } } /* private class PathCacheListener implements MapClearedListener, MapEvictedListener, EntryAddedListener<String, Path>, EntryRemovedListener<String, Path>, EntryUpdatedListener<String, Path> { @Override public void mapEvicted(MapEvent event) { // don't think we have to clear everything in this case } @Override public void mapCleared(MapEvent event) { cachePath.clear(); cacheType.clear(); } @Override public void entryUpdated(EntryEvent<String, Path> event) { Path path = event.getValue(); cachePath.put(path.getPathId(), path); Set<Path> paths = cacheType.get(path.getTypeId()); if (paths == null) { paths = new HashSet<>(); Set<Path> paths2 = cacheType.putIfAbsent(path.getTypeId(), paths); if (paths2 != null) { paths = paths2; } } paths.add(path); } @Override public void entryRemoved(EntryEvent<String, Path> event) { Path path = event.getValue(); cachePath.remove(path.getPathId()); Set<Path> paths = cacheType.get(path.getTypeId()); if (paths != null) { paths.remove(path); } } @Override public void entryAdded(EntryEvent<String, Path> event) { Path path = event.getValue(); cachePath.putIfAbsent(path.getPathId(), path); Set<Path> paths = cacheType.get(path.getTypeId()); if (paths == null) { paths = new HashSet<>(); Set<Path> paths2 = cacheType.putIfAbsent(path.getTypeId(), paths); if (paths2 != null) { paths = paths2; } } paths.add(path); } } */ }