You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by zo...@apache.org on 2011/02/27 22:21:22 UTC

svn commit: r1075149 [11/23] - in /aries/tags/blueprint-0.3.1: ./ blueprint-annotation-api/ blueprint-annotation-api/src/ blueprint-annotation-api/src/main/ blueprint-annotation-api/src/main/java/ blueprint-annotation-api/src/main/java/org/ blueprint-a...

Added: aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintContainerImpl.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintContainerImpl.java?rev=1075149&view=auto
==============================================================================
--- aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintContainerImpl.java (added)
+++ aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintContainerImpl.java Sun Feb 27 21:21:05 2011
@@ -0,0 +1,875 @@
+/*
+ * 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: 1072952 $, $Date: 2011-02-21 12:41:31 +0000 (Mon, 21 Feb 2011) $
+ */
+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 reload() {
+        unregisterServices();
+        untrackServiceReferences();
+        destroyComponents();
+        this.componentDefinitionRegistry.reset();
+        this.repository = null;
+        this.processors = new ArrayList<Processor>();
+        timeout = 5 * 60 * 1000;
+        waitForDependencies = true;
+        xmlValidation = true;
+        state = State.Unknown;
+        schedule();
+    }
+
+    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.info("Bundle {} is waiting for namespace handlers ", bundleContext.getBundle().getSymbolicName(), 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;
+                                    String[] missingDependecies = getMissingDependencies();
+                                    unregisterServices();
+                                    untrackServiceReferences();
+                                    destroyComponents();
+                                    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) {
+                                LOGGER.info("Bundle {} is waiting for dependencies {}", bundleContext.getBundle().getSymbolicName(), Arrays.asList(missingDependencies));
+                                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) {
+                                LOGGER.info("Bundle {} is waiting for dependencies {}", bundleContext.getBundle().getSymbolicName(), Arrays.asList(missingDependencies));
+                                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
+                        int bs = bundleContext.getBundle().getState();
+                        if (registration == null && (bs == Bundle.ACTIVE || bs == Bundle.STARTING)) {
+                            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();
+            }
+        }
+    }
+
+    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() {
+        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);
+    }
+    
+    protected void quiesce() {
+        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();
+        }
+        LOGGER.debug("Blueprint container quiesced: {}", 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();
+        }
+    }
+
+}
+

