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

File DefaultExtensionRepositoryManager.java

 

Coverage histogram

../../../../../img/srcFileCovDistChart8.png
56% of files have more coverage

Code metrics

24
105
22
2
448
317
49
0.47
4.77
11
2.23

Classes

Class Line # Actions
DefaultExtensionRepositoryManager 74 100 0% 45 41
0.711267671.1%
DefaultExtensionRepositoryManager.ExtensionRepositoryEntry 105 5 0% 4 2
0.777777877.8%
 

Contributing tests

This file is covered by 131 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.text.MessageFormat;
23    import java.util.Collection;
24    import java.util.Collections;
25    import java.util.HashSet;
26    import java.util.LinkedHashMap;
27    import java.util.List;
28    import java.util.Map;
29    import java.util.Set;
30    import java.util.SortedSet;
31    import java.util.TreeSet;
32    import java.util.stream.Collectors;
33    import java.util.stream.Stream;
34   
35    import javax.inject.Inject;
36    import javax.inject.Singleton;
37   
38    import org.apache.commons.collections4.map.LRUMap;
39    import org.apache.commons.lang3.exception.ExceptionUtils;
40    import org.slf4j.Logger;
41    import org.xwiki.component.annotation.Component;
42    import org.xwiki.component.manager.ComponentLookupException;
43    import org.xwiki.component.manager.ComponentManager;
44    import org.xwiki.component.phase.Initializable;
45    import org.xwiki.component.phase.InitializationException;
46    import org.xwiki.extension.Extension;
47    import org.xwiki.extension.ExtensionDependency;
48    import org.xwiki.extension.ExtensionId;
49    import org.xwiki.extension.ExtensionNotFoundException;
50    import org.xwiki.extension.ResolveException;
51    import org.xwiki.extension.repository.AbstractAdvancedSearchableExtensionRepository;
52    import org.xwiki.extension.repository.DefaultExtensionRepositoryDescriptor;
53    import org.xwiki.extension.repository.ExtensionRepository;
54    import org.xwiki.extension.repository.ExtensionRepositoryDescriptor;
55    import org.xwiki.extension.repository.ExtensionRepositoryException;
56    import org.xwiki.extension.repository.ExtensionRepositoryFactory;
57    import org.xwiki.extension.repository.ExtensionRepositoryId;
58    import org.xwiki.extension.repository.ExtensionRepositoryManager;
59    import org.xwiki.extension.repository.ExtensionRepositorySource;
60    import org.xwiki.extension.repository.result.IterableResult;
61    import org.xwiki.extension.repository.search.AdvancedSearchable;
62    import org.xwiki.extension.repository.search.ExtensionQuery;
63    import org.xwiki.extension.repository.search.SearchException;
64    import org.xwiki.extension.version.Version;
65   
66    /**
67    * Default implementation of {@link ExtensionRepositoryManager}.
68    *
69    * @version $Id: 97d255ed5b427d29fb5c7729e1680ed9eb7b3b6f $
70    * @since 4.0M1
71    */
72    @Component
73    @Singleton
 
