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.io.Serializable;
022    
023    import java.util.Collections;
024    import java.util.HashSet;
025    import java.util.Set;
026    import java.util.concurrent.ConcurrentHashMap;
027    import java.util.concurrent.ConcurrentMap;
028    
029    /**
030     * @author Shuyang Zhou
031     */
032    public class PortalCacheIndexer<I, K extends Serializable, V> {
033    
034            public PortalCacheIndexer(
035                    IndexEncoder<I, K> indexEncoder, PortalCache<K, V> portalCache) {
036    
037                    _indexEncoder = indexEncoder;
038    
039                    _portalCache = portalCache;
040    
041                    _portalCache.registerCacheListener(new IndexerCacheListener());
042    
043                    for (K indexedCacheKey : _portalCache.getKeys()) {
044                            _addIndexedCacheKey(indexedCacheKey);
045                    }
046            }
047    
048            public Set<K> getKeys(I index) {
049                    Set<K> keys = _indexedCacheKeys.get(index);
050    
051                    if (keys == null) {
052                            return Collections.emptySet();
053                    }
054    
055                    return new HashSet<K>(keys);
056            }
057    
058            public void removeKeys(I index) {
059                    Set<K> keys = _indexedCacheKeys.remove(index);
060    
061                    if (keys == null) {
062                            return;
063                    }
064    
065                    for (K key : keys) {
066                            _portalCache.remove(key);
067                    }
068            }
069    
070            private void _addIndexedCacheKey(K key) {
071                    I index = _indexEncoder.encode(key);
072    
073                    Set<K> keys = _indexedCacheKeys.get(index);
074    
075                    if (keys == null) {
076                            Set<K> newKeys = new ConcurrentHashSet<K>();
077    
078                            newKeys.add(key);
079    
080                            keys = _indexedCacheKeys.putIfAbsent(index, newKeys);
081    
082                            if (keys == null) {
083                                    return;
084                            }
085                    }
086    
087                    keys.add(key);
088            }
089    
090            private void _removeIndexedCacheKey(K key) {
091                    I index = _indexEncoder.encode(key);
092    
093                    Set<K> keys = _indexedCacheKeys.get(index);
094    
095                    if (keys == null) {
096                            return;
097                    }
098    
099                    keys.remove(key);
100    
101                    if (keys.isEmpty() && _indexedCacheKeys.remove(index, keys)) {
102                            for (K victimIndexedCacheKey : keys) {
103                                    _addIndexedCacheKey(victimIndexedCacheKey);
104                            }
105                    }
106            }
107    
108            private final ConcurrentMap<I, Set<K>> _indexedCacheKeys =
109                    new ConcurrentHashMap<I, Set<K>>();
110            private final IndexEncoder<I, K> _indexEncoder;
111            private final PortalCache<K, V> _portalCache;
112    
113            private class IndexerCacheListener implements CacheListener<K, V> {
114    
115                    @Override
116                    public void notifyEntryEvicted(
117                            PortalCache<K, V> portalCache, K key, V value) {
118    
119                            _removeIndexedCacheKey(key);
120                    }
121    
122                    @Override
123                    public void notifyEntryExpired(
124                            PortalCache<K, V> portalCache, K key, V value) {
125    
126                            _removeIndexedCacheKey(key);
127                    }
128    
129                    @Override
130                    public void notifyEntryPut(
131                            PortalCache<K, V> portalCache, K key, V value) {
132    
133                            _addIndexedCacheKey(key);
134                    }
135    
136                    @Override
137                    public void notifyEntryRemoved(
138                            PortalCache<K, V> portalCache, K key, V value) {
139    
140                            _removeIndexedCacheKey(key);
141                    }
142    
143                    @Override
144                    public void notifyEntryUpdated(
145                            PortalCache<K, V> portalCache, K key, V value) {
146                    }
147    
148                    @Override
149                    public void notifyRemoveAll(PortalCache<K, V> portalCache) {
150                            _indexedCacheKeys.clear();
151                    }
152    
153            }
154    
155    }