1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package com.xpn.xwiki.stats.impl.xwiki

File XWikiStatsReader.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart0.png
83% of files have more coverage

Code metrics

42
181
15
1
591
327
47
0.26
12.07
15
3.13

Classes

Class Line # Actions
XWikiStatsReader 63 181 0% 47 238
0.00%
 

Contributing tests

No tests hitting this source file were found.

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 com.xpn.xwiki.stats.impl.xwiki;
21   
22    import java.text.MessageFormat;
23    import java.util.ArrayList;
24    import java.util.Collection;
25    import java.util.Collections;
26    import java.util.Date;
27    import java.util.HashMap;
28    import java.util.List;
29    import java.util.Map;
30   
31    import org.apache.commons.collections4.CollectionUtils;
32    import org.apache.commons.lang3.StringUtils;
33    import org.joda.time.DateTime;
34    import org.slf4j.Logger;
35    import org.slf4j.LoggerFactory;
36    import org.xwiki.model.reference.DocumentReference;
37    import org.xwiki.model.reference.EntityReferenceSerializer;
38   
39    import com.xpn.xwiki.XWikiContext;
40    import com.xpn.xwiki.XWikiException;
41    import com.xpn.xwiki.criteria.impl.Duration;
42    import com.xpn.xwiki.criteria.impl.Period;
43    import com.xpn.xwiki.criteria.impl.Range;
44    import com.xpn.xwiki.criteria.impl.RangeFactory;
45    import com.xpn.xwiki.criteria.impl.Scope;
46    import com.xpn.xwiki.stats.impl.DocumentStats;
47    import com.xpn.xwiki.stats.impl.RefererStats;
48    import com.xpn.xwiki.stats.impl.StatsUtil;
49    import com.xpn.xwiki.stats.impl.StatsUtil.PeriodType;
50    import com.xpn.xwiki.stats.impl.VisitStats;
51    import com.xpn.xwiki.store.XWikiHibernateStore;
52    import com.xpn.xwiki.web.DownloadAction;
53    import com.xpn.xwiki.web.SaveAction;
54    import com.xpn.xwiki.web.Utils;
55    import com.xpn.xwiki.web.ViewAction;
56   
57    /**
58    * Reader statistics from XWiki database.
59    *
60    * @version $Id: 67b9a5d052e36e421e8e99cc262b5a1cb9a885ed $
61    * @since 1.4M2
62    */
 
