package osgi.enroute.guard; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.io.UnsupportedEncodingException; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Properties; import java.util.concurrent.atomic.AtomicBoolean; import org.osgi.framework.Bundle; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkEvent; import org.osgi.framework.FrameworkListener; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.framework.Version; import org.osgi.framework.VersionRange; import org.osgi.framework.namespace.PackageNamespace; import org.osgi.framework.wiring.BundleCapability; import org.osgi.framework.wiring.BundleWiring; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.log.LogService; /** * This bundle is a very small bundle that should be installed in any enRoute * system supporting a capability profile. It will check continuously if all the * elements of base are present and functioning. */ @Component public class Guard extends Thread implements BundleActivator { static AtomicBoolean components = new AtomicBoolean(false); private BundleContext context; Map<String,String> packages; Map<String,String> services; final Map<String,String> alerts = new HashMap<>(); volatile boolean quit; @Override public void start(BundleContext context) throws Exception { this.context = context; packages = load(context.getBundle(), "guardinfo/packages.properties", packages); services = load(context.getBundle(), "guardinfo/services.properties", services); Hashtable<String,Object> properties = new Hashtable<>(); properties.put("command.scope", "guard"); properties.put("command.function", new String[] { "restart", "clear" }); context.registerService(Object.class, new Object(), properties); final AtomicBoolean started = new AtomicBoolean(false); // // Wait before polling context.addFrameworkListener(new FrameworkListener() { @Override public void frameworkEvent(FrameworkEvent event) { if (event.getType() == FrameworkEvent.STARTED && started.getAndSet(true)) { start(); } } }); Thread.sleep(5000); if (started.getAndSet(true)) start(); } @SuppressWarnings({ "unchecked", "rawtypes" }) private Map<String,String> load(Bundle source, String path, Map<String,String> dest) throws UnsupportedEncodingException, IOException { Properties p = new Properties(); URL url = source.getResource(path); if ( url == null) throw new IllegalStateException("Resources for guard not in bundle " + path); Reader reader = new InputStreamReader(url.openStream(), "UTF-8"); p.load(reader); return (Map<String,String>)(Map)p; } @Override public void stop(BundleContext context) throws Exception { quit = true; interrupt(); } @Activate void activate() { components.set(true); } public void run() { while (!quit) try { alerts.clear(); while (!quit) { checkServices(); checkPackages(); Thread.sleep(5000); } } catch (InterruptedException e) { return; } } void checkServices() { for (String service : services.keySet()) { checkService(service); } } void checkService(String serviceName) { try { Class< ? > c = context.getBundle().loadClass(serviceName); ServiceReference< ? >[] references = context.getServiceReferences(serviceName, null); if (references == null || references.length == 0) { // // Check if it is in another class space // ServiceReference< ? >[] srfs = context.getAllServiceReferences(c.getName(), null); if (srfs != null && srfs.length > 0) { alert(serviceName, "Service not found, but it does exist in another class space."); } else { alert(serviceName, "Service does not exist."); } return; } // // Service does exist, see if we can get it // List<String> failures = new ArrayList<>(); for (ServiceReference< ? > ref : references) { Object s = context.getService(ref); if (s == null) failures.add("fetch from " + ref.getBundle()); context.ungetService(ref); } } catch (ClassNotFoundException | InvalidSyntaxException e) { alert(serviceName, e.getMessage()); } } private void alert(String name, String message) { String old = alerts.get(name); if (old != null && old.equals(message)) return; alerts.put(name, message); try { ServiceReference<LogService> ref = context.getServiceReference(LogService.class); if (ref != null) { LogService log = context.getService(ref); log.log(LogService.LOG_WARNING, name + ": " + message); context.ungetService(ref); return; } } catch (Throwable e) { // ignore } System.err.println(name + ": " + message); } void checkPackages() { for (Entry<String,String> e : packages.entrySet()) { checkPackage(e.getKey(), e.getValue()); } } private void checkPackage(String packageName, String versionRange) { try { Class< ? > pinfo = context.getBundle().loadClass(packageName + ".package-info"); Bundle exporter = FrameworkUtil.getBundle(pinfo); if (exporter == null) { alert(packageName, "Not exported by a bundle"); return; } if (versionRange == null) { alert(packageName, "Exporter has no version"); return; } BundleWiring wiring = exporter.adapt(BundleWiring.class); BundleCapability export = getCapability(wiring, PackageNamespace.PACKAGE_NAMESPACE, packageName); Object v = export.getAttributes().get("version"); if (v == null) { alert(packageName, "Exporter has no version"); return; } if (!(v instanceof Version)) { alert(packageName, "Exporter has version but it is not a Version type " + v); return; } Version version = (Version) v; VersionRange range = new VersionRange(versionRange); if (!range.includes(version)) { alert(packageName, "Exporter " + exporter + " has version " + version + " outside the set range " + range); return; } } catch (ClassNotFoundException e) { alert(packageName, "Not found"); } catch( Exception e) { alert(packageName, e.getMessage()); } } private BundleCapability getCapability(BundleWiring wiring, String ns, String name) { List<BundleCapability> capabilities = wiring.getCapabilities(ns); for (BundleCapability capability : capabilities) { Object object = capability.getAttributes().get(ns); if (name.equals(object)) return capability; } return null; } public void clear() { } public void restart() { } }