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

File AbstractEntityJob.java

 

Coverage histogram

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

Code metrics

28
63
16
2
317
180
33
0.52
3.94
8
2.06

Classes

Class Line # Actions
AbstractEntityJob 53 63 0% 33 5
0.9532710395.3%
AbstractEntityJob.Visitor 62 0 - 0 0
-1.0 -
 

Contributing tests

This file is covered by 51 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.refactoring.internal.job;
21   
22    import java.util.Collection;
23    import java.util.Iterator;
24   
25    import javax.inject.Inject;
26   
27    import org.xwiki.job.AbstractJob;
28    import org.xwiki.job.GroupedJob;
29    import org.xwiki.job.JobGroupPath;
30    import org.xwiki.job.Request;
31    import org.xwiki.model.EntityType;
32    import org.xwiki.model.reference.DocumentReference;
33    import org.xwiki.model.reference.EntityReference;
34    import org.xwiki.model.reference.EntityReferenceProvider;
35    import org.xwiki.model.reference.EntityReferenceTree;
36    import org.xwiki.model.reference.EntityReferenceTreeNode;
37    import org.xwiki.model.reference.SpaceReference;
38    import org.xwiki.refactoring.internal.ModelBridge;
39    import org.xwiki.refactoring.job.EntityJobStatus;
40    import org.xwiki.refactoring.job.EntityRequest;
41    import org.xwiki.refactoring.job.RefactoringJobs;
42    import org.xwiki.security.authorization.AuthorizationManager;
43    import org.xwiki.security.authorization.Right;
44   
45    /**
46    * Abstract job that targets multiple entities.
47    *
48    * @param <R> the request type
49    * @param <S> the job status type
50    * @version $Id: 204db8ed96532720e166ad743a48d94e3e4e9c38 $
51    * @since 7.2M1
52    */
 
