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

File XarExtensionJobFinishedListener.java

 

Coverage histogram

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

Code metrics

22
75
5
1
272
185
23
0.31
15
5
4.6

Classes

Class Line # Actions
XarExtensionJobFinishedListener 67 75 0% 23 21
0.794117679.4%
 

Contributing tests

This file is covered by 24 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.xar.internal.handler;
21   
22    import java.io.IOException;
23    import java.util.Arrays;
24    import java.util.Collections;
25    import java.util.HashSet;
26    import java.util.List;
27    import java.util.Map;
28    import java.util.Set;
29   
30    import javax.inject.Inject;
31    import javax.inject.Named;
32    import javax.inject.Provider;
33    import javax.inject.Singleton;
34   
35    import org.slf4j.Logger;
36    import org.xwiki.component.annotation.Component;
37    import org.xwiki.context.Execution;
38    import org.xwiki.context.ExecutionContext;
39    import org.xwiki.extension.LocalExtension;
40    import org.xwiki.extension.job.internal.InstallJob;
41    import org.xwiki.extension.job.internal.UninstallJob;
42    import org.xwiki.extension.repository.InstalledExtensionRepository;
43    import org.xwiki.extension.xar.internal.handler.packager.PackageConfiguration;
44    import org.xwiki.extension.xar.internal.handler.packager.Packager;
45    import org.xwiki.extension.xar.internal.repository.XarInstalledExtensionRepository;
46    import org.xwiki.extension.xar.question.CleanPagesQuestion;
47    import org.xwiki.job.Job;
48    import org.xwiki.job.Request;
49    import org.xwiki.job.event.JobFinishingEvent;
50    import org.xwiki.model.reference.DocumentReference;
51    import org.xwiki.observation.EventListener;
52    import org.xwiki.observation.event.Event;
53    import org.xwiki.xar.XarEntry;
54   
55    import com.xpn.xwiki.XWikiContext;
56    import com.xpn.xwiki.doc.XWikiDocument;
57   
58    /**
59    * Listen to job finished events to properly clean pages after upgrades.
60    *
61    * @version $Id: 8c4fa96353581f994157092a2da653871d8596cf $
62    * @since 4.3M1
63    */
64    @Component
65    @Singleton
66    @Named("XarExtensionJobFinishedListener")
 
