/* * 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.net.URI; import java.net.URL; import java.security.AccessControlContext; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import org.apache.aries.blueprint.ComponentDefinitionRegistryProcessor; import org.apache.aries.blueprint.ExtendedBeanMetadata; 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.parser.ComponentDefinitionRegistryImpl; import org.apache.aries.blueprint.parser.NamespaceHandlerSet; import org.apache.aries.blueprint.parser.Parser; import org.apache.aries.blueprint.reflect.MetadataUtil; import org.apache.aries.blueprint.reflect.PassThroughMetadataImpl; import org.apache.aries.blueprint.services.ExtendedBlueprintContainer; 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; public class BlueprintContainerImpl implements ExtendedBlueprintContainer { private static final Logger LOGGER = LoggerFactory.getLogger(BlueprintContainerImpl.class); private final ClassLoader loader; private final List<URL> resources; private final AggregateConverter converter; private final ComponentDefinitionRegistryImpl componentDefinitionRegistry; private final AtomicBoolean destroyed = new AtomicBoolean(false); private final IdSpace tempRecipeIdSpace = new IdSpace(); private BlueprintRepository repository; private List<Processor> processors = new ArrayList<Processor>(); private Map<String, String> properties; private NamespaceHandlerSet nsHandlerSet; public BlueprintContainerImpl(ClassLoader loader, List<URL> resources) throws Exception { this(loader, resources, null, true); } public BlueprintContainerImpl(ClassLoader loader, List<URL> resources, boolean init) throws Exception { this(loader, resources, null, init); } public BlueprintContainerImpl(ClassLoader loader, List<URL> resources, Map<String, String> properties, boolean init) throws Exception { this(loader, resources, properties, null, init); } public BlueprintContainerImpl(ClassLoader loader, List<URL> resources, Map<String, String> properties, NamespaceHandlerSet nsHandlerSet, boolean init) throws Exception { this.loader = loader; this.converter = new AggregateConverter(this); this.componentDefinitionRegistry = new ComponentDefinitionRegistryImpl(); this.resources = resources; this.properties = properties; this.nsHandlerSet = nsHandlerSet; if (init) { init(); } } public String getProperty(String key) { if (properties != null && properties.containsKey(key)) { return properties.get(key); } return System.getProperty(key); } protected NamespaceHandlerSet createNamespaceHandlerSet(Set<URI> namespaces) { NamespaceHandlerSet handlerSet = createNamespaceHandlerSet(); // Check namespaces Set<URI> unsupported = new LinkedHashSet<URI>(); for (URI ns : namespaces) { if (!handlerSet.getNamespaces().contains(ns)) { unsupported.add(ns); } } if (unsupported.size() > 0) { throw new IllegalArgumentException("Unsupported namespaces: " + unsupported.toString()); } return handlerSet; } protected NamespaceHandlerSet createNamespaceHandlerSet() { return nsHandlerSet == null ? new SimpleNamespaceHandlerSet() : nsHandlerSet; } public void init() throws Exception { init(true); } public void init(boolean validate) throws Exception { // Parse xml resources Parser parser = new Parser(); parser.parse(getResources()); // Check namespaces Set<URI> namespaces = parser.getNamespaces(); // Create handler set NamespaceHandlerSet handlerSet = createNamespaceHandlerSet(namespaces); // Add predefined beans componentDefinitionRegistry.registerComponentDefinition(new PassThroughMetadataImpl("blueprintContainer", this)); if (validate) { // Validate parser.validate(handlerSet.getSchema()); } // Populate parser.populate(handlerSet, componentDefinitionRegistry); // Create repository repository = new NoOsgiRecipeBuilder(this, tempRecipeIdSpace).createRepository(); // Processors handling processTypeConverters(); processProcessors(); // Instantiate eager singletons instantiateEagerComponents(); } public void destroy() { repository.destroy(); } public List<URL> getResources() { return resources; } public Converter getConverter() { return converter; } public Class loadClass(String name) throws ClassNotFoundException { return loader.loadClass(name); } public URL getResource(String name) { return loader.getResource(name); } public AccessControlContext getAccessControlContext() { return null; } public ComponentDefinitionRegistryImpl getComponentDefinitionRegistry() { return componentDefinitionRegistry; } 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 Set<String> getComponentIds() { return new LinkedHashSet<String>(componentDefinitionRegistry.getComponentDefinitionNames()); } public Object getComponentInstance(String id) { 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; } public BlueprintRepository getRepository() { return repository; } 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); } } } 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, Arrays.<Class<?>>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"); } } } private 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; } if (ComponentDefinitionRegistryProcessor.class.isAssignableFrom(clazz)) { Object obj = repository.create(bean.getId(), Arrays.<Class<?>>asList(ComponentDefinitionRegistryProcessor.class)); ((ComponentDefinitionRegistryProcessor) obj).process(componentDefinitionRegistry); } else if (Processor.class.isAssignableFrom(clazz)) { Object obj = repository.create(bean.getId(), Arrays.<Class<?>>asList(Processor.class)); this.processors.add((Processor) obj); } else { continue; } updateUninstantiatedRecipes(); } } private void updateUninstantiatedRecipes() { Repository tmpRepo = new NoOsgiRecipeBuilder(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 }); } } } protected 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); } } }