53    public abstract class AbstractEntityJob<R extends EntityRequest, S extends EntityJobStatus<? super R>>
54    extends AbstractJob<R, S> implements GroupedJob
55    {
56    /**
57    * Generic interface used to implement the Visitor pattern.
58    *
59    * @param <T> the type of nodes that are visited
60    * @version $Id: 204db8ed96532720e166ad743a48d94e3e4e9c38 $
61    */
 
62    public interface Visitor<T>
63    {
64    /**
65    * Visit the given node.
66    *
67    * @param node the node to visit
68    */
69    void visit(T node);
70    }
71   
72    private static final JobGroupPath ROOT_GROUP = new JobGroupPath(RefactoringJobs.GROUP, null);
73   
74    private static final String PREFERENCES_DOCUMENT_NAME = "WebPreferences";
75   
76    /**
77    * The component used to access the XWiki model and to perform low level operations on it.
78    */
79    @Inject
80    protected ModelBridge modelBridge;
81   
82    /**
83    * Specifies the group this job is part of. If all the entities involved in this operation are from the same wiki
84    * then this job is part of a group of refactoring jobs that run on that wiki (it will only block the refactoring
85    * jobs that run on that wiki). Otherwise, if there are at least two entities from different wikis then this job is
86    * part of the group of refactoring jobs that run at farm level.
87    */
88    private JobGroupPath groupPath;
89   
90    /**
91    * Used to check access permissions.
92    *
93    * @see #hasAccess(Right, EntityReference)
94    */
95    @Inject
96    private AuthorizationManager authorization;
97   
98    /**
99    * Used to distinguish the space home page.
100    *
101    * @see #isSpaceHomeReference(DocumentReference)
102    */
103    @Inject
104    private EntityReferenceProvider defaultEntityReferenceProvider;
105   
 
106  156 toggle @Override
107    public JobGroupPath getGroupPath()
108    {
109  156 return this.groupPath;
110    }
111   
 
112  206 toggle @Override
113    public void initialize(Request request)
114    {
115  206 super.initialize(request);
116   
117    // Build the job group path.
118  206 EntityReference commonParent = getCommonParent();
119  206 if (commonParent != null) {
120    // Build a JobGroupPath based on the common location of the concerned entities.
121    // The intent is to prevent having jobs concerning the same entities to run in parallel.
122    //
123    // Examples:
124    // - A.B.C and A.B cannot run in parallel
125    // But:
126    // - A.B.C and A.B.Z can run in parallel
127    // - A.B.C and E.F.G can run in parallel
128    //
129    // Example of use case:
130    // - user A renames A.B.C to A.B.D
131    // - user B deletes A.B.C in the same time
132  193 this.groupPath = ROOT_GROUP;
133  193 for (EntityReference reference : commonParent.getReversedReferenceChain()) {
134  460 this.groupPath = new JobGroupPath(reference.getName(), this.groupPath);
135    }
136    } else {
137  13 this.groupPath = ROOT_GROUP;
138    }
139    }
140   
 
141  206 toggle @Override
142    protected S createNewStatus(R request)
143    {
144  206 return (S) new EntityJobStatus<R>(getType(), request, this.observationManager, this.loggerManager, null);
145    }
146   
147    /**
148    * Return the deepest common parent shared by all concerned entities. For example, if we have:
149    * <ul>
150    * <li>"A.B.C"</li>
151    * <li>"A.B.D"</li>
152    * </ul>
153    * the results will be "A.B".
154    *
155    * @return the deepest common parent
156    * @since 9.1RC1
157    */
 
158  143 toggle protected EntityReference getCommonParent()
159    {
160  143 return getCommonParent(this.request.getEntityReferences());
161    }
162   
163    /**
164    * Return the deepest common parents shared by all concerned entities. For example, if we have:
165    * <ul>
166    * <li>A.B.C</li>
167    * <li>A.B.D</li>
168    * </ul>
169    * the results will be A.B.
170    *
171    * @param entityReferences a collection of entities
172    * @return the deepest common parent or null if there is no common ancestor
173    * @since 9.1RC1
174    */
 
175  204 toggle protected EntityReference getCommonParent(Collection<EntityReference> entityReferences)
176    {
177  204 if (entityReferences == null || entityReferences.isEmpty()) {
178  2 return null;
179    }
180   
181  202 Iterator<EntityReference> iterator = entityReferences.iterator();
182  202 EntityReference commonParent = iterator.next();
183  266 while (iterator.hasNext()) {
184  73 EntityReference entity = iterator.next();
185  197 while (!entity.hasParent(commonParent)) {
186  133 commonParent = commonParent.getParent();
187  133 if (commonParent == null) {
188  9 return null;
189    }
190    }
191    }
192   
193  193 return commonParent;
194    }
195   
 
196  51 toggle @Override
197    protected void runInternal() throws Exception
198    {
199  51 Collection<EntityReference> entityReferences = this.request.getEntityReferences();
200  51 if (entityReferences != null) {
201  51 setContextUser();
202  51 process(entityReferences);
203    }
204    }
205   
206    /**
207    * Set the context user before executing the job. We don't have to restore the previous context user when the job is
208    * finished because jobs are normally executed in a separate thread, with a separate execution context.
209    */
 
210  196 toggle protected void setContextUser()
211    {
212  196 this.modelBridge.setContextUserReference(this.request.getUserReference());
213    }
214   
 
215  184 toggle protected void process(Collection<EntityReference> entityReferences)
216    {
217  184 this.progressManager.pushLevelProgress(entityReferences.size(), this);
218   
219  184 try {
220  184 for (EntityReference entityReference : entityReferences) {
221  187 if (this.status.isCanceled()) {
222  0 break;
223    } else {
224  187 this.progressManager.startStep(this);
225  187 process(entityReference);
226  187 this.progressManager.endStep(this);
227    }
228    }
229    } finally {
230  184 this.progressManager.popLevelProgress(this);
231    }
232    }
233   
234    /**
235    * Process the specified entity.
236    *
237    * @param entityReference the entity to process
238    */
239    protected abstract void process(EntityReference entityReference);
240   
 
241  246 toggle protected boolean hasAccess(Right right, EntityReference reference)
242    {
243  246 return ((!this.request.isCheckRights()
244    || this.authorization.hasAccess(right, this.request.getUserReference(), reference))
245    && (!this.request.isCheckAuthorRights()
246    || this.authorization.hasAccess(right, this.request.getAuthorReference(), reference)));
247    }
248   
 
249  71 toggle protected boolean isSpaceHomeReference(DocumentReference documentReference)
250    {
251  71 return documentReference.getName()
252    .equals(this.defaultEntityReferenceProvider.getDefaultReference(documentReference.getType()).getName());
253    }
254   
 
255  214 toggle private boolean isSpacePreferencesReference(EntityReference entityReference)
256    {
257  214 return entityReference.getType() == EntityType.DOCUMENT
258    && PREFERENCES_DOCUMENT_NAME.equals(entityReference.getName());
259    }
260   
 
261  106 toggle protected void visitDocuments(SpaceReference spaceReference, Visitor<DocumentReference> visitor)
262    {
263  106 visitDocumentNodes(getDocumentReferenceTree(spaceReference), visitor);
264    }
265   
 
266  106 toggle private EntityReferenceTreeNode getDocumentReferenceTree(SpaceReference spaceReference)
267    {
268  106 return new EntityReferenceTree(this.modelBridge.getDocumentReferences(spaceReference)).get(spaceReference);
269    }
270   
 
271  320 toggle private void visitDocumentNodes(EntityReferenceTreeNode node, Visitor<DocumentReference> visitor)
272    {
273  320 if (node != null) {
274  262 EntityReference nodeReference = node.getReference();
275  262 EntityType nodeType = nodeReference != null ? nodeReference.getType() : null;
276  262 if (nodeType == EntityType.SPACE || nodeType == EntityType.WIKI || nodeType == null) {
277    // A node that corresponds to an entity that can contain documents.
278  83 visitDocumentAncestor(node, visitor);
279  179 } else if (nodeType == EntityType.DOCUMENT) {
280  179 visitor.visit((DocumentReference) node.getReference());
281    }
282    }
283    }
284   
 
285  83 toggle private void visitDocumentAncestor(EntityReferenceTreeNode node, Visitor<DocumentReference> visitor)
286    {
287  83 Collection<EntityReferenceTreeNode> children = node.getChildren();
288  83 this.progressManager.pushLevelProgress(children.size(), this);
289   
290  83 try {
291    // Visit the space preferences document at the end as otherwise we may loose the space access rights.
292  83 EntityReferenceTreeNode spacePreferencesNode = null;
293  83 for (EntityReferenceTreeNode child : children) {
294  214 if (isSpacePreferencesReference(child.getReference())) {
295  5 spacePreferencesNode = child;
296  5 continue;
297    }
298  209 visitDocumentAncestorStep(child, visitor);
299    }
300   
301  83 if (spacePreferencesNode != null) {
302  5 visitDocumentAncestorStep(spacePreferencesNode, visitor);
303    }
304    } finally {
305  83 this.progressManager.popLevelProgress(this);
306    }
307    }
308   
 
309  214 toggle private void visitDocumentAncestorStep(EntityReferenceTreeNode node, Visitor<DocumentReference> visitor)
310    {
311  214 this.progressManager.startStep(this);
312  214 if (!this.status.isCanceled()) {
313  214 visitDocumentNodes(node, visitor);
314    }
315  214 this.progressManager.endStep(this);
316    }
317    }