1. Project Clover database Sat Feb 2 2019 06:45:20 CET
  2. Package org.xwiki.user.internal.group

File AbstractGroupCache.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

12
71
21
2
325
200
29
0.41
3.38
10.5
1.38

Classes

Class Line # Actions
AbstractGroupCache 52 62 0% 24 1
0.9888888698.9%
AbstractGroupCache.GroupCacheEntry 62 9 0% 5 0
1.0100%
 

Contributing tests

This file is covered by 13 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.user.internal.group;
21   
22    import java.util.Collection;
23    import java.util.Collections;
24    import java.util.HashSet;
25    import java.util.Map;
26    import java.util.Set;
27    import java.util.concurrent.ConcurrentHashMap;
28    import java.util.concurrent.locks.ReentrantReadWriteLock;
29   
30    import javax.inject.Inject;
31   
32    import org.xwiki.cache.Cache;
33    import org.xwiki.cache.CacheManager;
34    import org.xwiki.cache.config.CacheConfiguration;
35    import org.xwiki.cache.event.AbstractCacheEntryListener;
36    import org.xwiki.cache.event.CacheEntryEvent;
37    import org.xwiki.cache.eviction.LRUEvictionConfiguration;
38    import org.xwiki.component.manager.ComponentLifecycleException;
39    import org.xwiki.component.phase.Disposable;
40    import org.xwiki.component.phase.Initializable;
41    import org.xwiki.component.phase.InitializationException;
42    import org.xwiki.model.reference.DocumentReference;
43    import org.xwiki.model.reference.EntityReferenceSerializer;
44    import org.xwiki.user.internal.group.AbstractGroupCache.GroupCacheEntry;
45   
46    /**
47    * Manipulate the cache of groups.
48    *
49    * @version $Id: e8799534a71e6a21e9942761ad2070af7c1911c3 $
50    * @since 10.8RC1
51    */
 