74    public class DefaultExtensionRepositoryManager extends AbstractAdvancedSearchableExtensionRepository
75    implements ExtensionRepositoryManager, Initializable
76    {
77    /**
78    * Used to lookup {@link ExtensionRepositoryFactory}s.
79    */
80    @Inject
81    private ComponentManager componentManager;
82   
83    /**
84    * The logger to log.
85    */
86    @Inject
87    private Logger logger;
88   
89    /**
90    * Used to initialize {@link #repositoryManager}.
91    */
92    @Inject
93    private List<ExtensionRepositorySource> repositoriesSources;
94   
95    /**
96    * The registered repositories.
97    */
98    private final Map<String, ExtensionRepositoryEntry> repositoryMap =
99    Collections.synchronizedMap(new LinkedHashMap<>());
100   
101    private List<ExtensionRepository> repositories = Collections.emptyList();
102   
103    private LRUMap<ExtensionRepositoryDescriptor, ExtensionRepository> repositoriesCache = new LRUMap<>(100);
104   
 
105    private class ExtensionRepositoryEntry implements Comparable<ExtensionRepositoryEntry>
106    {
107    private ExtensionRepository repository;
108   
109    private int priority;
110   
 
111  270 toggle public ExtensionRepositoryEntry(ExtensionRepository repository, int priority)
112    {
113  270 this.repository = repository;
114  270 this.priority = priority;
115    }
116   
 
117  56 toggle @Override
118    public int compareTo(ExtensionRepositoryEntry other)
119    {
120  56 return this.priority - other.priority;
121    }
122   
 
123  336 toggle public ExtensionRepository getRepository()
124    {
125  336 return this.repository;
126    }
127   
 
128  0 toggle @Override
129    public String toString()
130    {
131  0 return getRepository().toString();
132    }
133    }
134   
135    // Initializable
136   
 
137  265 toggle @Override
138    public void initialize() throws InitializationException
139    {
140    // Set descriptor
141  265 setDescriptor(new DefaultExtensionRepositoryDescriptor("remote"));
142   
143    // Load default extension repositories
144  265 for (ExtensionRepositorySource repositoriesSource : this.repositoriesSources) {
145  364 for (ExtensionRepositoryDescriptor repositoryDescriptor : repositoriesSource
146    .getExtensionRepositoryDescriptors()) {
147  25 try {
148  25 addRepository(repositoryDescriptor, repositoriesSource.getPriority());
149    } catch (ExtensionRepositoryException e) {
150  0 this.logger.error("Failed to add repository [" + repositoryDescriptor + "]", e);
151    }
152    }
153    }
154    }
155   
156    // ExtensionRepositoryManager
157   
 
158  271 toggle private void updateRepositories()
159    {
160    // Get values
161  271 Stream<ExtensionRepositoryEntry> entryStream = this.repositoryMap.values().stream();
162   
163    // Sort
164  271 entryStream = entryStream.sorted();
165   
166    // Convert to list of ExtensionRepository
167  271 this.repositories = entryStream.map(ExtensionRepositoryEntry::getRepository).collect(Collectors.toList());
168    }
169   
 
170  0 toggle @Override
171    @Deprecated
172    public ExtensionRepository addRepository(ExtensionRepositoryId repositoryId) throws ExtensionRepositoryException
173    {
174  0 return addRepository((ExtensionRepositoryDescriptor) repositoryId);
175    }
176   
 
177  47 toggle @Override
178    public ExtensionRepository addRepository(ExtensionRepositoryDescriptor repositoryDescriptor)
179    throws ExtensionRepositoryException
180    {
181  47 return addRepository(repositoryDescriptor, DEFAULT_PRIORITY);
182    }
183   
 
184  76 toggle @Override
185    public ExtensionRepository addRepository(ExtensionRepositoryDescriptor repositoryDescriptor, int priority)
186    throws ExtensionRepositoryException
187    {
188  76 ExtensionRepository repository;
189   
190  76 try {
191  76 ExtensionRepositoryFactory repositoryFactory =
192    this.componentManager.getInstance(ExtensionRepositoryFactory.class, repositoryDescriptor.getType());
193   
194  75 repository = repositoryFactory.createRepository(repositoryDescriptor);
195   
196  75 addRepository(repository, priority);
197    } catch (ComponentLookupException e) {
198  1 throw new ExtensionRepositoryException(
199    "Unsupported repository type [" + repositoryDescriptor.getType() + "]", e);
200    }
201   
202  75 return repository;
203    }
204   
 
205  195 toggle @Override
206    public void addRepository(ExtensionRepository repository)
207    {
208  195 addRepository(repository, DEFAULT_PRIORITY);
209    }
210   
 
211  270 toggle @Override
212    public void addRepository(ExtensionRepository repository, int priority)
213    {
214    // Update the map
215  270 this.repositoryMap.put(repository.getDescriptor().getId(), new ExtensionRepositoryEntry(repository, priority));
216   
217    // Update the list
218  270 updateRepositories();
219    }
220   
 
221  1 toggle @Override
222    public void removeRepository(String repositoryId)
223    {
224    // Update the map
225  1 this.repositoryMap.remove(repositoryId);
226   
227    // Update the list
228  1 updateRepositories();
229    }
230   
 
231  22 toggle @Override
232    public ExtensionRepository getRepository(String repositoryId)
233    {
234  22 ExtensionRepositoryEntry entry = this.repositoryMap.get(repositoryId);
235   
236  22 return entry != null ? entry.getRepository() : null;
237    }
238   
 
239  55 toggle private ExtensionRepository getRepository(ExtensionRepositoryDescriptor repositoryDescriptor)
240    throws ExtensionRepositoryException
241    {
242    // Try in the cache
243  55 ExtensionRepository repository = this.repositoriesCache.get(repositoryDescriptor);
244   
245  55 if (repository == null) {
246    // Try in the registered repositories
247  8 if (repositoryDescriptor.getId() != null) {
248  8 repository = getRepository(repositoryDescriptor.getId());
249    }
250   
251  8 if (repository == null || !repository.getDescriptor().equals(repositoryDescriptor)) {
252    // Create one
253  2 ExtensionRepositoryFactory repositoryFactory;
254  2 try {
255  2 repositoryFactory = this.componentManager.getInstance(ExtensionRepositoryFactory.class,
256    repositoryDescriptor.getType());
257    } catch (ComponentLookupException e) {
258  0 throw new ExtensionRepositoryException(
259    "Unsupported extension repository type [{" + repositoryDescriptor.getType() + "}]", e);
260    }
261   
262  2 repository = repositoryFactory.createRepository(repositoryDescriptor);
263    }
264   
265  8 this.repositoriesCache.put(repositoryDescriptor, repository);
266    }
267   
268  55 return repository;
269    }
270   
 
271  266 toggle @Override
272    public Collection<ExtensionRepository> getRepositories()
273    {
274  266 return this.repositories;
275    }
276   
277    // ExtensionRepository
278   
 
279  581 toggle @Override
280    public Extension resolve(ExtensionId extensionId) throws ResolveException
281    {
282  581 ResolveException lastException = null;
283   
284  581 for (ExtensionRepository repository : this.repositories) {
285  609 try {
286  609 return repository.resolve(extensionId);
287    } catch (ExtensionNotFoundException e1) {
288  101 this.logger.debug("Could not find extension [{}] in repository [{}]", extensionId,
289    repository.getDescriptor(), e1);
290    } catch (ResolveException e2) {
291  0 this.logger.error("Unexpected error when trying to find extension [{}] in repository [{}]", extensionId,
292    repository.getDescriptor(), e2);
293   
294  0 lastException = e2;
295    }
296    }
297   
298  73 if (lastException != null) {
299  0 throw new ResolveException(MessageFormat.format("Failed to resolve extension [{0}]", extensionId),
300    lastException);
301    } else {
302  73 throw new ExtensionNotFoundException(MessageFormat.format("Could not find extension [{0}]", extensionId));
303    }
304    }
305   
 
306  260 toggle @Override
307    public Extension resolve(ExtensionDependency extensionDependency) throws ResolveException
308    {
309  260 Set<ExtensionRepositoryDescriptor> checkedRepositories = new HashSet<>();
310   
311  260 Exception lastException = null;
312   
313    // Try repositories declared in the extension dependency
314  260 for (ExtensionRepositoryDescriptor repositoryDescriptor : extensionDependency.getRepositories()) {
315  55 if (checkedRepositories.contains(repositoryDescriptor)) {
316  0 continue;
317    }
318   
319    // Remember we tried that repository
320  55 checkedRepositories.add(repositoryDescriptor);
321   
322  55 ExtensionRepository repository;
323  55 try {
324  55 repository = getRepository(repositoryDescriptor);
325    } catch (ExtensionRepositoryException e) {
326  0 this.logger.warn("Invalid repository [{}] in extension dependency",
327    extensionDependency.getRepositories(), extensionDependency, ExceptionUtils.getRootCauseMessage(e));
328   
329  0 continue;
330    }
331   
332  55 try {
333  55 return repository.resolve(extensionDependency);
334    } catch (ExtensionNotFoundException e1) {
335  3 this.logger.debug("Could not find extension dependency [{}] in repository [{}]", extensionDependency,
336    repository.getDescriptor(), e1);
337    } catch (ResolveException e2) {
338  0 this.logger.warn("Unexpected error when trying to find extension dependency [{}] in repository [{}]: ",
339    extensionDependency, repository.getDescriptor(), ExceptionUtils.getRootCauseMessage(e2));
340   
341  0 lastException = e2;
342    }
343    }
344   
345    // Try configured repositories
346  208 for (ExtensionRepository repository : this.repositories) {
347  205 if (checkedRepositories.contains(repository.getDescriptor())) {
348  1 continue;
349    }
350   
351    // Remember we tried that repository
352  204 checkedRepositories.add(repository.getDescriptor());
353   
354  204 try {
355  204 return repository.resolve(extensionDependency);
356    } catch (ExtensionNotFoundException e1) {
357  6 this.logger.debug("Could not find extension dependency [{}] in repository [{}]", extensionDependency,
358    repository.getDescriptor(), e1);
359    } catch (ResolveException e2) {
360  0 this.logger.error("Unexpected error when trying to find extension dependency [{}] in repository [{}]",
361    extensionDependency, repository.getDescriptor(), e2);
362   
363  0 lastException = e2;
364    }
365    }
366   
367  10 if (lastException != null) {
368  0 throw new ResolveException(
369    MessageFormat.format("Failed to resolve extension dependency [{0}]", extensionDependency),
370    lastException);
371    } else {
372  10 throw new ExtensionNotFoundException(
373    MessageFormat.format("Could not find extension dependency [{0}]", extensionDependency));
374    }
375    }
376   
 
377  142 toggle @Override
378    public IterableResult<Version> resolveVersions(String id, int offset, int nb) throws ResolveException
379    {
380  142 SortedSet<Version> versionSet = new TreeSet<>();
381   
382  142 for (ExtensionRepository repository : this.repositories) {
383  127 try {
384  127 IterableResult<Version> versions = repository.resolveVersions(id, 0, -1);
385   
386  42 for (Version version : versions) {
387  69 versionSet.add(version);
388    }
389    } catch (ExtensionNotFoundException e1) {
390  85 this.logger.debug("Could not find extension with id [{}] in repository [{}]", id,
391    repository.getDescriptor(), e1);
392    } catch (ResolveException e2) {
393  0 this.logger.error("Unexpected error when trying to find versions for extension with id [{}]", id, e2);
394    }
395    }
396   
397  142 if (versionSet.isEmpty()) {
398  123 throw new ExtensionNotFoundException(
399    MessageFormat.format("Could not find versions for extension with id [{0}]", id));
400    }
401   
402  19 return RepositoryUtils.getIterableResult(offset, nb, versionSet);
403    }
404   
 
405  0 toggle @Override
406    public boolean exists(ExtensionId extensionId)
407    {
408  0 for (ExtensionRepository repository : this.repositories) {
409  0 if (repository.exists(extensionId)) {
410  0 return true;
411    }
412    }
413   
414  0 return false;
415    }
416   
417    // AdvancedSearchable
418   
 
419  71 toggle @Override
420    public IterableResult<Extension> search(ExtensionQuery query) throws SearchException
421    {
422  71 return RepositoryUtils.search(query, this.repositories);
423    }
424   
 
425  0 toggle @Override
426    public boolean isFilterable()
427    {
428  0 for (ExtensionRepository repository : this.repositories) {
429  0 if (repository instanceof AdvancedSearchable && ((AdvancedSearchable) repository).isFilterable()) {
430  0 return true;
431    }
432    }
433   
434  0 return false;
435    }
436   
 
437  0 toggle @Override
438    public boolean isSortable()
439    {
440  0 for (ExtensionRepository repository : this.repositories) {
441  0 if (repository instanceof AdvancedSearchable && ((AdvancedSearchable) repository).isSortable()) {
442  0 return true;
443    }
444    }
445   
446  0 return false;
447    }
448    }