1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.extension.repository.internal

File AbstractCachedExtensionRepository.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart9.png
38% of files have more coverage

Code metrics

32
66
14
1
279
164
34
0.52
4.71
14
2.43

Classes

Class Line # Actions
AbstractCachedExtensionRepository 52 66 0% 34 14
0.87587.5%
 

Contributing tests

This file is covered by 117 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: cecee057ae23ff94f0715437224b9ef8e4c2c605 $
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  258 toggle protected AbstractCachedExtensionRepository()
73    {
74  258 this(false);
75    }
76   
 
77  489 toggle protected AbstractCachedExtensionRepository(boolean strict)
78    {
79  489 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  3274 toggle protected void addCachedExtension(E extension)
100    {
101  3274 if (!this.extensions.containsKey(extension.getId())) {
102    // extensions
103  3089 this.extensions.put(extension.getId(), extension);
104   
105    // versions
106  3089 addCachedExtensionVersion(extension.getId().getId(), extension);
107  3089 if (!this.strictId) {
108  1604 for (String feature : extension.getFeatures()) {
109  386 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  3475 toggle protected void addCachedExtensionVersion(String feature, E extension)
122    {
123    // versions
124  3475 List<E> versions = this.extensionsVersions.get(feature);
125   
126  3475 if (versions == null) {
127  3375 versions = new ArrayList<E>();
128  3375 this.extensionsVersions.put(feature, versions);
129   
130  3375 versions.add(extension);
131    } else {
132  100 int index = 0;
133  183 while (index < versions.size()
134    && extension.getId().getVersion().compareTo(versions.get(index).getId().getVersion()) < 0) {
135  83 ++index;
136    }
137   
138  100 versions.add(index, extension);
139    }
140    }
141   
142    /**
143    * Remove extension from all caches.
144    *
145    * @param extension the extension
146    */
 
147  141 toggle protected void removeCachedExtension(E extension)
148    {
149    // Remove the extension from the memory.
150  141 this.extensions.remove(extension.getId());
151   
152    // versions
153  141 removeCachedExtensionVersion(extension.getId().getId(), extension);
154  141 if (!this.strictId) {
155  128 for (String feature : extension.getFeatures()) {
156  48 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  189 toggle protected void removeCachedExtensionVersion(String feature, E extension)
168    {
169    // versions
170  189 List<E> extensionVersions = this.extensionsVersions.get(feature);
171  189 extensionVersions.remove(extension);
172  189 if (extensionVersions.isEmpty()) {
173  174 this.extensionsVersions.remove(feature);
174    }
175    }
176   
177    // ExtensionRepository
178   
 
179  1126 toggle @Override
180    public E resolve(ExtensionId extensionId) throws ResolveException
181    {
182  1126 E extension = this.extensions.get(extensionId);
183   
184  1126 if (extension == null) {
185  204 throw new ExtensionNotFoundException("Can't find extension [" + extensionId + "]");
186    }
187   
188  922 return extension;
189    }
190   
 
191  117 toggle @Override
192    public E resolve(ExtensionDependency extensionDependency) throws ResolveException
193    {
194  117 List<E> versions = this.extensionsVersions.get(extensionDependency.getId());
195   
196  117 if (versions != null) {
197  45 for (E extension : versions) {
198  45 if (extensionDependency.getVersionConstraint().containsVersion(extension.getId().getVersion())) {
199    // Return the higher version which satisfy the version constraint
200  38 return extension;
201    }
202    }
203    }
204   
205  79 throw new ExtensionNotFoundException("Can't find extension dependency [" + extensionDependency + "]");
206    }
207   
 
208  181 toggle @Override
209    public boolean exists(ExtensionId extensionId)
210    {
211  181 return this.extensions.containsKey(extensionId);
212    }
213   
 
214  82 toggle @Override
215    public IterableResult<Version> resolveVersions(String id, int offset, int nb) throws ResolveException
216    {
217  82 if (id == null) {
218  0 return new CollectionIterableResult<Version>(0, offset, Collections.<Version>emptyList());
219    }
220   
221  82 List<E> versions = this.extensionsVersions.get(id);
222   
223  82 if (versions == null) {
224  0 throw new ExtensionNotFoundException("Can't find extension with id [" + id + "]");
225    }
226   
227  82 if (nb == 0 || offset >= versions.size()) {
228  0 return new CollectionIterableResult<Version>(versions.size(), offset, Collections.<Version>emptyList());
229    }
230   
231  82 int fromId = offset < 0 ? 0 : offset;
232  82 int toId = offset + nb > versions.size() || nb < 0 ? versions.size() - 1 : offset + nb;
233   
234  82 List<Version> result = new ArrayList<Version>(toId - fromId);
235   
236    // Invert to sort in ascendent order
237  82 for (int i = toId - 1; i >= fromId; --i) {
238  0 result.add(versions.get(i).getId().getVersion());
239    }
240   
241  82 return new CollectionIterableResult<Version>(versions.size(), offset, result);
242    }
243   
244    // Searchable
245   
 
246  28 toggle @Override
247    public IterableResult<Extension> search(String pattern, int offset, int nb) throws SearchException
248    {
249  28 ExtensionQuery query = new ExtensionQuery(pattern);
250   
251  28 query.setOffset(offset);
252  28 query.setLimit(nb);
253   
254  28 return search(query);
255    }
256   
 
257  32 toggle @Override
258    public IterableResult<Extension> search(ExtensionQuery query)
259    {
260  32 Pattern patternMatcher = RepositoryUtils.createPatternMatcher(query.getQuery());
261   
262  32 Set<Extension> set = new HashSet<Extension>();
263  32 List<Extension> result = new ArrayList<Extension>(this.extensionsVersions.size());
264   
265  32 for (List<E> versions : this.extensionsVersions.values()) {
266  448 E extension = versions.get(0);
267   
268  448 if (RepositoryUtils.matches(patternMatcher, query.getFilters(), extension) && !set.contains(extension)) {
269  310 result.add(extension);
270  310 set.add(extension);
271    }
272    }
273   
274    // Sort
275  32 RepositoryUtils.sort(result, query.getSortClauses());
276   
277  32 return RepositoryUtils.getIterableResult(query.getOffset(), query.getLimit(), result);
278    }
279    }