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

File ExtensionJobHistoryRecorder.java

 

Coverage histogram

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

Code metrics

30
43
9
1
189
130
27
0.63
4.78
9
3

Classes

Class Line # Actions
ExtensionJobHistoryRecorder 62 43 0% 27 15
0.8170731781.7%
 

Contributing tests

This file is covered by 172 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.lang.reflect.ParameterizedType;
23    import java.util.Date;
24    import java.util.HashMap;
25    import java.util.List;
26    import java.util.Map;
27    import java.util.concurrent.ConcurrentHashMap;
28   
29    import javax.inject.Inject;
30    import javax.inject.Named;
31    import javax.inject.Provider;
32    import javax.inject.Singleton;
33   
34    import org.apache.commons.lang3.StringUtils;
35    import org.xwiki.component.annotation.Component;
36    import org.xwiki.component.manager.ComponentLookupException;
37    import org.xwiki.component.manager.ComponentManager;
38    import org.xwiki.component.util.DefaultParameterizedType;
39    import org.xwiki.extension.job.ExtensionRequest;
40    import org.xwiki.extension.job.history.ExtensionJobHistory;
41    import org.xwiki.extension.job.history.ExtensionJobHistoryRecord;
42    import org.xwiki.extension.job.history.QuestionRecorder;
43    import org.xwiki.extension.job.history.ReplayJobStatus;
44    import org.xwiki.job.Job;
45    import org.xwiki.job.event.JobFinishedEvent;
46    import org.xwiki.job.event.JobStartedEvent;
47    import org.xwiki.job.event.status.JobStatus;
48    import org.xwiki.job.event.status.QuestionAnsweredEvent;
49    import org.xwiki.job.internal.AbstractJobStatus;
50    import org.xwiki.observation.AbstractEventListener;
51    import org.xwiki.observation.event.Event;
52   
53    /**
54    * Records the extension jobs that have been executed.
55    *
56    * @version $Id: 4b4fb420e8b1295c0d995bb3bc8b7205728d08fc $
57    * @since 7.1RC1
58    */
59    @Component
60    @Named(ExtensionJobHistoryRecorder.NAME)
61    @Singleton
 
62    public class ExtensionJobHistoryRecorder extends AbstractEventListener
63    {
64    /**
65    * The name of this event listener (and its component hint at the same time).
66    */
67    public static final String NAME = "ExtensionJobHistoryRecorder";
68   
69    @Inject
70    private ExtensionJobHistory history;
71   
72    @Inject
73    @Named("context")
74    private Provider<ComponentManager> contextComponentManagerProvider;
75   
76    /**
77    * The recorded answers. The key is the job id and the value represents the answers grouped by question type.
78    */
79    private Map<String, Map<String, QuestionRecorder<Object>>> answers = new ConcurrentHashMap<>();
80   
81    /**
82    * Default constructor.
83    */
 
84  299 toggle public ExtensionJobHistoryRecorder()
85    {
86  299 super(NAME, new JobStartedEvent(), new QuestionAnsweredEvent(), new JobFinishedEvent());
87    }
88   
 
89  43836 toggle @Override
90    public void onEvent(Event event, Object source, Object data)
91    {
92  43844 if (event instanceof JobStartedEvent) {
93  21912 onJobStarted((Job) source);
94  21931 } else if (event instanceof QuestionAnsweredEvent) {
95  15 onQuestionAnswered((JobStatus) source);
96  21916 } else if (event instanceof JobFinishedEvent) {
97  21915 onJobFinished((Job) source, data);
98    }
99    }
100   
 
101  21907 toggle private void onJobStarted(Job job)
102    {
103  21910 if (!(job.getRequest() instanceof ExtensionRequest)) {
104    // This is not an extension job.
105  21433 return;
106    }
107   
108  476 if (job.getStatus() instanceof AbstractJobStatus && isSubJob((AbstractJobStatus<?>) job.getStatus())) {
109    // We record only the jobs that have been triggered explicitly or that are part of a replay.
110  0 return;
111    }
112   
113  476 String jobId = StringUtils.join(job.getRequest().getId(), '/');
114  476 if (jobId != null) {
115  75 this.answers.put(jobId, new HashMap<String, QuestionRecorder<Object>>());
116    }
117    }
118   
 
119  0 toggle private <T extends AbstractJobStatus<?>> boolean isSubJob(T jobStatus)
120    {
121  0 return jobStatus.isSubJob() && !(jobStatus.getParentJobStatus() instanceof ReplayJobStatus);
122    }
123   
 
124  15 toggle private void onQuestionAnswered(JobStatus jobStatus)
125    {
126  15 Object question = jobStatus.getQuestion();
127  15 if (question != null) {
128  15 String jobId = StringUtils.join(getActualJobId(jobStatus), '/');
129  15 Map<String, QuestionRecorder<Object>> jobAnswers = this.answers.get(jobId);
130  15 if (jobAnswers != null) {
131  7 QuestionRecorder<Object> questionRecorder = getQuestionRecorder(question, jobAnswers);
132  7 if (questionRecorder != null) {
133  7 questionRecorder.record(question);
134    }
135    }
136    }
137    }
138   
 
139  15 toggle private List<String> getActualJobId(JobStatus jobStatus)
140    {
141  15 if (jobStatus instanceof ReplayJobStatus) {
142  0 ExtensionJobHistoryRecord currentRecord = ((ReplayJobStatus) jobStatus).getCurrentRecord();
143  0 return currentRecord != null ? currentRecord.getRequest().getId() : null;
144    } else {
145  15 return jobStatus.getRequest().getId();
146    }
147    }
148   
 
149  7 toggle private QuestionRecorder<Object> getQuestionRecorder(Object question,
150    Map<String, QuestionRecorder<Object>> questionRecorders)
151    {
152  7 String questionType = question.getClass().getName();
153  7 QuestionRecorder<Object> questionRecorder = questionRecorders.get(questionType);
154  7 if (questionRecorder == null) {
155  7 questionRecorder = getQuestionRecorder(question);
156  7 if (questionRecorder != null) {
157  7 questionRecorders.put(questionType, questionRecorder);
158    }
159    }
160  7 return questionRecorder;
161    }
162   
 
163  7 toggle private <T> QuestionRecorder<T> getQuestionRecorder(T question)
164    {
165  7 ParameterizedType questionRecorderType =
166    new DefaultParameterizedType(null, QuestionRecorder.class, question.getClass());
167  7 try {
168  7 return this.contextComponentManagerProvider.get().getInstance(questionRecorderType);
169    } catch (ComponentLookupException e) {
170  0 return null;
171    }
172    }
173   
 
174  21916 toggle private void onJobFinished(Job job, Object data)
175    {
176  21916 String jobId = StringUtils.join(job.getRequest().getId(), '/');
177  21916 Map<String, QuestionRecorder<Object>> jobAnswers = jobId != null ? this.answers.remove(jobId) : null;
178   
179  21916 if (data instanceof Throwable || jobAnswers == null) {
180    // The job execution has failed or the job has not been recorded.
181  21846 return;
182    }
183   
184    // We assume the job ended at this moment because the actual end date is set on the job status after all the
185    // event listeners are called so it's not available right now.
186  70 this.history.addRecord(new ExtensionJobHistoryRecord(job.getType(), (ExtensionRequest) job.getRequest(),
187    jobAnswers, job.getStatus().getStartDate(), new Date()));
188    }
189    }