package bndtools.central; import java.io.Closeable; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.concurrent.atomic.AtomicBoolean; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleEvent; import org.osgi.framework.ServiceRegistration; import org.osgi.framework.hooks.weaving.WeavingHook; import org.osgi.framework.hooks.weaving.WovenClass; import org.osgi.framework.wiring.BundleWiring; import org.osgi.util.tracker.BundleTracker; import aQute.bnd.header.Attrs; import aQute.bnd.header.Parameters; import aQute.bnd.osgi.About; import aQute.bnd.osgi.Constants; import aQute.bnd.osgi.Verifier; import aQute.bnd.version.Version; import aQute.bnd.version.VersionRange; /** * This class extends the dynamic imports of bndlib with any exported package from OSGi that specifies a 'bnd-plugins' * attribute. Its value is either true or a version range on the bnd version. */ class Auxiliary implements Closeable, WeavingHook { private final BundleContext context; private final AtomicBoolean closed = new AtomicBoolean(false); private final ServiceRegistration<WeavingHook> hook; private final BundleTracker<Bundle> tracker; private Bundle bndlib; private List<String> delta = new ArrayList<>(); Auxiliary(BundleContext context, Bundle bndlib) { this.bndlib = bndlib; this.context = context; this.tracker = new BundleTracker<Bundle>(context, Bundle.RESOLVED + Bundle.ACTIVE + Bundle.STARTING, null) { @Override public Bundle addingBundle(Bundle bundle, BundleEvent event) { if (!doImport(bundle.getHeaders().get(Constants.EXPORT_PACKAGE))) return null; return super.addingBundle(bundle, event); } }; this.tracker.open(); this.hook = this.context.registerService(WeavingHook.class, this, null); } /* * Parse the exports and see * */ private boolean doImport(String exports) { if (closed.get() || exports == null || exports.isEmpty()) return false; Parameters out = new Parameters(); Parameters p = new Parameters(exports); for (Entry<String,Attrs> e : p.entrySet()) { Attrs attrs = e.getValue(); if (attrs == null) continue; String plugins = attrs.get("bnd-plugins"); if (plugins == null) continue; if (!(plugins.isEmpty() || "true".equalsIgnoreCase(plugins))) { if (Verifier.isVersionRange(plugins)) { VersionRange range = new VersionRange(plugins); if (!range.includes(About._3_0)) // TODO continue; } } // // Ok, matched // String v = attrs.getVersion(); if (v == null) v = "0"; for (Iterator<String> i = attrs.keySet().iterator(); i.hasNext();) { String key = i.next(); if (key.endsWith(":")) i.remove(); } if (Verifier.isVersion(v)) { Version version = new Version(v); attrs.put("version", new VersionRange(true, version, version, true).toString()); } out.put(e.getKey(), attrs); } if (out.isEmpty()) return false; String imports = out.toString(); synchronized (this) { if (delta == null) delta = new ArrayList<>(); delta.add(imports); } return true; } @Override public void weave(WovenClass wovenClass) { if (delta == null || delta.isEmpty()) return; BundleWiring wiring = wovenClass.getBundleWiring(); if (wiring == null) return; if (wiring.getBundle() != bndlib) return; List<String> extra; synchronized (this) { extra = delta; delta = null; } if (extra != null) wovenClass.getDynamicImports().addAll(extra); } @Override public void close() throws IOException { if (closed.getAndSet(true) == false) { hook.unregister(); tracker.close(); } } }