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();
}
}
}