diff options
author | gjoranv <gv@verizonmedia.com> | 2022-08-24 16:00:35 +0200 |
---|---|---|
committer | gjoranv <gv@verizonmedia.com> | 2022-08-25 17:34:02 +0200 |
commit | b0a398eaeadfaf12e31bcfef2e41892439db1149 (patch) | |
tree | 34f6d7bf053cb8fb31cfd9b73e37e979bd392a39 /container-core/src/main/java/com/yahoo/container/core/config/ApplicationBundleLoader.java | |
parent | c05ab2660c017cb54ee9a591e2e02434e78bd176 (diff) |
Clean up bundles and allowed duplicates after a failed reconfig.
Diffstat (limited to 'container-core/src/main/java/com/yahoo/container/core/config/ApplicationBundleLoader.java')
-rw-r--r-- | container-core/src/main/java/com/yahoo/container/core/config/ApplicationBundleLoader.java | 88 |
1 files changed, 56 insertions, 32 deletions
diff --git a/container-core/src/main/java/com/yahoo/container/core/config/ApplicationBundleLoader.java b/container-core/src/main/java/com/yahoo/container/core/config/ApplicationBundleLoader.java index f0ca5aee8a2..972d6677e3b 100644 --- a/container-core/src/main/java/com/yahoo/container/core/config/ApplicationBundleLoader.java +++ b/container-core/src/main/java/com/yahoo/container/core/config/ApplicationBundleLoader.java @@ -6,13 +6,14 @@ import com.yahoo.osgi.Osgi; import org.osgi.framework.Bundle; import java.util.ArrayList; +import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Logger; -import java.util.stream.Collectors; /** * Manages the set of installed and active/inactive bundles. @@ -24,16 +25,15 @@ public class ApplicationBundleLoader { private static final Logger log = Logger.getLogger(ApplicationBundleLoader.class.getName()); - /** - * Map of file refs of active bundles (not scheduled for uninstall) to the installed bundle. - * - * Used to: - * 1. Avoid installing already installed bundles. Just an optimization, installing the same bundle location is a NOP - * 2. Start bundles (all are started every time) - * 3. Calculate the set of bundles to uninstall - */ + // The active bundles for the current application generation. private final Map<FileReference, Bundle> reference2Bundle = new LinkedHashMap<>(); + // The bundles that are obsolete from the previous generation, but kept in case the generation is reverted. + private Map<FileReference, Bundle> obsoleteBundles = Map.of(); + + // The bundles that exclusively belong to the current application generation. + private Map<FileReference, Bundle> bundlesFromNewGeneration = Map.of(); + private final Osgi osgi; private final FileAcquirerBundleInstaller bundleInstaller; @@ -48,54 +48,76 @@ public class ApplicationBundleLoader { */ public synchronized Set<Bundle> useBundles(List<FileReference> newFileReferences) { - Set<FileReference> obsoleteReferences = getObsoleteFileReferences(newFileReferences); - Set<Bundle> bundlesToUninstall = getObsoleteBundles(obsoleteReferences); + obsoleteBundles = removeObsoleteBundles(newFileReferences); + Set<Bundle> bundlesToUninstall = new LinkedHashSet<>(obsoleteBundles.values()); log.info("Bundles to schedule for uninstall: " + bundlesToUninstall); osgi.allowDuplicateBundles(bundlesToUninstall); - removeInactiveFileReferences(obsoleteReferences); - installBundles(newFileReferences); + bundlesFromNewGeneration = installBundles(newFileReferences); BundleStarter.startBundles(reference2Bundle.values()); log.info(installedBundlesMessage()); return bundlesToUninstall; } - private Set<FileReference> getObsoleteFileReferences(List<FileReference> newReferences) { - Set<FileReference> obsoleteReferences = new HashSet<>(reference2Bundle.keySet()); - obsoleteReferences.removeAll(newReferences); - return obsoleteReferences; + /** + * Restores state from the previous application generation and returns the set of bundles that + * exclusively belongs to the latest (failed) application generation. Uninstalling must + * be done by the Deconstructor as they may still be needed by components from the failed gen. + */ + public synchronized Collection<Bundle> revertToPreviousGeneration() { + reference2Bundle.putAll(obsoleteBundles); + bundlesFromNewGeneration.forEach(reference2Bundle::remove); + Collection<Bundle> ret = bundlesFromNewGeneration.values(); + + // No duplicate bundles should be allowed until the next call to useBundles. + osgi.allowDuplicateBundles(Set.of()); + + // Clear restore info in case this method is called multiple times, for some reason. + bundlesFromNewGeneration = Map.of(); + obsoleteBundles = Map.of(); + + return ret; } /** - * Returns the bundles that will not be retained by the new application generation. + * Calculates the set of bundles that are not needed by the new application generation and + * removes them from the map of active bundles. + * + * Returns the map of bundles that are not needed by the new application generation. */ - private Set<Bundle> getObsoleteBundles(Set<FileReference> obsoleteReferences) { - return obsoleteReferences.stream().map(reference2Bundle::get).collect(Collectors.toSet()); - } + private Map<FileReference, Bundle> removeObsoleteBundles(List<FileReference> newReferences) { + Map<FileReference, Bundle> obsoleteReferences = new LinkedHashMap<>(reference2Bundle); + newReferences.forEach(obsoleteReferences::remove); - private void removeInactiveFileReferences(Set<FileReference> fileReferencesToRemove) { - fileReferencesToRemove.forEach(reference2Bundle::remove); + obsoleteReferences.forEach(reference2Bundle::remove); + return obsoleteReferences; } - private void installBundles(List<FileReference> references) { + /** + * Returns the set of new bundles that were installed. + */ + private Map<FileReference, Bundle> installBundles(List<FileReference> references) { Set<FileReference> bundlesToInstall = new HashSet<>(references); // This is just an optimization, as installing a bundle with the same location id returns the already installed bundle. bundlesToInstall.removeAll(reference2Bundle.keySet()); - if (!bundlesToInstall.isEmpty()) { - if (bundleInstaller.hasFileDistribution()) { - installWithFileDistribution(bundlesToInstall, bundleInstaller); - } else { - log.warning("Can't retrieve bundles since file distribution is disabled."); - } + if (bundlesToInstall.isEmpty()) return Map.of(); + + if (bundleInstaller.hasFileDistribution()) { + return installWithFileDistribution(bundlesToInstall, bundleInstaller); + } else { + log.warning("Can't retrieve bundles since file distribution is disabled."); + return Map.of(); } } - private void installWithFileDistribution(Set<FileReference> bundlesToInstall, - FileAcquirerBundleInstaller bundleInstaller) { + private Map<FileReference, Bundle> installWithFileDistribution(Set<FileReference> bundlesToInstall, + FileAcquirerBundleInstaller bundleInstaller) { + var newBundles = new LinkedHashMap<FileReference, Bundle>(); + for (FileReference reference : bundlesToInstall) { try { log.info("Installing bundle with reference '" + reference.value() + "'"); @@ -108,11 +130,13 @@ public class ApplicationBundleLoader { throw new RuntimeException("Bundle '" + bundles.get(0).getSymbolicName() + "' tried to pre-install bundles from disk."); } reference2Bundle.put(reference, bundles.get(0)); + newBundles.put(reference, bundles.get(0)); } catch(Exception e) { throw new RuntimeException("Could not install bundle with reference '" + reference + "'", e); } } + return newBundles; } private String installedBundlesMessage() { |