aboutsummaryrefslogtreecommitdiffstats
path: root/jdisc_core
diff options
context:
space:
mode:
Diffstat (limited to 'jdisc_core')
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/BundleCollisionHook.java142
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixFramework.java25
-rw-r--r--jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixParams.java1
3 files changed, 163 insertions, 5 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..58ad5df9b0d
--- /dev/null
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/BundleCollisionHook.java
@@ -0,0 +1,142 @@
+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 org.osgi.framework.hooks.bundle.FindHook;
+
+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, FindHook {
+
+ private ServiceRegistration<?> registration;
+ private Map<Bundle, BsnVersion> allowedDuplicates = new HashMap<>(5);
+
+ public void start(BundleContext context) {
+ if (registration != null) {
+ throw new IllegalStateException();
+ }
+ String[] serviceClasses = {CollisionHook.class.getName(), EventHook.class.getName(), FindHook.class.getName()};
+ registration = context.registerService(serviceClasses, 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));
+ }
+ }
+
+ /**
+ * 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());
+ }
+ }
+
+ /**
+ * Removes duplicates of the allowed duplicate bundles from the given collision candidates.
+ */
+ @Override
+ public synchronized void filterCollisions(int operationType, Bundle target, Collection<Bundle> collisionCandidates) {
+ Set<Bundle> whitelistedCandidates = new HashSet<>();
+ for (var bundle : collisionCandidates) {
+ // This is O(n), but n should be small here, plus this is only called when bundles collide.
+ if (allowedDuplicates.containsValue(new BsnVersion(bundle))) {
+ whitelistedCandidates.add(bundle);
+ }
+ }
+ collisionCandidates.removeAll(whitelistedCandidates);
+ }
+
+ /**
+ * Filters out the set of bundles that should not be visible to the bundle associated with the given context.
+ * If the given context represents one of the allowed duplicates, this method filters out all bundles
+ * that are duplicates of the allowed duplicates. Otherwise this method filters out the allowed duplicates,
+ * so they are not visible to other bundles.
+ *
+ * NOTE: This hook method is added for a consistent view of the installed bundles, but is not actively
+ * used by jdisc. The OSGi framework does not use FindHooks when calculating bundle wiring.
+ */
+ @Override
+ public synchronized void find(BundleContext context, Collection<Bundle> bundles) {
+ Set<Bundle> bundlesToHide = new HashSet<>();
+ if (allowedDuplicates.containsKey(context.getBundle())) {
+ for (var bundle : bundles) {
+ // isDuplicate... is O(n), but n should be small here, plus this is only run for duplicate bundles.
+ if (isDuplicateOfAllowedDuplicates(bundle)) {
+ bundlesToHide.add(bundle);
+ }
+ }
+ } else {
+ for (var bundle : bundles) {
+ if (allowedDuplicates.containsKey(bundle)) {
+ bundlesToHide.add(bundle);
+ }
+ }
+ }
+ bundles.removeAll(bundlesToHide);
+ }
+
+ private boolean isDuplicateOfAllowedDuplicates(Bundle bundle) {
+ return ! allowedDuplicates.containsKey(bundle) && allowedDuplicates.containsValue(new BsnVersion(bundle));
+ }
+
+
+ static class BsnVersion {
+
+ private final String symbolicName;
+ private final Version version;
+
+ BsnVersion(Bundle bundle) {
+ this.symbolicName = bundle.getSymbolicName();
+ this.version = bundle.getVersion();
+ }
+
+ @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);
+ }
+
+ }
+
+}
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixFramework.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixFramework.java
index 96fc0c91d2d..19a1707e97c 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixFramework.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixFramework.java
@@ -15,6 +15,7 @@ import org.osgi.framework.wiring.FrameworkWiring;
import java.io.File;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
@@ -26,6 +27,7 @@ import java.util.logging.Logger;
/**
* @author Simon Thoresen Hult
+ * @author gjoranv
*/
public class FelixFramework implements OsgiFramework {
@@ -35,11 +37,14 @@ public class FelixFramework implements OsgiFramework {
private final ConsoleLogManager logListener;
private final Felix felix;
+ private final BundleCollisionHook collisionHook;
+
@Inject
public FelixFramework(FelixParams params) {
deleteDirContents(new File(params.getCachePath()));
felix = new Felix(params.toConfig());
logListener = params.isLoggerEnabled() ? new ConsoleLogManager() : null;
+ collisionHook = new BundleCollisionHook();
}
@Override
@@ -48,6 +53,7 @@ public class FelixFramework implements OsgiFramework {
felix.start();
BundleContext ctx = felix.getBundleContext();
+ collisionHook.start(ctx);
logService.start(ctx);
logHandler.install(ctx);
if (logListener != null) {
@@ -65,6 +71,7 @@ public class FelixFramework implements OsgiFramework {
}
logHandler.uninstall();
logService.stop();
+ collisionHook.stop();
}
felix.stop();
try {
@@ -90,8 +97,8 @@ public class FelixFramework implements OsgiFramework {
for (Bundle bundle : bundles) {
if (!privileged && OsgiHeader.isSet(bundle, OsgiHeader.PRIVILEGED_ACTIVATOR)) {
log.log(Level.INFO, "OSGi bundle '" + bundle.getSymbolicName() + "' " +
- "states that it requires privileged " +
- "initialization, but privileges are not available. YMMV.");
+ "states that it requires privileged " +
+ "initialization, but privileges are not available. YMMV.");
}
if (bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null) {
continue; // fragments can not be started
@@ -102,7 +109,7 @@ public class FelixFramework implements OsgiFramework {
}
private String startedBundlesMessage(List<Bundle> bundles) {
- StringBuilder sb = new StringBuilder("Started bundles: {" );
+ StringBuilder sb = new StringBuilder("Started bundles: {");
for (Bundle b : bundles)
sb.append("[" + b.getBundleId() + "]" + b.getSymbolicName() + ":" + b.getVersion() + ", ");
sb.setLength(sb.length() - 2);
@@ -127,9 +134,9 @@ public class FelixFramework implements OsgiFramework {
});
try {
long TIMEOUT_SECONDS = 60L;
- if ( ! latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
+ if (!latch.await(TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
log.warning("No PACKAGES_REFRESHED FrameworkEvent received within " + TIMEOUT_SECONDS +
- " seconds of calling FrameworkWiring.refreshBundles()");
+ " seconds of calling FrameworkWiring.refreshBundles()");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
@@ -146,6 +153,14 @@ public class FelixFramework implements OsgiFramework {
return Arrays.asList(felix.getBundleContext().getBundles());
}
+ public List<Bundle> getBundles(Bundle requestingBundle) {
+ return Arrays.asList(requestingBundle.getBundleContext().getBundles());
+ }
+
+ public void allowDuplicateBundles(Collection<Bundle> bundles) {
+ collisionHook.allowDuplicateBundles(bundles);
+ }
+
private void installBundle(String bundleLocation, Set<String> mask, List<Bundle> out) throws BundleException {
bundleLocation = BundleLocationResolver.resolve(bundleLocation);
if (mask.contains(bundleLocation)) {
diff --git a/jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixParams.java b/jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixParams.java
index 9b877f68efd..0bca9388885 100644
--- a/jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixParams.java
+++ b/jdisc_core/src/main/java/com/yahoo/jdisc/core/FelixParams.java
@@ -56,6 +56,7 @@ public class FelixParams {
ret.put(Constants.FRAMEWORK_SYSTEMPACKAGES, exportPackages.toString());
ret.put(Constants.SUPPORTS_BOOTCLASSPATH_EXTENSION, "true");
ret.put(Constants.FRAMEWORK_BOOTDELEGATION, "com.yourkit.runtime,com.yourkit.probes,com.yourkit.probes.builtin,com.singularity.*");
+ ret.put(Constants.FRAMEWORK_BSNVERSION, Constants.FRAMEWORK_BSNVERSION_MANAGED);
return ret;
}
}