/* * 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.PrivilegedAction; import java.security.PrivilegedActionException; import java.security.PrivilegedExceptionAction; 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.ExecutorService; 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.NamespaceHandler; import org.apache.aries.blueprint.NamespaceHandler2; import org.apache.aries.blueprint.Processor; import org.apache.aries.blueprint.di.ExecutionContext; import org.apache.aries.blueprint.di.Recipe; import org.apache.aries.blueprint.di.Repository; import org.apache.aries.blueprint.namespace.MissingNamespaceException; import org.apache.aries.blueprint.namespace.NamespaceHandlerRegistryImpl; import org.apache.aries.blueprint.parser.ComponentDefinitionRegistryImpl; import org.apache.aries.blueprint.parser.NamespaceHandlerSet; import org.apache.aries.blueprint.parser.Parser; import org.apache.aries.blueprint.proxy.ProxyUtils; import org.apache.aries.blueprint.reflect.MetadataUtil; import org.apache.aries.blueprint.reflect.PassThroughMetadataImpl; import org.apache.aries.blueprint.services.ExtendedBlueprintContainer; import org.apache.aries.blueprint.utils.HeaderParser; import org.apache.aries.blueprint.utils.HeaderParser.PathElement; import org.apache.aries.blueprint.utils.JavaUtils; import org.apache.aries.blueprint.utils.ServiceUtil; import org.apache.aries.proxy.ProxyManager; import org.apache.aries.util.AriesFrameworkUtil; 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.framework.wiring.BundleWiring; 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; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; /** * TODO: javadoc * * @version $Rev$, $Date$ */ @SuppressWarnings("deprecation") // due to the deprecated org.apache.aries.blueprint.ExtendedBlueprintContainer public class BlueprintContainerImpl implements ExtendedBlueprintContainer, NamespaceHandlerSet.Listener, Runnable, SatisfiableRecipe.SatisfactionListener, org.apache.aries.blueprint.ExtendedBlueprintContainer { private static final Logger LOGGER = LoggerFactory.getLogger(BlueprintContainerImpl.class); private static final Class[] SECURITY_BUGFIX = { BlueprintDomainCombiner.class, BlueprintProtectionDomain.class, }; public enum State { Unknown, WaitForNamespaceHandlers, Populated, WaitForInitialReferences, InitialReferencesSatisfied, WaitForInitialReferences2, Create, Created, Failed, } private final BundleContext bundleContext; private final Bundle bundle; 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 ExecutorService executors; private final ScheduledExecutorService timer; private final Collection<URI> additionalNamespaces; private Set<URI> namespaces; private State state = State.Unknown; private NamespaceHandlerSet handlerSet; private final AtomicBoolean destroyed = new AtomicBoolean(false); 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 String xmlValidation; private ScheduledFuture timeoutFuture; private final AtomicBoolean scheduled = new AtomicBoolean(); private List<ServiceRecipe> services; private final AccessControlContext accessControlContext; private final IdSpace tempRecipeIdSpace = new IdSpace(); private final ProxyManager proxyManager; public BlueprintContainerImpl(Bundle bundle, BundleContext bundleContext, Bundle extenderBundle, BlueprintListener eventDispatcher, NamespaceHandlerRegistry handlers, ExecutorService executor, ScheduledExecutorService timer, List<Object> pathList, ProxyManager proxyManager, Collection<URI> namespaces) { this.bundle = bundle; 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 = executor != null ? new ExecutorServiceWrapper(executor) : null; this.timer = timer; this.processors = new ArrayList<Processor>(); if (System.getSecurityManager() != null) { this.accessControlContext = BlueprintDomainCombiner.createAccessControlContext(bundleContext); } else { this.accessControlContext = null; } this.proxyManager = proxyManager; this.additionalNamespaces = namespaces; } public ExecutorService getExecutors() { return executors; } public Bundle getExtenderBundle() { return extenderBundle; } public ProxyManager getProxyManager() { return proxyManager; } 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 readDirectives() { 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); } xmlValidation = paths.get(0).getDirective(BlueprintConstants.XML_VALIDATION); // enabled if null or "true"; structure-only if "structure"; disabled otherwise LOGGER.debug("Xml-validation directive: {}", xmlValidation); } public void schedule() { if (scheduled.compareAndSet(false, true)) { executors.submit(this); } } public void reload() { synchronized (scheduled) { if (destroyed.get()) { return; } tidyupComponents(); resetComponentDefinitionRegistry(); cancelFutureIfPresent(); this.repository = null; this.processors = new ArrayList<Processor>(); timeout = 5 * 60 * 1000; waitForDependencies = true; xmlValidation = null; if (handlerSet != null) { handlerSet.removeListener(this); handlerSet.destroy(); handlerSet = null; } state = State.Unknown; schedule(); } } protected void resetComponentDefinitionRegistry() { this.componentDefinitionRegistry.reset(); componentDefinitionRegistry.registerComponentDefinition(new PassThroughMetadataImpl("blueprintContainer", this)); componentDefinitionRegistry.registerComponentDefinition(new PassThroughMetadataImpl("blueprintBundle", bundle)); componentDefinitionRegistry.registerComponentDefinition(new PassThroughMetadataImpl("blueprintBundleContext", bundleContext)); componentDefinitionRegistry.registerComponentDefinition(new PassThroughMetadataImpl("blueprintConverter", converter)); } public void run() { scheduled.set(false); synchronized (scheduled) { doRun(); } } public State getState() { return state; } /** * This method must be called inside a synchronized block to ensure this method is not run concurrently */ private void doRun() { try { for (;;) { if (destroyed.get()) { return; } if (bundle.getState() != Bundle.ACTIVE && bundle.getState() != Bundle.STARTING) { return; } if (bundle.getBundleContext() != bundleContext) { return; } LOGGER.debug("Running blueprint container for bundle {}/{} in state {}", getBundle().getSymbolicName(), getBundle().getVersion(), state); switch (state) { case Unknown: readDirectives(); eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.CREATING, getBundle(), getExtenderBundle())); parser = new Parser(); parser.parse(getResources()); namespaces = parser.getNamespaces(); if (additionalNamespaces != null) { namespaces.addAll(additionalNamespaces); } handlerSet = handlers.getNamespaceHandlers(namespaces, getBundle()); handlerSet.addListener(this); state = State.WaitForNamespaceHandlers; break; case WaitForNamespaceHandlers: { List<String> missing = new ArrayList<String>(); List<URI> missingURIs = new ArrayList<URI>(); for (URI ns : handlerSet.getNamespaces()) { if (handlerSet.getNamespaceHandler(ns) == null) { missing.add("(&(" + Constants.OBJECTCLASS + "=" + NamespaceHandler.class.getName() + ")(" + NamespaceHandlerRegistryImpl.NAMESPACE + "=" + ns + "))"); missingURIs.add(ns); } } if (missing.size() > 0) { LOGGER.info("Bundle {}/{} is waiting for namespace handlers {}", getBundle().getSymbolicName(), getBundle().getVersion(), missingURIs); eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.GRACE_PERIOD, getBundle(), getExtenderBundle(), missing.toArray(new String[missing.size()]))); return; } resetComponentDefinitionRegistry(); if (xmlValidation == null || "true".equals(xmlValidation)) { for (URI ns : handlerSet.getNamespaces()) { NamespaceHandler handler = handlerSet.getNamespaceHandler(ns); if (handler instanceof NamespaceHandler2) { if (((NamespaceHandler2) handler).usePsvi()) { xmlValidation = "psvi"; break; } } } } try { if (xmlValidation == null || "true".equals(xmlValidation)) { parser.validate(handlerSet.getSchema(parser.getSchemaLocations())); } else if ("structure".equals(xmlValidation)) { parser.validate(handlerSet.getSchema(parser.getSchemaLocations()), new ValidationHandler()); } else if ("psvi".equals(xmlValidation)) { parser.validatePsvi(handlerSet.getSchema(parser.getSchemaLocations())); } parser.populate(handlerSet, componentDefinitionRegistry); state = State.Populated; } catch (MissingNamespaceException e) { // If we found a missing namespace when parsing the schema, // we remain in the current state handlerSet.getNamespaces().add(e.getNamespace()); } break; } case Populated: getRepository(); trackServiceReferences(); Runnable r = new Runnable() { public void run() { synchronized (scheduled) { if (destroyed.get()) { return; } String[] missingDependecies = getMissingDependencies(); if (missingDependecies.length == 0) { return; } Throwable t = new TimeoutException(); state = State.Failed; tidyupComponents(); LOGGER.error("Unable to start blueprint container for bundle {}/{} due to unresolved dependencies {}", getBundle().getSymbolicName(), getBundle().getVersion(), Arrays.asList(missingDependecies), t); eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.FAILURE, getBundle(), getExtenderBundle(), missingDependecies, t)); } } }; timeoutFuture = timer.schedule(r, timeout, TimeUnit.MILLISECONDS); state = State.WaitForInitialReferences; break; case WaitForInitialReferences: if (waitForDependencies) { String[] missingDependencies = getMissingDependencies(); if (missingDependencies.length > 0) { LOGGER.info("Bundle {}/{} is waiting for dependencies {}", getBundle().getSymbolicName(), getBundle().getVersion(), Arrays.asList(missingDependencies)); eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.GRACE_PERIOD, 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) { LOGGER.info("Bundle {}/{} is waiting for dependencies {}", getBundle().getSymbolicName(), getBundle().getVersion(), Arrays.asList(missingDependencies)); eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.GRACE_PERIOD, getBundle(), getExtenderBundle(), missingDependencies)); return; } } state = State.Create; break; case Create: cancelFutureIfPresent(); instantiateEagerComponents(); //Register the services after the eager components are ready, as per 121.6 registerServices(); // Register the BlueprintContainer in the OSGi registry int bs = bundle.getState(); if (registration == null && (bs == Bundle.ACTIVE || bs == Bundle.STARTING)) { Properties props = new Properties(); props.put(BlueprintConstants.CONTAINER_SYMBOLIC_NAME_PROPERTY, bundle.getSymbolicName()); props.put(BlueprintConstants.CONTAINER_VERSION_PROPERTY, JavaUtils.getBundleVersion(bundle)); registration = registerService(new String [] { BlueprintContainer.class.getName() }, this, props); } eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.CREATED, getBundle(), getExtenderBundle())); state = State.Created; break; case Created: case Failed: return; } } } catch (Throwable t) { try { state = State.Failed; cancelFutureIfPresent(); tidyupComponents(); LOGGER.error("Unable to start blueprint container for bundle {}/{}", getBundle().getSymbolicName(), getBundle().getVersion(), t); eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.FAILURE, getBundle(), getExtenderBundle(), t)); } catch (RuntimeException re) { LOGGER.debug("Tidying up components failed. ", re); throw re; } } } 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 = bundle.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 bundle.loadClass(name); } else { try { return AccessController.doPrivileged(new PrivilegedExceptionAction<Class>() { public Class run() throws Exception { return bundle.loadClass(name); } }, accessControlContext); } catch (PrivilegedActionException e) { Exception cause = e.getException(); if (cause instanceof ClassNotFoundException) { throw (ClassNotFoundException) cause; } throw new IllegalStateException("Unexpected checked exception", cause); } } } @Override public ClassLoader getClassLoader() { return getBundle().adapt(BundleWiring.class).getClassLoader(); } 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); } } public AccessControlContext getAccessControlContext() { return accessControlContext; } public BlueprintRepository getRepository() { if (repository == null) { repository = new RecipeBuilder(this, tempRecipeIdSpace).createRepository(); } return repository; } protected 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 = getRepository().createAll(typeConverters, ProxyUtils.asList(Converter.class)); 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"); } } } protected void processProcessors() throws Exception { // Instantiate 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; } Object obj = null; if (ComponentDefinitionRegistryProcessor.class.isAssignableFrom(clazz)) { obj = repository.create(bean.getId(), ProxyUtils.asList(ComponentDefinitionRegistryProcessor.class)); ((ComponentDefinitionRegistryProcessor) obj).process(componentDefinitionRegistry); } if (Processor.class.isAssignableFrom(clazz)) { obj = repository.create(bean.getId(), ProxyUtils.asList(Processor.class)); this.processors.add((Processor) obj); } if (obj == null) { continue; } untrackServiceReferences(); updateUninstantiatedRecipes(); getSatisfiableDependenciesMap(true); trackServiceReferences(); } } private void updateUninstantiatedRecipes() { 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 }); } } } 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); } } } synchronized (satisfiablesLock) { satisfiables = null; } } 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) { if (destroyed.get()) { return; } LOGGER.debug("Notified satisfaction {} in bundle {}/{}: {}", satisfiable.getName(), bundle.getSymbolicName(), getBundle().getVersion(), 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(); } } } protected 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() { return new LinkedHashSet<String>(componentDefinitionRegistry.getComponentDefinitionNames()); } public Object getComponentInstance(String id) throws NoSuchComponentException { if (repository == null || destroyed.get()) { 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 Bundle getBundle() { return bundle; } public void destroy() { synchronized (scheduled) { destroyed.set(true); } cancelFutureIfPresent(); eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.DESTROYING, getBundle(), getExtenderBundle())); executors.shutdownNow(); if (handlerSet != null) { handlerSet.removeListener(this); handlerSet.destroy(); } try { executors.awaitTermination(5 * 60, TimeUnit.SECONDS); } catch (InterruptedException e) { LOGGER.debug("Interrupted waiting for executor to shut down"); } tidyupComponents(); eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.DESTROYED, getBundle(), getExtenderBundle())); LOGGER.debug("Blueprint container {} destroyed", getBundle().getSymbolicName(), getBundle().getVersion()); } public static void safeUnregisterService(ServiceRegistration reg) { if(reg != null) { try { reg.unregister(); } catch (IllegalStateException e) { //This can be safely ignored } } } protected void quiesce() { destroyed.set(true); eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.DESTROYING, getBundle(), getExtenderBundle())); cancelFutureIfPresent(); ServiceUtil.safeUnregisterService(registration); if (handlerSet != null) { handlerSet.removeListener(this); handlerSet.destroy(); } LOGGER.debug("Blueprint container {} quiesced", getBundle().getSymbolicName(), getBundle().getVersion()); } private void cancelFutureIfPresent() { if (timeoutFuture != null) { timeoutFuture.cancel(false); } } public void namespaceHandlerRegistered(URI uri) { if (handlerSet != null && handlerSet.getNamespaces().contains(uri)) { schedule(); } } public void namespaceHandlerUnregistered(URI uri) { if (handlerSet != null && handlerSet.getNamespaces().contains(uri)) { synchronized (scheduled) { if (destroyed.get()) { return; } tidyupComponents(); resetComponentDefinitionRegistry(); cancelFutureIfPresent(); this.repository = null; handlerSet.removeListener(this); handlerSet.destroy(); handlerSet = handlers.getNamespaceHandlers(namespaces, getBundle()); handlerSet.addListener(this); state = State.WaitForNamespaceHandlers; schedule(); } } } private void tidyupComponents() { unregisterServices(); destroyComponents(); untrackServiceReferences(); } public void injectBeanInstance(BeanMetadata bmd, Object o) throws IllegalArgumentException, ComponentDefinitionException { ExecutionContext origContext = ExecutionContext.Holder.setContext((ExecutionContext)getRepository()); try { ComponentMetadata cmd = componentDefinitionRegistry.getComponentDefinition(bmd.getId()); if (cmd == null || cmd != bmd) { throw new IllegalArgumentException(bmd.getId() + " not found in blueprint container"); } Recipe r = this.getRepository().getRecipe(bmd.getId()); if (r instanceof BeanRecipe) { BeanRecipe br = (BeanRecipe)r; if (!br.getType().isInstance(o)) { throw new IllegalArgumentException("Instance class " + o.getClass().getName() + " is not an instance of " + br.getClass()); } br.setProperties(o); } else { throw new IllegalArgumentException(bmd.getId() + " does not refer to a BeanRecipe"); } } finally { ExecutionContext.Holder.setContext(origContext); } } // this could be parameterized/customized, but for now, hard-coded for ignoring datatype validation private static class ValidationHandler implements ErrorHandler { @Override public void warning(SAXParseException exception) throws SAXException { // ignore } @Override public void error(SAXParseException exception) throws SAXException { final String cvctext = exception.getMessage(); if (cvctext != null && (cvctext.startsWith("cvc-datatype-valid.1") || cvctext.startsWith("cvc-attribute.3"))) { return; } throw exception; } @Override public void fatalError(SAXParseException exception) throws SAXException { throw exception; } } }