/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.kernel.cache.index; import com.liferay.portal.kernel.cache.PortalCache; import com.liferay.portal.kernel.cache.PortalCacheListener; import com.liferay.portal.kernel.concurrent.ConcurrentHashSet; import java.io.Serializable; import java.util.Collections; import java.util.HashSet; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * @author Shuyang Zhou */ public class PortalCacheIndexer<I, K extends Serializable, V> { public PortalCacheIndexer( IndexEncoder<I, K> indexEncoder, PortalCache<K, V> portalCache) { _indexEncoder = indexEncoder; _portalCache = portalCache; _portalCache.registerPortalCacheListener( new IndexerPortalCacheListener()); for (K indexedCacheKey : _portalCache.getKeys()) { _addIndexedCacheKey(indexedCacheKey); } } public Set<K> getKeys(I index) { Set<K> keys = _indexedCacheKeys.get(index); if (keys == null) { return Collections.emptySet(); } return new HashSet<>(keys); } public void removeKeys(I index) { Set<K> keys = _indexedCacheKeys.remove(index); if (keys == null) { return; } for (K key : keys) { _portalCache.remove(key); } } private void _addIndexedCacheKey(K key) { I index = _indexEncoder.encode(key); Set<K> keys = _indexedCacheKeys.get(index); if (keys == null) { Set<K> newKeys = new ConcurrentHashSet<>(); newKeys.add(key); keys = _indexedCacheKeys.putIfAbsent(index, newKeys); if (keys == null) { return; } } keys.add(key); } private void _removeIndexedCacheKey(K key) { I index = _indexEncoder.encode(key); Set<K> keys = _indexedCacheKeys.get(index); if (keys == null) { return; } keys.remove(key); if (keys.isEmpty() && _indexedCacheKeys.remove(index, keys)) { for (K victimIndexedCacheKey : keys) { _addIndexedCacheKey(victimIndexedCacheKey); } } } private final ConcurrentMap<I, Set<K>> _indexedCacheKeys = new ConcurrentHashMap<>(); private final IndexEncoder<I, K> _indexEncoder; private final PortalCache<K, V> _portalCache; private class IndexerPortalCacheListener implements PortalCacheListener<K, V> { @Override public void dispose() { _indexedCacheKeys.clear(); } @Override public void notifyEntryEvicted( PortalCache<K, V> portalCache, K key, V value, int timeToLive) { _removeIndexedCacheKey(key); } @Override public void notifyEntryExpired( PortalCache<K, V> portalCache, K key, V value, int timeToLive) { _removeIndexedCacheKey(key); } @Override public void notifyEntryPut( PortalCache<K, V> portalCache, K key, V value, int timeToLive) { _addIndexedCacheKey(key); } @Override public void notifyEntryRemoved( PortalCache<K, V> portalCache, K key, V value, int timeToLive) { _removeIndexedCacheKey(key); } @Override public void notifyEntryUpdated( PortalCache<K, V> portalCache, K key, V value, int timeToLive) { } @Override public void notifyRemoveAll(PortalCache<K, V> portalCache) { _indexedCacheKeys.clear(); } } }