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

File AbstractCachedExtensionRepository.java

 

Coverage histogram

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

Code metrics

26
64
14
1
274
162
30
0.47
4.57
14
2.14

Classes

Class Line # Actions
AbstractCachedExtensionRepository 52 64 0% 30 8
0.923076992.3%
 

Contributing tests

This file is covered by 124 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.extension.repository.internal;
21   
22    import java.util.ArrayList;
23    import java.util.Collections;
24    import java.util.HashSet;
25    import java.util.List;
26    import java.util.Map;
27    import java.util.Set;
28    import java.util.concurrent.ConcurrentHashMap;
29    import java.util.regex.Pattern;
30   
31    import org.xwiki.extension.Extension;
32    import org.xwiki.extension.ExtensionDependency;
33    import org.xwiki.extension.ExtensionId;
34    import org.xwiki.extension.ExtensionNotFoundException;
35    import org.xwiki.extension.ResolveException;
36    import org.xwiki.extension.repository.AbstractExtensionRepository;
37    import org.xwiki.extension.repository.result.CollectionIterableResult;
38    import org.xwiki.extension.repository.result.IterableResult;
39    import org.xwiki.extension.repository.search.AdvancedSearchable;
40    import org.xwiki.extension.repository.search.ExtensionQuery;
41    import org.xwiki.extension.repository.search.SearchException;
42    import org.xwiki.extension.version.Version;
43   
44    /**
45    * Base class for {@link org.xwiki.extension.repository.ExtensionRepository} implementations maintaining a cache of all
46    * extensions.
47    *
48    * @param <E> the type of the extension
49    * @version $Id: 6016a992a02b781357fd6db11dcf6dec0d37079e $
50    * @since 5.4M1
51    */
 
