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

File XarExtensionHandler.java

 

Coverage histogram

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

Code metrics

46
113
9
1
373
273
45
0.4
12.56
9
5

Classes

Class Line # Actions
XarExtensionHandler 75 113 0% 45 29
0.8273809682.7%
 

Contributing tests

This file is covered by 18 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.File;
23    import java.io.IOException;
24    import java.util.Collection;
25    import java.util.HashMap;
26    import java.util.Map;
27    import java.util.concurrent.TimeUnit;
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.math.NumberUtils;
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.context.Execution;
39    import org.xwiki.context.ExecutionContext;
40    import org.xwiki.extension.ExtensionException;
41    import org.xwiki.extension.InstallException;
42    import org.xwiki.extension.InstalledExtension;
43    import org.xwiki.extension.LocalExtension;
44    import org.xwiki.extension.UninstallException;
45    import org.xwiki.extension.handler.internal.AbstractExtensionHandler;
46    import org.xwiki.extension.internal.validator.AbstractExtensionValidator;
47    import org.xwiki.extension.job.internal.AbstractExtensionJob;
48    import org.xwiki.extension.job.plan.ExtensionPlan;
49    import org.xwiki.extension.repository.InstalledExtensionRepository;
50    import org.xwiki.extension.repository.LocalExtensionRepository;
51    import org.xwiki.extension.xar.internal.handler.packager.PackageConfiguration;
52    import org.xwiki.extension.xar.internal.handler.packager.Packager;
53    import org.xwiki.extension.xar.internal.repository.XarInstalledExtension;
54    import org.xwiki.extension.xar.question.ConflictQuestion;
55    import org.xwiki.extension.xar.question.ConflictQuestion.ConflictType;
56    import org.xwiki.extension.xar.question.ConflictQuestion.GlobalAction;
57    import org.xwiki.extension.xar.question.DefaultConflictActionQuestion;
58    import org.xwiki.job.Job;
59    import org.xwiki.job.JobContext;
60    import org.xwiki.job.Request;
61    import org.xwiki.logging.marker.TranslationMarker;
62    import org.xwiki.model.reference.DocumentReference;
63    import org.xwiki.xar.XarEntry;
64    import org.xwiki.xar.XarException;
65   
66    import com.xpn.xwiki.XWikiContext;
67   
68    /**
69    * @version $Id: dbb62e9a720f55f980f2d5668c24a085b22e4cd8 $
70    * @since 4.0M1
71    */
72    @Component
73    @Singleton
74    @Named(XarExtensionHandler.TYPE)
 
