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

File ExtensionJobHistoryRecorder.java

 

Coverage histogram

../../../../../../img/srcFileCovDistChart8.png
54% 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 16
0.8048780680.5%
 

Contributing tests

This file is covered by 154 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  261 toggle public ExtensionJobHistoryRecorder()
85    {
86  261 super(NAME, new JobStartedEvent(), new QuestionAnsweredEvent(), new JobFinishedEvent());
87    }
88   
 
89  1089 toggle @Override
90    public void onEvent(Event event, Object source, Object data)
91    {
92  1089 if (event instanceof JobStartedEvent) {
93  542 onJobStarted((Job) source);
94  547 } else if (event instanceof QuestionAnsweredEvent) {
95  5 onQuestionAnswered((JobStatus) source);
96  542 } else if (event instanceof JobFinishedEvent) {
97  542 onJobFinished((Job) source, data);
98    }
99    }
100   
 
101  542 toggle private void onJobStarted(Job job)
102    {
103  542 if (!(job.getRequest() instanceof ExtensionRequest)) {
104    // This is not an extension job.
105  113 return;
106    }
107   
108  429 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  429 String jobId = StringUtils.join(job.getRequest().getId(), '/');
114  429 if (jobId != null) {
115  47 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  5 toggle private void onQuestionAnswered(JobStatus jobStatus)
125    {
126  5 Object question = jobStatus.getQuestion();
127  5 if (question != null) {
128  5 String jobId = StringUtils.join(getActualJobId(jobStatus), '/');
129  5 Map<String, QuestionRecorder<Object>> jobAnswers = this.answers.get(jobId);
130  5 if (jobAnswers != null) {
131  5 QuestionRecorder<Object> questionRecorder = getQuestionRecorder(question, jobAnswers);
132  5 if (questionRecorder != null) {
133  5 questionRecorder.record(question);
134    }
135    }
136    }
137    }
138   
 
139  5 toggle private List<String> getActualJobId(JobStatus jobStatus)
140    {
141  5 if (jobStatus instanceof ReplayJobStatus) {
142  0 ExtensionJobHistoryRecord currentRecord = ((ReplayJobStatus) jobStatus).getCurrentRecord();
143  0 return currentRecord != null ? currentRecord.getRequest().getId() : null;
144    } else {
145  5 return jobStatus.getRequest().getId();
146    }
147    }
148   
 
149  5 toggle private QuestionRecorder<Object> getQuestionRecorder(Object question,
150    Map<String, QuestionRecorder<Object>> questionRecorders)
151    {
152  5 String questionType = question.getClass().getName();
153  5 QuestionRecorder<Object> questionRecorder = questionRecorders.get(questionType);
154  5 if (questionRecorder == null) {
155  5 questionRecorder = getQuestionRecorder(question);
156  5 if (questionRecorder != null) {
157  5 questionRecorders.put(questionType, questionRecorder);
158    }
159    }
160  5 return questionRecorder;
161    }
162   
 
163  5 toggle private <T> QuestionRecorder<T> getQuestionRecorder(T question)
164    {
165  5 ParameterizedType questionRecorderType =
166    new DefaultParameterizedType(null, QuestionRecorder.class, question.getClass());
167  5 try {
168  5 return this.contextComponentManagerProvider.get().getInstance(questionRecorderType);
169    } catch (ComponentLookupException e) {
170  0 return null;
171    }
172    }
173   
 
174  542 toggle private void onJobFinished(Job job, Object data)
175    {
176  542 String jobId = StringUtils.join(job.getRequest().getId(), '/');
177  542 Map<String, QuestionRecorder<Object>> jobAnswers = jobId != null ? this.answers.remove(jobId) : null;
178   
179  542 if (data instanceof Throwable || jobAnswers == null) {
180    // The job execution has failed or the job has not been recorded.
181  499 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  43 this.history.addRecord(new ExtensionJobHistoryRecord(job.getType(), (ExtensionRequest) job.getRequest(),
187    jobAnswers, job.getStatus().getStartDate(), new Date()));
188    }
189    }