Added: aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintEventDispatcher.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintEventDispatcher.java?rev=1075149&view=auto
==============================================================================
--- aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintEventDispatcher.java (added)
+++ aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintEventDispatcher.java Sun Feb 27 21:21:05 2011
@@ -0,0 +1,296 @@
+/**
+ * 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.util.Arrays;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.blueprint.container.BlueprintEvent;
+import org.osgi.service.blueprint.container.BlueprintListener;
+import org.osgi.service.blueprint.container.EventConstants;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.aries.blueprint.utils.JavaUtils;
+
+/**
+ * The delivery of {@link BlueprintEvent}s is complicated.  The blueprint extender and its containers use this class to
+ * deliver {@link BlueprintEvent}s.
+ *
+ * @version $Rev: 982158 $, $Date: 2010-08-04 09:32:15 +0100 (Wed, 04 Aug 2010) $
+ */
+class BlueprintEventDispatcher implements BlueprintListener {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(BlueprintEventDispatcher.class);
+
+    private final Set<BlueprintListener> listeners = new CopyOnWriteArraySet<BlueprintListener>();
+    private final Map<Bundle, BlueprintEvent> states = new ConcurrentHashMap<Bundle, BlueprintEvent>();
+    private final ExecutorService executor = Executors.newSingleThreadExecutor(new BlueprintThreadFactory("Blueprint Event Dispatcher"));
+    private final ExecutorService sharedExecutor;
+    private final EventAdminListener eventAdminListener;
+    private final ServiceTracker containerListenerTracker;
+
+    BlueprintEventDispatcher(final BundleContext bundleContext, ExecutorService sharedExecutor) {
+
+        assert bundleContext != null;
+        assert sharedExecutor != null;
+
+        this.sharedExecutor = sharedExecutor;
+
+        EventAdminListener listener = null;
+        try {
+            getClass().getClassLoader().loadClass("org.osgi.service.event.EventAdmin");
+            listener = new EventAdminListener(bundleContext);
+        } catch (Throwable t) {
+            // Ignore, if the EventAdmin package is not available, just don't use it
+            LOGGER.debug("EventAdmin package is not available, just don't use it");
+        }
+        this.eventAdminListener = listener;
+
+        this.containerListenerTracker = new ServiceTracker(bundleContext, BlueprintListener.class.getName(), new ServiceTrackerCustomizer() {
+            public Object addingService(ServiceReference reference) {
+                BlueprintListener listener = (BlueprintListener) bundleContext.getService(reference);
+
+                synchronized (listeners) {
+                    sendInitialEvents(listener);
+                    listeners.add(listener);
+                }
+
+                return listener;
+            }
+
+            public void modifiedService(ServiceReference reference, Object service) {
+            }
+
+            public void removedService(ServiceReference reference, Object service) {
+                listeners.remove(service);
+                bundleContext.ungetService(reference);
+            }
+        });
+        this.containerListenerTracker.open();
+    }
+
+    private void sendInitialEvents(BlueprintListener listener) {
+        for (Map.Entry<Bundle, BlueprintEvent> entry : states.entrySet()) {
+            try {
+                callListener(listener, new BlueprintEvent(entry.getValue(), true));
+            } catch (RejectedExecutionException ree) {
+                LOGGER.warn("Executor shut down", ree);
+                break;
+            }
+        }
+    }
+
+    public void blueprintEvent(final BlueprintEvent event) {
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("Sending blueprint container event {} for bundle {}", toString(event), event.getBundle().getSymbolicName());
+        }
+
+        synchronized (listeners) {
+            callListeners(event);
+            states.put(event.getBundle(), event);
+        }
+
+        if (eventAdminListener != null) {
+            try {
+                sharedExecutor.submit(new Runnable() {
+                    public void run() {
+                        eventAdminListener.blueprintEvent(event);
+                    }
+                });
+            } catch (RejectedExecutionException ree) {
+                LOGGER.warn("Executor shut down", ree);
+            }
+        }
+    }
+
+    @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"})
+    private static String toString(BlueprintEvent event) {
+        return "BlueprintEvent[type=" + getEventType(event.getType())
+                + (event.getDependencies() != null ? ", dependencies=" + Arrays.asList(event.getDependencies()) : "")
+                + (event.getCause() != null ? ", exception=" + event.getCause().getMessage() : "")
+                + "]";
+    }
+
+    private static String getEventType(int type) {
+        switch (type) {
+            case BlueprintEvent.CREATING:
+                return "CREATING";
+            case BlueprintEvent.CREATED:
+                return "CREATED";
+            case BlueprintEvent.DESTROYING:
+                return "DESTROYING";
+            case BlueprintEvent.DESTROYED:
+                return "DESTROYED";
+            case BlueprintEvent.FAILURE:
+                return "FAILURE";
+            case BlueprintEvent.GRACE_PERIOD:
+                return "GRACE_PERIOD";
+            case BlueprintEvent.WAITING:
+                return "WAITING";
+            default:
+                return "UNKNOWN";
+        }
+    }
+
+    private void callListeners(BlueprintEvent event) {
+        for (final BlueprintListener listener : listeners) {
+            try {
+                callListener(listener, event);
+            } catch (RejectedExecutionException ree) {
+                LOGGER.warn("Executor shut down", ree);
+                break;
+            }
+        }
+    }
+
+    private void callListener(final BlueprintListener listener, final BlueprintEvent event) throws RejectedExecutionException {
+        try {
+            executor.invokeAny(Collections.<Callable<Void>>singleton(new Callable<Void>() {
+                public Void call() throws Exception {
+                    listener.blueprintEvent(event);
+                    return null;
+                }
+            }), 60L, TimeUnit.SECONDS);
+        } catch (InterruptedException ie) {
+            LOGGER.warn("Thread interrupted", ie);
+            Thread.currentThread().interrupt();
+        } catch (TimeoutException te) {
+            LOGGER.warn("Listener timed out, will be ignored", te);
+            listeners.remove(listener);
+        } catch (ExecutionException ee) {
+            LOGGER.warn("Listener caused an exception, will be ignored", ee);
+            listeners.remove(listener);
+        }
+    }
+
+    void destroy() {
+        executor.shutdown();
+        // wait for the queued tasks to execute
+        try {
+            executor.awaitTermination(60, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            // ignore
+        }
+        containerListenerTracker.close();
+        // clean up the EventAdmin tracker if we're using that
+        if (eventAdminListener != null) {
+            eventAdminListener.destroy();
+        }
+    }
+
+    public void removeBlueprintBundle(Bundle bundle) {
+        states.remove(bundle);
+    }
+
+    private static class EventAdminListener implements BlueprintListener {
+
+        private final ServiceTracker tracker;
+
+        EventAdminListener(BundleContext context) {
+            tracker = new ServiceTracker(context, EventAdmin.class.getName(), null);
+            tracker.open();
+        }
+
+        @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"})
+        public void blueprintEvent(BlueprintEvent event) {
+            EventAdmin eventAdmin = (EventAdmin) tracker.getService();
+            if (eventAdmin == null) {
+                return;
+            }
+
+            Dictionary<String, Object> props = new Hashtable<String, Object>();
+            props.put(EventConstants.TYPE, event.getType());
+            props.put(EventConstants.EVENT, event);
+            props.put(EventConstants.TIMESTAMP, event.getTimestamp());
+            props.put(EventConstants.BUNDLE, event.getBundle());
+            props.put(EventConstants.BUNDLE_SYMBOLICNAME, event.getBundle().getSymbolicName());
+            props.put(EventConstants.BUNDLE_ID, event.getBundle().getBundleId());
+            props.put(EventConstants.BUNDLE_VERSION, JavaUtils.getBundleVersion(event.getBundle()));
+            props.put(EventConstants.EXTENDER_BUNDLE, event.getExtenderBundle());
+            props.put(EventConstants.EXTENDER_BUNDLE_ID, event.getExtenderBundle().getBundleId());
+            props.put(EventConstants.EXTENDER_BUNDLE_SYMBOLICNAME, event.getExtenderBundle().getSymbolicName());
+            props.put(EventConstants.EXTENDER_BUNDLE_VERSION, JavaUtils.getBundleVersion(event.getExtenderBundle()));
+
+            if (event.getCause() != null) {
+                props.put(EventConstants.CAUSE, event.getCause());
+            }
+            if (event.getDependencies() != null) {
+                props.put(EventConstants.DEPENDENCIES, event.getDependencies());
+            }
+            String topic;
+            switch (event.getType()) {
+                case BlueprintEvent.CREATING:
+                    topic = EventConstants.TOPIC_CREATING;
+                    break;
+                case BlueprintEvent.CREATED:
+                    topic = EventConstants.TOPIC_CREATED;
+                    break;
+                case BlueprintEvent.DESTROYING:
+                    topic = EventConstants.TOPIC_DESTROYING;
+                    break;
+                case BlueprintEvent.DESTROYED:
+                    topic = EventConstants.TOPIC_DESTROYED;
+                    break;
+                case BlueprintEvent.FAILURE:
+                    topic = EventConstants.TOPIC_FAILURE;
+                    break;
+                case BlueprintEvent.GRACE_PERIOD:
+                    topic = EventConstants.TOPIC_GRACE_PERIOD;
+                    break;
+                case BlueprintEvent.WAITING:
+                    topic = EventConstants.TOPIC_WAITING;
+                    break;
+                default:
+                    throw new IllegalStateException("Unknown blueprint event type: " + event.getType());
+            }
+            eventAdmin.postEvent(new Event(topic, props));
+        }
+
+        /**
+         * Perform cleanup at Blueprint extender shutdown.
+         */
+        public void destroy() {
+            tracker.close();
+        }
+
+    }
+
+}

