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

File ExtensionHistoryScriptService.java

 

Coverage histogram

../../../../img/srcFileCovDistChart8.png
54% of files have more coverage

Code metrics

8
53
15
2
290
163
25
0.47
3.53
7.5
1.67

Classes

Class Line # Actions
ExtensionHistoryScriptService 61 45 0% 20 22
0.650793765.1%
ExtensionHistoryScriptService.ExtensionHistoryFilter 66 8 0% 5 0
1.0100%
 

Contributing tests

This file is covered by 4 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.script;
21   
22    import java.io.InputStream;
23    import java.io.InputStreamReader;
24    import java.util.ArrayList;
25    import java.util.Arrays;
26    import java.util.Collection;
27    import java.util.Collections;
28    import java.util.Date;
29    import java.util.List;
30    import java.util.concurrent.ThreadLocalRandom;
31   
32    import javax.inject.Inject;
33    import javax.inject.Named;
34    import javax.inject.Singleton;
35   
36    import org.apache.commons.collections4.Predicate;
37    import org.apache.commons.collections4.PredicateUtils;
38    import org.xwiki.component.annotation.Component;
39    import org.xwiki.extension.job.AbstractExtensionRequest;
40    import org.xwiki.extension.job.history.ExtensionJobHistory;
41    import org.xwiki.extension.job.history.ExtensionJobHistoryRecord;
42    import org.xwiki.extension.job.history.ExtensionJobHistorySerializer;
43    import org.xwiki.extension.job.history.ReplayJobStatus;
44    import org.xwiki.extension.job.history.ReplayRequest;
45    import org.xwiki.extension.job.history.internal.ReplayJob;
46    import org.xwiki.job.AbstractRequest;
47    import org.xwiki.job.Job;
48    import org.xwiki.job.JobException;
49    import org.xwiki.model.reference.WikiReference;
50    import org.xwiki.security.authorization.Right;
51   
52    /**
53    * Various script APIs related to extension job history.
54    *
55    * @version $Id: 0cb46887f40c14874612843de1327178219be5b2 $
56    * @since 7.1M2
57    */
58    @Component
59    @Named(ExtensionManagerScriptService.ROLEHINT + '.' + ExtensionHistoryScriptService.ID)
60    @Singleton
 
