[AMQ-5821] Use wiring to check for extensions

This closes #119
This commit is contained in:
Christian Schneider 2015-06-23 14:54:06 +02:00 committed by Daniel Kulp
parent f0cb95c792
commit 97cd60fb7e
1 changed files with 55 additions and 17 deletions

View File

@ -16,14 +16,18 @@
*/ */
package org.apache.activemq.util.osgi; package org.apache.activemq.util.osgi;
import static org.osgi.framework.wiring.BundleRevision.PACKAGE_NAMESPACE;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.URL; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
@ -38,6 +42,9 @@ import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext; import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent; import org.osgi.framework.BundleEvent;
import org.osgi.framework.SynchronousBundleListener; import org.osgi.framework.SynchronousBundleListener;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleWire;
import org.osgi.framework.wiring.BundleWiring;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -50,9 +57,10 @@ public class Activator implements BundleActivator, SynchronousBundleListener, Ob
private static final Logger LOG = LoggerFactory.getLogger(Activator.class); private static final Logger LOG = LoggerFactory.getLogger(Activator.class);
private final ConcurrentMap<String, Class> serviceCache = new ConcurrentHashMap<String, Class>(); private final ConcurrentMap<String, Class<?>> serviceCache = new ConcurrentHashMap<String, Class<?>>();
private final ConcurrentMap<Long, BundleWrapper> bundleWrappers = new ConcurrentHashMap<Long, BundleWrapper>(); private final ConcurrentMap<Long, BundleWrapper> bundleWrappers = new ConcurrentHashMap<Long, BundleWrapper>();
private BundleContext bundleContext; private BundleContext bundleContext;
private Set<BundleCapability> packageCapabilities = new HashSet<BundleCapability>();
// ================================================================ // ================================================================
// BundleActivator interface impl // BundleActivator interface impl
@ -67,6 +75,9 @@ public class Activator implements BundleActivator, SynchronousBundleListener, Ob
debug("activating"); debug("activating");
this.bundleContext = bundleContext; this.bundleContext = bundleContext;
cachePackageCapabilities(Service.class, Transport.class, DiscoveryAgent.class, PersistenceAdapter.class);
debug("checking existing bundles"); debug("checking existing bundles");
bundleContext.addBundleListener(this); bundleContext.addBundleListener(this);
for (Bundle bundle : bundleContext.getBundles()) { for (Bundle bundle : bundleContext.getBundles()) {
@ -78,6 +89,27 @@ public class Activator implements BundleActivator, SynchronousBundleListener, Ob
debug("activated"); debug("activated");
} }
/**
* Caches the package capabilities that are needed for a set of interface classes
*
* @param classes interfaces we want to track
*/
private void cachePackageCapabilities(Class<?> ... classes) {
BundleWiring ourWiring = bundleContext.getBundle().adapt(BundleWiring.class);
Set<String> packageNames = new HashSet<String>();
for (Class<?> clazz: classes) {
packageNames.add(clazz.getPackage().getName());
}
List<BundleCapability> ourExports = ourWiring.getCapabilities(PACKAGE_NAMESPACE);
for (BundleCapability ourExport : ourExports) {
String ourPkgName = (String) ourExport.getAttributes().get(PACKAGE_NAMESPACE);
if (packageNames.contains(ourPkgName)) {
packageCapabilities.add(ourExport);
}
}
}
@Override @Override
public synchronized void stop(BundleContext bundleContext) throws Exception { public synchronized void stop(BundleContext bundleContext) throws Exception {
@ -105,12 +137,15 @@ public class Activator implements BundleActivator, SynchronousBundleListener, Ob
protected void register(final Bundle bundle) { protected void register(final Bundle bundle) {
debug("checking bundle " + bundle.getBundleId()); debug("checking bundle " + bundle.getBundleId());
if( !isImportingUs(bundle) ) { if (isOurBundle(bundle) || isImportingUs(bundle) ) {
debug("The bundle does not import us: "+ bundle.getBundleId()); debug("Registering bundle for extension resolution: "+ bundle.getBundleId());
return;
}
bundleWrappers.put(bundle.getBundleId(), new BundleWrapper(bundle)); bundleWrappers.put(bundle.getBundleId(), new BundleWrapper(bundle));
} }
}
private boolean isOurBundle(final Bundle bundle) {
return bundle.getBundleId() == bundleContext.getBundle().getBundleId();
}
/** /**
* When bundles unload.. we remove them thier cached Class entries from the * When bundles unload.. we remove them thier cached Class entries from the
@ -137,7 +172,7 @@ public class Activator implements BundleActivator, SynchronousBundleListener, Ob
@Override @Override
public Object create(String path) throws IllegalAccessException, InstantiationException, IOException, ClassNotFoundException { public Object create(String path) throws IllegalAccessException, InstantiationException, IOException, ClassNotFoundException {
Class clazz = serviceCache.get(path); Class<?> clazz = serviceCache.get(path);
if (clazz == null) { if (clazz == null) {
StringBuffer warnings = new StringBuffer(); StringBuffer warnings = new StringBuffer();
// We need to look for a bundle that has that class. // We need to look for a bundle that has that class.
@ -205,20 +240,23 @@ public class Activator implements BundleActivator, SynchronousBundleListener, Ob
} }
} }
/**
* We consider a bundle to be a candidate for objects if it imports at least
* one of the packages of our interfaces
*
* @param bundle
* @return
*/
private boolean isImportingUs(Bundle bundle) { private boolean isImportingUs(Bundle bundle) {
return isImportingClass(bundle, Service.class) BundleWiring wiring = bundle.adapt(BundleWiring.class);
|| isImportingClass(bundle, Transport.class) List<BundleWire> imports = wiring.getRequiredWires(PACKAGE_NAMESPACE);
|| isImportingClass(bundle, DiscoveryAgent.class) for (BundleWire importWire : imports) {
|| isImportingClass(bundle, PersistenceAdapter.class); if (packageCapabilities.contains(importWire.getCapability())) {
return true;
}
} }
private boolean isImportingClass(Bundle bundle, Class clazz) {
try {
return bundle.loadClass(clazz.getName())==clazz;
} catch (ClassNotFoundException e) {
return false; return false;
} }
}
private static class BundleWrapper { private static class BundleWrapper {
private final Bundle bundle; private final Bundle bundle;