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

File DefaultExtensionJobHistory.java

 

Coverage histogram

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

Code metrics

14
44
10
2
198
146
23
0.52
4.4
5
2.3

Classes

Class Line # Actions
DefaultExtensionJobHistory 61 33 0% 19 9
0.826923182.7%
DefaultExtensionJobHistory.SaveRunnable 63 11 0% 4 3
0.812581.2%
 

Contributing tests

This file is covered by 128 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.job.history.internal;
21   
22    import java.io.File;
23    import java.io.FileFilter;
24    import java.io.IOException;
25    import java.text.SimpleDateFormat;
26    import java.util.ArrayList;
27    import java.util.Arrays;
28    import java.util.Collections;
29    import java.util.Date;
30    import java.util.Deque;
31    import java.util.Iterator;
32    import java.util.List;
33    import java.util.concurrent.BlockingQueue;
34    import java.util.concurrent.ConcurrentLinkedDeque;
35    import java.util.concurrent.LinkedBlockingQueue;
36   
37    import javax.inject.Inject;
38    import javax.inject.Singleton;
39   
40    import org.apache.commons.collections4.Predicate;
41    import org.apache.commons.lang3.exception.ExceptionUtils;
42    import org.slf4j.Logger;
43    import org.xwiki.component.annotation.Component;
44    import org.xwiki.component.manager.ComponentLifecycleException;
45    import org.xwiki.component.phase.Disposable;
46    import org.xwiki.component.phase.Initializable;
47    import org.xwiki.component.phase.InitializationException;
48    import org.xwiki.extension.job.history.ExtensionJobHistory;
49    import org.xwiki.extension.job.history.ExtensionJobHistoryConfiguration;
50    import org.xwiki.extension.job.history.ExtensionJobHistoryRecord;
51    import org.xwiki.extension.job.history.ExtensionJobHistorySerializer;
52   
53    /**
54    * Default {@link ExtensionJobHistory} implementation.
55    *
56    * @version $Id: dd960cf1519238c7d66ac9ce1a6bea3233c53978 $
57    * @since 7.1RC1
58    */
59    @Component
60    @Singleton
 
61    public class DefaultExtensionJobHistory implements ExtensionJobHistory, Initializable, Disposable
62    {
 
63    private class SaveRunnable implements Runnable
64    {
 
65  298 toggle @Override
66    public void run()
67    {
68  298 logger.debug("Start extension job history saving thread.");
69   
70  369 while (!Thread.interrupted()) {
71  369 ExtensionJobHistoryRecord record;
72  369 try {
73  369 record = saveQueue.take();
74    } catch (InterruptedException e) {
75  0 logger.warn("Extension job history saving thread has been interrupted. Root cause [{}].",
76    ExceptionUtils.getRootCauseMessage(e));
77  0 record = SAVE_QUEUE_END;
78    }
79   
80  366 if (record == SAVE_QUEUE_END) {
81  295 break;
82    } else {
83  71 save(record);
84    }
85    }
86   
87  295 logger.debug("Stop extension job history saving thread.");
88    }
89    }
90   
91    private static final SimpleDateFormat FILE_NAME_FORMAT = new SimpleDateFormat("yyyy.MM.dd.'xml'");
92   
93    private static final ExtensionJobHistoryRecord SAVE_QUEUE_END = new ExtensionJobHistoryRecord("SAVE_QUEUE_END",
94    null, null, null, null);
95   
96    @Inject
97    private Logger logger;
98   
99    @Inject
100    private ExtensionJobHistoryConfiguration config;
101   
102    @Inject
103    private ExtensionJobHistorySerializer serializer;
104   
105    private final Deque<ExtensionJobHistoryRecord> records = new ConcurrentLinkedDeque<>();
106   
107    private BlockingQueue<ExtensionJobHistoryRecord> saveQueue = new LinkedBlockingQueue<>();
108   
 
109  299 toggle @Override
110    public void initialize() throws InitializationException
111    {
112  299 load();
113   
114  298 Thread saveThread = new Thread(new SaveRunnable());
115  298 saveThread.setName("XWiki's extension job history saving thread");
116  298 saveThread.setDaemon(true);
117  298 saveThread.setPriority(Thread.MIN_PRIORITY);
118  298 saveThread.start();
119    }
120   
 
121  295 toggle @Override
122    public void dispose() throws ComponentLifecycleException
123    {
124    // Stop the history saving thread by sending the stop signal.
125  295 this.saveQueue.offer(SAVE_QUEUE_END);
126    }
127   
 
128  71 toggle @Override
129    public void addRecord(ExtensionJobHistoryRecord record)
130    {
131  71 this.records.offerFirst(record);
132  71 this.saveQueue.offer(record);
133    }
134   
 
135  16 toggle @Override
136    public List<ExtensionJobHistoryRecord> getRecords(Predicate<ExtensionJobHistoryRecord> filter,
137    String offsetRecordId, int limit)
138    {
139  16 Iterator<ExtensionJobHistoryRecord> iterator = this.records.iterator();
140  16 if (offsetRecordId != null) {
141  1 while (iterator.hasNext() && !offsetRecordId.equals(iterator.next().getId())) {
142    // Do nothing.
143    }
144    }
145   
146  16 List<ExtensionJobHistoryRecord> page = new ArrayList<>();
147  78 while (iterator.hasNext() && (limit < 0 || page.size() < limit)) {
148  62 ExtensionJobHistoryRecord record = iterator.next();
149  62 if (filter.evaluate(record)) {
150  5 page.add(record);
151    }
152    }
153   
154  16 return page;
155    }
156   
 
157  71 toggle private void save(ExtensionJobHistoryRecord record)
158    {
159  71 try {
160  71 this.serializer.append(record, new File(this.config.getStorage(), getFileName()));
161    } catch (IOException e) {
162  0 this.logger.error("Failed to save extension job history.", e);
163    }
164    }
165   
 
166  71 toggle private String getFileName()
167    {
168  71 return FILE_NAME_FORMAT.format(new Date());
169    }
170   
 
171  299 toggle private void load()
172    {
173  299 for (File historyFile : getHistoryFiles()) {
174  0 try {
175  0 List<ExtensionJobHistoryRecord> storedRecords = this.serializer.read(historyFile);
176  0 Collections.reverse(storedRecords);
177  0 this.records.addAll(storedRecords);
178    } catch (Exception e) {
179  0 this.logger.error("Failed to read extension job history from [{}].", historyFile.getAbsolutePath(), e);
180    }
181    }
182    }
183   
 
184  299 toggle private List<File> getHistoryFiles()
185    {
186  299 File[] files = this.config.getStorage().listFiles(new FileFilter()
187    {
 
188  0 toggle @Override
189    public boolean accept(File file)
190    {
191  0 return file.isFile() && file.getName().endsWith(".xml");
192    }
193    });
194  298 List<File> fileList = files != null ? Arrays.asList(files) : Collections.<File>emptyList();
195  298 Collections.sort(fileList, Collections.reverseOrder());
196  298 return fileList;
197    }
198    }