diff options
author | gjoranv <gv@verizonmedia.com> | 2020-04-17 18:46:12 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-17 18:46:12 +0200 |
commit | a3cb09f37cef3595776eff9884042ab534b08eed (patch) | |
tree | 786ba7c2d52f087aaac9e221ff1fd3ea6c4de32b | |
parent | 00840534118e457b3694cfe508d9c3d6dcef6d64 (diff) | |
parent | 83df0f345a9f2e9f1fb9415a0906e6dd2563115b (diff) |
Merge pull request #12955 from vespa-engine/gjoranv/bundle-loader-refactor-and-unit-test
Gjoranv/bundle loader refactor and unit test
-rw-r--r-- | container-core/src/main/java/com/yahoo/container/core/config/BundleInstaller.java | 21 | ||||
-rw-r--r-- | container-core/src/main/java/com/yahoo/container/core/config/BundleManager.java (renamed from container-core/src/main/java/com/yahoo/container/core/config/BundleLoader.java) | 204 | ||||
-rw-r--r-- | container-core/src/main/java/com/yahoo/container/core/config/DiskBundleInstaller.java | 31 | ||||
-rw-r--r-- | container-core/src/main/java/com/yahoo/container/core/config/FileAcquirerBundleInstaller.java | 30 | ||||
-rw-r--r-- | container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java | 7 | ||||
-rw-r--r-- | container-core/src/test/java/com/yahoo/container/core/config/BundleManagerTest.java | 106 | ||||
-rw-r--r-- | container-core/src/test/java/com/yahoo/container/core/config/TestBundle.java | 102 | ||||
-rw-r--r-- | container-core/src/test/java/com/yahoo/container/core/config/TestBundleInstaller.java | 20 | ||||
-rw-r--r-- | container-core/src/test/java/com/yahoo/container/core/config/TestOsgi.java | 57 |
9 files changed, 472 insertions, 106 deletions
diff --git a/container-core/src/main/java/com/yahoo/container/core/config/BundleInstaller.java b/container-core/src/main/java/com/yahoo/container/core/config/BundleInstaller.java new file mode 100644 index 00000000000..fc919571b6c --- /dev/null +++ b/container-core/src/main/java/com/yahoo/container/core/config/BundleInstaller.java @@ -0,0 +1,21 @@ +// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.container.core.config; + +import com.yahoo.config.FileReference; +import com.yahoo.osgi.Osgi; +import org.osgi.framework.Bundle; + +import java.util.List; + +/** + * @author gjoranv + */ +public interface BundleInstaller { + + /** + * Installs the bundle with the given file reference, plus all bundles in its X-JDisc-Preinstall-Bundle directive. + * Returns all bundles installed to the given OSGi framework as a result of this call. + */ + List<Bundle> installBundles(FileReference reference, Osgi osgi) throws InterruptedException; + +} diff --git a/container-core/src/main/java/com/yahoo/container/core/config/BundleLoader.java b/container-core/src/main/java/com/yahoo/container/core/config/BundleManager.java index ca11ad387ee..5e9b42a5fda 100644 --- a/container-core/src/main/java/com/yahoo/container/core/config/BundleLoader.java +++ b/container-core/src/main/java/com/yahoo/container/core/config/BundleManager.java @@ -1,4 +1,4 @@ -// Copyright 2017 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.container.core.config; import com.yahoo.collections.PredicateSplit; @@ -9,13 +9,12 @@ import com.yahoo.osgi.Osgi; import org.osgi.framework.Bundle; import org.osgi.framework.wiring.BundleRevision; -import java.io.File; +import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; -import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -23,12 +22,12 @@ import static com.yahoo.collections.PredicateSplit.partition; import static com.yahoo.container.core.BundleLoaderProperties.DISK_BUNDLE_PREFIX; /** - * Manages the set of installed 3rd-party component bundles. + * Manages the set of installed and active/inactive bundles. * - * @author Tony Vaagenes * @author gjoranv + * @author Tony Vaagenes */ -public class BundleLoader { +public class BundleManager { /* Map of file refs of active bundles (not scheduled for uninstall) to a list of all bundles that were installed * (pre-install directive) by the bundle pointed to by the file ref (including itself). @@ -40,20 +39,96 @@ public class BundleLoader { */ private final Map<FileReference, List<Bundle>> reference2Bundles = new LinkedHashMap<>(); - private final Logger log = Logger.getLogger(BundleLoader.class.getName()); + private final Logger log = Logger.getLogger(BundleManager.class.getName()); private final Osgi osgi; - public BundleLoader(Osgi osgi) { + // A custom bundle installer for non-disk bundles, to be used for testing + private BundleInstaller customBundleInstaller = null; + + public BundleManager(Osgi osgi) { this.osgi = osgi; } - private void install(List<FileReference> references) { + /** + * Installs the given set of bundles and returns the set of bundles that is no longer used + * by the application, and should therefore be scheduled for uninstall. + */ + public synchronized Set<Bundle> use(List<FileReference> newFileReferences) { + // Must be done before allowing duplicates because allowed duplicates affect osgi.getCurrentBundles + Set<Bundle> bundlesToUninstall = getObsoleteBundles(newFileReferences); + + Set<FileReference> obsoleteReferences = getObsoleteFileReferences(newFileReferences); + allowDuplicateBundles(obsoleteReferences); + removeInactiveFileReferences(obsoleteReferences); + + installBundles(newFileReferences); + startBundles(); + + bundlesToUninstall.removeAll(allActiveBundles()); + log.info("Bundles to schedule for uninstall: " + bundlesToUninstall); + + log.info(installedBundlesMessage()); + return bundlesToUninstall; + } + + /** + * Returns the bundles that are not assumed to be retained by the new application generation. + * Note that at this point we don't yet know the full set of new bundles, because of the potential + * pre-install directives in the new bundles. However, only "disk bundles" (file:) can be listed + * in the pre-install directive, so we know about all the obsolete application bundles. + */ + private Set<Bundle> getObsoleteBundles(List<FileReference> newReferences) { + Set<Bundle> bundlesToRemove = new HashSet<>(osgi.getCurrentBundles()); + + for (FileReference fileReferenceToKeep : newReferences) { + if (reference2Bundles.containsKey(fileReferenceToKeep)) { + bundlesToRemove.removeAll(reference2Bundles.get(fileReferenceToKeep)); + } + } + bundlesToRemove.removeAll(osgi.getInitialBundles()); + return bundlesToRemove; + } + + + private Set<FileReference> getObsoleteFileReferences(List<FileReference> newReferences) { + Set<FileReference> obsoleteReferences = new HashSet<>(reference2Bundles.keySet()); + obsoleteReferences.removeAll(newReferences); + return obsoleteReferences; + } + + /** + * Allow duplicates (bsn+version) for each bundle that corresponds to obsolete file references, + * and avoid allowing duplicates for bundles that were installed via the + * X-JDisc-Preinstall-Bundle directive. These bundles are always "disk bundles" (library + * bundles installed on the node, and not transferred via file distribution). + * Such bundles will never have duplicates because they always have the same location id. + */ + private void allowDuplicateBundles(Set<FileReference> obsoleteReferences) { + // The bundle at index 0 for each file reference always corresponds to the bundle at the file reference location + Set<Bundle> allowedDuplicates = obsoleteReferences.stream() + .filter(reference -> ! isDiskBundle(reference)) + .map(reference -> reference2Bundles.get(reference).get(0)) + .collect(Collectors.toSet()); + + log.info(() -> allowedDuplicates.isEmpty() ? "" : "Adding bundles to allowed duplicates: " + allowedDuplicates); + osgi.allowDuplicateBundles(allowedDuplicates); + } + + /** + * Cleans up the map of active file references + */ + private void removeInactiveFileReferences(Set<FileReference> fileReferencesToRemove) { + // Clean up the map of active bundles + fileReferencesToRemove.forEach(reference2Bundles::remove); + } + + private void 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(reference2Bundles.keySet()); - PredicateSplit<FileReference> bundlesToInstall_isDisk = partition(bundlesToInstall, BundleLoader::isDiskBundle); + PredicateSplit<FileReference> bundlesToInstall_isDisk = partition(bundlesToInstall, BundleManager::isDiskBundle); installBundlesFromDisk(bundlesToInstall_isDisk.trueValues); installBundlesFromFileDistribution(bundlesToInstall_isDisk.falseValues); @@ -81,7 +156,9 @@ public class BundleLoader { FileAcquirer fileAcquirer = Container.get().getFileAcquirer(); boolean hasFileDistribution = (fileAcquirer != null); if (hasFileDistribution) { - installWithFileDistribution(bundlesToInstall, fileAcquirer); + installWithFileDistribution(bundlesToInstall, new FileAcquirerBundleInstaller(fileAcquirer)); + } else if (customBundleInstaller != null) { + installWithFileDistribution(bundlesToInstall, customBundleInstaller); } else { log.warning("Can't retrieve bundles since file distribution is disabled."); } @@ -89,25 +166,18 @@ public class BundleLoader { } private void installBundleFromDisk(FileReference reference) { - assert(reference.value().startsWith(DISK_BUNDLE_PREFIX)); - String referenceFileName = reference.value().substring(DISK_BUNDLE_PREFIX.length()); log.info("Installing bundle from disk with reference '" + reference.value() + "'"); - File file = new File(referenceFileName); - if ( ! file.exists()) { - throw new IllegalArgumentException("Reference '" + reference.value() + "' not found on disk."); - } - - List<Bundle> bundles = osgi.install(file.getAbsolutePath()); - + var bundleInstaller = new DiskBundleInstaller(); + List<Bundle> bundles = bundleInstaller.installBundles(reference, osgi); reference2Bundles.put(reference, bundles); } - private void installWithFileDistribution(List<FileReference> bundlesToInstall, FileAcquirer fileAcquirer) { + private void installWithFileDistribution(List<FileReference> bundlesToInstall, BundleInstaller bundleInstaller) { for (FileReference reference : bundlesToInstall) { try { log.info("Installing bundle with reference '" + reference.value() + "'"); - List<Bundle> bundles = obtainBundles(reference, fileAcquirer); + List<Bundle> bundles = bundleInstaller.installBundles(reference, osgi); reference2Bundles.put(reference, bundles); } catch(Exception e) { @@ -116,11 +186,6 @@ public class BundleLoader { } } - private List<Bundle> obtainBundles(FileReference reference, FileAcquirer fileAcquirer) throws InterruptedException { - File file = fileAcquirer.waitFor(reference, 7, TimeUnit.DAYS); - return osgi.install(file.getAbsolutePath()); - } - /** * Resolves and starts (calls the Bundles BundleActivator) all bundles. Bundle resolution must take place * after all bundles are installed to ensure that the framework can resolve dependencies between bundles. @@ -146,87 +211,12 @@ public class BundleLoader { return (bundleRevision.getTypes() & BundleRevision.TYPE_FRAGMENT) != 0; } - /** - * Returns the bundles that are not assumed to be retained by the new application generation. - * Note that at this point we don't yet know the full set of new bundles, because of the potential - * pre-install directives in the new bundles. However, only "disk bundles" (file:) can be listed - * in the pre-install directive, so we know about all the obsolete application bundles. - */ - private Set<Bundle> getObsoleteBundles(List<FileReference> newReferences) { - Set<Bundle> bundlesToRemove = new HashSet<>(osgi.getCurrentBundles()); - - for (FileReference fileReferenceToKeep : newReferences) { - if (reference2Bundles.containsKey(fileReferenceToKeep)) { - bundlesToRemove.removeAll(reference2Bundles.get(fileReferenceToKeep)); - } - } - bundlesToRemove.removeAll(osgi.getInitialBundles()); - return bundlesToRemove; - } - - /** - * Cleans up the map of active file references - */ - private void removeInactiveFileReferences(List<FileReference> newReferences) { - // Clean up the map of active bundles - Set<FileReference> fileReferencesToRemove = getObsoleteFileReferences(newReferences); - fileReferencesToRemove.forEach(reference2Bundles::remove); - } - - - /** - * Allow duplicates (bsn+version) for each bundle that corresponds to obsolete file references, - * and avoid allowing duplicates for bundles that were installed via the - * X-JDisc-Preinstall-Bundle directive. These bundles are always "disk bundles" (library - * bundles installed on the node, and not transferred via file distribution). - * Such bundles will never have duplicates because they always have the same location id. - */ - private void allowDuplicateBundles(List<FileReference> newReferences) { - Set<FileReference> obsoleteReferences = getObsoleteFileReferences(newReferences); - - // The bundle at index 0 for each file reference always corresponds to the bundle at the file reference location - Set<Bundle> allowedDuplicates = obsoleteReferences.stream() - .filter(reference -> ! isDiskBundle(reference)) - .map(reference -> reference2Bundles.get(reference).get(0)) - .collect(Collectors.toSet()); - - log.info(() -> allowedDuplicates.isEmpty() ? "" : "Adding bundles to allowed duplicates: " + allowedDuplicates); - osgi.allowDuplicateBundles(allowedDuplicates); - } - - private Set<FileReference> getObsoleteFileReferences(List<FileReference> newReferences) { - Set<FileReference> obsoleteReferences = new HashSet<>(reference2Bundles.keySet()); - obsoleteReferences.removeAll(newReferences); - return obsoleteReferences; - } - private Set<Bundle> allActiveBundles() { return reference2Bundles.keySet().stream() .flatMap(reference -> reference2Bundles.get(reference).stream()) .collect(Collectors.toSet()); } - /** - * Installs the given set of bundles and returns the set of bundles that is no longer used - * by the application, and should therefore be scheduled for uninstall. - */ - public synchronized Set<Bundle> use(List<FileReference> newBundles) { - // Must be done before allowing duplicates because allowed duplicates affect osgi.getCurrentBundles - Set<Bundle> bundlesToUninstall = getObsoleteBundles(newBundles); - - allowDuplicateBundles(newBundles); - removeInactiveFileReferences(newBundles); - - install(newBundles); - startBundles(); - - bundlesToUninstall.removeAll(allActiveBundles()); - log.info("Bundles to schedule for uninstall: " + bundlesToUninstall); - - log.info(installedBundlesMessage()); - return bundlesToUninstall; - } - private String installedBundlesMessage() { StringBuilder sb = new StringBuilder("Installed bundles: {" ); for (Bundle b : osgi.getBundles()) @@ -236,4 +226,14 @@ public class BundleLoader { return sb.toString(); } + // Only for testing + void useCustomBundleInstaller(BundleInstaller bundleInstaller) { + customBundleInstaller = bundleInstaller; + } + + // Only for testing + List<FileReference> getActiveFileReferences() { + return new ArrayList<>(reference2Bundles.keySet()); + } + } diff --git a/container-core/src/main/java/com/yahoo/container/core/config/DiskBundleInstaller.java b/container-core/src/main/java/com/yahoo/container/core/config/DiskBundleInstaller.java new file mode 100644 index 00000000000..3edabe9f861 --- /dev/null +++ b/container-core/src/main/java/com/yahoo/container/core/config/DiskBundleInstaller.java @@ -0,0 +1,31 @@ +// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.container.core.config; + +import com.yahoo.config.FileReference; +import com.yahoo.osgi.Osgi; +import org.osgi.framework.Bundle; + +import java.io.File; +import java.util.List; + +import static com.yahoo.container.core.BundleLoaderProperties.DISK_BUNDLE_PREFIX; + +/** + * @author gjoranv + */ +public class DiskBundleInstaller implements BundleInstaller { + + @Override + public List<Bundle> installBundles(FileReference reference, Osgi osgi) { + assert(reference.value().startsWith(DISK_BUNDLE_PREFIX)); + String referenceFileName = reference.value().substring(DISK_BUNDLE_PREFIX.length()); + + File file = new File(referenceFileName); + if ( ! file.exists()) { + throw new IllegalArgumentException("Reference '" + reference.value() + "' not found on disk."); + } + + return osgi.install(file.getAbsolutePath()); + } + +} diff --git a/container-core/src/main/java/com/yahoo/container/core/config/FileAcquirerBundleInstaller.java b/container-core/src/main/java/com/yahoo/container/core/config/FileAcquirerBundleInstaller.java new file mode 100644 index 00000000000..6f6f86a6366 --- /dev/null +++ b/container-core/src/main/java/com/yahoo/container/core/config/FileAcquirerBundleInstaller.java @@ -0,0 +1,30 @@ +// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.container.core.config; + +import com.yahoo.config.FileReference; +import com.yahoo.filedistribution.fileacquirer.FileAcquirer; +import com.yahoo.osgi.Osgi; +import org.osgi.framework.Bundle; + +import java.io.File; +import java.util.List; +import java.util.concurrent.TimeUnit; + +/** + * @author gjoranv + */ +public class FileAcquirerBundleInstaller implements BundleInstaller { + + private final FileAcquirer fileAcquirer; + + public FileAcquirerBundleInstaller(FileAcquirer fileAcquirer) { + this.fileAcquirer = fileAcquirer; + } + + @Override + public List<Bundle> installBundles(FileReference reference, Osgi osgi) throws InterruptedException { + File file = fileAcquirer.waitFor(reference, 7, TimeUnit.DAYS); + return osgi.install(file.getAbsolutePath()); + } + +} diff --git a/container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java b/container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java index ef132694e10..d87b38e8b18 100644 --- a/container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java +++ b/container-core/src/main/java/com/yahoo/container/core/config/HandlersConfigurerDi.java @@ -9,7 +9,6 @@ import com.yahoo.component.ComponentSpecification; import com.yahoo.component.provider.ComponentRegistry; import com.yahoo.concurrent.ThreadFactoryFactory; import com.yahoo.config.FileReference; -import com.yahoo.container.core.config.testutil.MockOsgiWrapper; import com.yahoo.container.di.ComponentDeconstructor; import com.yahoo.container.di.Container; import com.yahoo.container.di.componentgraph.core.ComponentGraph; @@ -82,7 +81,7 @@ public class HandlersConfigurerDi { OsgiFramework osgiFramework) { this(subscriberFactory, vespaContainer, configId, deconstructor, discInjector, - new ContainerAndDiOsgi(osgiFramework, new BundleLoader(new OsgiImpl(osgiFramework)))); + new ContainerAndDiOsgi(osgiFramework, new BundleManager(new OsgiImpl(osgiFramework)))); } // Only public for testing @@ -102,9 +101,9 @@ public class HandlersConfigurerDi { private static class ContainerAndDiOsgi extends OsgiImpl implements OsgiWrapper { private final OsgiFramework osgiFramework; - private final BundleLoader bundleLoader; + private final BundleManager bundleLoader; - public ContainerAndDiOsgi(OsgiFramework osgiFramework, BundleLoader bundleLoader) { + public ContainerAndDiOsgi(OsgiFramework osgiFramework, BundleManager bundleLoader) { super(osgiFramework); this.osgiFramework = osgiFramework; this.bundleLoader = bundleLoader; diff --git a/container-core/src/test/java/com/yahoo/container/core/config/BundleManagerTest.java b/container-core/src/test/java/com/yahoo/container/core/config/BundleManagerTest.java new file mode 100644 index 00000000000..414e6b05128 --- /dev/null +++ b/container-core/src/test/java/com/yahoo/container/core/config/BundleManagerTest.java @@ -0,0 +1,106 @@ +// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.container.core.config; + +import com.yahoo.config.FileReference; +import org.junit.Before; +import org.junit.Test; +import org.osgi.framework.Bundle; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * @author gjoranv + */ +public class BundleManagerTest { + + private static final FileReference BUNDLE_1_REF = new FileReference("bundle-1"); + private static final Bundle BUNDLE_1 = new TestBundle(BUNDLE_1_REF.value()); + private static final FileReference BUNDLE_2_REF = new FileReference("bundle-2"); + private static final Bundle BUNDLE_2 = new TestBundle(BUNDLE_2_REF.value()); + + private BundleManager bundleLoader; + private TestOsgi osgi; + + @Before + public void setup() { + osgi = new TestOsgi(testBundles()); + var bundleInstaller = new TestBundleInstaller(); + bundleLoader = new BundleManager(osgi); + bundleLoader.useCustomBundleInstaller(bundleInstaller); + } + + @Test + public void bundles_are_installed_and_started() { + bundleLoader.use(List.of(BUNDLE_1_REF)); + assertEquals(1, osgi.getInstalledBundles().size()); + + // The bundle is installed and started + TestBundle installedBundle = (TestBundle)osgi.getInstalledBundles().get(0); + assertEquals(BUNDLE_1.getSymbolicName(), installedBundle.getSymbolicName()); + assertTrue(installedBundle.started); + + // The file reference is active + assertEquals(1, bundleLoader.getActiveFileReferences().size()); + assertEquals(BUNDLE_1_REF, bundleLoader.getActiveFileReferences().get(0)); + } + + @Test + public void new_bundle_can_be_installed_in_reconfig() { + bundleLoader.use(List.of(BUNDLE_1_REF)); + Set<Bundle> obsoleteBundles = bundleLoader.use(List.of(BUNDLE_1_REF, BUNDLE_2_REF)); + + // No bundles are obsolete + assertTrue(obsoleteBundles.isEmpty()); + + // Both bundles are installed + assertEquals(2, osgi.getInstalledBundles().size()); + assertEquals(BUNDLE_1.getSymbolicName(), osgi.getInstalledBundles().get(0).getSymbolicName()); + assertEquals(BUNDLE_2.getSymbolicName(), osgi.getInstalledBundles().get(1).getSymbolicName()); + + // Both bundles are current + assertEquals(2, osgi.getCurrentBundles().size()); + assertEquals(BUNDLE_1.getSymbolicName(), osgi.getCurrentBundles().get(0).getSymbolicName()); + assertEquals(BUNDLE_2.getSymbolicName(), osgi.getCurrentBundles().get(1).getSymbolicName()); + + + // Both file references are active + assertEquals(2, bundleLoader.getActiveFileReferences().size()); + assertEquals(BUNDLE_1_REF, bundleLoader.getActiveFileReferences().get(0)); + assertEquals(BUNDLE_2_REF, bundleLoader.getActiveFileReferences().get(1)); + } + + @Test + public void unused_bundle_is_marked_obsolete_after_reconfig() { + bundleLoader.use(List.of(BUNDLE_1_REF)); + Set<Bundle> obsoleteBundles = bundleLoader.use(List.of(BUNDLE_2_REF)); + + // The returned set of obsolete bundles contains bundle-1 + assertEquals(1, obsoleteBundles.size()); + assertEquals(BUNDLE_1.getSymbolicName(), obsoleteBundles.iterator().next().getSymbolicName()); + + // Both bundles are installed + assertEquals(2, osgi.getInstalledBundles().size()); + assertEquals(BUNDLE_1.getSymbolicName(), osgi.getInstalledBundles().get(0).getSymbolicName()); + assertEquals(BUNDLE_2.getSymbolicName(), osgi.getInstalledBundles().get(1).getSymbolicName()); + + // Only bundle-2 is current + assertEquals(1, osgi.getCurrentBundles().size()); + assertEquals(BUNDLE_2.getSymbolicName(), osgi.getCurrentBundles().get(0).getSymbolicName()); + + // Only the bundle-2 file reference is active + assertEquals(1, bundleLoader.getActiveFileReferences().size()); + assertEquals(BUNDLE_2_REF, bundleLoader.getActiveFileReferences().get(0)); + } + + + private static Map<String, Bundle> testBundles() { + return Map.of(BUNDLE_1_REF.value(), BUNDLE_1, + BUNDLE_2_REF.value(), BUNDLE_2); + } + +} diff --git a/container-core/src/test/java/com/yahoo/container/core/config/TestBundle.java b/container-core/src/test/java/com/yahoo/container/core/config/TestBundle.java new file mode 100644 index 00000000000..421f4302c27 --- /dev/null +++ b/container-core/src/test/java/com/yahoo/container/core/config/TestBundle.java @@ -0,0 +1,102 @@ +// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.container.core.config; + +import com.yahoo.container.bundle.MockBundle; +import org.osgi.framework.Bundle; +import org.osgi.framework.Version; +import org.osgi.framework.wiring.BundleCapability; +import org.osgi.framework.wiring.BundleRequirement; +import org.osgi.framework.wiring.BundleRevision; +import org.osgi.framework.wiring.BundleWiring; +import org.osgi.resource.Capability; +import org.osgi.resource.Requirement; + +import java.util.List; + +/** + * @author gjoranv + */ +class TestBundle extends MockBundle { + + private static final BundleRevision revision = new TestBundleRevision(); + + private final String symbolicName; + + boolean started = false; + + TestBundle(String symbolicName) { + this.symbolicName = symbolicName; + } + + @Override + public void start() { + started = true; + } + + @Override + public String getSymbolicName() { + return symbolicName; + } + + + @SuppressWarnings("unchecked") + @Override + public <T> T adapt(Class<T> type) { + if (type.equals(BundleRevision.class)) { + return (T) revision; + } else { + throw new UnsupportedOperationException(); + } + } + + + static class TestBundleRevision implements BundleRevision { + + // Ensure this is not seen as a fragment bundle. + @Override + public int getTypes() { + return 0; + } + + @Override + public String getSymbolicName() { + throw new UnsupportedOperationException(); + } + + @Override + public Version getVersion() { + throw new UnsupportedOperationException(); + } + + @Override + public List<BundleCapability> getDeclaredCapabilities(String namespace) { + throw new UnsupportedOperationException(); + } + + @Override + public List<BundleRequirement> getDeclaredRequirements(String namespace) { + throw new UnsupportedOperationException(); + } + + @Override + public BundleWiring getWiring() { + throw new UnsupportedOperationException(); + } + + @Override + public List<Capability> getCapabilities(String namespace) { + throw new UnsupportedOperationException(); + } + + @Override + public List<Requirement> getRequirements(String namespace) { + throw new UnsupportedOperationException(); + } + + @Override + public Bundle getBundle() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/container-core/src/test/java/com/yahoo/container/core/config/TestBundleInstaller.java b/container-core/src/test/java/com/yahoo/container/core/config/TestBundleInstaller.java new file mode 100644 index 00000000000..43a5268eabf --- /dev/null +++ b/container-core/src/test/java/com/yahoo/container/core/config/TestBundleInstaller.java @@ -0,0 +1,20 @@ +// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.container.core.config; + +import com.yahoo.config.FileReference; +import com.yahoo.osgi.Osgi; +import org.osgi.framework.Bundle; + +import java.util.List; + +/** + * @author gjoranv + */ +class TestBundleInstaller implements BundleInstaller { + + @Override + public List<Bundle> installBundles(FileReference reference, Osgi osgi) { + return osgi.install(reference.value()); + } + +} diff --git a/container-core/src/test/java/com/yahoo/container/core/config/TestOsgi.java b/container-core/src/test/java/com/yahoo/container/core/config/TestOsgi.java new file mode 100644 index 00000000000..54a3159239c --- /dev/null +++ b/container-core/src/test/java/com/yahoo/container/core/config/TestOsgi.java @@ -0,0 +1,57 @@ +// Copyright 2020 Oath Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.container.core.config; + +import com.yahoo.osgi.MockOsgi; +import org.osgi.framework.Bundle; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * @author gjoranv + */ +class TestOsgi extends MockOsgi { + + private final Map<String, Bundle> availableBundles; + + private final List<Bundle> installedBundles = new ArrayList<>(); + private final List<Bundle> allowedDuplicates = new ArrayList<>(); + + TestOsgi(Map<String, Bundle> availableBundles) { + this.availableBundles = availableBundles; + } + + @Override + public List<Bundle> install(String fileReferenceValue) { + if (! availableBundles.containsKey(fileReferenceValue)) + throw new IllegalArgumentException("No such bundle: " + fileReferenceValue); + + Bundle bundle = availableBundles.get(fileReferenceValue); + installedBundles.add(bundle); + return List.of(bundle); + } + + @Override + public Bundle[] getBundles() { + return installedBundles.toArray(new Bundle[0]); + } + + public List<Bundle> getInstalledBundles() { + return installedBundles; + } + + @Override + public List<Bundle> getCurrentBundles() { + var currentBundles = new ArrayList<>(installedBundles); + currentBundles.removeAll(allowedDuplicates); + return currentBundles; + } + + @Override + public void allowDuplicateBundles(Collection<Bundle> bundles) { + allowedDuplicates.addAll(bundles); + } + +} |