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

File AbstractJobStatus.java

 

Coverage histogram

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

Code metrics

16
57
22
1
354
185
30
0.53
2.59
22
1.36

Classes

Class Line # Actions
AbstractJobStatus 47 57 0% 30 4
0.9578947495.8%
 

Contributing tests

This file is covered by 177 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.job;
21   
22    import java.util.Date;
23    import java.util.List;
24    import java.util.concurrent.locks.Condition;
25    import java.util.concurrent.locks.ReentrantLock;
26   
27    import org.xwiki.job.event.status.JobProgress;
28    import org.xwiki.job.event.status.JobStatus;
29    import org.xwiki.job.event.status.QuestionAnsweredEvent;
30    import org.xwiki.job.event.status.QuestionAskedEvent;
31    import org.xwiki.job.internal.DefaultJobProgress;
32    import org.xwiki.logging.LogLevel;
33    import org.xwiki.logging.LogQueue;
34    import org.xwiki.logging.LoggerManager;
35    import org.xwiki.logging.event.LogEvent;
36    import org.xwiki.logging.event.LoggerListener;
37    import org.xwiki.observation.ObservationManager;
38    import org.xwiki.observation.WrappedThreadEventListener;
39   
40    /**
41    * Base implementation of {@link JobStatus}.
42    *
43    * @param <R> the request type associated to the job
44    * @version $Id: 68a4db359c40439226444a4fd01891ce7aa42e91 $
45    * @since 7.4M1
46    */
 
