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

File AbstractJobStatus.java

 

Coverage histogram

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

Code metrics

24
75
31
1
471
249
43
0.57
2.42
31
1.39

Classes

Class Line # Actions
AbstractJobStatus 49 75 0% 43 13
0.990%
 

Contributing tests

This file is covered by 232 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.TimeUnit;
25    import java.util.concurrent.locks.Condition;
26    import java.util.concurrent.locks.ReentrantLock;
27   
28    import org.xwiki.job.event.status.CancelableJobStatus;
29    import org.xwiki.job.event.status.JobProgress;
30    import org.xwiki.job.event.status.JobStatus;
31    import org.xwiki.job.event.status.QuestionAnsweredEvent;
32    import org.xwiki.job.event.status.QuestionAskedEvent;
33    import org.xwiki.job.internal.DefaultJobProgress;
34    import org.xwiki.logging.LogLevel;
35    import org.xwiki.logging.LogQueue;
36    import org.xwiki.logging.LoggerManager;
37    import org.xwiki.logging.event.LogEvent;
38    import org.xwiki.logging.event.LoggerListener;
39    import org.xwiki.observation.ObservationManager;
40    import org.xwiki.observation.WrappedThreadEventListener;
41   
42    /**
43    * Base implementation of {@link JobStatus}.
44    *
45    * @param <R> the request type associated to the job
46    * @version $Id: 1aa5a431e7f9ad88fef3659245d551752364a9f2 $
47    * @since 7.4M1
48    */
 
