package org.osgi.cdi.impl.extension.services; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import javax.annotation.PreDestroy; import javax.enterprise.context.ApplicationScoped; import javax.enterprise.context.spi.CreationalContext; import javax.enterprise.event.Event; import javax.enterprise.event.Observes; import javax.enterprise.inject.Instance; import javax.enterprise.inject.spi.AnnotatedType; import javax.enterprise.inject.spi.BeanManager; import javax.enterprise.inject.spi.InjectionTarget; import javax.inject.Inject; import javax.inject.Provider; import org.osgi.cdi.api.extension.BundleState; import org.osgi.cdi.api.extension.Registration; import org.osgi.cdi.api.extension.Service; import org.osgi.cdi.api.extension.ServiceRegistry; import org.osgi.cdi.api.extension.events.AbstractServiceEvent; import org.osgi.cdi.api.extension.events.BundleContainerInitialized; import org.osgi.cdi.api.extension.events.Invalid; import org.osgi.cdi.api.extension.events.ServiceArrival; import org.osgi.cdi.api.extension.events.ServiceChanged; import org.osgi.cdi.api.extension.events.ServiceDeparture; import org.osgi.cdi.api.extension.events.Valid; import org.osgi.cdi.impl.extension.CDIOSGiExtension; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; /** * * @author Mathieu ANCELIN - SERLI (mathieu.ancelin@serli.com) */ @ApplicationScoped public class ServiceRegistryImpl implements ServiceRegistry { @Inject private BundleContext registry; @Inject private Bundle bundle; @Inject private Instance<Object> instances; @Inject private RegistrationsHolder holder; @Inject private BeanManager manager; @Inject private Event<Valid> validEvent; @Inject private Event<Invalid> invalidEvent; @Inject private CDIOSGiExtension extension; @Inject private BundleHolder bundleHolder; private Set<Class<?>> osgiServiceDependencies; private Map<Class<?>, Beantype<?>> types = new HashMap<Class<?>, Beantype<?>>(); @Override public <T> Registration<T> registerService(Class<T> contract, Class<? extends T> implementation) { ServiceRegistration reg = registry.registerService(contract.getName(), instances.select(implementation).get(), null); holder.addRegistration(reg); return new RegistrationImpl<T>( contract, reg, registry, bundle, holder); } @Override public <T, U extends T> Registration<T> registerService(Class<T> contract, U implementation) { ServiceRegistration reg = registry.registerService(contract.getName(), implementation, null); holder.addRegistration(reg); return new RegistrationImpl<T>( contract, reg, registry, bundle, holder); } @Override public <T> Service<T> getServiceReferences(Class<T> contract) { return getServiceReference(contract); } @Override public <T> Service<T> getServiceReference(Class<T> contract) { return new ServiceImpl<T>(contract, registry); } @PreDestroy public void stop() { for (Beantype<?> type : types.values()) { type.destroy(); } } public <T> void registerNewType(Class<T> type) { if (!types.containsKey(type)) { types.put(type, new Beantype<T>(type, manager)); } } @Override public <T> Provider<T> newTypeInstance(Class<T> unmanagedType) { if (!types.containsKey(unmanagedType)) { types.put(unmanagedType, new Beantype<T>(unmanagedType, manager)); } return (Provider<T>) types.get(unmanagedType); } public void listenStartup(@Observes BundleContainerInitialized event) { osgiServiceDependencies = extension.getRequiredOsgiServiceDependencies(); checkForValidDependencies(null); } public void bind(@Observes ServiceArrival arrival) { checkForValidDependencies(arrival); } public void changed(@Observes ServiceChanged changed) { checkForValidDependencies(changed); } public void unbind(@Observes ServiceDeparture departure) { checkForValidDependencies(departure); } private void checkForValidDependencies(AbstractServiceEvent event) { if (event == null || applicable(event.getServiceClasses())) { boolean valid = true; if (osgiServiceDependencies.isEmpty()) { valid = false; } else { for (Class<?> clazz : osgiServiceDependencies) { try { ServiceReference[] refs = registry.getServiceReferences(clazz.getName(), null); if (refs != null) { int available = refs.length; if (available <= 0) { valid = false; } } else { valid = false; } } catch (InvalidSyntaxException ex) { // nothing here } } } // TODO : synchronize here to change the state of the bundle if (valid && bundleHolder.getState().equals(BundleState.INVALID)) { bundleHolder.setState(BundleState.VALID); validEvent.fire(new Valid()); } else if (!valid && bundleHolder.getState().equals(BundleState.VALID)) { bundleHolder.setState(BundleState.INVALID); invalidEvent.fire(new Invalid()); } } } private boolean applicable(List<Class<?>> classes) { for (Class<?> clazz : classes) { if (osgiServiceDependencies.contains(clazz)) { return true; } } return false; } private class Beantype<T> implements Provider<T> { private final Class<T> clazz; private final BeanManager manager; private final AnnotatedType annoted; private final InjectionTarget it; private final CreationalContext<?> cc; private Collection<T> instances = new ArrayList<T>(); public Beantype(Class<T> clazz, BeanManager manager) { this.clazz = clazz; this.manager = manager; annoted = manager.createAnnotatedType(clazz); it = manager.createInjectionTarget(annoted); cc = manager.createCreationalContext(null); } public void destroy() { for (T instance : instances) { it.preDestroy(instance); it.dispose(instance); } cc.release(); } @Override public T get() { T instance = (T) it.produce(cc); it.inject(instance, cc); it.postConstruct(instance); instances.add(instance); return instance; } } }