diff --git a/xwiki-commons-core/xwiki-commons-extension/xwiki-commons-extension-api/src/main/java/org/xwiki/extension/job/internal/AbstractInstallPlanJob.java b/xwiki-commons-core/xwiki-commons-extension/xwiki-commons-extension-api/src/main/java/org/xwiki/extension/job/internal/AbstractInstallPlanJob.java index 392b276044..f50a773623 100644 --- a/xwiki-commons-core/xwiki-commons-extension/xwiki-commons-extension-api/src/main/java/org/xwiki/extension/job/internal/AbstractInstallPlanJob.java +++ b/xwiki-commons-core/xwiki-commons-extension/xwiki-commons-extension-api/src/main/java/org/xwiki/extension/job/internal/AbstractInstallPlanJob.java @@ -1301,7 +1301,17 @@ private Set getReplacedInstalledExtensions(Extension newExte throws IncompatibleVersionConstraintException, ResolveException, InstallException { if (getRequest().isInstalledIgnored()) { - return Set.of(); + // Even when installed extensions are ignored for the plan, we still need to include the currently + // installed version (if any) as a "previous" extension so that the install job can properly replace it + // using the UPGRADE flow (which correctly unregisters then re-registers the extension in the installed + // extension repository, instead of failing with "already installed"). + Set previousExtensions = new LinkedHashSet<>(); + InstalledExtension installedExtension = + this.installedExtensionRepository.getInstalledExtension(newExtension.getId().getId(), namespace); + if (installedExtension != null && installedExtension.isInstalled(namespace)) { + previousExtensions.add(installedExtension); + } + return previousExtensions; } // If a namespace extension already exist on root, fail the install diff --git a/xwiki-commons-core/xwiki-commons-extension/xwiki-commons-extension-api/src/main/java/org/xwiki/extension/repository/internal/local/LocalExtensionStorage.java b/xwiki-commons-core/xwiki-commons-extension/xwiki-commons-extension-api/src/main/java/org/xwiki/extension/repository/internal/local/LocalExtensionStorage.java index 3e27bd7a6a..4e180fa04b 100644 --- a/xwiki-commons-core/xwiki-commons-extension/xwiki-commons-extension-api/src/main/java/org/xwiki/extension/repository/internal/local/LocalExtensionStorage.java +++ b/xwiki-commons-core/xwiki-commons-extension/xwiki-commons-extension-api/src/main/java/org/xwiki/extension/repository/internal/local/LocalExtensionStorage.java @@ -310,7 +310,7 @@ public void removeExtension(DefaultLocalExtension extension) throws IOException // Delete the extension descriptor file try { Files.delete(extensionDescriptorFilePath); - } catch (FileNotFoundException e) { + } catch (IOException e) { LOGGER.warn( "Couldn't delete the extension descriptor file [{}] when removing extension [{}], " + "because it doesn't exist. Root error: [{}]", @@ -323,7 +323,7 @@ public void removeExtension(DefaultLocalExtension extension) throws IOException // Delete the extension file try { Files.delete(extensionFile.getFile().toPath()); - } catch (FileNotFoundException e) { + } catch (IOException e) { LOGGER.warn("Extension file [{}] was not found while removing [{}] extension", extensionFile.getAbsolutePath(), extension.getId().getId()); } @@ -333,13 +333,13 @@ public void removeExtension(DefaultLocalExtension extension) throws IOException Path extensionVersionFolderPath = extensionDescriptorFilePath.getParent(); try { // Delete the extension version folder - Files.delete(extensionDescriptorFilePath); + Files.delete(extensionVersionFolderPath); // Try to delete the extension folder deleteExtensionFolderIfEmpty(extensionVersionFolderPath.getParent()); } catch (DirectoryNotEmptyException e) { LOGGER.warn("Extension version folder [{}] was not empty after removing the extension [{}]. Keeping it.", - extensionDescriptorFilePath, extension.getId().getId()); + extensionVersionFolderPath, extension.getId().getId()); } }