61    public class ExtensionHistoryScriptService extends AbstractExtensionScriptService
62    {
63    /**
64    * Utility class to build filters for the extension job history.
65    */
 
66    public class ExtensionHistoryFilter
67    {
68    /**
69    * The list of constraints applied by this filter.
70    */
71    private final List<Predicate<ExtensionJobHistoryRecord>> constraints = new ArrayList<>();
72   
73    /**
74    * Filters the history records with the specified job type.
75    *
76    * @param jobTypes the type of jobs that should be included in the result
77    * @return this
78    */
 
79  1 toggle public ExtensionHistoryFilter ofType(final List<String> jobTypes)
80    {
81  1 this.constraints.add(new Predicate<ExtensionJobHistoryRecord>()
82    {
 
83  4 toggle @Override
84    public boolean evaluate(ExtensionJobHistoryRecord record)
85    {
86  4 return jobTypes.contains(record.getJobType());
87    }
88    });
89  1 return this;
90    }
91   
92    /**
93    * Filters the jobs that have been executed on the current wiki or on the entire farm.
94    *
95    * @return this
96    */
 
97  1 toggle public ExtensionHistoryFilter fromThisWiki()
98    {
99  1 final String currentWikiNamespace = WIKI_NAMESPACE_PREFIX + xcontextProvider.get().getWikiId();
100  1 this.constraints.add(new Predicate<ExtensionJobHistoryRecord>()
101    {
 
102  5 toggle @Override
103    public boolean evaluate(ExtensionJobHistoryRecord record)
104    {
105  5 return !record.getRequest().hasNamespaces()
106    || record.getRequest().getNamespaces().contains(currentWikiNamespace);
107    }
108    });
109  1 return this;
110    }
111   
112    /**
113    * Lists the history records that match this filter and that are older than the specified offset record.
114    *
115    * @param offsetRecordId specifies the offset record (where to start from); pass {@code null} to start from the
116    * most recent record in the history
117    * @param limit the maximum number of records to return from the specified offset
118    * @return a list of history records that match this filter and are older than the specified offset record
119    */
 
120  1 toggle public List<ExtensionJobHistoryRecord> list(String offsetRecordId, int limit)
121    {
122  1 return history.getRecords(PredicateUtils.allPredicate(this.constraints), offsetRecordId, limit);
123    }
124    }
125   
126    /**
127    * The identifier of the sub extension {@link org.xwiki.script.service.ScriptService}.
128    */
129    public static final String ID = "history";
130   
131    @Inject
132    private ExtensionJobHistory history;
133   
134    @Inject
135    private ExtensionJobHistorySerializer serializer;
136   
137    /**
138    * @return a new history filter
139    */
 
140  1 toggle public ExtensionHistoryFilter getRecords()
141    {
142  1 return new ExtensionHistoryFilter();
143    }
144   
145    /**
146    * Serializes a history record.
147    *
148    * @param record the history record to serialize
149    * @return the string serialization of the given history record
150    */
 
151  0 toggle public String serialize(ExtensionJobHistoryRecord record)
152    {
153  0 setError(null);
154   
155  0 try {
156  0 return this.serializer.serialize(record);
157    } catch (Exception e) {
158  0 setError(e);
159  0 return null;
160    }
161    }
162   
163    /**
164    * Deserializes a list of history records.
165    *
166    * @param serializedHistoryRecords the serialized list of history records
167    * @return the list of history records
168    */
 
169  0 toggle public List<ExtensionJobHistoryRecord> deserialize(String serializedHistoryRecords)
170    {
171  0 setError(null);
172   
173  0 try {
174  0 return this.serializer.deserialize(serializedHistoryRecords);
175    } catch (Exception e) {
176  0 setError(e);
177  0 return null;
178    }
179    }
180   
181    /**
182    * Reads a list of history records from a given input stream (e.g. the attachment content input stream).
183    *
184    * @param inputStream an input stream that provides a list of serialized history records
185    * @return the list of history records
186    */
 
187  0 toggle public List<ExtensionJobHistoryRecord> read(InputStream inputStream)
188    {
189  0 setError(null);
190   
191  0 try {
192  0 return this.serializer.read(new InputStreamReader(inputStream));
193    } catch (Exception e) {
194  0 setError(e);
195  0 return null;
196    }
197    }
198   
199    /**
200    * Replay the given list of extension history records.
201    *
202    * @param records the history records to replay
203    * @return the {@link Job} object that can be used to monitor the progress of the replay process, or {@code null} in
204    * case of failure
205    */
 
206  3 toggle public Job replay(List<ExtensionJobHistoryRecord> records)
207    {
208  3 setError(null);
209   
210  3 ReplayRequest request = createReplayRequest(createReplayPlan(records, true, null));
211   
212  3 try {
213  3 return this.jobExecutor.execute(ReplayJob.JOB_TYPE, request);
214    } catch (JobException e) {
215  0 setError(e);
216  0 return null;
217    }
218    }
219   
220    /**
221    * Prepares a list of history records for replay.
222    *
223    * @param records the history records to prepare for replay
224    * @param preserveUsers {@code true} if the given history records should be replayed using their original users,
225    * {@code false} if the current user should be used instead
226    * @param namespaces the namespaces where to replay the given history records; pass {@code null} or an empty
227    * collection if you want to preserve the original namespaces
228    * @return the modified history records, prepared to be replayed
229    */
 
230  3 toggle public List<ExtensionJobHistoryRecord> createReplayPlan(List<ExtensionJobHistoryRecord> records,
231    boolean preserveUsers, Collection<String> namespaces)
232    {
233  3 String currentWiki = this.xcontextProvider.get().getWikiId();
234  3 if (!this.authorization.hasAccess(Right.ADMIN, new WikiReference(currentWiki))) {
235  1 return Collections.emptyList();
236  2 } else if (!this.authorization.hasAccess(Right.PROGRAM)) {
237    // Replay on the current wiki using the current user.
238  1 return createReplayPlanInternal(records, false, Arrays.asList(WIKI_NAMESPACE_PREFIX + currentWiki));
239    } else {
240    // Only the users that have PR can preserve the rights-related properties of the original extension request.
241  1 return createReplayPlanInternal(records, preserveUsers, namespaces);
242    }
243    }
244   
245    /**
246    * @param id identifies the replay job
247    * @return the status of the specified replay job
248    */
 
249  0 toggle public ReplayJobStatus getReplayJobStatus(String id)
250    {
251  0 return (ReplayJobStatus) getJobStatus(getReplayJobId(id));
252    }
253   
 
254  2 toggle private List<ExtensionJobHistoryRecord> createReplayPlanInternal(List<ExtensionJobHistoryRecord> records,
255    boolean preserveUsers, Collection<String> namespaces)
256    {
257  2 for (ExtensionJobHistoryRecord record : records) {
258  2 if (!preserveUsers) {
259  1 setRightsProperties((AbstractRequest) record.getRequest());
260    }
261  2 if (record.getRequest().hasNamespaces() && namespaces != null && namespaces.size() > 0) {
262  1 ((AbstractRequest) record.getRequest()).setProperty("namespaces", namespaces);
263    }
264    }
265  2 return records;
266    }
267   
 
268  3 toggle private ReplayRequest createReplayRequest(List<ExtensionJobHistoryRecord> records)
269    {
270  3 ReplayRequest request = new ReplayRequest();
271  3 String suffix = new Date().getTime() + "-" + ThreadLocalRandom.current().nextInt(100, 1000);
272  3 request.setId(getReplayJobId(suffix));
273    // There may be questions for which there isn't a specified answer.
274  3 request.setInteractive(true);
275  3 request.setRecords(records);
276   
277    // Provide information on what started the job.
278  3 request.setProperty(PROPERTY_CONTEXT_WIKI, this.xcontextProvider.get().getWikiId());
279  3 request.setProperty(PROPERTY_CONTEXT_ACTION, this.xcontextProvider.get().getAction());
280   
281  3 setRightsProperties(request);
282   
283  3 return request;
284    }
285   
 
286  3 toggle private List<String> getReplayJobId(String suffix)
287    {
288  3 return Arrays.asList(AbstractExtensionRequest.JOBID_PREFIX, ID, suffix);
289    }
290    }