63    public class XWikiStatsReader
64    {
65    /**
66    * Logging tool.
67    */
68    private static final Logger LOGGER = LoggerFactory.getLogger(XWikiStatsReader.class);
69   
70    /**
71    * Used to convert a proper Document Reference to a string but without the wiki name.
72    */
73    private EntityReferenceSerializer<String> compactwikiEntityReferenceSerializer = Utils.getComponent(
74    EntityReferenceSerializer.TYPE_STRING, "compactwiki");
75   
76    /**
77    * Return the statistics action stored.
78    *
79    * @param action the action.
80    * @param size the maximum size of the list to return.
81    * @param context the XWiki context.
82    * @return the list of recent statistics action stored.
83    */
 
84  0 toggle public Collection<Object> getRecentActions(String action, int size, XWikiContext context)
85    {
86  0 List<Object> list = new ArrayList<Object>();
87   
88  0 if ((action.equals(ViewAction.VIEW_ACTION) || (action.equals(SaveAction.ACTION_NAME)))) {
89  0 Collection<?> actions = StatsUtil.getRecentActionFromSessions(context, action);
90   
91  0 if (actions != null) {
92  0 Object[] actionsarray = actions.toArray();
93  0 CollectionUtils.reverseArray(actionsarray);
94  0 int nb = Math.min(actions.size(), size);
95  0 for (int i = 0; i < nb; i++) {
96  0 list.add(actionsarray[i]);
97    }
98    }
99    }
100   
101  0 return list;
102    }
103   
104    /**
105    * @param range the range.
106    * @return the corresponding sort order.
107    */
 
108  0 toggle private String getHqlSortOrderFromRange(Range range)
109    {
110  0 String sortOrder;
111   
112  0 if (range.getSize() < 0) {
113  0 sortOrder = "asc";
114    } else {
115  0 sortOrder = "desc";
116    }
117   
118  0 return sortOrder;
119    }
120   
121    /**
122    * @param domain the provided domain.
123    * @return the domain to use in HQL query.
124    */
 
125  0 toggle private String getHqlValidDomain(String domain)
126    {
127  0 if (domain == null || domain.trim().length() == 0) {
128  0 return "%";
129    }
130   
131  0 return domain;
132    }
133   
134    /**
135    * @param scope the set of documents for which to retrieve statistics.
136    * @param paramList the values to insert in the SQL query.
137    * @return the name filter HQL query part.
138    */
 
139  0 toggle private String getHqlNameFilterFromScope(Scope scope, List<Object> paramList)
140    {
141  0 String nameFilter;
142   
143    // Note that these queries are made to work with Oracle which treats empty strings as null.
144  0 if (scope.getType() == Scope.SPACE_SCOPE && StringUtils.isEmpty(scope.getName())) {
145    // Select all names that are not page names (i.e. they are space names), excluding empty names which
146    // represent global scopes.
147  0 nameFilter = "name not like '%.%' and (name <> '' or (name is not null and '' is null))";
148  0 } else if (scope.getType() == Scope.GLOBAL_SCOPE && StringUtils.isEmpty(scope.getName())) {
149    // Select all names that are empty (i.e. global)
150  0 nameFilter = "name = '' or name is null";
151  0 } else if (scope.getType() == Scope.PAGE_SCOPE && StringUtils.isEmpty(scope.getName())) {
152    // Select all names that are page names
153  0 nameFilter = "name like '%.%'";
154    } else {
155  0 nameFilter = "name like ?";
156  0 paramList.add(scope.getPattern());
157    }
158   
159  0 return nameFilter;
160    }
161   
162    /**
163    * Shows how the statistics for the specified action have evolved over the specified period of time.
164    *
165    * @param action the action for which to retrieve statistics.
166    * @param scope the set of documents to consider.
167    * @param period the period of time, including its start date but excluding its end date.
168    * @param step the step used for sampling the period.
169    * @param context the XWiki context.
170    * @return a map of (date, actionCount) pairs.
171    */
 
172  0 toggle public Map<DateTime, Integer> getActionStatistics(String action, Scope scope, Period period, Duration step,
173    XWikiContext context)
174    {
175  0 DateTime stepStart = new DateTime(period.getStart());
176  0 DateTime periodEnd = new DateTime(period.getEnd());
177  0 org.joda.time.Period stepDuration =
178    new org.joda.time.Period(step.getYears(), step.getMonths(), step.getWeeks(), step.getDays(), 0, 0, 0, 0);
179   
180  0 Map<DateTime, Integer> activity = new HashMap<DateTime, Integer>();
181  0 while (stepStart.compareTo(periodEnd) < 0) {
182  0 DateTime stepEnd = stepStart.plus(stepDuration);
183  0 if (stepEnd.compareTo(periodEnd) > 0) {
184  0 stepEnd = periodEnd;
185    }
186  0 List<DocumentStats> stats =
187    getDocumentStatistics(action, scope, new Period(stepStart.getMillis(), stepEnd.getMillis()),
188    RangeFactory.FIRST, context);
189  0 int actionCount = 0;
190  0 if (stats.size() > 0) {
191  0 actionCount = stats.get(0).getPageViews();
192    }
193  0 activity.put(stepStart, actionCount);
194  0 stepStart = stepEnd;
195    }
196   
197  0 return activity;
198    }
199   
200    /**
201    * Retrieves document statistics.
202    *
203    * @param action the action the results should be ordered by. It can be one of: "view", "save" or "download". If the
204    * action is "view" then the documents are ordered by the number of times they have been viewed so far.
205    * @param scope the set of documents for which to retrieve statistics.
206    * @param period the period of time, including its start date but excluding its end date.
207    * @param range the sub-range to return from the entire result set. Use this parameter for pagination.
208    * @param context the XWiki context.
209    * @return A list of DocumentStats objects
210    */
 
211  0 toggle public List<DocumentStats> getDocumentStatistics(String action, Scope scope, Period period, Range range,
212    XWikiContext context)
213    {
214  0 List<DocumentStats> documentStatsList;
215   
216  0 List<Object> paramList = new ArrayList<Object>(4);
217   
218  0 String nameFilter = getHqlNameFilterFromScope(scope, paramList);
219   
220  0 String sortOrder = getHqlSortOrderFromRange(range);
221   
222  0 XWikiHibernateStore store = context.getWiki().getHibernateStore();
223   
224  0 try {
225  0 String query =
226    MessageFormat.format("select name, sum(pageViews) from DocumentStats"
227    + " where ({0}) and action=? and ? <= period and period < ? group by name order"
228    + " by sum(pageViews) {1}", nameFilter, sortOrder);
229   
230  0 paramList.add(action);
231  0 paramList.add(period.getStartCode());
232  0 paramList.add(period.getEndCode());
233   
234  0 List<?> solist =
235    store.search(query, range.getAbsoluteSize(), range.getAbsoluteStart(), paramList, context);
236   
237  0 documentStatsList = getDocumentStatistics(solist, action);
238  0 if (range.getSize() < 0) {
239  0 Collections.reverse(documentStatsList);
240    }
241    } catch (XWikiException e) {
242  0 documentStatsList = Collections.emptyList();
243    }
244   
245  0 return documentStatsList;
246    }
247   
248    /**
249    * Converts the rows retrieved from the database to a list of DocumentStats instances.
250    *
251    * @param resultSet the result of a database query for document statistics.
252    * @param action the action for which the statistics were retrieved.
253    * @return a list of {@link com.xpn.xwiki.stats.impl.DocumentStats} objects.
254    * @see #getDocumentStatistics(String, Scope, com.xpn.xwiki.criteria.impl.Period , Range , XWikiContext)
255    */
 
256  0 toggle private List<DocumentStats> getDocumentStatistics(List<?> resultSet, String action)
257    {
258  0 List<DocumentStats> documentStatsList = new ArrayList<DocumentStats>(resultSet.size());
259   
260  0 Date now = new Date();
261   
262  0 for (Object name : resultSet) {
263  0 Object[] result = (Object[]) name;
264    // We can't represent a custom period (e.g. year, week or some time interval) in the
265    // database and thus we use a default one, which sould be ignored
266  0 DocumentStats docStats = new DocumentStats((String) result[0], action, now, PeriodType.DAY);
267  0 docStats.setPageViews(((Number) result[1]).intValue());
268  0 documentStatsList.add(docStats);
269    }
270   
271  0 return documentStatsList;
272    }
273   
274    /**
275    * Retrieves back-link statistics.
276    *
277    * @param domain the domain used for filtering the results.
278    * @param scope the scope of referred documents for which to retrieve statistics.
279    * @param period the period of time, including its start date but excluding its end date.
280    * @param range the sub-range to return from the entire result set. Use this parameter for pagination.
281    * @param context the XWiki context.
282    * @return a list of DocumentStats objects.
283    */
 
284  0 toggle public List<DocumentStats> getBackLinkStatistics(String domain, Scope scope, Period period, Range range,
285    XWikiContext context)
286    {
287  0 List<DocumentStats> documentStatsList;
288   
289  0 List<Object> paramList = new ArrayList<Object>(4);
290   
291  0 String nameFilter = getHqlNameFilterFromScope(scope, paramList);
292   
293  0 String sortOrder = getHqlSortOrderFromRange(range);
294   
295  0 XWikiHibernateStore store = context.getWiki().getHibernateStore();
296  0 try {
297  0 String query =
298    MessageFormat.format("select name, sum(pageViews) from RefererStats"
299    + " where ({0}) and referer like ? and ? <= period and period < ? group by name"
300    + " order by sum(pageViews) {1}", nameFilter, sortOrder);
301   
302  0 paramList.add(getHqlValidDomain(domain));
303  0 paramList.add(period.getStartCode());
304  0 paramList.add(period.getEndCode());
305   
306  0 List<?> solist =
307    store.search(query, range.getAbsoluteSize(), range.getAbsoluteStart(), paramList, context);
308   
309  0 documentStatsList = getDocumentStatistics(solist, "refer");
310  0 if (range.getSize() < 0) {
311  0 Collections.reverse(documentStatsList);
312    }
313    } catch (XWikiException e) {
314  0 documentStatsList = Collections.emptyList();
315    }
316   
317  0 return documentStatsList;
318    }
319   
320    /**
321    * Retrieves referrer statistics.
322    *
323    * @param domain the domain for which to retrieve statistics. To retrieve statistics for all domains use the empty
324    * string.
325    * @param scope the scope of referred documents to use for filtering the results.
326    * @param period the period of time, including its start date but excluding its end date.
327    * @param range the sub-range to return from the entire result set. Use this parameter for pagination.
328    * @param context the XWiki context.
329    * @return a list of RefererStats objects.
330    */
 
331  0 toggle public List<RefererStats> getRefererStatistics(String domain, Scope scope, Period period, Range range,
332    XWikiContext context)
333    {
334  0 List<RefererStats> refererList;
335   
336  0 List<Object> paramList = new ArrayList<Object>(4);
337   
338  0 String nameFilter = getHqlNameFilterFromScope(scope, paramList);
339   
340  0 String sortOrder = getHqlSortOrderFromRange(range);
341   
342  0 XWikiHibernateStore store = context.getWiki().getHibernateStore();
343  0 try {
344  0 String query =
345    MessageFormat.format("select referer, sum(pageViews) from RefererStats"
346    + " where ({0}) and referer like ? and ? <= period and period < ?"
347    + " group by referer order by sum(pageViews) {1}", nameFilter, sortOrder);
348   
349  0 paramList.add(getHqlValidDomain(domain));
350  0 paramList.add(period.getStartCode());
351  0 paramList.add(period.getEndCode());
352   
353  0 List<?> solist =
354    store.search(query, range.getAbsoluteSize(), range.getAbsoluteStart(), paramList, context);
355   
356  0 refererList = getRefererStatistics(solist);
357  0 if (range.getSize() < 0) {
358  0 Collections.reverse(refererList);
359    }
360    } catch (XWikiException e) {
361  0 refererList = Collections.emptyList();
362    }
363   
364  0 return refererList;
365    }
366   
367    /**
368    * Converts the rows retrieved from the database to a list of {@link com.xpn.xwiki.stats.impl.RefererStats}
369    * instances.
370    *
371    * @param resultSet The result of a database query for referer statistics
372    * @return A list of {@link com.xpn.xwiki.stats.impl.RefererStats} objects
373    * @see #getRefererStatistics(String, Scope, Period, Range , XWikiContext)
374    */
 
375  0 toggle private List<RefererStats> getRefererStatistics(List<?> resultSet)
376    {
377  0 Date now = new Date();
378  0 List<RefererStats> stats = new ArrayList<RefererStats>(resultSet.size());
379   
380  0 for (Object name : resultSet) {
381  0 Object[] result = (Object[]) name;
382   
383    // We can't represent a custom period (e.g. year, week or some time interval) in the
384    // database and thus we use a default one, which sould be ignored
385  0 RefererStats refStats = new RefererStats("", (String) result[0], now, PeriodType.DAY);
386  0 refStats.setPageViews(((Number) result[1]).intValue());
387  0 stats.add(refStats);
388    }
389   
390  0 return stats;
391    }
392   
393    /**
394    * Generate the HQL query used in {@link #getVisitStatistics(String, Period, Range, XWikiContext)}.
395    *
396    * @param action the action the results should be ordered by. It can be one of: "view", "save" or "download". If the
397    * action is "view" then the visitors are ordered by the number of pages they have viewed so far.
398    * @param period the period of time, including its start date but excluding its end date.
399    * @param range the sub-range to return from the entire result set. Use this parameter for pagination.
400    * @param paramList the list of query parameters to fill.
401    * @param context the XWiki context.
402    * @return the HQL query
403    */
 
404  0 toggle private String createVisitStatisticsQuery(String action, Period period, Range range, List<Object> paramList,
405    XWikiContext context)
406    {
407  0 StringBuilder query = new StringBuilder("select name, sum(pageSaves), sum(pageViews), sum(downloads)");
408   
409  0 query.append(" from VisitStats");
410   
411  0 query.append(" where");
412   
413    // user filter
414   
415  0 StringBuilder userListWhere = new StringBuilder();
416  0 try {
417  0 for (DocumentReference user : StatsUtil.getRequestFilteredUsers(context)) {
418  0 if (userListWhere.length() > 0) {
419  0 userListWhere.append(", ");
420    }
421  0 userListWhere.append('?');
422   
423  0 paramList.add(this.compactwikiEntityReferenceSerializer.serialize(user));
424    }
425    } catch (Exception e) {
426  0 LOGGER.error("Faild to get filter users list", e);
427    }
428   
429  0 if (userListWhere.length() > 0) {
430  0 query.append(" name NOT IN (");
431  0 query.append(userListWhere);
432  0 query.append(") and ");
433    }
434   
435    // date filter
436   
437  0 query.append(" ? <= startDate and endDate < ? group by name");
438  0 paramList.add(new Date(period.getStart()));
439  0 paramList.add(new Date(period.getEnd()));
440   
441    // order
442   
443  0 query.append(' ');
444   
445  0 String sortOrder = getHqlSortOrderFromRange(range);
446   
447  0 if (action.equals(SaveAction.ACTION_NAME)) {
448  0 query.append("order by sum(pageSaves) " + sortOrder);
449  0 } else if (action.equals(ViewAction.VIEW_ACTION)) {
450  0 query.append("order by sum(pageViews) " + sortOrder);
451  0 } else if (action.equals(DownloadAction.ACTION_NAME)) {
452  0 query.append("order by sum(downloads) " + sortOrder);
453    } else {
454  0 query.append(MessageFormat.format("order by sum(pageSaves) {0}, sum(pageViews) {0}, sum(downloads) {0}",
455    sortOrder));
456    }
457   
458  0 return query.toString();
459    }
460   
461    /**
462    * Retrieves visit statistics.
463    *
464    * @param action the action the results should be ordered by. It can be one of: "view", "save" or "download". If the
465    * action is "view" then the visitors are ordered by the number of pages they have viewed so far.
466    * @param period the period of time, including its start date but excluding its end date.
467    * @param range the sub-range to return from the entire result set. Use this parameter for pagination.
468    * @param context the XWiki context.
469    * @return a list of VisitStats objects.
470    */
 
471  0 toggle public List<VisitStats> getVisitStatistics(String action, Period period, Range range, XWikiContext context)
472    {
473  0 List<VisitStats> visiStatList;
474   
475  0 List<Object> paramList = new ArrayList<Object>(2);
476   
477  0 String query = createVisitStatisticsQuery(action, period, range, paramList, context);
478   
479  0 XWikiHibernateStore store = context.getWiki().getHibernateStore();
480  0 try {
481  0 List<?> solist =
482    store.search(query, range.getAbsoluteSize(), range.getAbsoluteStart(), paramList, context);
483   
484  0 visiStatList = getVisitStatistics(solist, new DateTime(period.getStart()), new DateTime(period.getEnd()));
485  0 if (range.getSize() < 0) {
486  0 Collections.reverse(visiStatList);
487    }
488    } catch (XWikiException e) {
489  0 LOGGER.error("Faild to search for vist statistics", e);
490   
491  0 visiStatList = Collections.emptyList();
492    }
493   
494  0 return visiStatList;
495    }
496   
497    /**
498    * Converts the rows retrieved from the database to a list of VisitStats instances.
499    *
500    * @param resultSet the result of a database query for visitor statistics.
501    * @param startDate the start date used in the query.
502    * @param endDate the end date used in the query.
503    * @return a list of {@link com.xpn.xwiki.stats.impl.VisitStats} objects.
504    * @see #getVisitStatistics(String, Period, Range, XWikiContext)
505    */
 
506  0 toggle private List<VisitStats> getVisitStatistics(List<?> resultSet, DateTime startDate, DateTime endDate)
507    {
508  0 List<VisitStats> stats = new ArrayList<VisitStats>(resultSet.size());
509   
510  0 for (Object name2 : resultSet) {
511  0 Object[] result = (Object[]) name2;
512   
513  0 String name = (String) result[0];
514  0 String uniqueID = "";
515  0 String cookie = "";
516  0 String ip = "";
517  0 String userAgent = "";
518  0 int pageSaves = ((Number) result[1]).intValue();
519  0 int pageViews = ((Number) result[2]).intValue();
520  0 int downloads = ((Number) result[3]).intValue();
521   
522  0 VisitStats vs =
523    new VisitStats(name, uniqueID, cookie, ip, userAgent, new Date(startDate.getMillis()), PeriodType.DAY);
524  0 vs.setStartDate(new Date(startDate.getMillis()));
525  0 vs.setEndDate(new Date(endDate.getMillis()));
526  0 vs.setPageSaves(pageSaves);
527  0 vs.setPageViews(pageViews);
528  0 vs.setDownloads(downloads);
529   
530  0 stats.add(vs);
531    }
532   
533  0 return stats;
534    }
535   
536    // ////////////////////////////////////////////////////////////////////////////////////////
537    // Deprecated methods
538    // ////////////////////////////////////////////////////////////////////////////////////////
539   
540    /**
541    * Gets monthly statistics on a document for a specific action.
542    *
543    * @param docname fully qualified document name.
544    * @param action can be "view", "edit", "save", etc..
545    * @param month the month.
546    * @param context the XWiki context.
547    * @return DocumentStats - statistics object.
548    * @deprecated use {@link #getDocumentStatistics(String, Scope, Period, Range , XWikiContext)} instead.
549    */
 
550  0 toggle @Deprecated
551    public DocumentStats getDocMonthStats(String docname, String action, Date month, XWikiContext context)
552    {
553  0 XWikiHibernateStore store = context.getWiki().getHibernateStore();
554  0 DocumentStats object = new DocumentStats(docname, action, month, PeriodType.MONTH);
555  0 try {
556    // TODO Fix use of deprecated call.
557  0 store.loadXWikiCollection(object, context, true);
558  0 return object;
559    } catch (XWikiException e) {
560  0 e.printStackTrace();
561  0 return new DocumentStats();
562    }
563    }
564   
565    /**
566    * Gets monthly referer statistics.
567    *
568    * @param docName fully qualified document name.
569    * @param month the month.
570    * @param context the XWiki context.
571    * @return the monthly referer statistics.
572    * @throws XWikiException error when searching for referer statistics.
573    * @deprecated use {@link #getRefererStatistics(String, Scope, Period, Range, XWikiContext)} instead.
574    */
 
575  0 toggle @Deprecated
576    public List<?> getRefMonthStats(String docName, Date month, XWikiContext context) throws XWikiException
577    {
578  0 XWikiHibernateStore store = context.getWiki().getHibernateStore();
579   
580  0 List<?> solist;
581  0 if (store != null) {
582  0 List<Object> paramList = new ArrayList<Object>(1);
583  0 paramList.add(docName);
584  0 solist = store.search("from RefererStats as obj where obj.name=?", 0, 0, paramList, context);
585    } else {
586  0 solist = Collections.emptyList();
587    }
588   
589  0 return solist;
590    }
591    }