001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.kernel.cache.index;
016    
017    import com.liferay.portal.kernel.cache.CacheListener;
018    import com.liferay.portal.kernel.cache.PortalCache;
019    import com.liferay.portal.kernel.concurrent.ConcurrentHashSet;
020    
021    import java.util.Collections;
022    import java.util.HashSet;
023    import java.util.Set;
024    import java.util.concurrent.ConcurrentHashMap;
025    import java.util.concurrent.ConcurrentMap;
026    
027    /**
028     * @author Shuyang Zhou
029     */
030    public class PortalCacheIndexer<I, K extends IndexedCacheKey<I>, V> {
031    
032            public PortalCacheIndexer(PortalCache<K, V> portalCache) {
033                    _portalCache = portalCache;
034    
035                    _portalCache.registerCacheListener(new IndexerCacheListener());
036    
037                    for (K indexedCacheKey : _portalCache.getKeys()) {
038                            _addIndexedCacheKey(indexedCacheKey);
039                    }
040            }
041    
042            public Set<K> getIndexedCacheKeys(I index) {
043                    Set<K> indexedCacheKeys = _indexedCacheKeys.get(index);
044    
045                    if (indexedCacheKeys == null) {
046                            return Collections.emptySet();
047                    }
048    
049                    return new HashSet<>(indexedCacheKeys);
050            }
051    
052            public void removeIndexedCacheKeys(I index) {
053                    Set<K> indexedCacheKeys = _indexedCacheKeys.remove(index);
054    
055                    if (indexedCacheKeys == null) {
056                            return;
057                    }
058    
059                    for (K indexedCacheKey : indexedCacheKeys) {
060                            _portalCache.remove(indexedCacheKey);
061                    }
062            }
063    
064            private void _addIndexedCacheKey(K indexedCacheKey) {
065                    I index = indexedCacheKey.getIndex();
066    
067                    Set<K> indexedCacheKeys = _indexedCacheKeys.get(index);
068    
069                    if (indexedCacheKeys == null) {
070                            Set<K> newIndexedCacheKeys = new ConcurrentHashSet<>();
071    
072                            newIndexedCacheKeys.add(indexedCacheKey);
073    
074                            indexedCacheKeys = _indexedCacheKeys.putIfAbsent(
075                                    index, newIndexedCacheKeys);
076    
077                            if (indexedCacheKeys == null) {
078                                    return;
079                            }
080                    }
081    
082                    indexedCacheKeys.add(indexedCacheKey);
083            }
084    
085            private void _removeIndexedCacheKey(K indexedCacheKey) {
086                    I index = indexedCacheKey.getIndex();
087    
088                    Set<K> indexedCacheKeys = _indexedCacheKeys.get(index);
089    
090                    if (indexedCacheKeys == null) {
091                            return;
092                    }
093    
094                    indexedCacheKeys.remove(indexedCacheKey);
095    
096                    if (indexedCacheKeys.isEmpty() &&
097                            _indexedCacheKeys.remove(index, indexedCacheKeys)) {
098    
099                            for (K victimIndexedCacheKey : indexedCacheKeys) {
100                                    _addIndexedCacheKey(victimIndexedCacheKey);
101                            }
102                    }
103            }
104    
105            private final ConcurrentMap<I, Set<K>> _indexedCacheKeys =
106                    new ConcurrentHashMap<>();
107            private final PortalCache<K, V> _portalCache;
108    
109            private class IndexerCacheListener implements CacheListener<K, V> {
110    
111                    @Override
112                    public void dispose() {
113                            _indexedCacheKeys.clear();
114                    }
115    
116                    @Override
117                    public void notifyEntryEvicted(
118                            PortalCache<K, V> portalCache, K indexedCacheKey, V value,
119                            int timeToLive) {
120    
121                            _removeIndexedCacheKey(indexedCacheKey);
122                    }
123    
124                    @Override
125                    public void notifyEntryExpired(
126                            PortalCache<K, V> portalCache, K indexedCacheKey, V value,
127                            int timeToLive) {
128    
129                            _removeIndexedCacheKey(indexedCacheKey);
130                    }
131    
132                    @Override
133                    public void notifyEntryPut(
134                            PortalCache<K, V> portalCache, K indexedCacheKey, V value,
135                            int timeToLive) {
136    
137                            _addIndexedCacheKey(indexedCacheKey);
138                    }
139    
140                    @Override
141                    public void notifyEntryRemoved(
142                            PortalCache<K, V> portalCache, K indexedCacheKey, V value,
143                            int timeToLive) {
144    
145                            _removeIndexedCacheKey(indexedCacheKey);
146                    }
147    
148                    @Override
149                    public void notifyEntryUpdated(
150                            PortalCache<K, V> portalCache, K indexedCacheKey, V value,
151                            int timeToLive) {
152                    }
153    
154                    @Override
155                    public void notifyRemoveAll(PortalCache<K, V> portalCache) {
156                            _indexedCacheKeys.clear();
157                    }
158    
159            }
160    
161    }