49    public abstract class AbstractJobStatus<R extends Request> implements JobStatus, CancelableJobStatus
50    {
51    /**
52    * Used register itself to receive logging and progress related events.
53    */
54    private final transient ObservationManager observationManager;
55   
56    /**
57    * Used to isolate job related log.
58    */
59    private final transient LoggerManager loggerManager;
60   
61    /**
62    * Used to lock #ask().
63    */
64    private final transient ReentrantLock askLock = new ReentrantLock();
65   
66    /**
67    * Condition for waiting answer.
68    */
69    private final transient Condition answered = this.askLock.newCondition();
70   
71    /**
72    * The status of the parent job, i.e. the status of the job that started this one. This is {@code null} if the job
73    * has no parent, i.e. if the job hasn't been started by another job.
74    * <p>
75    * We don't want to serialize it.
76    */
77    private final transient JobStatus parentJobStatus;
78   
79    /**
80    * Take care of progress related events to produce a progression information usually used in a progress bar.
81    */
82    private final DefaultJobProgress progress = new DefaultJobProgress();
83   
84    /**
85    * Log sent during job execution.
86    */
87    private final LogQueue logs;
88   
89    /**
90    * Used to listen to all the log produced during job execution.
91    */
92    private transient LoggerListener logListener;
93   
94    /**
95    * The question.
96    */
97    private transient volatile Object question;
98   
99    /**
100    * @see #getJobType()
101    */
102    private String jobType;
103   
104    /**
105    * General state of the job.
106    */
107    private State state = State.NONE;
108   
109    /**
110    * @see #getError()
111    */
112    private Throwable error;
113   
114    /**
115    * Request provided when starting the job.
116    */
117    private R request;
118   
119    /**
120    * @see #getStartDate()
121    */
122    private Date startDate;
123   
124    /**
125    * @see #getEndDate()
126    */
127    private Date endDate;
128   
129    /**
130    * Indicate if Job log should be grabbed.
131    */
132    private boolean isolated = true;
133   
134    /**
135    * Flag indicating if the job was canceled.
136    */
137    private boolean canceled;
138   
139    /**
140    * Flag indicating if the job can be canceled.
141    */
142    private boolean cancelable;
143   
144    private boolean serialized = true;
145   
146    private long quesionEnd = -1;
147   
148    /**
149    * @param request the request provided when started the job
150    * @param parentJobStatus the status of the parent job (i.e. the status of the job that started this one); pass
151    * {@code null} if this job hasn't been started by another job (i.e. if this is not a sub-job)
152    * @param observationManager the observation manager component
153    * @param loggerManager the logger manager component
154    * @deprecated since 9.2RC1, use
155    * {@link #AbstractJobStatus(String, Request, JobStatus, ObservationManager, LoggerManager)} instead
156    */
 
157  21 toggle @Deprecated
158    public AbstractJobStatus(R request, JobStatus parentJobStatus, ObservationManager observationManager,
159    LoggerManager loggerManager)
160    {
161  21 this(null, request, parentJobStatus, observationManager, loggerManager);
162    }
163   
164    /**
165    * @param jobType the type of the job
166    * @param request the request provided when started the job
167    * @param parentJobStatus the status of the parent job (i.e. the status of the job that started this one); pass
168    * {@code null} if this job hasn't been started by another job (i.e. if this is not a sub-job)
169    * @param observationManager the observation manager component
170    * @param loggerManager the logger manager component
171    */
 
172  220668 toggle public AbstractJobStatus(String jobType, R request, JobStatus parentJobStatus,
173    ObservationManager observationManager, LoggerManager loggerManager)
174    {
175  220668 this.jobType = jobType;
176  220670 this.request = request;
177  220670 this.parentJobStatus = parentJobStatus;
178   
179  220670 this.isolated = parentJobStatus == null;
180   
181  220670 this.observationManager = observationManager;
182  220670 this.loggerManager = loggerManager;
183   
184  220670 this.logs = new LogQueue();
185    }
186   
187    /**
188    * Start listening to events.
189    */
 
190  21959 toggle public void startListening()
191    {
192    // Register progress listener
193  21975 this.observationManager.addListener(new WrappedThreadEventListener(this.progress));
194   
195    // Isolate log for the job status
196  21975 this.logListener = new LoggerListener(LoggerListener.class.getName() + '_' + hashCode(), this.logs);
197  21974 if (isIsolated()) {
198  496 this.loggerManager.pushLogListener(this.logListener);
199    } else {
200  21478 this.observationManager.addListener(new WrappedThreadEventListener(this.logListener));
201    }
202    }
203   
204    /**
205    * Stop listening to events.
206    */
 
207  21974 toggle public void stopListening()
208    {
209  21972 if (isIsolated()) {
210  495 this.loggerManager.popLogListener();
211    } else {
212  21478 this.observationManager.removeListener(this.logListener.getName());
213    }
214  21973 this.observationManager.removeListener(this.progress.getName());
215   
216    // Make sure the progress is closed
217  21974 this.progress.getRootStep().finish();
218    }
219   
220    // JobStatus
221   
 
222  323 toggle @Override
223    public String getJobType()
224    {
225  323 return this.jobType;
226    }
227   
 
228  11595 toggle @Override
229    public State getState()
230    {
231  11595 return this.state;
232    }
233   
234    /**
235    * @param state the general state of the job
236    */
 
237  242528 toggle public void setState(State state)
238    {
239  242526 this.state = state;
240    }
241   
 
242  707 toggle @Override
243    public Throwable getError()
244    {
245  707 return this.error;
246    }
247   
248    /**
249    * @param error the {@link Throwable} on which the job stopped
250    * @since 8.1RC1
251    */
 
252  21973 toggle public void setError(Throwable error)
253    {
254  21971 this.error = error;
255    }
256   
 
257  335672 toggle @Override
258    public R getRequest()
259    {
260  335682 return this.request;
261    }
262   
 
263  1143 toggle @Override
264    public LogQueue getLog()
265    {
266    // Make sure to always return something (it could be null if unserialized as such)
267  1143 return this.logs != null ? this.logs : new LogQueue();
268    }
269   
 
270  353 toggle @Override
271    public JobProgress getProgress()
272    {
273  353 return this.progress;
274    }
275   
 
276  17 toggle @Override
277    public void ask(Object question) throws InterruptedException
278    {
279  17 ask(question, 0, null);
280    }
281   
 
282  17 toggle @Override
283    public boolean ask(Object question, long time, TimeUnit unit) throws InterruptedException
284    {
285  17 boolean notTimeout = true;
286   
287  17 this.question = question;
288   
289  17 this.askLock.lockInterruptibly();
290   
291  17 try {
292    // Wait for the answer
293  17 this.state = State.WAITING;
294  17 if (isSubJob()) {
295  1 this.parentJobStatus.ask(question);
296    } else {
297  16 String questionType = question != null ? question.getClass().getName() : null;
298  16 QuestionAskedEvent event = new QuestionAskedEvent(questionType, this.request.getId());
299  16 this.observationManager.notify(event, this);
300  16 if (event.isAnswered()) {
301  1 answered();
302    } else {
303  15 if (unit != null) {
304    // Remember timeout
305  0 this.quesionEnd = System.nanoTime() + unit.toNanos(time);
306   
307  0 notTimeout = this.answered.await(time, unit);
308   
309    // Reset time left
310  0 this.quesionEnd = -1;
311    } else {
312  15 this.answered.await();
313    }
314    }
315    }
316  16 this.state = State.RUNNING;
317    } finally {
318  16 this.askLock.unlock();
319    }
320   
321  16 return notTimeout;
322    }
323   
 
324  13 toggle @Override
325    public long getQuestionTimeLeft(TimeUnit unit)
326    {
327  13 return quesionEnd > -1 ? this.quesionEnd - System.nanoTime() : -1;
328    }
329   
 
330  48 toggle @Override
331    public Object getQuestion()
332    {
333  48 return this.question;
334    }
335   
 
336  17 toggle @Override
337    public void answered()
338    {
339  17 this.askLock.lock();
340   
341  17 try {
342  17 if (isSubJob()) {
343  1 this.question = null;
344  1 this.parentJobStatus.answered();
345    } else {
346  16 String questionType = this.question != null ? this.question.getClass().getName() : null;
347  16 this.observationManager.notify(new QuestionAnsweredEvent(questionType, this.request.getId()), this);
348  16 this.question = null;
349  16 this.answered.signal();
350    }
351    } finally {
352  17 this.askLock.unlock();
353    }
354    }
355   
 
356  2558 toggle @Override
357    public Date getStartDate()
358    {
359  2558 return this.startDate;
360    }
361   
362    /**
363    * @param startDate the date and time when the job has been started
364    */
 
365  21961 toggle public void setStartDate(Date startDate)
366    {
367  21969 this.startDate = startDate;
368    }
369   
 
370  136 toggle @Override
371    public Date getEndDate()
372    {
373  136 return this.endDate;
374    }
375   
376    /**
377    * @param endDate the date and time when the job finished
378    */
 
379  21974 toggle public void setEndDate(Date endDate)
380    {
381  21970 this.endDate = endDate;
382    }
383   
384    /**
385    * @return true if the job is part of another job execution
386    */
 
387  34 toggle public boolean isSubJob()
388    {
389  34 return getParentJobStatus() != null;
390    }
391   
 
392  44018 toggle @Override
393    public boolean isIsolated()
394    {
395  44018 Boolean isolatedRequest = getRequest().isStatusLogIsolated();
396   
397  44014 return isolatedRequest != null ? isolatedRequest : this.isolated;
398    }
399   
400    /**
401    * @param isolated true if the job log should be grabbed
402    */
 
403  21269 toggle public void setIsolated(boolean isolated)
404    {
405  21269 this.isolated = isolated;
406    }
407   
408    /**
409    * @return the status of the parent job, i.e. the status of the job that started this one; returns {@code null} if
410    * the job has no parent, i.e. if the job hasn't been started by another job
411    */
 
412  34 toggle public JobStatus getParentJobStatus()
413    {
414  34 return this.parentJobStatus;
415    }
416   
 
417  0 toggle @Override
418    public boolean isCancelable()
419    {
420  0 return this.cancelable;
421    }
422   
423    /**
424    * @param cancelable true if the job can be canceled
425    */
 
426  220 toggle public void setCancelable(boolean cancelable)
427    {
428  220 this.cancelable = cancelable;
429    }
430   
431    /**
432    * {@inheritDoc}
433    *
434    * @see org.xwiki.job.event.status.CancelableJobStatus#cancel()
435    * @since 9.4RC1
436    */
 
437  1 toggle @Override
438    public void cancel()
439    {
440  1 this.canceled = true;
441    }
442   
443    /**
444    * {@inheritDoc}
445    *
446    * @see org.xwiki.job.event.status.CancelableJobStatus#isCanceled()
447    * @since 9.4RC1
448    */
 
449  588 toggle @Override
450    public boolean isCanceled()
451    {
452  588 return this.canceled;
453    }
454   
 
455  21593 toggle @Override
456    public boolean isSerialized()
457    {
458  21593 Boolean serializdRequest = getRequest().isStatusSerialized();
459   
460  21593 return serializdRequest != null ? serializdRequest : this.serialized;
461    }
462   
463    // Deprecated
464   
 
465  0 toggle @Override
466    @Deprecated
467    public List<LogEvent> getLog(LogLevel level)
468    {
469  0 return getLog().getLogs(level);
470    }
471    }