47    public abstract class AbstractJobStatus<R extends Request> implements JobStatus
48    {
49    /**
50    * Used register itself to receive logging and progress related events.
51    */
52    private final transient ObservationManager observationManager;
53   
54    /**
55    * Used to isolate job related log.
56    */
57    private final transient LoggerManager loggerManager;
58   
59    /**
60    * Used to lock #ask().
61    */
62    private final transient ReentrantLock askLock = new ReentrantLock();
63   
64    /**
65    * Condition for waiting answer.
66    */
67    private final transient Condition answered = this.askLock.newCondition();
68   
69    /**
70    * The status of the parent job, i.e. the status of the job that started this one. This is {@code null} if the job
71    * has no parent, i.e. if the job hasn't been started by another job.
72    * <p>
73    * We don't want to serialize it.
74    */
75    private final transient JobStatus parentJobStatus;
76   
77    /**
78    * Take care of progress related events to produce a progression information usually used in a progress bar.
79    */
80    private final DefaultJobProgress progress = new DefaultJobProgress();
81   
82    /**
83    * Log sent during job execution.
84    */
85    private final LogQueue logs;
86   
87    /**
88    * Used to listen to all the log produced during job execution.
89    */
90    private transient LoggerListener logListener;
91   
92    /**
93    * The question.
94    */
95    private transient volatile Object question;
96   
97    /**
98    * General state of the job.
99    */
100    private State state = State.NONE;
101   
102    /**
103    * @see #getError()
104    */
105    private Throwable error;
106   
107    /**
108    * Request provided when starting the job.
109    */
110    private R request;
111   
112    /**
113    * @see #getStartDate()
114    */
115    private Date startDate;
116   
117    /**
118    * @see #getEndDate()
119    */
120    private Date endDate;
121   
122    /**
123    * Indicate if Job log should be grabbed.
124    */
125    private boolean isolated = true;
126   
127    /**
128    * @param request the request provided when started the job
129    * @param parentJobStatus the status of the parent job (i.e. the status of the job that started this one); pass
130    * {@code null} if this job hasn't been started by another job (i.e. if this is not a sub-job)
131    * @param observationManager the observation manager component
132    * @param loggerManager the logger manager component
133    */
 
134  656 toggle public AbstractJobStatus(R request, JobStatus parentJobStatus, ObservationManager observationManager,
135    LoggerManager loggerManager)
136    {
137  656 this.request = request;
138  656 this.parentJobStatus = parentJobStatus;
139   
140  656 this.isolated = parentJobStatus == null;
141   
142  656 this.observationManager = observationManager;
143  656 this.loggerManager = loggerManager;
144   
145  656 this.logs = new LogQueue();
146    }
147   
148    /**
149    * Start listening to events.
150    */
 
151  579 toggle public void startListening()
152    {
153    // Register progress listener
154  579 this.observationManager.addListener(new WrappedThreadEventListener(this.progress));
155   
156    // Isolate log for the job status
157  579 this.logListener = new LoggerListener(LoggerListener.class.getName() + '_' + hashCode(), this.logs);
158  579 if (isIsolated()) {
159  322 this.loggerManager.pushLogListener(this.logListener);
160    } else {
161  257 this.observationManager.addListener(new WrappedThreadEventListener(this.logListener));
162    }
163    }
164   
165    /**
166    * Stop listening to events.
167    */
 
168  579 toggle public void stopListening()
169    {
170  579 if (isIsolated()) {
171  322 this.loggerManager.popLogListener();
172    } else {
173  257 this.observationManager.removeListener(this.logListener.getName());
174    }
175  579 this.observationManager.removeListener(this.progress.getName());
176   
177    // Make sure the progress is closed
178  579 this.progress.getRootStep().finish();
179    }
180   
181    // JobStatus
182   
 
183  1263 toggle @Override
184    public State getState()
185    {
186  1263 return this.state;
187    }
188   
189    /**
190    * @param state the general state of the job
191    */
 
192  1158 toggle public void setState(State state)
193    {
194  1158 this.state = state;
195    }
196   
 
197  288 toggle @Override
198    public Throwable getError()
199    {
200  288 return this.error;
201    }
202   
203    /**
204    * @param error the {@link Throwable} on which the job stopped
205    * @since 8.1RC1
206    */
 
207  579 toggle public void setError(Throwable error)
208    {
209  579 this.error = error;
210    }
211   
 
212  2756 toggle @Override
213    public R getRequest()
214    {
215  2756 return this.request;
216    }
217   
 
218  520 toggle @Override
219    public LogQueue getLog()
220    {
221    // Make sure to always return something (it could be null if unserialized as such)
222  520 return this.logs != null ? this.logs : new LogQueue();
223    }
224   
 
225  133 toggle @Override
226    public JobProgress getProgress()
227    {
228  133 return this.progress;
229    }
230   
 
231  6 toggle @Override
232    public void ask(Object question) throws InterruptedException
233    {
234  6 this.question = question;
235   
236  6 this.askLock.lockInterruptibly();
237   
238  6 try {
239    // Wait for the answer
240  6 this.state = State.WAITING;
241  6 if (isSubJob()) {
242  1 this.parentJobStatus.ask(question);
243    } else {
244  5 String questionType = question != null ? question.getClass().getName() : null;
245  5 QuestionAskedEvent event = new QuestionAskedEvent(questionType, this.request.getId());
246  5 this.observationManager.notify(event, this);
247  5 if (event.isAnswered()) {
248  1 answered();
249    } else {
250  4 this.answered.await();
251    }
252    }
253  6 this.state = State.RUNNING;
254    } finally {
255  6 this.askLock.unlock();
256    }
257    }
258   
 
259  20 toggle @Override
260    public Object getQuestion()
261    {
262  20 return this.question;
263    }
264   
 
265  7 toggle @Override
266    public void answered()
267    {
268  7 this.askLock.lock();
269   
270  7 try {
271  7 if (isSubJob()) {
272  1 this.question = null;
273  1 this.parentJobStatus.answered();
274    } else {
275  6 String questionType = this.question != null ? this.question.getClass().getName() : null;
276  6 this.observationManager.notify(new QuestionAnsweredEvent(questionType, this.request.getId()), this);
277  6 this.question = null;
278  6 this.answered.signal();
279    }
280    } finally {
281  7 this.askLock.unlock();
282    }
283    }
284   
 
285  574 toggle @Override
286    public Date getStartDate()
287    {
288  574 return this.startDate;
289    }
290   
291    /**
292    * @param startDate the date and time when the job has been started
293    */
 
294  579 toggle public void setStartDate(Date startDate)
295    {
296  579 this.startDate = startDate;
297    }
298   
 
299  62 toggle @Override
300    public Date getEndDate()
301    {
302  62 return this.endDate;
303    }
304   
305    /**
306    * @param endDate the date and time when the job finished
307    */
 
308  579 toggle public void setEndDate(Date endDate)
309    {
310  579 this.endDate = endDate;
311    }
312   
313    /**
314    * @return true if the job is part of another job execution
315    */
 
316  13 toggle public boolean isSubJob()
317    {
318  13 return getParentJobStatus() != null;
319    }
320   
321    /**
322    * @return true if the job log should be grabbed
323    */
 
324  1158 toggle public boolean isIsolated()
325    {
326  1158 return this.isolated;
327    }
328   
329    /**
330    * @param isolated true if the job log should be grabbed
331    */
 
332  64 toggle public void setIsolated(boolean isolated)
333    {
334  64 this.isolated = isolated;
335    }
336   
337    /**
338    * @return the status of the parent job, i.e. the status of the job that started this one; returns {@code null} if
339    * the job has no parent, i.e. if the job hasn't been started by another job
340    */
 
341  13 toggle public JobStatus getParentJobStatus()
342    {
343  13 return this.parentJobStatus;
344    }
345   
346    // Deprecated
347   
 
348  0 toggle @Override
349    @Deprecated
350    public List<LogEvent> getLog(LogLevel level)
351    {
352  0 return getLog().getLogs(level);
353    }
354    }