package org.foo.dosgi.helper; import java.util.Collection; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import org.apache.felix.sigil.common.osgi.LDAPExpr; import org.apache.felix.sigil.common.osgi.LDAPParser; import org.foo.dosgi.registry.Registry; import org.foo.dosgi.registry.RegistryEvent; import org.foo.dosgi.registry.RegistryListener; import org.foo.dosgi.registry.RegistryServiceReference; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceFactory; import org.osgi.framework.ServiceRegistration; public class RegistryWatcher implements RegistryListener { private static final LogUtil log = LogUtil.getLog(RegistryWatcher.class.getName()); static class Watch { final String clazz; final String filter; private final int hash; private LDAPExpr ldap; Watch(String clazz, String filter) { this.clazz = clazz; this.filter = filter; int h = 2683; if (clazz != null) { h ^= clazz.hashCode(); } if (filter != null) { h ^= filter.hashCode(); } hash = h; } public boolean equals(Object o) { if (o == this) return true; if (o == null) return false; try { Watch w = (Watch) o; return clazz == null ? w.clazz == null : clazz.equals(w.clazz) && filter == null ? w.filter == null : filter.equals(w.filter); } catch (ClassCastException e) { return false; } } public int hashCode() { return hash; } public boolean matches(RegistryServiceReference ref) { if (ldap == null) { ldap = LDAPParser.parseExpression(filter); } return clazz == null || clazz.equals(ref.getInterface()) && ldap.eval(ref.getProperties()); } } class Registration implements Callable<ServiceRegistration> { private final RegistryServiceReference ref; public Registration(RegistryServiceReference ref) { this.ref = ref; } public ServiceRegistration call() throws Exception { Hashtable props = new Hashtable(ref.getProperties()); return ctx.registerService(ref.getInterface(), ref.getService(), props); } } static class Unregister implements Callable<Void> { private final Future<ServiceRegistration> future; public Unregister(Future<ServiceRegistration> future) { this.future = future; } public Void call() throws Exception { future.get().unregister(); return null; } } private final BundleContext ctx; private final Registry registry; private final ExecutorService exec; private Map<RegistryServiceReference, Future<ServiceRegistration>> regs = new HashMap<RegistryServiceReference, Future<ServiceRegistration>>(); private HashMap<Watch, Integer> watches = new HashMap<Watch, Integer>(); public RegistryWatcher(BundleContext ctx, Registry registry) { this.ctx = ctx; this.registry = registry; exec = Executors.newSingleThreadExecutor(); registry.addListener(this); } public void destroy() { exec.shutdownNow(); registry.removeListener(this); } public void addWatch(String clazz, String filter) { Watch watch = new Watch(clazz, filter); synchronized (watches) { Integer count = watches.get(watch); if (count == null) { log.info("Adding watch " + clazz + " -> " + filter); Collection<RegistryServiceReference> services = registry .findServices(clazz, filter); for (RegistryServiceReference ref : services) { if (!regs.containsKey(ref)) { log.debug("Registering " + ref); Future<ServiceRegistration> future = exec .submit(new Registration(ref)); regs.put(ref, future); } else { log.debug("Already registered " + ref); } } } else { watches.put( watch, count + 1 ); } } } public void findServices(String clazz, String filter) { synchronized (watches) { if (!watches.containsKey(new Watch(clazz, filter))) { //LogUtil.info("Finding services " + clazz + " -> " + filter); Collection<RegistryServiceReference> services = registry .findServices(clazz, filter); if ( !"org.osgi.service.log.LogService".equals(clazz) ) { log.debug("Found " + services.size() + " for " + clazz + " -> " + filter); } for (RegistryServiceReference ref : services) { if (!regs.containsKey(ref)) { Future<ServiceRegistration> future = exec .submit(new Registration(ref)); regs.put(ref, future); } } } } } public void removeWatch(String clazz, String filter) { Watch removed = new Watch(clazz, filter); synchronized (watches) { Integer count = watches.get(removed); count = count - 1; if ( count == 0 ) { watches.remove(removed); log.info("Removing watch " + clazz + " -> " + filter); for (Iterator<RegistryServiceReference> iter = regs.keySet() .iterator(); iter.hasNext();) { RegistryServiceReference ref = iter.next(); if (removed.matches(ref)) { boolean found = false; for (Watch w : watches.keySet()) { if (w.matches(ref)) { found = true; break; } } if (!found) { Future<ServiceRegistration> f = regs.get(ref); exec.submit(new Unregister(f)); iter.remove(); } } } } else { watches.put(removed, count); } } } public void handleEvent(RegistryEvent event) { RegistryServiceReference ref = event.getReference(); synchronized (watches) { switch (event.getType()) { case ADDED: handleAdd(ref); break; case REMOVED: handleRemove(ref); break; } } } private void handleAdd(RegistryServiceReference ref) { if (!regs.containsKey(ref)) { log.debug("Added " + ref); for (Watch w : watches.keySet()) { log.debug("Checking " + w); if (w.matches(ref)) { Future<ServiceRegistration> future = exec .submit(new Registration(ref)); regs.put(ref, future); break; } } } else { log.debug("Discarding " + ref); } } private void handleRemove(RegistryServiceReference ref) { Future<ServiceRegistration> future = regs.remove(ref); log.debug("Removed " + ref); if (future != null) { exec.submit(new Unregister(future)); log.debug("Unregistering " + ref); } else { log.debug("Unknown registration"); } } }