Added: aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintExtender.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintExtender.java?rev=1075149&view=auto
==============================================================================
--- aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintExtender.java (added)
+++ aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintExtender.java Sun Feb 27 21:21:05 2011
@@ -0,0 +1,446 @@
+/**
+ * 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.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.aries.blueprint.BlueprintConstants;
+import org.apache.aries.blueprint.ParserService;
+import org.apache.aries.blueprint.annotation.service.BlueprintAnnotationScanner;
+import org.apache.aries.blueprint.namespace.NamespaceHandlerRegistryImpl;
+import org.apache.aries.blueprint.utils.HeaderParser;
+import org.apache.aries.blueprint.utils.HeaderParser.PathElement;
+import org.apache.aries.proxy.ProxyManager;
+import org.apache.aries.util.SingleServiceTracker;
+import org.apache.aries.util.SingleServiceTracker.SingleServiceListener;
+import org.apache.aries.util.tracker.RecursiveBundleTracker;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.SynchronousBundleListener;
+import org.osgi.service.blueprint.container.BlueprintContainer;
+import org.osgi.service.blueprint.container.BlueprintEvent;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is the blueprint extender that listens to blueprint bundles.  
+ *
+ * @version $Rev: 1030761 $, $Date: 2010-11-03 23:48:47 +0000 (Wed, 03 Nov 2010) $
+ */
+public class BlueprintExtender implements BundleActivator, SynchronousBundleListener {
+
+	/** The QuiesceParticipant implementation class name */
+	private static final String QUIESCE_PARTICIPANT_CLASS = "org.apache.aries.quiesce.participant.QuiesceParticipant";
+    private static final Logger LOGGER = LoggerFactory.getLogger(BlueprintExtender.class);
+
+    private BundleContext context;
+    private ScheduledExecutorService executors;
+    private Map<Bundle, BlueprintContainerImpl> containers;
+    private BlueprintEventDispatcher eventDispatcher;
+    private NamespaceHandlerRegistry handlers;
+    private RecursiveBundleTracker bt;
+    private ServiceRegistration parserServiceReg;
+    private ServiceRegistration quiesceParticipantReg;
+    private static SingleServiceTracker<ProxyManager> proxyManager;
+    
+    public void start(BundleContext ctx) {
+        LOGGER.debug("Starting blueprint extender...");
+
+        this.context = ctx;
+        handlers = new NamespaceHandlerRegistryImpl(ctx);
+        executors = Executors.newScheduledThreadPool(3, new BlueprintThreadFactory("Blueprint Extender"));
+        eventDispatcher = new BlueprintEventDispatcher(ctx, executors);
+        containers = new HashMap<Bundle, BlueprintContainerImpl>();
+
+        int stateMask = Bundle.INSTALLED | Bundle.RESOLVED | Bundle.STARTING | Bundle.ACTIVE
+        | Bundle.STOPPING;
+        bt = new RecursiveBundleTracker(ctx, stateMask, new BlueprintBundleTrackerCustomizer());
+        
+        proxyManager = new SingleServiceTracker<ProxyManager>(ctx, ProxyManager.class, new SingleServiceListener() {
+          public void serviceFound() {
+            LOGGER.debug("Found ProxyManager service, starting to process blueprint bundles");
+            bt.open();
+          }
+          public void serviceLost() {
+            // TODO we should probably close here, not sure.
+          }
+          public void serviceReplaced() {
+          }
+        });
+        proxyManager.open();
+        
+        // Create and publish a ParserService
+        parserServiceReg = ctx.registerService(ParserService.class.getName(), 
+            new ParserServiceImpl (handlers), 
+            new Hashtable<Object, Object>()); 
+
+        try{
+            ctx.getBundle().loadClass(QUIESCE_PARTICIPANT_CLASS);
+            //Class was loaded, register
+
+            quiesceParticipantReg = ctx.registerService(QUIESCE_PARTICIPANT_CLASS, 
+              new BlueprintQuiesceParticipant(ctx, this), 
+              new Hashtable<Object, Object>()); 
+        } 
+        catch (ClassNotFoundException e) 
+        {
+            LOGGER.info("No quiesce support is available, so blueprint components will not participate in quiesce operations");
+        }
+        
+        LOGGER.debug("Blueprint extender started");
+    }
+
+    /**
+     * this method checks the initial bundle that are installed/active before
+     * bundle tracker is opened.
+     *
+     * @param b the bundle to check
+     */
+    private void checkInitialBundle(Bundle b) {
+        // If the bundle is active, check it
+        if (b.getState() == Bundle.ACTIVE) {
+            checkBundle(b);
+            // Also check bundles in the starting state with a lazy activation
+            // policy
+        } else if (b.getState() == Bundle.STARTING) {
+            String activationPolicyHeader = (String) b.getHeaders().get(
+                    Constants.BUNDLE_ACTIVATIONPOLICY);
+            if (activationPolicyHeader != null
+                    && activationPolicyHeader
+                            .startsWith(Constants.ACTIVATION_LAZY)) {
+                checkBundle(b);
+            }
+        }
+        
+    }
+
+    public void stop(BundleContext context) {
+        LOGGER.debug("Stopping blueprint extender...");
+        if (bt != null) {
+        	bt.close();
+        }
+        
+        parserServiceReg.unregister();
+        
+        if (quiesceParticipantReg != null) 
+          	quiesceParticipantReg.unregister();
+
+        // Orderly shutdown of containers
+        while (!containers.isEmpty()) {
+            for (Bundle bundle : getBundlesToDestroy()) {
+                destroyContext(bundle);
+            }
+        }
+        this.eventDispatcher.destroy();
+        this.handlers.destroy();
+        executors.shutdown();
+        LOGGER.debug("Blueprint extender stopped");
+    }
+    
+    /**
+     * @return the proxy manager. This will return null if the blueprint is not yet managing bundles.
+     */
+    public static ProxyManager getProxyManager()
+    {
+      return proxyManager.getService();
+    }
+
+    private List<Bundle> getBundlesToDestroy() {
+        List<Bundle> bundlesToDestroy = new ArrayList<Bundle>();
+        for (Bundle bundle : containers.keySet()) {
+            ServiceReference[] references = bundle.getRegisteredServices();
+            int usage = 0;
+            if (references != null) {
+                for (ServiceReference reference : references) {
+                    usage += getServiceUsage(reference);
+                }
+            }
+            LOGGER.debug("Usage for bundle {} is {}", bundle, usage);
+            if (usage == 0) {
+                bundlesToDestroy.add(bundle);
+            }
+        }
+        if (!bundlesToDestroy.isEmpty()) {
+            Collections.sort(bundlesToDestroy, new Comparator<Bundle>() {
+                public int compare(Bundle b1, Bundle b2) {
+                    return (int) (b2.getLastModified() - b1.getLastModified());
+                }
+            });
+            LOGGER.debug("Selected bundles {} for destroy (no services in use)", bundlesToDestroy);
+        } else {
+            ServiceReference ref = null;
+            for (Bundle bundle : containers.keySet()) {
+                ServiceReference[] references = bundle.getRegisteredServices();
+                for (ServiceReference reference : references) {
+                    if (getServiceUsage(reference) == 0) {
+                        continue;
+                    }
+                    if (ref == null || reference.compareTo(ref) < 0) {
+                        LOGGER.debug("Currently selecting bundle {} for destroy (with reference {})", bundle, reference);
+                        ref = reference;
+                    }
+                }
+            }
+            bundlesToDestroy.add(ref.getBundle());
+            LOGGER.debug("Selected bundle {} for destroy (lowest ranking service)", bundlesToDestroy);
+        }
+        return bundlesToDestroy;
+    }
+
+    private static int getServiceUsage(ServiceReference ref) {
+        Bundle[] usingBundles = ref.getUsingBundles();
+        return (usingBundles != null) ? usingBundles.length : 0;        
+    }
+    
+    public void bundleChanged(BundleEvent event) {
+        Bundle bundle = event.getBundle();
+        if (event.getType() == BundleEvent.LAZY_ACTIVATION) {
+            checkBundle(bundle);
+        } else if (event.getType() == BundleEvent.STARTED) {
+            BlueprintContainerImpl blueprintContainer = containers.get(bundle);
+            if (blueprintContainer == null) {
+                checkBundle(bundle);
+            }
+        } else if (event.getType() == BundleEvent.STOPPING) {
+            destroyContext(bundle);
+        }
+    }
+
+    private void destroyContext(Bundle bundle) {
+        BlueprintContainerImpl blueprintContainer = containers.remove(bundle);
+        if (blueprintContainer != null) {
+            LOGGER.debug("Destroying BlueprintContainer for bundle {}", bundle.getSymbolicName());
+            blueprintContainer.destroy();
+        }
+        eventDispatcher.removeBlueprintBundle(bundle);
+    }
+    
+    private void checkBundle(Bundle bundle) {
+        LOGGER.debug("Scanning bundle {} for blueprint application", bundle.getSymbolicName());
+        try {
+            List<Object> pathList = new ArrayList<Object>();
+            String blueprintHeader = (String) bundle.getHeaders().get(BlueprintConstants.BUNDLE_BLUEPRINT_HEADER);
+            String blueprintHeaderAnnotation = (String) bundle.getHeaders().get(BlueprintConstants.BUNDLE_BLUEPRINT_ANNOTATION_HEADER);
+            if (blueprintHeader == null) {
+                blueprintHeader = "OSGI-INF/blueprint/";
+            } 
+            List<PathElement> paths = HeaderParser.parseHeader(blueprintHeader);
+            for (PathElement path : paths) {
+                String name = path.getName();
+                if (name.endsWith("/")) {
+                    addEntries(bundle, name, "*.xml", pathList);
+                } else {
+                    String baseName;
+                    String filePattern;
+                    int pos = name.lastIndexOf('/');
+                    if (pos < 0) {
+                        baseName = "/";
+                        filePattern = name;
+                    } else {
+                        baseName = name.substring(0, pos + 1);
+                        filePattern = name.substring(pos + 1);
+                    }
+                    if (hasWildcards(filePattern)) {
+                        addEntries(bundle, baseName, filePattern, pathList);
+                    } else {
+                        addEntry(bundle, name, pathList);
+                    }                    
+                }
+            }
+            
+            if (pathList.isEmpty() && blueprintHeaderAnnotation != null && blueprintHeaderAnnotation.trim().equalsIgnoreCase("true")) {
+                LOGGER.debug("Scanning bundle {} for blueprint annotations", bundle.getSymbolicName());
+                ServiceReference sr = this.context.getServiceReference("org.apache.aries.blueprint.annotation.service.BlueprintAnnotationScanner");
+                           
+                if (sr != null) {
+                    BlueprintAnnotationScanner bas = (BlueprintAnnotationScanner)this.context.getService(sr);
+                    // try to generate the blueprint definition XML
+                    URL url = bas.createBlueprintModel(bundle);
+                        
+                    if (url != null) {
+                        pathList.add(url);
+                    }
+                    
+                    this.context.ungetService(sr);
+                }
+             
+            }
+            
+            if (!pathList.isEmpty()) {
+                LOGGER.debug("Found blueprint application in bundle {} with paths: {}", bundle.getSymbolicName(), pathList);
+                // Check compatibility
+                // TODO: For lazy bundles, the class is either loaded from an imported package or not found, so it should
+                // not trigger the activation.  If it does, we need to use something else like package admin or
+                // ServiceReference, or just not do this check, which could be quite harmful.
+                boolean compatible = isCompatible(bundle);
+                if (compatible) {
+                    final BlueprintContainerImpl blueprintContainer = new BlueprintContainerImpl(bundle.getBundleContext(), context.getBundle(), eventDispatcher, handlers, executors, pathList);
+                    containers.put(bundle, blueprintContainer);
+                    blueprintContainer.schedule();
+                } else {
+                    LOGGER.info("Bundle {} is not compatible with this blueprint extender", bundle.getSymbolicName());
+                }
+
+            } else {
+                LOGGER.debug("No blueprint application found in bundle {}", bundle.getSymbolicName());   
+            }
+        } catch (Throwable t) {
+            eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.FAILURE, bundle, context.getBundle(), t));
+        }
+    }
+
+    private boolean isCompatible(Bundle bundle) {
+        // Check compatibility
+        boolean compatible;
+        if (bundle.getState() == Bundle.ACTIVE) {
+            try {
+                Class<?> clazz = bundle.getBundleContext().getBundle().loadClass(BlueprintContainer.class.getName());
+                compatible = (clazz == BlueprintContainer.class);
+            } catch (ClassNotFoundException e) {
+                compatible = true;
+            }
+        } else {
+            // for lazy bundle, we can't load the class, so just assume it's ok
+            compatible = true;
+        }
+        return compatible;
+    }
+    
+    private boolean hasWildcards(String path) {
+        return path.indexOf("*") >= 0; 
+    }
+    
+    private String getFilePart(URL url) {
+        String path = url.getPath();
+        int index = path.lastIndexOf('/');
+        return path.substring(index + 1);
+    }
+    
+    private String cachePath(Bundle bundle, String filePath)
+    {
+      return Integer.toHexString(bundle.hashCode()) + "/" + filePath;
+    }    
+    
+    private URL getOverrideURLForCachePath(String privatePath){
+        URL override = null;
+        File privateDataVersion = context.getDataFile(privatePath);
+        if (privateDataVersion != null
+                && privateDataVersion.exists()) {
+            try {
+                override = privateDataVersion.toURI().toURL();
+            } catch (MalformedURLException e) {
+                LOGGER.error("Unexpected URL Conversion Issue", e);
+            }
+        }
+        return override;
+    }
+    
+    private URL getOverrideURL(Bundle bundle, String path){
+        String cachePath = cachePath(bundle, path);
+        return getOverrideURLForCachePath(cachePath);
+    }
+    
+    private URL getOverrideURL(Bundle bundle, URL path, String basePath){
+        String cachePath = cachePath(bundle, basePath + getFilePart(path));
+        return getOverrideURLForCachePath(cachePath);
+    }    
+    
+    private void addEntry(Bundle bundle, String path, List<Object> pathList) {
+        URL override = getOverrideURL(bundle, path);
+        if(override == null) {
+            pathList.add(path);
+        } else {
+            pathList.add(override);
+        }
+    }
+    
+    private void addEntries(Bundle bundle, String path, String filePattern, List<Object> pathList) {
+        Enumeration<?> e = bundle.findEntries(path, filePattern, false);
+        while (e != null && e.hasMoreElements()) {
+            URL u = (URL) e.nextElement();
+            URL override = getOverrideURL(bundle, u, path);
+            if(override == null) {
+                pathList.add(u);
+            } else {
+                pathList.add(override);
+            }
+        }
+    }
+    
+    // blueprint bundle tracker calls bundleChanged to minimize changes.
+    private class BlueprintBundleTrackerCustomizer implements
+            BundleTrackerCustomizer {
+
+        public BlueprintBundleTrackerCustomizer() {
+        }
+
+        public Object addingBundle(Bundle b, BundleEvent event) {
+            if (event == null) {
+                // existing bundles first added to the tracker with no event change
+                checkInitialBundle(b);
+            } else {
+                bundleChanged(event);
+            }
+
+            return b;
+        }
+
+        public void modifiedBundle(Bundle b, BundleEvent event, Object arg2) {
+            if (event == null) {
+                // cannot think of why we would be interested in a modified bundle with no bundle event
+                return;
+            }
+
+            bundleChanged(event);
+
+        }
+
+        // don't think we would be interested in removedBundle, as that is
+        // called when bundle is removed from the tracker
+        public void removedBundle(Bundle b, BundleEvent event, Object arg2) {
+        }
+    }
+    
+    protected BlueprintContainerImpl getBlueprintContainerImpl(Bundle bundle)
+    {
+    	return containers.get(bundle);
+    }
+    
+}