75    public class XarExtensionHandler extends AbstractExtensionHandler
76    {
77    public static final String TYPE = "xar";
78   
79    private static final TranslationMarker LOG_EXTENSIONPLAN_BEGIN =
80    new TranslationMarker("extension.xar.log.extensionplan.begin");
81   
82    private static final TranslationMarker LOG_EXTENSIONPLAN_END =
83    new TranslationMarker("extension.xar.log.extensionplan.end");
84   
85    private static final String CONTEXTKEY_PACKAGECONFIGURATION = "extension.xar.packageconfiguration";
86   
87    @Inject
88    private Packager packager;
89   
90    @Inject
91    @Named(XarExtensionHandler.TYPE)
92    private InstalledExtensionRepository xarRepository;
93   
94    @Inject
95    private ComponentManager componentManager;
96   
97    @Inject
98    private LocalExtensionRepository localRepository;
99   
100    @Inject
101    private Provider<XWikiContext> xcontextProvider;
102   
103    /**
104    * Used to access the execution context.
105    */
106    @Inject
107    private Execution execution;
108   
 
109  104 toggle protected static DocumentReference getRequestUserReference(String property, Request request)
110    {
111  104 Object obj = request.getProperty(property);
112   
113  104 if (obj instanceof DocumentReference) {
114  103 return (DocumentReference) obj;
115    }
116   
117  1 return null;
118    }
119   
 
120  274 toggle private void initializePagesIndex(Request request) throws ExtensionException, XarException, IOException
121    {
122  274 ExecutionContext context = this.execution.getContext();
123   
124  274 if (context != null && context.getProperty(XarExtensionPlan.CONTEXTKEY_XARINSTALLPLAN) == null) {
125  61 ExtensionPlan plan = (ExtensionPlan) context.getProperty(AbstractExtensionJob.CONTEXTKEY_PLAN);
126   
127  61 if (plan != null) {
128  58 if (request.isVerbose()) {
129  57 this.logger.info(LOG_EXTENSIONPLAN_BEGIN, "Preparing XAR extension plan");
130    }
131   
132  58 context.setProperty(XarExtensionPlan.CONTEXTKEY_XARINSTALLPLAN,
133    new XarExtensionPlan(plan, this.xarRepository, this.localRepository));
134   
135  58 if (request.isVerbose()) {
136  57 this.logger.info(LOG_EXTENSIONPLAN_END, "XAR extension plan ready");
137    }
138    }
139    }
140    }
141   
 
142  58 toggle private XarExtensionPlan getXARExtensionPlan()
143    {
144  58 ExecutionContext context = this.execution.getContext();
145   
146  58 if (context != null) {
147  58 return (XarExtensionPlan) context.getProperty(XarExtensionPlan.CONTEXTKEY_XARINSTALLPLAN);
148    }
149   
150  0 return null;
151    }
152   
 
153  143 toggle @Override
154    public void install(LocalExtension localExtension, String namespace, Request request) throws InstallException
155    {
156    // Only import XAR when it's a local order (otherwise it will be imported several times and the wiki will
157    // probably not be in an expected state)
158  143 if (!request.isRemote()) {
159  143 String wiki;
160  143 try {
161  143 wiki = XarHandlerUtils.getWikiFromNamespace(namespace);
162    } catch (UnsupportedNamespaceException e) {
163  0 throw new InstallException("Failed to extract wiki id from namespace", e);
164    }
165   
166  143 installInternal(localExtension, wiki, request);
167    }
168    }
169   
 
170  107 toggle @Override
171    public void upgrade(Collection<InstalledExtension> previousLocalExtensions, LocalExtension newLocalExtension,
172    String namespace, Request request) throws InstallException
173    {
174    // Only import XAR when it's a local order (otherwise it will be imported several times and the wiki will
175    // probably not be in an expected state)
176  107 if (!request.isRemote()) {
177  107 String wiki;
178  107 try {
179  107 wiki = XarHandlerUtils.getWikiFromNamespace(namespace);
180    } catch (UnsupportedNamespaceException e) {
181  0 throw new InstallException("Failed to extract wiki id from namespace", e);
182    }
183   
184    // Install new pages
185  107 installInternal(newLocalExtension, wiki, request);
186    }
187    }
188   
 
189  250 toggle private void installInternal(LocalExtension newLocalExtension, String wiki, Request request) throws InstallException
190    {
191  250 try {
192  250 initializePagesIndex(request);
193  250 initJobPackageConfiguration(request, true);
194    } catch (Exception e) {
195  0 throw new InstallException("Failed to initialize extension plan index", e);
196    }
197   
198    // import xar into wiki (add new version when the page already exists)
199  250 PackageConfiguration configuration = createPackageConfiguration(newLocalExtension, request, wiki);
200  250 try {
201  250 this.packager.importXAR("Install extension [" + newLocalExtension + "]",
202    new File(newLocalExtension.getFile().getAbsolutePath()), configuration);
203    } catch (Exception e) {
204  0 throw new InstallException("Failed to import xar for extension [" + newLocalExtension + "]", e);
205    }
206    }
207   
 
208  24 toggle @Override
209    public void uninstall(InstalledExtension installedExtension, String namespace, Request request)
210    throws UninstallException
211    {
212  24 try {
213  24 initializePagesIndex(request);
214  24 initJobPackageConfiguration(request, false);
215    } catch (Exception e) {
216  0 throw new UninstallException("Failed to initialize extension plan index", e);
217    }
218   
219    // Only remove XAR when it's a local order (otherwise it will be deleted several times and the wiki will
220    // probably not be in an expected state)
221  24 if (!request.isRemote()) {
222  24 Job currentJob;
223  24 try {
224  24 currentJob = this.componentManager.<JobContext>getInstance(JobContext.class).getCurrentJob();
225    } catch (ComponentLookupException e) {
226  0 currentJob = null;
227    }
228   
229  24 if (currentJob == null) {
230  0 String wiki;
231  0 try {
232  0 wiki = XarHandlerUtils.getWikiFromNamespace(namespace);
233    } catch (UnsupportedNamespaceException e) {
234  0 throw new UninstallException("Failed to extract wiki id from namespace", e);
235    }
236   
237  0 PackageConfiguration configuration = createPackageConfiguration(null, request, wiki);
238   
239  0 try {
240  0 XarInstalledExtension xarLocalExtension =
241    (XarInstalledExtension) this.xarRepository.resolve(installedExtension.getId());
242  0 Collection<XarEntry> pages = xarLocalExtension.getXarPackage().getEntries();
243  0 this.packager.unimportPages(pages, configuration);
244    } catch (Exception e) {
245    // Not supposed to be possible
246  0 throw new UninstallException(
247    "Failed to get xar extension [" + installedExtension.getId() + "] from xar repository", e);
248    }
249    } else {
250    // The actual delete of pages is done in XarExtensionJobFinishedListener
251    }
252    }
253    }
254   
 
255  274 toggle private void initJobPackageConfiguration(Request request, boolean defaultConflict) throws InterruptedException
256    {
257  274 ExecutionContext context = this.execution.getContext();
258   
259  274 if (context != null && context.getProperty(CONTEXTKEY_PACKAGECONFIGURATION) == null) {
260  61 Job currentJob = null;
261  61 try {
262  61 currentJob = this.componentManager.<JobContext>getInstance(JobContext.class).getCurrentJob();
263    } catch (Exception e) {
264  0 this.logger.error("Failed to lookup JobContext, it will be impossible to do interactive install");
265    }
266   
267  61 if (currentJob != null) {
268  58 PackageConfiguration configuration = new PackageConfiguration();
269  58 context.setProperty(CONTEXTKEY_PACKAGECONFIGURATION, configuration);
270   
271  58 DocumentReference userReference =
272    getRequestUserReference(AbstractExtensionValidator.PROPERTY_USERREFERENCE, request);
273   
274  58 configuration.setInteractive(request.isInteractive());
275  58 configuration.setUser(userReference);
276  58 configuration.setVerbose(request.isVerbose());
277  58 configuration.setSkipMandatorytDocuments(true);
278  58 configuration.setXarExtensionPlan(getXARExtensionPlan());
279   
280  58 configuration.setJobStatus(currentJob.getStatus());
281   
282    // Non blocker conflicts
283  58 configuration.setConflictAction(ConflictType.CURRENT_DELETED,
284    request.getProperty(ConflictQuestion.REQUEST_CONFLICT_DEFAULTANSWER_CURRENT_DELETED),
285    GlobalAction.CURRENT);
286  58 configuration.setConflictAction(ConflictType.MERGE_SUCCESS,
287    request.getProperty(ConflictQuestion.REQUEST_CONFLICT_DEFAULTANSWER_MERGE_SUCCESS),
288    GlobalAction.MERGED);
289    // Blocker conflicts
290  58 configuration.setConflictAction(ConflictType.CURRENT_EXIST,
291    request.getProperty(ConflictQuestion.REQUEST_CONFLICT_DEFAULTANSWER_CURRENT_EXIST),
292  58 configuration.isInteractive() ? GlobalAction.ASK : GlobalAction.NEXT);
293  58 configuration.setConflictAction(ConflictType.MERGE_FAILURE,
294    request.getProperty(ConflictQuestion.REQUEST_CONFLICT_DEFAULTANSWER_MERGE_FAILURE),
295  58 configuration.isInteractive() ? GlobalAction.ASK : GlobalAction.MERGED);
296   
297    // If user asked to be asked about conflict behavior
298  58 if (defaultConflict && currentJob.getStatus().getRequest().isInteractive()) {
299  6 XWikiContext xcontext = xcontextProvider.get();
300    // Make sure the context has the right user
301  6 xcontext.setUserReference(userReference);
302  6 int extensionConflictSetup =
303    NumberUtils.toInt(xcontext.getWiki().getUserPreference("extensionConflictSetup", xcontext), 0);
304   
305  6 if (extensionConflictSetup == 1) {
306  0 DefaultConflictActionQuestion question = new DefaultConflictActionQuestion(configuration);
307   
308  0 currentJob.getStatus().ask(question, 1, TimeUnit.HOURS);
309    }
310    }
311    }
312    }
313    }
314   
 
315  250 toggle private PackageConfiguration createPackageConfiguration(LocalExtension extension, Request request, String wiki)
316    {
317  250 PackageConfiguration configuration;
318   
319    // Search job configuration in the context
320  250 ExecutionContext context = this.execution.getContext();
321  250 if (context != null) {
322  250 configuration = (PackageConfiguration) context.getProperty(CONTEXTKEY_PACKAGECONFIGURATION);
323    } else {
324  0 configuration = null;
325    }
326   
327    // Create a configuration for this extension
328  250 if (configuration != null) {
329  247 configuration = configuration.clone();
330    } else {
331  3 configuration = new PackageConfiguration();
332   
333  3 DocumentReference userReference =
334    getRequestUserReference(AbstractExtensionValidator.PROPERTY_USERREFERENCE, request);
335   
336  3 configuration.setInteractive(request.isInteractive());
337  3 configuration.setUser(userReference);
338  3 configuration.setVerbose(request.isVerbose());
339  3 configuration.setSkipMandatorytDocuments(true);
340    }
341   
342  250 configuration.setWiki(wiki);
343   
344    // Filter entries to import if there is a plan
345  250 if (extension != null && configuration.getXarExtensionPlan() != null) {
346  247 Map<String, Map<XarEntry, LocalExtension>> nextXAREntries =
347    configuration.getXarExtensionPlan().nextXAREntries;
348   
349  247 Map<String, XarEntry> entriesToImport = new HashMap<>();
350   
351  247 Map<XarEntry, LocalExtension> nextXAREntriesOnRoot = nextXAREntries.get(null);
352  247 if (nextXAREntriesOnRoot != null) {
353  16 for (Map.Entry<XarEntry, LocalExtension> entry : nextXAREntriesOnRoot.entrySet()) {
354  166 if (entry.getValue() == extension) {
355  91 entriesToImport.put(entry.getKey().getEntryName(), entry.getKey());
356    }
357    }
358    }
359  247 Map<XarEntry, LocalExtension> nextXAREntriesOnWiki = nextXAREntries.get(wiki);
360  247 if (nextXAREntriesOnWiki != null) {
361  247 for (Map.Entry<XarEntry, LocalExtension> entry : nextXAREntriesOnWiki.entrySet()) {
362  153680 if (entry.getValue() == extension) {
363  3243 entriesToImport.put(entry.getKey().getEntryName(), entry.getKey());
364    }
365    }
366    }
367   
368  247 configuration.setEntriesToImport(entriesToImport);
369    }
370   
371  250 return configuration;
372    }
373    }