67    public class XarExtensionJobFinishedListener implements EventListener
68    {
69    /**
70    * The list of events observed.
71    */
72    private static final List<Event> EVENTS =
73    Arrays.<Event>asList(new JobFinishingEvent(InstallJob.JOBTYPE), new JobFinishingEvent(UninstallJob.JOBTYPE));
74   
75    @Inject
76    private Execution execution;
77   
78    @Inject
79    private Provider<Packager> packagerProvider;
80   
81    @Inject
82    private Provider<XWikiContext> xcontextProvider;
83   
84    @Inject
85    private Logger logger;
86   
87    @Inject
88    @Named(XarExtensionHandler.TYPE)
89    private InstalledExtensionRepository xarRepository;
90   
 
91  104 toggle @Override
92    public String getName()
93    {
94  104 return "XarExtensionJobFinishedListener";
95    }
96   
 
97  26 toggle @Override
98    public List<Event> getEvents()
99    {
100  26 return EVENTS;
101    }
102   
 
103  60 toggle @Override
104    public void onEvent(Event event, Object source, Object data)
105    {
106  60 JobFinishingEvent jobFinishingEvent = (JobFinishingEvent) event;
107   
108  60 if (!jobFinishingEvent.getRequest().isRemote()) {
109  60 ExecutionContext context = this.execution.getContext();
110   
111  60 if (context != null) {
112  60 XarExtensionPlan xarExtensionPlan =
113    (XarExtensionPlan) context.getProperty(XarExtensionPlan.CONTEXTKEY_XARINSTALLPLAN);
114   
115  60 if (xarExtensionPlan != null) {
116  53 try {
117  53 Map<String, Map<XarEntry, XarExtensionPlanEntry>> previousXAREntries =
118    xarExtensionPlan.previousXAREntries;
119  53 Map<String, Map<XarEntry, LocalExtension>> nextXAREntries = xarExtensionPlan.nextXAREntries;
120   
121  53 Map<XarEntry, LocalExtension> rootNextPages = nextXAREntries.get(null);
122  53 if (rootNextPages == null) {
123  42 rootNextPages = Collections.emptyMap();
124    }
125   
126  53 XWikiContext xcontext = this.xcontextProvider.get();
127   
128  53 Packager packager = this.packagerProvider.get();
129   
130    // Get pages to delete
131   
132  53 Set<DocumentReference> pagesToDelete = new HashSet<DocumentReference>();
133   
134  53 for (Map.Entry<String, Map<XarEntry, XarExtensionPlanEntry>> previousWikiEntry : previousXAREntries
135    .entrySet()) {
136  22 if (!previousWikiEntry.getValue().isEmpty()) {
137  22 try {
138  22 List<DocumentReference> references =
139    packager.getDocumentReferences(previousWikiEntry.getValue().keySet(),
140    createPackageConfiguration(jobFinishingEvent.getRequest(),
141    previousWikiEntry.getKey()));
142   
143  22 for (DocumentReference reference : references) {
144    // Ignore document that are part of other installed extensions (don't even
145    // propose to enable them)
146  146 if (((XarInstalledExtensionRepository) this.xarRepository)
147    .getXarInstalledExtensions(reference).isEmpty()) {
148  92 pagesToDelete.add(reference);
149    }
150    }
151    } catch (Exception e) {
152  0 this.logger.warn(
153    "Exception when cleaning pages removed since previous xar extension version",
154    e);
155    }
156    }
157    }
158   
159    // Create cleanup question
160   
161  53 CleanPagesQuestion question = new CleanPagesQuestion(pagesToDelete);
162   
163  53 Map<DocumentReference, Boolean> pages = question.getPages();
164   
165    // Remove pages which are in the next XAR packages
166  53 for (DocumentReference previousReference : pagesToDelete) {
167  92 if (xarExtensionPlan.containsNewPage(previousReference)) {
168  0 pages.remove(previousReference);
169    }
170    }
171   
172    // Deal with conflicts before sending the question
173   
174  53 for (Map.Entry<DocumentReference, Boolean> entry : pages.entrySet()) {
175  92 DocumentReference reference = entry.getKey();
176   
177    // Get current
178  92 XWikiDocument currentDocument;
179  92 try {
180  92 currentDocument = xcontext.getWiki().getDocument(reference, xcontext);
181    } catch (Exception e) {
182  0 this.logger.error("Failed to get document [{}]", reference, e);
183    // Lets be safe and skip removing that page
184  0 pages.put(reference, false);
185  0 continue;
186    }
187  92 if (currentDocument.isNew()) {
188    // Current already removed
189  0 pages.put(reference, false);
190  0 continue;
191    }
192   
193    // Get previous
194  92 XWikiDocument previousDocument;
195  92 try {
196  92 previousDocument = xarExtensionPlan.getPreviousXWikiDocument(reference, packager);
197    } catch (Exception e) {
198  0 this.logger.error("Failed to get previous version of document [{}]", reference, e);
199    // Lets be safe and skip removing that page
200  0 pages.put(reference, false);
201  0 continue;
202    }
203   
204    // Compare previous and current
205  92 try {
206  92 currentDocument.loadAttachmentsContent(xcontext);
207  92 if (!currentDocument.equalsData(previousDocument)) {
208    // conflict between current and new
209  22 pages.put(reference, false);
210    }
211    } catch (Exception e) {
212  0 this.logger.error("Failed to load attachments", e);
213    // Lets be safe and skip removing that page
214  0 pages.put(reference, false);
215  0 continue;
216    }
217    }
218   
219    // Ask confirmation
220  53 if (!pages.isEmpty() && jobFinishingEvent.getRequest().isInteractive()) {
221  3 try {
222  3 ((Job) source).getStatus().ask(question);
223    } catch (InterruptedException e) {
224  0 this.logger.warn("The thread has been interrupted", e);
225   
226    // The thread has been interrupted, do nothing
227  0 return;
228    }
229    }
230   
231    // Delete pages
232   
233  53 PackageConfiguration configuration = createPackageConfiguration(jobFinishingEvent.getRequest());
234   
235  53 for (Map.Entry<DocumentReference, Boolean> entry : pages.entrySet()) {
236  92 if (entry.getValue()) {
237  70 packager.deleteDocument(entry.getKey(), configuration);
238    }
239    }
240    } finally {
241    // Cleanup extension plan
242  53 try {
243  53 xarExtensionPlan.close();
244    } catch (IOException e) {
245  0 this.logger.error("Failed to close XAR extension plan", e);
246    }
247  53 context.setProperty(XarExtensionPlan.CONTEXTKEY_XARINSTALLPLAN, null);
248    }
249    }
250    }
251    }
252    }
253   
 
254  53 toggle private PackageConfiguration createPackageConfiguration(Request request)
255    {
256  53 return createPackageConfiguration(request, null);
257    }
258   
 
259  75 toggle private PackageConfiguration createPackageConfiguration(Request request, String wiki)
260    {
261  75 PackageConfiguration configuration = new PackageConfiguration();
262   
263  75 configuration.setInteractive(false);
264  75 configuration
265    .setUser(XarExtensionHandler.getRequestUserReference(XarExtensionHandler.PROPERTY_USERREFERENCE, request));
266  75 configuration.setWiki(wiki);
267  75 configuration.setVerbose(request.isVerbose());
268  75 configuration.setSkipMandatorytDocuments(true);
269   
270  75 return configuration;
271    }
272    }