Added: aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintQuiesceParticipant.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintQuiesceParticipant.java?rev=1075149&view=auto
==============================================================================
--- aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintQuiesceParticipant.java (added)
+++ aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintQuiesceParticipant.java Sun Feb 27 21:21:05 2011
@@ -0,0 +1,169 @@
+/*
+ * 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.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.aries.blueprint.di.Recipe;
+import org.apache.aries.quiesce.manager.QuiesceCallback;
+import org.apache.aries.quiesce.participant.QuiesceParticipant;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+public class BlueprintQuiesceParticipant implements QuiesceParticipant 
+{
+	private final BundleContext ctx;
+	private final BlueprintExtender extender;
+	
+	public BlueprintQuiesceParticipant(BundleContext context, BlueprintExtender extender)
+	{
+		this.ctx = context;
+		this.extender = extender;
+	}
+
+	/**
+	 * A Threadpool for running quiesce operations
+	 */
+	private final ExecutorService executor = new ThreadPoolExecutor(0, 10, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory() 
+	{
+		public Thread newThread(Runnable r) 
+		{
+	        Thread t = new Thread(r, "Blueprint-Container-ThreadPool");
+	        t.setDaemon(true);
+	        return t;
+		}
+    });
+	
+	public void quiesce(QuiesceCallback callback, List<Bundle> bundlesToQuiesce) 
+	{
+	    boolean shutdownMe = false;
+		for(Bundle b : bundlesToQuiesce) 
+		{
+		  try 
+		  {
+		    executor.execute(new QuiesceBundle(callback, b, extender));
+		  } 
+		  catch (RejectedExecutionException re) {
+		  }
+		  
+          //If we are quiescing, then we need to quiesce this threadpool!
+		  shutdownMe |= b.equals(ctx.getBundle());
+		}
+		
+		if (shutdownMe) executor.shutdown();
+	}
+
+  /**
+   * A runnable Quiesce operation for a single bundle
+   */
+	private static final class QuiesceBundle implements Runnable 
+	{
+		/** The bundle being quiesced */
+		private final Bundle bundleToQuiesce;
+		private final QuiesceCallback callback;
+		private final BlueprintExtender extender;
+		
+		public QuiesceBundle(QuiesceCallback callback, Bundle bundleToQuiesce, 
+				BlueprintExtender extender) 
+		{	
+			super();
+			this.callback = callback;
+			this.bundleToQuiesce = bundleToQuiesce;
+			this.extender = extender;
+		}
+
+		public void run() 
+		{
+			BlueprintContainerImpl container = extender.getBlueprintContainerImpl(bundleToQuiesce);
+						
+			// have we got an actual blueprint bundle
+			if (container != null) {
+  			BlueprintRepository repository = container.getRepository();
+  			Set<String> names = repository.getNames();
+  			container.quiesce();
+  			boolean hasServices = false;
+  			
+  			for (String name: names)
+  			{
+  				Recipe recipe = repository.getRecipe(name);
+  				if (recipe instanceof ServiceRecipe)
+  				{
+  					hasServices = true;
+  					((ServiceRecipe)recipe).quiesce(new QuiesceDelegatingCallback(callback, bundleToQuiesce));
+  				}
+  			}
+  			//If the bundle has no services we can quiesce immediately
+  			if (!hasServices)
+  			{
+  				callback.bundleQuiesced(bundleToQuiesce);
+  			}
+			} else {
+			  // for non-Blueprint bundles just call return completed
+			  
+			  callback.bundleQuiesced(bundleToQuiesce);
+			}
+		}
+	}
+	
+    /**
+	 * A wrapper to protect our internals from the Quiesce API so that we can make it
+	 * an optional dependency
+	 */
+	private static final class QuiesceDelegatingCallback implements DestroyCallback 
+	{
+	  
+	  /** The callback to delegate to */
+	  private final QuiesceCallback callback;
+	
+	  /** The single bundle being quiesced by this DestroyCallback */
+	  private final Bundle toQuiesce;
+	
+	  private final Set<String> services = new HashSet<String>();
+	    
+	  public QuiesceDelegatingCallback(QuiesceCallback cbk, Bundle b) 
+	  {
+	    callback = cbk;
+	    toQuiesce = b;
+	      
+	    ServiceReference[] serviceRefs = b.getRegisteredServices();
+	    
+	    for (ServiceReference ref : serviceRefs)
+	    {
+	  	  services.add(b.getBundleContext().getService(ref).toString());
+	    }
+	  }
+	    
+	  public void callback(Object key) 
+	  {
+	    if (key != null && services.remove(key.toString()) && services.isEmpty())
+	    {
+	 	  	callback.bundleQuiesced(toQuiesce);
+	    }
+	  }
+	}
+}