aboutsummaryrefslogtreecommitdiffstats
path: root/container-core/src/main/java/com/yahoo/container/core/config/ApplicationBundleLoader.java
diff options
context:
space:
mode:
authorgjoranv <gv@verizonmedia.com>2022-08-24 16:00:35 +0200
committergjoranv <gv@verizonmedia.com>2022-08-25 17:34:02 +0200
commitb0a398eaeadfaf12e31bcfef2e41892439db1149 (patch)
tree34f6d7bf053cb8fb31cfd9b73e37e979bd392a39 /container-core/src/main/java/com/yahoo/container/core/config/ApplicationBundleLoader.java
parentc05ab2660c017cb54ee9a591e2e02434e78bd176 (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.java88
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() {