| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.aries.blueprint.container; |
| |
| import java.io.FileNotFoundException; |
| import java.net.URI; |
| import java.net.URL; |
| import java.security.AccessControlContext; |
| import java.security.AccessController; |
| import java.security.DomainCombiner; |
| import java.security.Permission; |
| import java.security.PrivilegedAction; |
| import java.security.PrivilegedActionException; |
| import java.security.PrivilegedExceptionAction; |
| import java.security.ProtectionDomain; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.Dictionary; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.concurrent.ScheduledExecutorService; |
| import java.util.concurrent.ScheduledFuture; |
| import java.util.concurrent.TimeUnit; |
| import java.util.concurrent.TimeoutException; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| |
| import org.apache.aries.blueprint.BlueprintConstants; |
| import org.apache.aries.blueprint.ComponentDefinitionRegistryProcessor; |
| import org.apache.aries.blueprint.ExtendedBeanMetadata; |
| import org.apache.aries.blueprint.ExtendedBlueprintContainer; |
| import org.apache.aries.blueprint.NamespaceHandler; |
| import org.apache.aries.blueprint.Processor; |
| import org.apache.aries.blueprint.di.Recipe; |
| import org.apache.aries.blueprint.di.Repository; |
| import org.apache.aries.blueprint.namespace.ComponentDefinitionRegistryImpl; |
| import org.apache.aries.blueprint.namespace.NamespaceHandlerRegistryImpl; |
| import org.apache.aries.blueprint.reflect.MetadataUtil; |
| import org.apache.aries.blueprint.reflect.PassThroughMetadataImpl; |
| import org.apache.aries.blueprint.utils.HeaderParser; |
| import org.apache.aries.blueprint.utils.JavaUtils; |
| import org.apache.aries.blueprint.utils.HeaderParser.PathElement; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.framework.ServiceRegistration; |
| import org.osgi.service.blueprint.container.BlueprintContainer; |
| import org.osgi.service.blueprint.container.BlueprintEvent; |
| import org.osgi.service.blueprint.container.BlueprintListener; |
| import org.osgi.service.blueprint.container.ComponentDefinitionException; |
| import org.osgi.service.blueprint.container.Converter; |
| import org.osgi.service.blueprint.container.NoSuchComponentException; |
| import org.osgi.service.blueprint.reflect.BeanArgument; |
| import org.osgi.service.blueprint.reflect.BeanMetadata; |
| import org.osgi.service.blueprint.reflect.BeanProperty; |
| import org.osgi.service.blueprint.reflect.CollectionMetadata; |
| import org.osgi.service.blueprint.reflect.ComponentMetadata; |
| import org.osgi.service.blueprint.reflect.MapEntry; |
| import org.osgi.service.blueprint.reflect.MapMetadata; |
| import org.osgi.service.blueprint.reflect.Metadata; |
| import org.osgi.service.blueprint.reflect.PropsMetadata; |
| import org.osgi.service.blueprint.reflect.RefMetadata; |
| import org.osgi.service.blueprint.reflect.ReferenceListener; |
| import org.osgi.service.blueprint.reflect.RegistrationListener; |
| import org.osgi.service.blueprint.reflect.ServiceMetadata; |
| import org.osgi.service.blueprint.reflect.ServiceReferenceMetadata; |
| import org.osgi.service.blueprint.reflect.Target; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * TODO: javadoc |
| * |
| * @version $Rev: 921845 $, $Date: 2010-03-11 13:45:28 +0000 (Thu, 11 Mar 2010) $ |
| */ |
| public class BlueprintContainerImpl implements ExtendedBlueprintContainer, NamespaceHandlerRegistry.Listener, Runnable, SatisfiableRecipe.SatisfactionListener { |
| |
| private static final Logger LOGGER = LoggerFactory.getLogger(BlueprintContainerImpl.class); |
| |
| private enum State { |
| Unknown, |
| WaitForNamespaceHandlers, |
| Populated, |
| WaitForInitialReferences, |
| InitialReferencesSatisfied, |
| WaitForInitialReferences2, |
| Create, |
| Created, |
| Failed, |
| } |
| |
| private final BundleContext bundleContext; |
| private final Bundle extenderBundle; |
| private final BlueprintListener eventDispatcher; |
| private final NamespaceHandlerRegistry handlers; |
| private final List<Object> pathList; |
| private final ComponentDefinitionRegistryImpl componentDefinitionRegistry; |
| private final AggregateConverter converter; |
| private final ScheduledExecutorService executors; |
| private Set<URI> namespaces; |
| private State state = State.Unknown; |
| private NamespaceHandlerRegistry.NamespaceHandlerSet handlerSet; |
| private boolean destroyed; |
| private Parser parser; |
| private BlueprintRepository repository; |
| private ServiceRegistration registration; |
| private List<Processor> processors; |
| private final Object satisfiablesLock = new Object(); |
| private Map<String, List<SatisfiableRecipe>> satisfiables; |
| private long timeout = 5 * 60 * 1000; |
| private boolean waitForDependencies = true; |
| private boolean xmlValidation = true; |
| private ScheduledFuture timeoutFuture; |
| private final AtomicBoolean scheduled = new AtomicBoolean(); |
| private final AtomicBoolean running = new AtomicBoolean(); |
| private List<ServiceRecipe> services; |
| private AccessControlContext accessControlContext; |
| private final IdSpace tempRecipeIdSpace = new IdSpace(); |
| |
| public BlueprintContainerImpl(BundleContext bundleContext, Bundle extenderBundle, BlueprintListener eventDispatcher, NamespaceHandlerRegistry handlers, ScheduledExecutorService executors, List<Object> pathList) { |
| this.bundleContext = bundleContext; |
| this.extenderBundle = extenderBundle; |
| this.eventDispatcher = eventDispatcher; |
| this.handlers = handlers; |
| this.pathList = pathList; |
| this.converter = new AggregateConverter(this); |
| this.componentDefinitionRegistry = new ComponentDefinitionRegistryImpl(); |
| this.executors = executors; |
| this.processors = new ArrayList<Processor>(); |
| if (System.getSecurityManager() != null) { |
| this.accessControlContext = createAccessControlContext(); |
| } |
| } |
| |
| public Bundle getExtenderBundle() { |
| return extenderBundle; |
| } |
| |
| public <T extends Processor> List<T> getProcessors(Class<T> clazz) { |
| List<T> p = new ArrayList<T>(); |
| for (Processor processor : processors) { |
| if (clazz.isInstance(processor)) { |
| p.add(clazz.cast(processor)); |
| } |
| } |
| return p; |
| } |
| |
| public BlueprintListener getEventDispatcher() { |
| return eventDispatcher; |
| } |
| |
| private void checkDirectives() { |
| Bundle bundle = bundleContext.getBundle(); |
| Dictionary headers = bundle.getHeaders(); |
| String symbolicName = (String)headers.get(Constants.BUNDLE_SYMBOLICNAME); |
| List<PathElement> paths = HeaderParser.parseHeader(symbolicName); |
| |
| String timeoutDirective = paths.get(0).getDirective(BlueprintConstants.TIMEOUT_DIRECTIVE); |
| if (timeoutDirective != null) { |
| LOGGER.debug("Timeout directive: {}", timeoutDirective); |
| timeout = Integer.parseInt(timeoutDirective); |
| } |
| |
| String graceperiod = paths.get(0).getDirective(BlueprintConstants.GRACE_PERIOD); |
| if (graceperiod != null) { |
| LOGGER.debug("Grace-period directive: {}", graceperiod); |
| waitForDependencies = Boolean.parseBoolean(graceperiod); |
| } |
| |
| String xmlValidationDirective = paths.get(0).getDirective(BlueprintConstants.XML_VALIDATION); |
| if (xmlValidationDirective != null) { |
| LOGGER.debug("Xml-validation directive: {}", xmlValidationDirective); |
| xmlValidation = Boolean.parseBoolean(xmlValidationDirective); |
| } |
| } |
| |
| public void schedule() { |
| if (scheduled.compareAndSet(false, true)) { |
| executors.submit(this); |
| } |
| } |
| |
| public void run() { |
| scheduled.set(false); |
| synchronized (scheduled) { |
| synchronized (running) { |
| running.set(true); |
| try { |
| doRun(); |
| } finally { |
| running.set(false); |
| running.notifyAll(); |
| } |
| } |
| } |
| } |
| |
| /** |
| * This method must be called inside a synchronized block to ensure this method is not run concurrently |
| */ |
| private void doRun() { |
| try { |
| for (;;) { |
| if (destroyed) { |
| return; |
| } |
| LOGGER.debug("Running blueprint container for bundle {} in state {}", bundleContext.getBundle().getSymbolicName(), state); |
| switch (state) { |
| case Unknown: |
| checkDirectives(); |
| eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.CREATING, getBundleContext().getBundle(), getExtenderBundle())); |
| parser = new Parser(); |
| parser.parse(getResources()); |
| namespaces = parser.getNamespaces(); |
| handlerSet = handlers.getNamespaceHandlers(namespaces, getBundleContext().getBundle()); |
| handlerSet.addListener(this); |
| state = State.WaitForNamespaceHandlers; |
| break; |
| case WaitForNamespaceHandlers: |
| { |
| List<String> missing = new ArrayList<String>(); |
| for (URI ns : namespaces) { |
| if (handlerSet.getNamespaceHandler(ns) == null) { |
| missing.add("(&(" + Constants.OBJECTCLASS + "=" + NamespaceHandler.class.getName() + ")(" + NamespaceHandlerRegistryImpl.NAMESPACE + "=" + ns + "))"); |
| } |
| } |
| if (missing.size() > 0) { |
| LOGGER.warn("Bundle " + bundleContext.getBundle().getSymbolicName() + " is waiting for namespace handlers " + missing); |
| eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.GRACE_PERIOD, getBundleContext().getBundle(), getExtenderBundle(), missing.toArray(new String[missing.size()]))); |
| return; |
| } |
| componentDefinitionRegistry.registerComponentDefinition(new PassThroughMetadataImpl("blueprintContainer", this)); |
| componentDefinitionRegistry.registerComponentDefinition(new PassThroughMetadataImpl("blueprintBundle", bundleContext.getBundle())); |
| componentDefinitionRegistry.registerComponentDefinition(new PassThroughMetadataImpl("blueprintBundleContext", bundleContext)); |
| componentDefinitionRegistry.registerComponentDefinition(new PassThroughMetadataImpl("blueprintConverter", converter)); |
| if (xmlValidation) { |
| parser.validate(handlerSet.getSchema()); |
| } |
| parser.populate(handlerSet, componentDefinitionRegistry); |
| state = State.Populated; |
| break; |
| } |
| case Populated: |
| getRepository(); |
| trackServiceReferences(); |
| Runnable r = new Runnable() { |
| public void run() { |
| synchronized (scheduled) { |
| Throwable t = new TimeoutException(); |
| state = State.Failed; |
| unregisterServices(); |
| untrackServiceReferences(); |
| destroyComponents(); |
| String[] missingDependecies = getMissingDependencies(); |
| LOGGER.error("Unable to start blueprint container for bundle " + bundleContext.getBundle().getSymbolicName() + " due to unresolved dependencies " + Arrays.asList(missingDependecies), t); |
| eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.FAILURE, getBundleContext().getBundle(), getExtenderBundle(), missingDependecies, t)); |
| } |
| } |
| }; |
| timeoutFuture = executors.schedule(r, timeout, TimeUnit.MILLISECONDS); |
| state = State.WaitForInitialReferences; |
| break; |
| case WaitForInitialReferences: |
| if (waitForDependencies) { |
| String[] missingDependencies = getMissingDependencies(); |
| if (missingDependencies.length > 0) { |
| eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.GRACE_PERIOD, getBundleContext().getBundle(), getExtenderBundle(), missingDependencies)); |
| return; |
| } |
| } |
| state = State.InitialReferencesSatisfied; |
| break; |
| case InitialReferencesSatisfied: |
| processTypeConverters(); |
| processProcessors(); |
| state = State.WaitForInitialReferences2; |
| break; |
| case WaitForInitialReferences2: |
| if (waitForDependencies) { |
| String[] missingDependencies = getMissingDependencies(); |
| if (missingDependencies.length > 0) { |
| eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.GRACE_PERIOD, getBundleContext().getBundle(), getExtenderBundle(), missingDependencies)); |
| return; |
| } |
| } |
| state = State.Create; |
| break; |
| case Create: |
| timeoutFuture.cancel(false); |
| registerServices(); |
| instantiateEagerComponents(); |
| |
| // Register the BlueprintContainer in the OSGi registry |
| if (registration == null) { |
| Properties props = new Properties(); |
| props.put(BlueprintConstants.CONTAINER_SYMBOLIC_NAME_PROPERTY, |
| bundleContext.getBundle().getSymbolicName()); |
| props.put(BlueprintConstants.CONTAINER_VERSION_PROPERTY, |
| JavaUtils.getBundleVersion(bundleContext.getBundle())); |
| registration = registerService(new String [] { BlueprintContainer.class.getName() }, this, props); |
| eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.CREATED, getBundleContext().getBundle(), getExtenderBundle())); |
| state = State.Created; |
| } |
| break; |
| case Created: |
| case Failed: |
| return; |
| } |
| } |
| } catch (Throwable t) { |
| state = State.Failed; |
| if (timeoutFuture != null) { |
| timeoutFuture.cancel(false); |
| } |
| unregisterServices(); |
| untrackServiceReferences(); |
| destroyComponents(); |
| LOGGER.error("Unable to start blueprint container for bundle " + bundleContext.getBundle().getSymbolicName(), t); |
| eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.FAILURE, getBundleContext().getBundle(), getExtenderBundle(), t)); |
| } |
| } |
| |
| private List<URL> getResources() throws FileNotFoundException { |
| List<URL> resources = new ArrayList<URL>(); |
| for (Object path : pathList) { |
| if (path instanceof URL) { |
| resources.add((URL) path); |
| } else if (path instanceof String) { |
| URL url = bundleContext.getBundle().getEntry((String) path); |
| if (url == null) { |
| throw new FileNotFoundException("Unable to find configuration file for " + path); |
| } else { |
| resources.add(url); |
| } |
| } else { |
| throw new IllegalArgumentException("Unexpected path type: " + path.getClass()); |
| } |
| } |
| return resources; |
| } |
| |
| public Class loadClass(final String name) throws ClassNotFoundException { |
| if (accessControlContext == null) { |
| return bundleContext.getBundle().loadClass(name); |
| } else { |
| try { |
| return AccessController.doPrivileged(new PrivilegedExceptionAction<Class>() { |
| public Class run() throws Exception { |
| return bundleContext.getBundle().loadClass(name); |
| } |
| }, accessControlContext); |
| } catch (PrivilegedActionException e) { |
| Exception cause = e.getException(); |
| if (cause instanceof ClassNotFoundException) { |
| throw (ClassNotFoundException) cause; |
| } |
| throw new IllegalStateException("Unexpected checked exception", cause); |
| } |
| } |
| } |
| |
| public ServiceRegistration registerService(final String[] classes, final Object service, final Dictionary properties) { |
| if (accessControlContext == null) { |
| return bundleContext.registerService(classes, service, properties); |
| } else { |
| return AccessController.doPrivileged(new PrivilegedAction<ServiceRegistration>() { |
| public ServiceRegistration run() { |
| return bundleContext.registerService(classes, service, properties); |
| } |
| }, accessControlContext); |
| } |
| } |
| |
| public Object getService(final ServiceReference reference) { |
| if (accessControlContext == null) { |
| return bundleContext.getService(reference); |
| } else { |
| return AccessController.doPrivileged(new PrivilegedAction<Object>() { |
| public Object run() { |
| return bundleContext.getService(reference); |
| } |
| }, accessControlContext); |
| } |
| } |
| |
| private AccessControlContext createAccessControlContext() { |
| return new AccessControlContext(AccessController.getContext(), |
| new DomainCombiner() { |
| public ProtectionDomain[] combine(ProtectionDomain[] arg0, |
| ProtectionDomain[] arg1) { |
| return new ProtectionDomain[] { new ProtectionDomain(null, null) { |
| public boolean implies(Permission permission) { |
| return bundleContext.getBundle().hasPermission(permission); |
| } |
| } |
| }; |
| } |
| }); |
| } |
| |
| public AccessControlContext getAccessControlContext() { |
| return accessControlContext; |
| } |
| |
| public BlueprintRepository getRepository() { |
| if (repository == null) { |
| repository = new RecipeBuilder(this, tempRecipeIdSpace).createRepository(); |
| } |
| return repository; |
| } |
| |
| private void processTypeConverters() throws Exception { |
| List<String> typeConverters = new ArrayList<String>(); |
| for (Target target : componentDefinitionRegistry.getTypeConverters()) { |
| if (target instanceof ComponentMetadata) { |
| typeConverters.add(((ComponentMetadata) target).getId()); |
| } else if (target instanceof RefMetadata) { |
| typeConverters.add(((RefMetadata) target).getComponentId()); |
| } else { |
| throw new ComponentDefinitionException("Unexpected metadata for type converter: " + target); |
| } |
| } |
| |
| Map<String, Object> objects = repository.createAll(typeConverters); |
| for (String name : typeConverters) { |
| Object obj = objects.get(name); |
| if (obj instanceof Converter) { |
| converter.registerConverter((Converter) obj); |
| } else { |
| throw new ComponentDefinitionException("Type converter " + obj + " does not implement the " + Converter.class.getName() + " interface"); |
| } |
| } |
| } |
| |
| private void processProcessors() throws Exception { |
| // Instanciate ComponentDefinitionRegistryProcessor and BeanProcessor |
| for (BeanMetadata bean : getMetadata(BeanMetadata.class)) { |
| if (bean instanceof ExtendedBeanMetadata && !((ExtendedBeanMetadata) bean).isProcessor()) { |
| continue; |
| } |
| |
| Class clazz = null; |
| if (bean instanceof ExtendedBeanMetadata) { |
| clazz = ((ExtendedBeanMetadata) bean).getRuntimeClass(); |
| } |
| if (clazz == null && bean.getClassName() != null) { |
| clazz = loadClass(bean.getClassName()); |
| } |
| if (clazz == null) { |
| continue; |
| } |
| |
| if (ComponentDefinitionRegistryProcessor.class.isAssignableFrom(clazz)) { |
| Object obj = repository.create(bean.getId()); |
| ((ComponentDefinitionRegistryProcessor) obj).process(componentDefinitionRegistry); |
| } else if (Processor.class.isAssignableFrom(clazz)) { |
| Object obj = repository.create(bean.getId()); |
| this.processors.add((Processor) obj); |
| } else { |
| continue; |
| } |
| // Update repository with recipes processed by the processors |
| untrackServiceReferences(); |
| Repository tmpRepo = new RecipeBuilder(this, tempRecipeIdSpace).createRepository(); |
| |
| LOGGER.debug("Updating blueprint repository"); |
| |
| for (String name : repository.getNames()) { |
| if (repository.getInstance(name) == null) { |
| LOGGER.debug("Removing uninstantiated recipe {}", new Object[] { name }); |
| repository.removeRecipe(name); |
| } else { |
| LOGGER.debug("Recipe {} is already instantiated", new Object[] { name }); |
| } |
| } |
| |
| for (String name : tmpRepo.getNames()) { |
| if (repository.getInstance(name) == null) { |
| LOGGER.debug("Adding new recipe {}", new Object[] { name }); |
| Recipe r = tmpRepo.getRecipe(name); |
| if (r != null) { |
| repository.putRecipe(name, r); |
| } |
| } else { |
| LOGGER.debug("Recipe {} is already instantiated and cannot be updated", new Object[] { name }); |
| } |
| } |
| |
| getSatisfiableDependenciesMap(true); |
| trackServiceReferences(); |
| } |
| } |
| |
| private Map<String, List<SatisfiableRecipe>> getSatisfiableDependenciesMap() { |
| return getSatisfiableDependenciesMap(false); |
| } |
| |
| private Map<String, List<SatisfiableRecipe>> getSatisfiableDependenciesMap(boolean recompute) { |
| synchronized (satisfiablesLock) { |
| if ((recompute || satisfiables == null) && repository != null) { |
| satisfiables = new HashMap<String, List<SatisfiableRecipe>>(); |
| for (Recipe r : repository.getAllRecipes()) { |
| List<SatisfiableRecipe> recipes = repository.getAllRecipes(SatisfiableRecipe.class, r.getName()); |
| if (!recipes.isEmpty()) { |
| satisfiables.put(r.getName(), recipes); |
| } |
| } |
| } |
| return satisfiables; |
| } |
| } |
| |
| private void trackServiceReferences() { |
| Map<String, List<SatisfiableRecipe>> dependencies = getSatisfiableDependenciesMap(); |
| Set<String> satisfiables = new HashSet<String>(); |
| for (List<SatisfiableRecipe> recipes : dependencies.values()) { |
| for (SatisfiableRecipe satisfiable : recipes) { |
| if (satisfiables.add(satisfiable.getName())) { |
| satisfiable.start(this); |
| } |
| } |
| } |
| LOGGER.debug("Tracking service references: {}", satisfiables); |
| } |
| |
| private void untrackServiceReferences() { |
| Map<String, List<SatisfiableRecipe>> dependencies = getSatisfiableDependenciesMap(); |
| if (dependencies != null) { |
| Set<String> stopped = new HashSet<String>(); |
| for (List<SatisfiableRecipe> recipes : dependencies.values()) { |
| for (SatisfiableRecipe satisfiable : recipes) { |
| untrackServiceReference(satisfiable, stopped, dependencies); |
| } |
| } |
| } |
| } |
| |
| private void untrackServiceReference(SatisfiableRecipe recipe, Set<String> stopped, Map<String, List<SatisfiableRecipe>> dependencies) { |
| if (stopped.add(recipe.getName())) { |
| for (Map.Entry<String, List<SatisfiableRecipe>> entry : dependencies.entrySet()) { |
| if (entry.getValue().contains(recipe)) { |
| Recipe r = getRepository().getRecipe(entry.getKey()); |
| if (r instanceof SatisfiableRecipe) { |
| untrackServiceReference((SatisfiableRecipe) r, stopped, dependencies); |
| } |
| } |
| } |
| recipe.stop(); |
| } |
| } |
| |
| public void notifySatisfaction(SatisfiableRecipe satisfiable) { |
| LOGGER.debug("Notified satisfaction {} in bundle {}: {}", |
| new Object[] { satisfiable.getName(), bundleContext.getBundle().getSymbolicName(), satisfiable.isSatisfied() }); |
| if (state == State.Create || state == State.Created ) { |
| Map<String, List<SatisfiableRecipe>> dependencies = getSatisfiableDependenciesMap(); |
| for (Map.Entry<String, List<SatisfiableRecipe>> entry : dependencies.entrySet()) { |
| String name = entry.getKey(); |
| ComponentMetadata metadata = componentDefinitionRegistry.getComponentDefinition(name); |
| if (metadata instanceof ServiceMetadata) { |
| ServiceRecipe reg = (ServiceRecipe) repository.getRecipe(name); |
| synchronized (reg) { |
| boolean satisfied = true; |
| for (SatisfiableRecipe recipe : entry.getValue()) { |
| if (!recipe.isSatisfied()) { |
| satisfied = false; |
| break; |
| } |
| } |
| if (satisfied && !reg.isRegistered()) { |
| LOGGER.debug("Registering service {} due to satisfied references", name); |
| reg.register(); |
| } else if (!satisfied && reg.isRegistered()) { |
| LOGGER.debug("Unregistering service {} due to unsatisfied references", name); |
| reg.unregister(); |
| } |
| } |
| } |
| } |
| } else { |
| schedule(); |
| } |
| } |
| |
| private void instantiateEagerComponents() { |
| List<String> components = new ArrayList<String>(); |
| for (String name : componentDefinitionRegistry.getComponentDefinitionNames()) { |
| ComponentMetadata component = componentDefinitionRegistry.getComponentDefinition(name); |
| boolean eager = component.getActivation() == ComponentMetadata.ACTIVATION_EAGER; |
| if (component instanceof BeanMetadata) { |
| BeanMetadata local = (BeanMetadata) component; |
| eager &= MetadataUtil.isSingletonScope(local); |
| } |
| if (eager) { |
| components.add(name); |
| } |
| } |
| LOGGER.debug("Instantiating components: {}", components); |
| try { |
| repository.createAll(components); |
| } catch (ComponentDefinitionException e) { |
| throw e; |
| } catch (Throwable t) { |
| throw new ComponentDefinitionException("Unable to instantiate components", t); |
| } |
| } |
| |
| private void registerServices() { |
| services = repository.getAllRecipes(ServiceRecipe.class); |
| for (ServiceRecipe r : services) { |
| List<SatisfiableRecipe> dependencies = getSatisfiableDependenciesMap().get(r.getName()); |
| boolean enabled = true; |
| if (dependencies != null) { |
| for (SatisfiableRecipe recipe : dependencies) { |
| if (!recipe.isSatisfied()) { |
| enabled = false; |
| break; |
| } |
| } |
| } |
| if (enabled) { |
| r.register(); |
| } |
| } |
| } |
| |
| private void unregisterServices() { |
| if (repository != null) { |
| List<ServiceRecipe> recipes = this.services; |
| this.services = null; |
| if (recipes != null) { |
| for (ServiceRecipe r : recipes) { |
| r.unregister(); |
| } |
| } |
| } |
| } |
| |
| private void destroyComponents() { |
| if (repository != null) { |
| repository.destroy(); |
| } |
| } |
| |
| private String[] getMissingDependencies() { |
| List<String> missing = new ArrayList<String>(); |
| Map<String, List<SatisfiableRecipe>> dependencies = getSatisfiableDependenciesMap(); |
| Set<SatisfiableRecipe> recipes = new HashSet<SatisfiableRecipe>(); |
| for (List<SatisfiableRecipe> deps : dependencies.values()) { |
| for (SatisfiableRecipe recipe : deps) { |
| if (!recipe.isSatisfied()) { |
| recipes.add(recipe); |
| } |
| } |
| } |
| for (SatisfiableRecipe recipe : recipes) { |
| missing.add(recipe.getOsgiFilter()); |
| } |
| return missing.toArray(new String[missing.size()]); |
| } |
| |
| public Set<String> getComponentIds() { |
| Set<String> set = new LinkedHashSet<String>(); |
| set.addAll(componentDefinitionRegistry.getComponentDefinitionNames()); |
| set.add("blueprintContainer"); |
| set.add("blueprintBundle"); |
| set.add("blueprintBundleContext"); |
| set.add("blueprintConverter"); |
| return set; |
| } |
| |
| public Object getComponentInstance(String id) throws NoSuchComponentException { |
| if (repository == null) { |
| throw new NoSuchComponentException(id); |
| } |
| try { |
| LOGGER.debug("Instantiating component {}", id); |
| return repository.create(id); |
| } catch (NoSuchComponentException e) { |
| throw e; |
| } catch (ComponentDefinitionException e) { |
| throw e; |
| } catch (Throwable t) { |
| throw new ComponentDefinitionException("Cound not create component instance for " + id, t); |
| } |
| } |
| |
| public ComponentMetadata getComponentMetadata(String id) { |
| ComponentMetadata metadata = componentDefinitionRegistry.getComponentDefinition(id); |
| if (metadata == null) { |
| throw new NoSuchComponentException(id); |
| } |
| return metadata; |
| } |
| |
| public <T extends ComponentMetadata> Collection<T> getMetadata(Class<T> clazz) { |
| Collection<T> metadatas = new ArrayList<T>(); |
| for (String name : componentDefinitionRegistry.getComponentDefinitionNames()) { |
| ComponentMetadata component = componentDefinitionRegistry.getComponentDefinition(name); |
| getMetadata(clazz, component, metadatas); |
| } |
| metadatas = Collections.unmodifiableCollection(metadatas); |
| return metadatas; |
| } |
| |
| private <T extends ComponentMetadata> void getMetadata(Class<T> clazz, Metadata component, Collection<T> metadatas) { |
| if (component == null) { |
| return; |
| } |
| if (clazz.isInstance(component)) { |
| metadatas.add(clazz.cast(component)); |
| } |
| if (component instanceof BeanMetadata) { |
| getMetadata(clazz, ((BeanMetadata) component).getFactoryComponent(), metadatas); |
| for (BeanArgument arg : ((BeanMetadata) component).getArguments()) { |
| getMetadata(clazz, arg.getValue(), metadatas); |
| } |
| for (BeanProperty prop : ((BeanMetadata) component).getProperties()) { |
| getMetadata(clazz, prop.getValue(), metadatas); |
| } |
| } |
| if (component instanceof CollectionMetadata) { |
| for (Metadata m : ((CollectionMetadata) component).getValues()) { |
| getMetadata(clazz, m, metadatas); |
| } |
| } |
| if (component instanceof MapMetadata) { |
| for (MapEntry m : ((MapMetadata) component).getEntries()) { |
| getMetadata(clazz, m.getKey(), metadatas); |
| getMetadata(clazz, m.getValue(), metadatas); |
| } |
| } |
| if (component instanceof PropsMetadata) { |
| for (MapEntry m : ((PropsMetadata) component).getEntries()) { |
| getMetadata(clazz, m.getKey(), metadatas); |
| getMetadata(clazz, m.getValue(), metadatas); |
| } |
| } |
| if (component instanceof ServiceReferenceMetadata) { |
| for (ReferenceListener l : ((ServiceReferenceMetadata) component).getReferenceListeners()) { |
| getMetadata(clazz, l.getListenerComponent(), metadatas); |
| } |
| } |
| if (component instanceof ServiceMetadata) { |
| getMetadata(clazz, ((ServiceMetadata) component).getServiceComponent(), metadatas); |
| for (MapEntry m : ((ServiceMetadata) component).getServiceProperties()) { |
| getMetadata(clazz, m.getKey(), metadatas); |
| getMetadata(clazz, m.getValue(), metadatas); |
| } |
| for (RegistrationListener l : ((ServiceMetadata) component).getRegistrationListeners()) { |
| getMetadata(clazz, l.getListenerComponent(), metadatas); |
| } |
| } |
| } |
| |
| public Converter getConverter() { |
| return converter; |
| } |
| |
| public ComponentDefinitionRegistryImpl getComponentDefinitionRegistry() { |
| return componentDefinitionRegistry; |
| } |
| |
| public BundleContext getBundleContext() { |
| return bundleContext; |
| } |
| |
| public void destroy() { |
| destroyed = true; |
| eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.DESTROYING, getBundleContext().getBundle(), getExtenderBundle())); |
| |
| if (timeoutFuture != null) { |
| timeoutFuture.cancel(false); |
| } |
| if (registration != null) { |
| registration.unregister(); |
| } |
| if (handlerSet != null) { |
| handlerSet.removeListener(this); |
| handlerSet.destroy(); |
| } |
| unregisterServices(); |
| untrackServiceReferences(); |
| |
| synchronized (running) { |
| while (running.get()) { |
| try { |
| running.wait(); |
| } catch (InterruptedException e) { |
| // Ignore |
| } |
| } |
| } |
| |
| destroyComponents(); |
| |
| eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.DESTROYED, getBundleContext().getBundle(), getExtenderBundle())); |
| LOGGER.debug("Blueprint container destroyed: {}", this.bundleContext); |
| } |
| |
| public void namespaceHandlerRegistered(URI uri) { |
| if (namespaces != null && namespaces.contains(uri)) { |
| schedule(); |
| } |
| } |
| |
| public void namespaceHandlerUnregistered(URI uri) { |
| if (namespaces != null && namespaces.contains(uri)) { |
| unregisterServices(); |
| untrackServiceReferences(); |
| destroyComponents(); |
| state = State.WaitForNamespaceHandlers; |
| schedule(); |
| } |
| } |
| |
| } |
| |