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

File DatabaseDocumentIterator.java

 

Coverage histogram

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

Code metrics

28
69
8
1
289
175
25
0.36
8.62
8
3.12

Classes

Class Line # Actions
DatabaseDocumentIterator 62 69 0% 25 7
0.9333333493.3%
 

Contributing tests

This file is covered by 2 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.search.solr.internal.job;
21   
22    import java.util.ArrayList;
23    import java.util.Arrays;
24    import java.util.Collections;
25    import java.util.Iterator;
26    import java.util.List;
27    import java.util.Map;
28   
29    import javax.inject.Inject;
30    import javax.inject.Named;
31   
32    import org.apache.commons.lang3.LocaleUtils;
33    import org.apache.commons.lang3.StringUtils;
34    import org.apache.commons.lang3.tuple.ImmutablePair;
35    import org.apache.commons.lang3.tuple.Pair;
36    import org.xwiki.component.annotation.Component;
37    import org.xwiki.component.annotation.InstantiationStrategy;
38    import org.xwiki.component.descriptor.ComponentInstantiationStrategy;
39    import org.xwiki.model.EntityType;
40    import org.xwiki.model.reference.DocumentReference;
41    import org.xwiki.model.reference.EntityReference;
42    import org.xwiki.model.reference.EntityReferenceResolver;
43    import org.xwiki.model.reference.EntityReferenceSerializer;
44    import org.xwiki.model.reference.SpaceReference;
45    import org.xwiki.model.reference.WikiReference;
46    import org.xwiki.query.Query;
47    import org.xwiki.query.QueryException;
48    import org.xwiki.query.QueryFilter;
49    import org.xwiki.query.QueryManager;
50    import org.xwiki.wiki.descriptor.WikiDescriptorManager;
51    import org.xwiki.wiki.manager.WikiManagerException;
52   
53    /**
54    * Iterates the documents from the XWiki database.
55    *
56    * @version $Id: 3d50e640a613174cf60a18d4a9a6717c8331c80b $
57    * @since 5.4.5
58    */
59    @Component
60    @InstantiationStrategy(ComponentInstantiationStrategy.PER_LOOKUP)
61    @Named("database")
 
