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

File AbstractDocumentConfigurationSource.java

 

Coverage histogram

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

Code metrics

36
82
21
1
378
247
49
0.6
3.9
21
2.33

Classes

Class Line # Actions
AbstractDocumentConfigurationSource 69 82 0% 49 7
0.949640395%
 

Contributing tests

This file is covered by 10 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.configuration.internal;
21   
22    import java.util.ArrayList;
23    import java.util.Arrays;
24    import java.util.Collections;
25    import java.util.List;
26    import java.util.Set;
27    import java.util.regex.Pattern;
28   
29    import javax.inject.Inject;
30    import javax.inject.Provider;
31   
32    import org.slf4j.Logger;
33    import org.xwiki.bridge.event.WikiDeletedEvent;
34    import org.xwiki.cache.Cache;
35    import org.xwiki.cache.CacheException;
36    import org.xwiki.cache.CacheManager;
37    import org.xwiki.cache.config.CacheConfiguration;
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.EntityType;
43    import org.xwiki.model.reference.DocumentReference;
44    import org.xwiki.model.reference.EntityReferenceSerializer;
45    import org.xwiki.model.reference.LocalDocumentReference;
46    import org.xwiki.model.reference.RegexEntityReference;
47    import org.xwiki.model.reference.WikiReference;
48    import org.xwiki.observation.EventListener;
49    import org.xwiki.observation.ObservationManager;
50    import org.xwiki.observation.event.Event;
51    import org.xwiki.properties.ConverterManager;
52    import org.xwiki.wiki.descriptor.WikiDescriptorManager;
53   
54    import com.xpn.xwiki.XWikiContext;
55    import com.xpn.xwiki.XWikiException;
56    import com.xpn.xwiki.doc.XWikiDocument;
57    import com.xpn.xwiki.internal.event.XObjectAddedEvent;
58    import com.xpn.xwiki.internal.event.XObjectDeletedEvent;
59    import com.xpn.xwiki.internal.event.XObjectUpdatedEvent;
60    import com.xpn.xwiki.objects.BaseObject;
61    import com.xpn.xwiki.objects.BaseProperty;
62   
63    /**
64    * Common features for all Document sources (ie configuration data coming from wiki pages).
65    *
66    * @version $Id: 6a709aa44a66edd8e575cd9f6fa747e81387de7a $
67    * @since 2.0M2
68    */
 
