diff options
author | gjoranv <gv@verizonmedia.com> | 2019-10-05 00:06:55 +0200 |
---|---|---|
committer | gjoranv <gv@verizonmedia.com> | 2019-10-16 11:41:33 +0200 |
commit | af771506b97cf996dd7c25053c07e1cc8f9eba99 (patch) | |
tree | e77bdc57d861ee747f4211e638a163f394472238 /jdisc_core/src/main/java/com/yahoo/jdisc/core/BundleCollisionHook.java | |
parent | 685704ab391abc6efdb9577c24bdf7ed48333f8e (diff) |
Allow duplicate bsn+version for a given set of bundles.
- Set 'org.osgi.framework.bsnversion' to 'managed'
- Add bundle collision/event hook to handle duplicates and their
lifecycle.
- Add unit tests for duplicate bundles.
o Add bundle 'l1-dup' which is a duplicate of the existing 'l1',
but returns a different value from its implemented class.
o Add bundle 'ml-dup' that is exactly the same as 'ml'
Diffstat (limited to 'jdisc_core/src/main/java/com/yahoo/jdisc/core/BundleCollisionHook.java')
-rw-r--r-- | jdisc_core/src/main/java/com/yahoo/jdisc/core/BundleCollisionHook.java | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/BundleCollisionHook.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/BundleCollisionHook.java new file mode 100644 index 00000000000..6fc7c368ad1 --- /dev/null +++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/BundleCollisionHook.java @@ -0,0 +1,107 @@ +package com.yahoo.jdisc.core; + +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.ServiceRegistration; +import org.osgi.framework.Version; +import org.osgi.framework.hooks.bundle.CollisionHook; +import org.osgi.framework.hooks.bundle.EventHook; + +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * A bundle {@link CollisionHook} that contains a set of bundles that are allowed to collide with + * bundles that are about to be installed. In order to clean up when bundles are uninstalled, this + * is also a bundle {@link EventHook}. + * + * Thread safe + * + * @author gjoranv + */ +public class BundleCollisionHook implements CollisionHook, EventHook { + + private ServiceRegistration<?> registration; + private Map<Bundle, BsnVersion> allowedDuplicates = new HashMap<>(5); + + public void start(BundleContext context) { + if (registration != null) { + throw new IllegalStateException(); + } + registration = context.registerService(new String[]{CollisionHook.class.getName(), EventHook.class.getName()}, + this, null); + } + + public void stop() { + registration.unregister(); + registration = null; + } + + /** + * Adds a collection of bundles to the allowed duplicates. + */ + synchronized void allowDuplicateBundles(Collection<Bundle> bundles) { + for (var bundle : bundles) { + allowedDuplicates.put(bundle, new BsnVersion(bundle.getSymbolicName(), bundle.getVersion())); + } + } + + /** + * Cleans up the allowed duplicates when a bundle is uninstalled. + */ + @Override + public void event(BundleEvent event, Collection<BundleContext> contexts) { + if (event.getType() != BundleEvent.UNINSTALLED) return; + + synchronized (this) { + allowedDuplicates.remove(event.getBundle()); + } + } + + @Override + public synchronized void filterCollisions(int operationType, Bundle target, Collection<Bundle> collisionCandidates) { + Set<Bundle> whitelistedCandidates = new HashSet<>(); + for (var bundle : collisionCandidates) { + var bsnVersion = new BsnVersion(bundle.getSymbolicName(), bundle.getVersion()); + + // This is O(n), but n should be small here, plus this is only called when bundles collide. + if (allowedDuplicates.containsValue(bsnVersion)) { + whitelistedCandidates.add(bundle); + } + } + collisionCandidates.removeAll(whitelistedCandidates); + } + + + static class BsnVersion { + + private final String symbolicName; + private final Version version; + + BsnVersion(String symbolicName, Version version) { + this.symbolicName = symbolicName; + this.version = version; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + BsnVersion that = (BsnVersion) o; + return Objects.equals(symbolicName, that.symbolicName) && + version.equals(that.version); + } + + @Override + public int hashCode() { + return Objects.hash(symbolicName, version); + } + + } + +} |