52    public abstract class AbstractCachedExtensionRepository<E extends Extension> extends AbstractExtensionRepository
53    implements AdvancedSearchable
54    {
55    /**
56    * The cached extensions.
57    */
58    protected transient Map<ExtensionId, E> extensions = new ConcurrentHashMap<ExtensionId, E>();
59   
60    /**
61    * The cached extensions grouped by ids and ordered by version DESC.
62    * <p>
63    * <extension id, extensions>
64    */
65    protected Map<String, List<E>> extensionsVersions = new ConcurrentHashMap<String, List<E>>();
66   
67    /**
68    * Indicate features should be used map key at the same levels than the actual ids.
69    */
70    private boolean strictId;
71   
 
72  359 toggle protected AbstractCachedExtensionRepository()
73    {
74  359 this(false);
75    }
76   
 
77  616 toggle protected AbstractCachedExtensionRepository(boolean strict)
78    {
79  616 this.strictId = strict;
80    }
81   
 
82  0 toggle @Override
83    public boolean isFilterable()
84    {
85  0 return true;
86    }
87   
 
88  0 toggle @Override
89    public boolean isSortable()
90    {
91  0 return true;
92    }
93   
94    /**
95    * Register a new extension.
96    *
97    * @param extension the new extension
98    */
 
99  7661 toggle protected void addCachedExtension(E extension)
100    {
101  7661 if (!this.extensions.containsKey(extension.getId())) {
102    // extensions
103  7306 this.extensions.put(extension.getId(), extension);
104   
105    // versions
106  7306 addCachedExtensionVersion(extension.getId().getId(), extension);
107  7306 if (!this.strictId) {
108  4290 for (String feature : extension.getFeatures()) {
109  1615 addCachedExtensionVersion(feature, extension);
110    }
111    }
112    }
113    }
114   
115    /**
116    * Register extension in all caches.
117    *
118    * @param feature the feature
119    * @param extension the extension
120    */
 
121  8921 toggle protected void addCachedExtensionVersion(String feature, E extension)
122    {
123    // versions
124  8921 List<E> versions = this.extensionsVersions.get(feature);
125   
126  8921 if (versions == null) {
127  8711 versions = new ArrayList<E>();
128  8711 this.extensionsVersions.put(feature, versions);
129   
130  8711 versions.add(extension);
131    } else {
132  210 int index = 0;
133  304 while (index < versions.size()
134    && extension.getId().getVersion().compareTo(versions.get(index).getId().getVersion()) < 0) {
135  94 ++index;
136    }
137   
138  210 versions.add(index, extension);
139    }
140    }
141   
142    /**
143    * Remove extension from all caches.
144    *
145    * @param extension the extension
146    */
 
147  349 toggle protected void removeCachedExtension(E extension)
148    {
149    // Remove the extension from the memory.
150  349 this.extensions.remove(extension.getId());
151   
152    // versions
153  349 removeCachedExtensionVersion(extension.getId().getId(), extension);
154  349 if (!this.strictId) {
155  336 for (String feature : extension.getFeatures()) {
156  147 removeCachedExtensionVersion(feature, extension);
157    }
158    }
159    }
160   
161    /**
162    * Remove passed extension associated to passed feature from the cache.
163    *
164    * @param feature the feature associated to the extension
165    * @param extension the extension
166    */
 
167  496 toggle protected void removeCachedExtensionVersion(String feature, E extension)
168    {
169    // versions
170  496 List<E> extensionVersions = this.extensionsVersions.get(feature);
171  496 extensionVersions.remove(extension);
172  496 if (extensionVersions.isEmpty()) {
173  481 this.extensionsVersions.remove(feature);
174    }
175    }
176   
177    // ExtensionRepository
178   
 
179  18019 toggle @Override
180    public E resolve(ExtensionId extensionId) throws ResolveException
181    {
182  18019 E extension = this.extensions.get(extensionId);
183   
184  18019 if (extension == null) {
185  445 throw new ExtensionNotFoundException("Can't find extension [" + extensionId + "]");
186    }
187   
188  17574 return extension;
189    }
190   
 
191  29450 toggle @Override
192    public E resolve(ExtensionDependency extensionDependency) throws ResolveException
193    {
194  29450 List<E> versions = this.extensionsVersions.get(extensionDependency.getId());
195   
196  29450 if (versions != null) {
197  28706 for (E extension : versions) {
198  28706 if (extensionDependency.getVersionConstraint().containsVersion(extension.getId().getVersion())) {
199    // Return the higher version which satisfy the version constraint
200  28699 return extension;
201    }
202    }
203    }
204   
205  751 throw new ExtensionNotFoundException("Can't find extension dependency [" + extensionDependency + "]");
206    }
207   
 
208  425 toggle @Override
209    public boolean exists(ExtensionId extensionId)
210    {
211  425 return this.extensions.containsKey(extensionId);
212    }
213   
 
214  138 toggle @Override
215    public IterableResult<Version> resolveVersions(String id, int offset, int nb) throws ResolveException
216    {
217  138 if (id == null) {
218  0 return new CollectionIterableResult<>(0, offset, Collections.<Version>emptyList());
219    }
220   
221  138 List<E> extensionVersions = this.extensionsVersions.get(id);
222   
223  138 if (extensionVersions == null) {
224  10 throw new ExtensionNotFoundException("Can't find extension with id [" + id + "]");
225    }
226   
227  128 if (nb == 0 || offset >= extensionVersions.size()) {
228  0 return new CollectionIterableResult<>(extensionVersions.size(), offset, Collections.<Version>emptyList());
229    }
230   
231  128 List<Version> versions = new ArrayList<>(extensionVersions.size());
232  128 for (E extension : extensionVersions) {
233  155 versions.add(extension.getId().getVersion());
234    }
235   
236  128 return RepositoryUtils.getIterableResult(offset, nb, versions);
237    }
238   
239    // Searchable
240   
 
241  28 toggle @Override
242    public IterableResult<Extension> search(String pattern, int offset, int nb) throws SearchException
243    {
244  28 ExtensionQuery query = new ExtensionQuery(pattern);
245   
246  28 query.setOffset(offset);
247  28 query.setLimit(nb);
248   
249  28 return search(query);
250    }
251   
 
252  48 toggle @Override
253    public IterableResult<Extension> search(ExtensionQuery query)
254    {
255  48 Pattern patternMatcher = RepositoryUtils.createPatternMatcher(query.getQuery());
256   
257  48 Set<Extension> set = new HashSet<Extension>();
258  48 List<Extension> result = new ArrayList<Extension>(this.extensionsVersions.size());
259   
260  48 for (List<E> versions : this.extensionsVersions.values()) {
261  1525 E extension = versions.get(0);
262   
263  1525 if (RepositoryUtils.matches(patternMatcher, query.getFilters(), extension) && !set.contains(extension)) {
264  330 result.add(extension);
265  330 set.add(extension);
266    }
267    }
268   
269    // Sort
270  48 RepositoryUtils.sort(result, query.getSortClauses());
271   
272  48 return RepositoryUtils.getIterableResult(query.getOffset(), query.getLimit(), result);
273    }
274    }