62    public class DatabaseDocumentIterator extends AbstractDocumentIterator<String>
63    {
64    /**
65    * The current index in the list of {@link #results}.
66    */
67    private int index;
68   
69    /**
70    * A 'page' of results taken from the database.
71    */
72    private List<Object[]> results = Collections.emptyList();
73   
74    /**
75    * Used to get the list of available wikis.
76    */
77    @Inject
78    private WikiDescriptorManager wikiDescriptorManager;
79   
80    /**
81    * Iterates over the available wikis.
82    */
83    private Iterator<String> wikisIterator;
84   
85    /**
86    * The wiki that is currently being iterated.
87    */
88    private String wiki;
89   
90    /**
91    * The offset in the current wiki.
92    */
93    private int offset;
94   
95    /**
96    * Used to query the underlying storage.
97    */
98    @Inject
99    private QueryManager queryManager;
100   
101    @Inject
102    @Named("explicit")
103    private EntityReferenceResolver<String> explicitEntityReferenceResolver;
104   
105    @Inject
106    @Named("local")
107    private EntityReferenceSerializer<String> localEntityReferenceSerializer;
108   
109    /**
110    * The query used to fetch the documents from the database.
111    */
112    private Query query;
113   
114    /**
115    * The query used to count the documents from the database.
116    */
117    private Query countQuery;
118   
119    /**
120    * The query filter used to count the documents from the database.
121    */
122    @Inject
123    @Named("count")
124    private QueryFilter countFilter;
125   
 
126  1451 toggle @Override
127    public boolean hasNext()
128    {
129  1451 return getResults().size() > index;
130    }
131   
 
132  486 toggle @Override
133    public Pair<DocumentReference, String> next()
134    {
135  486 Object[] result = getResults().get(index++);
136  486 String localSpaceReference = (String) result[0];
137  486 String name = (String) result[1];
138  486 String locale = (String) result[2];
139  486 String version = (String) result[3];
140  486 SpaceReference spaceReference =
141    new SpaceReference(this.explicitEntityReferenceResolver.resolve(localSpaceReference, EntityType.SPACE,
142    new WikiReference(wiki)));
143  486 DocumentReference documentReference = new DocumentReference(name, spaceReference);
144  486 if (!StringUtils.isEmpty(locale)) {
145  63 documentReference = new DocumentReference(documentReference, LocaleUtils.toLocale(locale));
146    }
147  486 return new ImmutablePair<DocumentReference, String>(documentReference, version);
148    }
149   
 
150  4 toggle @Override
151    public long size()
152    {
153  4 long size = 0;
154   
155  4 try {
156  4 getQuery();
157   
158  4 for (String wikiName : getWikis()) {
159  5 size += (long) countQuery.setWiki(wikiName).execute().get(0);
160    }
161    } catch (QueryException e) {
162  0 logger.error("Failed to count the documents.", e);
163    }
164   
165  4 return size;
166    }
167   
168    /**
169    * The current 'page' of results. If the current page has been fully iterated then a new page is fetched
170    * automatically.
171    *
172    * @return the current 'page' of results taken from the database
173    */
 
174  1937 toggle private List<Object[]> getResults()
175    {
176  1937 if (index >= results.size()) {
177  15 if (wiki == null) {
178  5 wiki = getNextWiki();
179    }
180  21 while (wiki != null) {
181  16 fetchNextResults();
182  16 if (results.size() > 0) {
183  10 break;
184    }
185  6 wiki = getNextWiki();
186  6 offset = 0;
187    }
188  15 index = 0;
189    }
190  1937 return results;
191    }
192   
193    /**
194    * Fetches the next 'page' of results from the database.
195    */
 
196  16 toggle private void fetchNextResults()
197    {
198  16 try {
199    // We use basic pagination (absolute offset) because we don't expect the database to change too much while
200    // the synchronization takes place. Also, the database is used as the reference store, meaning that we
201    // update the Solr index to match the database, not the other way around.
202  16 results = getQuery().setWiki(wiki).setOffset(offset).execute();
203  16 offset += LIMIT;
204    } catch (QueryException e) {
205  0 results = Collections.emptyList();
206  0 logger.error("Failed to query the database.", e);
207    }
208    }
209   
210    /**
211    * @return the query used to fetch the documents from the database
212    * @throws QueryException if creating the query fails
213    */
 
214  20 toggle private Query getQuery() throws QueryException
215    {
216  20 if (query == null) {
217    // This iterator must have the same order as the SolrDocumentIterator, otherwise the synchronization fails.
218  5 String select = "select doc.space, doc.name, doc.language, doc.version from XWikiDocument doc";
219  5 String orderBy = " order by doc.space, doc.name, doc.language";
220   
221  5 EntityReference spaceReference = null;
222  5 EntityReference documentReference = null;
223  5 if (rootReference != null) {
224  1 spaceReference = rootReference.extractReference(EntityType.SPACE);
225  1 documentReference = rootReference.extractReference(EntityType.DOCUMENT);
226    }
227   
228  5 String whereClause = "";
229  5 if (spaceReference != null) {
230  1 whereClause += " where doc.space = :space";
231  1 if (documentReference != null) {
232  1 whereClause += " and doc.name = :name";
233    }
234    }
235   
236  5 query = queryManager.createQuery(select + whereClause + orderBy, Query.HQL).setLimit(LIMIT);
237  5 countQuery = queryManager.createQuery(whereClause, Query.HQL).addFilter(countFilter);
238   
239  5 if (spaceReference != null) {
240  1 query.bindValue("space", this.localEntityReferenceSerializer.serialize(spaceReference));
241  1 if (documentReference != null) {
242  1 query.bindValue("name", documentReference.getName());
243    }
244    }
245   
246  5 for (Map.Entry<String, Object> parameter : query.getNamedParameters().entrySet()) {
247  2 countQuery.bindValue(parameter.getKey(), parameter.getValue());
248    }
249    }
250  20 return query;
251    }
252   
253    /**
254    * @return the next wiki, in alphabetical order
255    */
 
256  11 toggle private String getNextWiki()
257    {
258  11 if (wikisIterator == null) {
259  5 List<String> wikis = getWikis();
260  5 Collections.sort(wikis);
261  5 wikisIterator = wikis.iterator();
262    }
263  11 return wikisIterator.hasNext() ? wikisIterator.next() : null;
264    }
265   
266    /**
267    * If the root entity is not specified then all the available wikis are returned. Otherwise only the wiki
268    * corresponding to the root entity is returned.
269    *
270    * @return the list of wikis to iterate
271    */
 
272  9 toggle private List<String> getWikis()
273    {
274  9 if (rootReference == null) {
275  8 List<String> wikis;
276  8 try {
277  8 wikis = new ArrayList<String>(wikiDescriptorManager.getAllIds());
278    } catch (WikiManagerException e) {
279  0 logger.error("Failed to get the list of available wikis.", e);
280   
281  0 wikis = Collections.emptyList();
282    }
283   
284  8 return wikis;
285    } else {
286  1 return Arrays.asList(rootReference.extractReference(EntityType.WIKI).getName());
287    }
288    }
289    }