52    public abstract class AbstractGroupCache extends AbstractCacheEntryListener<GroupCacheEntry>
53    implements Initializable, Disposable
54    {
55    private static final int DEFAULT_CAPACITY = 1000;
56   
57    /**
58    * An entry of the group cache.
59    *
60    * @version $Id: e8799534a71e6a21e9942761ad2070af7c1911c3 $
61    */
 
62    public class GroupCacheEntry
63    {
64    private final String key;
65   
66    private Collection<DocumentReference> direct;
67   
68    private Collection<DocumentReference> all;
69   
 
70  144 toggle GroupCacheEntry(String key)
71    {
72  144 this.key = key;
73    }
74   
75    /**
76    * @return direct the direct entities.
77    */
 
78  331 toggle public Collection<DocumentReference> getDirect()
79    {
80  331 return this.direct;
81    }
82   
83    /**
84    * @param direct the direct entities.
85    * @return the new unmodificable list
86    */
 
87  144 toggle public Collection<DocumentReference> setDirect(Collection<DocumentReference> direct)
88    {
89  144 this.direct = Collections.unmodifiableCollection(direct);
90   
91  144 addToIndex(this.key, direct);
92   
93  144 return this.direct;
94    }
95   
96    /**
97    * @return the recursive entities.
98    */
 
99  625 toggle public Collection<DocumentReference> getAll()
100    {
101  625 return this.all;
102    }
103   
104    /**
105    * @param all the recursive entities.
106    * @return the new unmodificable list
107    */
 
108  142 toggle public Collection<DocumentReference> setAll(Collection<DocumentReference> all)
109    {
110  142 this.all = Collections.unmodifiableCollection(all);
111   
112  142 addToIndex(this.key, all);
113   
114  142 return this.all;
115    }
116    }
117   
118    @Inject
119    protected EntityReferenceSerializer<String> serializer;
120   
121    protected Cache<GroupCacheEntry> cache;
122   
123    @Inject
124    private CacheManager cacheManager;
125   
126    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
127   
128    private final String id;
129   
130    /**
131    * Keep an index of what's in the cache to clean just what's needed.
132    */
133    private Map<DocumentReference, Set<String>> cacheDocumentIndex = new ConcurrentHashMap<>();
134   
135    /**
136    * @param id the id of the cache
137    */
 
138  51 toggle public AbstractGroupCache(String id)
139    {
140  51 this.id = id;
141    }
142   
 
143  51 toggle @Override
144    public void initialize() throws InitializationException
145    {
146  51 CacheConfiguration cacheConfig = new CacheConfiguration();
147  51 cacheConfig.setConfigurationId(this.id);
148  51 LRUEvictionConfiguration lru = new LRUEvictionConfiguration();
149  51 lru.setMaxEntries(DEFAULT_CAPACITY);
150  51 cacheConfig.put(LRUEvictionConfiguration.CONFIGURATIONID, lru);
151  51 try {
152  51 this.cache = this.cacheManager.createNewCache(cacheConfig);
153  51 this.cache.addCacheEntryListener(this);
154    } catch (Exception e) {
155  0 throw new InitializationException("Failed to create the group cache", e);
156    }
157    }
158   
 
159  508 toggle protected GroupCacheEntry getCacheEntry(String key, DocumentReference reference, boolean create)
160    {
161  508 lockRead();
162   
163  508 GroupCacheEntry entry;
164  508 try {
165  508 entry = this.cache.get(key);
166    } finally {
167  508 unlockRead();
168    }
169   
170  508 if (entry == null && create) {
171  144 lockWrite();
172   
173  144 try {
174  144 entry = new GroupCacheEntry(key);
175  144 this.cache.set(key, entry);
176  144 addToIndex(key, reference);
177    } finally {
178  144 unlockWrite();
179    }
180    }
181   
182  508 return entry;
183    }
184   
 
185  360 toggle private void addToIndex(String key, DocumentReference reference)
186    {
187  360 this.cacheDocumentIndex.computeIfAbsent(reference, k -> new HashSet<>()).add(key);
188    }
189   
 
190  286 toggle private void addToIndex(String key, Collection<DocumentReference> references)
191    {
192  286 for (DocumentReference reference : references) {
193  216 addToIndex(key, reference);
194    }
195    }
196   
 
197  140 toggle private void cleanIndex(String key, Collection<DocumentReference> references)
198    {
199  140 if (references != null) {
200  138 for (DocumentReference reference : references) {
201  112 Set<String> keys = this.cacheDocumentIndex.get(reference);
202   
203  112 if (keys != null) {
204  50 keys.remove(key);
205   
206  50 if (keys.isEmpty()) {
207  8 this.cacheDocumentIndex.remove(reference);
208    }
209    }
210    }
211    }
212    }
213   
214    /**
215    * Lock write lock.
216    */
 
217  487 toggle public void lockWrite()
218    {
219  487 this.lock.writeLock().lock();
220    }
221   
222    /**
223    * Unlock write lock.
224    */
 
225  487 toggle public void unlockWrite()
226    {
227  487 this.lock.writeLock().unlock();
228    }
229   
230    /**
231    * Lock read lock.
232    */
 
233  508 toggle public void lockRead()
234    {
235  508 this.lock.readLock().lock();
236    }
237   
238    /**
239    * Unlock read lock.
240    */
 
241  508 toggle public void unlockRead()
242    {
243  508 this.lock.readLock().unlock();
244    }
245   
246    /**
247    * Remove anything related to the passed reference from the cache.
248    *
249    * @param reference the reference of the entity to remove from the cache
250    */
 
251  295 toggle public void cleanCache(DocumentReference reference)
252    {
253  295 lockWrite();
254   
255  295 try {
256  295 cleanDocumentCache(reference);
257    } finally {
258  295 unlockWrite();
259    }
260    }
261   
 
262  298 toggle private void cleanDocumentCache(DocumentReference reference)
263    {
264  298 Set<String> keys = this.cacheDocumentIndex.remove(reference);
265   
266  298 if (keys != null) {
267  40 for (String key : keys) {
268  66 this.cache.remove(key);
269    }
270    }
271    }
272   
273    /**
274    * Remove anything related to the passed wiki from the cache.
275    *
276    * @param wiki the identifier of the wiki to remove from the cache
277    */
 
278  4 toggle public void cleanCache(String wiki)
279    {
280  4 lockWrite();
281   
282  4 try {
283  4 for (Map.Entry<DocumentReference, Set<String>> entry : this.cacheDocumentIndex.entrySet()) {
284  11 DocumentReference reference = entry.getKey();
285  11 if (reference.getWikiReference().getName().equals(wiki)) {
286  3 cleanDocumentCache(reference);
287    }
288    }
289    } finally {
290  4 unlockWrite();
291    }
292    }
293   
294    /**
295    * Empty the cache.
296    */
 
297  44 toggle public void removeAll()
298    {
299  44 lockWrite();
300   
301  44 try {
302  44 this.cache.removeAll();
303  37 this.cacheDocumentIndex.clear();
304    } finally {
305  44 unlockWrite();
306    }
307    }
308   
 
309  70 toggle @Override
310    public void cacheEntryRemoved(CacheEntryEvent<GroupCacheEntry> event)
311    {
312  70 String key = event.getEntry().getKey();
313  70 GroupCacheEntry entry = event.getEntry().getValue();
314   
315  70 cleanIndex(key, entry.getDirect());
316  70 cleanIndex(key, entry.getAll());
317    }
318   
 
319  44 toggle @Override
320    public void dispose() throws ComponentLifecycleException
321    {
322    // Make sure nothing is left behind when the component is disposed
323  44 removeAll();
324    }
325    }