69    public abstract class AbstractDocumentConfigurationSource extends AbstractConfigurationSource implements Initializable,
70    Disposable
71    {
72    /**
73    * Represents no value (ie the default value will be used) in xproperties.
74    */
75    // TODO: remove when XWIKI-10853 is fixed
76    protected static final String NO_VALUE = "---";
77   
78    @Inject
79    protected WikiDescriptorManager wikiManager;
80   
81    @Inject
82    protected CacheManager cacheManager;
83   
84    @Inject
85    protected EntityReferenceSerializer<String> referenceSerializer;
86   
87    @Inject
88    protected ObservationManager observation;
89   
90    @Inject
91    protected Provider<XWikiContext> xcontextProvider;
92   
93    @Inject
94    protected ConverterManager converter;
95   
96    @Inject
97    protected Logger logger;
98   
99    protected Cache<Object> cache;
100   
101    /**
102    * @return the document reference of the document containing an XWiki Object with configuration data or null if
103    * there no such document in which case this configuration source will be skipped
104    */
105    protected abstract DocumentReference getDocumentReference();
106   
107    /**
108    * @return the XWiki Class reference of the XWiki Object containing the configuration properties
109    */
110    protected abstract LocalDocumentReference getClassReference();
111   
112    /**
113    * @return the identifier to use for the cache
114    */
115    protected abstract String getCacheId();
116   
117    /**
118    * @return the prefix used to generate a cache key combined to the actual configuration property name
119    */
 
120  39860 toggle protected String getCacheKeyPrefix()
121    {
122  39852 return this.referenceSerializer.serialize(getDocumentReference());
123    }
124   
 
125  322 toggle @Override
126    public void initialize() throws InitializationException
127    {
128    // Initialize cache
129  322 try {
130  322 this.cache = this.cacheManager.createNewCache(new CacheConfiguration(getCacheId()));
131    } catch (CacheException e) {
132  0 throw new InitializationException("Failed to initialize cache", e);
133    }
134   
135    // Start listening to configuration modifications
136  322 this.observation.addListener(new EventListener()
137    {
 
138  132 toggle @Override
139    public void onEvent(Event event, Object source, Object data)
140    {
141  132 onCacheCleanup(event, source, data);
142    }
143   
 
144  2936 toggle @Override
145    public String getName()
146    {
147  2936 return getCacheId();
148    }
149   
 
150  312 toggle @Override
151    public List<Event> getEvents()
152    {
153  312 return getCacheCleanupEvents();
154    }
155    });
156    }
157   
 
158  294 toggle @Override
159    public void dispose() throws ComponentLifecycleException
160    {
161  294 this.observation.removeListener(getCacheId());
162    }
163   
 
164  312 toggle protected List<Event> getCacheCleanupEvents()
165    {
166  312 RegexEntityReference classMatcher =
167    new RegexEntityReference(Pattern.compile(".*:" + this.referenceSerializer.serialize(getClassReference())
168    + "\\[\\d*\\]"), EntityType.OBJECT);
169   
170  312 return Arrays.<Event>asList(new XObjectAddedEvent(classMatcher), new XObjectDeletedEvent(classMatcher),
171    new XObjectUpdatedEvent(classMatcher), new WikiDeletedEvent());
172    }
173   
 
174  132 toggle protected void onCacheCleanup(Event event, Object source, Object data)
175    {
176    // TODO: do finer grain cache invalidation
177  132 this.cache.removeAll();
178    }
179   
180    /**
181    * @return the reference pointing to the current wiki
182    */
 
183  13775 toggle protected WikiReference getCurrentWikiReference()
184    {
185  13777 return new WikiReference(this.wikiManager.getCurrentWikiId());
186    }
187   
 
188  340231 toggle @Override
189    public boolean containsKey(String key)
190    {
191  340225 XWikiContext xcontext = this.xcontextProvider.get();
192   
193  340217 if (xcontext != null && xcontext.getWiki() != null) {
194  261999 Object value = getPropertyValue(key, null);
195  262000 return value != null;
196    }
197   
198  78197 return false;
199    }
200   
 
201  1054 toggle protected BaseObject getBaseObject() throws XWikiException
202    {
203  1054 DocumentReference documentReference = getFailsafeDocumentReference();
204  1054 LocalDocumentReference classReference = getFailsafeClassReference();
205   
206  1054 if (documentReference != null && classReference != null) {
207  709 XWikiContext xcontext = this.xcontextProvider.get();
208   
209  709 XWikiDocument document = xcontext.getWiki().getDocument(getDocumentReference(), xcontext);
210   
211  709 return document.getXObject(classReference);
212    }
213   
214  345 return null;
215    }
216   
 
217  1052 toggle protected Object getBaseProperty(String propertyName, boolean text) throws XWikiException
218    {
219  1052 BaseObject baseObject = getBaseObject();
220   
221  1052 if (baseObject != null) {
222  319 BaseProperty property = (BaseProperty) baseObject.getField(propertyName);
223   
224  42 Object value = property != null ? (text ? property.toText() : property.getValue()) : null;
225   
226    // TODO: In the future we would need the notion of initialized/not-initialized property values in the wiki.
227    // When this is implemented modify the code below.
228  319 if (isEmpty(value)) {
229  297 value = null;
230    }
231   
232  319 return value;
233    }
234   
235  733 return null;
236    }
237   
 
238  15 toggle @Override
239    public List<String> getKeys()
240    {
241  15 List<String> keys = Collections.emptyList();
242   
243  15 XWikiContext xcontext = this.xcontextProvider.get();
244   
245  15 if (xcontext != null && xcontext.getWiki() != null) {
246  3 BaseObject baseObject;
247  3 try {
248  3 baseObject = getBaseObject();
249   
250  3 if (baseObject != null) {
251  2 Set<String> properties = baseObject.getPropertyList();
252  2 keys = new ArrayList<String>(properties.size());
253  2 for (String key : properties) {
254    // We need to check if the key really have a value as otherwise it does not really make sense to
255    // return it
256  5 if (containsKey(key)) {
257  3 keys.add(key);
258    }
259    }
260    }
261    } catch (XWikiException e) {
262  0 this.logger.error("Failed to access configuration", e);
263    }
264    }
265   
266  15 return keys;
267    }
268   
 
269  529 toggle @Override
270    public <T> T getProperty(String key, T defaultValue)
271    {
272  529 T result = getPropertyValue(key, defaultValue != null ? (Class<T>) defaultValue.getClass() : null);
273   
274    // Make sure we don't return null values for List and Properties (they must return empty elements
275    // when using the typed API).
276  529 if (result == null) {
277  4 result = defaultValue;
278    }
279   
280  529 return result;
281    }
282   
 
283  552051 toggle @Override
284    public <T> T getProperty(String key, Class<T> valueClass)
285    {
286  552047 T result = getPropertyValue(key, valueClass);
287   
288    // Make sure we don't return null values for List and Properties (they must return empty elements
289    // when using the typed API).
290  552111 if (result == null) {
291  551971 result = getDefault(valueClass);
292    }
293   
294  552073 return result;
295    }
296   
 
297  312 toggle @Override
298    @SuppressWarnings("unchecked")
299    public <T> T getProperty(String key)
300    {
301  312 return (T) getPropertyValue(key, null);
302    }
303   
 
304  814870 toggle protected <T> T getPropertyValue(String key, Class<T> valueClass)
305    {
306  814948 String cacheKey = getCacheKeyPrefix() + ':' + (valueClass != null ? valueClass.getName() : null) + ':' + key;
307   
308  814912 Object result = this.cache.get(cacheKey);
309   
310  814953 if (result == null) {
311  14265 XWikiContext xcontext = this.xcontextProvider.get();
312   
313  14264 if (xcontext != null && xcontext.getWiki() != null) {
314  14265 try {
315  14266 result = getBaseProperty(key, valueClass == String.class);
316   
317  14266 if (valueClass != null && result != null) {
318  36 result = this.converter.convert(valueClass, result);
319    }
320   
321    // Void.TYPE is used to keep track of fields that don't exist
322  14264 this.cache.set(cacheKey, result == null ? Void.TYPE : result);
323    } catch (XWikiException e) {
324  0 this.logger.error("Failed to access configuration property", e);
325    }
326    }
327    }
328   
329    // Void.TYPE is used to keep track of fields that don't exist
330  814926 if (result == Void.TYPE) {
331  798971 result = null;
332    }
333   
334  814939 return (T) result;
335    }
336   
 
337  2 toggle @Override
338    public boolean isEmpty()
339    {
340  2 return getKeys().isEmpty();
341    }
342   
 
343  27282 toggle protected DocumentReference getFailsafeDocumentReference()
344    {
345  27284 DocumentReference documentReference;
346   
347  27288 try {
348  27288 documentReference = getDocumentReference();
349    } catch (Exception e) {
350    // We verify that no error has happened and if one happened then we skip this configuration source. This
351    // ensures the system will continue to work even if this source has a problem.
352  0 documentReference = null;
353    }
354   
355  27291 return documentReference;
356    }
357   
 
358  27286 toggle protected LocalDocumentReference getFailsafeClassReference()
359    {
360  27288 LocalDocumentReference classReference;
361   
362  27288 try {
363  27287 classReference = getClassReference();
364    } catch (Exception e) {
365    // We verify that no error has happened and if one happened then we skip this configuration source. This
366    // ensures the system will continue to work even if this source has a problem.
367  0 classReference = null;
368    }
369   
370  27286 return classReference;
371    }
372   
 
373  13592 toggle protected boolean isEmpty(Object value)
374    {
375    // TODO: remove the NO_VALUE test when XWIKI-10853 is fixed
376  13593 return value == null || (value instanceof String && (value.equals("") || value.equals(NO_VALUE)));
377    }
378    }