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 21:50:51 UTC

svn commit: r1075143 [11/23] - in /aries/tags/blueprint-0.2.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.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintEventDispatcher.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintEventDispatcher.java?rev=1075143&view=auto
==============================================================================
--- aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintEventDispatcher.java (added)
+++ aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintEventDispatcher.java Sun Feb 27 20:50:38 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.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintExtender.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintExtender.java?rev=1075143&view=auto
==============================================================================
--- aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintExtender.java (added)
+++ aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintExtender.java Sun Feb 27 20:50:38 2011
@@ -0,0 +1,395 @@
+/**
+ * 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 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.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.BundleTracker;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+import org.osgi.util.tracker.ServiceTracker;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is the blueprint extender that listens to blueprint bundles.  
+ *
+ * @version $Rev: 982158 $, $Date: 2010-08-04 09:32:15 +0100 (Wed, 04 Aug 2010) $
+ */
+public class BlueprintExtender implements BundleActivator, SynchronousBundleListener {
+
+    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;
+
+    public void start(BundleContext context) {
+        LOGGER.debug("Starting blueprint extender...");
+
+        this.context = context;
+        handlers = new NamespaceHandlerRegistryImpl(context);
+        executors = Executors.newScheduledThreadPool(3, new BlueprintThreadFactory("Blueprint Extender"));
+        eventDispatcher = new BlueprintEventDispatcher(context, executors);
+        containers = new HashMap<Bundle, BlueprintContainerImpl>();
+
+        int stateMask = Bundle.INSTALLED | Bundle.RESOLVED | Bundle.STARTING | Bundle.ACTIVE
+        | Bundle.STOPPING;
+        bt = new RecursiveBundleTracker(context, stateMask, new BlueprintBundleTrackerCustomizer());
+        bt.open();
+
+        // Create and publish a ParserService
+        parserServiceReg = context.registerService(ParserService.class.getName(), 
+            new ParserServiceImpl (handlers), 
+            new Hashtable<Object, Object>()); 
+        
+        LOGGER.debug("Blueprint extender started");
+    }
+
+    /**
+     * this method checks the initial bundle that are installed/active before
+     * bundle tracker is opened.  
+     */
+    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();
+
+        // 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");
+    }
+
+    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.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) {
+        }
+    }
+    
+}

Added: aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintRepository.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintRepository.java?rev=1075143&view=auto
==============================================================================
--- aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintRepository.java (added)
+++ aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintRepository.java Sun Feb 27 20:50:38 2011
@@ -0,0 +1,383 @@
+/**
+ *
+ * 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.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.apache.aries.blueprint.ExtendedBlueprintContainer;
+import org.apache.aries.blueprint.di.CircularDependencyException;
+import org.apache.aries.blueprint.di.ExecutionContext;
+import org.apache.aries.blueprint.di.IdRefRecipe;
+import org.apache.aries.blueprint.di.Recipe;
+import org.apache.aries.blueprint.di.RefRecipe;
+import org.apache.aries.blueprint.di.Repository;
+import org.apache.aries.blueprint.di.CollectionRecipe;
+import org.osgi.service.blueprint.container.ReifiedType;
+import org.osgi.service.blueprint.container.ComponentDefinitionException;
+import org.osgi.service.blueprint.container.NoSuchComponentException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The default repository implementation
+ */
+public class BlueprintRepository implements Repository, ExecutionContext {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(BlueprintRepository.class);
+
+    /**
+     * The blueprint container
+     */
+    private final ExtendedBlueprintContainer blueprintContainer;
+
+    /**
+     * Contains object recipes
+     */
+    private final Map<String, Recipe> recipes = new ConcurrentHashMap<String, Recipe>();
+
+    /**
+     * Contains object instances
+     */
+    private final Map<String, Object> instances = new ConcurrentHashMap<String, Object>();
+
+    /**
+     * Keep track of creation order
+     */
+    private final List<String> creationOrder = new CopyOnWriteArrayList<String>();
+
+    /**
+     * Lock for object instance creation
+     */
+    private final Object instanceLock = new Object();
+
+    /**
+     * Contains partial objects.
+     */
+    private final Map<String, Object> partialObjects = new ConcurrentHashMap<String, Object>();
+
+    /**
+     * Before each recipe is executed it is pushed on the stack.  The
+     * stack is used to detect circular dependencies.
+     */
+    private final LinkedList<Recipe> stack = new LinkedList<Recipe>();
+    
+    public BlueprintRepository(ExtendedBlueprintContainer container) {
+        blueprintContainer = container;
+    }
+    
+    public Object getInstance(String name) {
+        return instances.get(name);
+    }
+
+    public Recipe getRecipe(String name) {
+        return recipes.get(name);
+    }
+
+    public Set<String> getNames() {
+        Set<String> names = new HashSet<String>();
+        names.addAll(recipes.keySet());
+        names.addAll(instances.keySet());
+        return names;
+    }
+
+    public void putRecipe(String name, Recipe recipe) {
+        if (instances.get(name) != null) {
+            throw new ComponentDefinitionException("Name " + name + " is already registered to instance " + instances.get(name));
+        }
+        recipes.put(name, recipe);
+    }
+    
+    public void removeRecipe(String name) {
+        if (instances.get(name) != null)
+            throw new ComponentDefinitionException("Name " + name + " is already instanciated as " + instances.get(name) + " and cannot be removed.");
+
+        recipes.remove(name);
+    }
+
+    private Object convert(String name, Object instance) throws ComponentDefinitionException {
+        try {
+            // Make sure to go through the conversion step in case we have a Convertible object
+            return convert(instance, new ReifiedType(Object.class));
+        } catch (Exception e) {
+            throw new ComponentDefinitionException("Unable to convert instance " + name, e);
+        }
+    }
+        
+    public Object create(String name) throws ComponentDefinitionException {
+        ExecutionContext oldContext = ExecutionContext.Holder.setContext(this);
+        try {
+            Object instance = createInstance(name);                       
+            return convert(name, instance);
+        } finally {
+            ExecutionContext.Holder.setContext(oldContext);
+        }
+    }
+    
+    public Map<String, Object> createAll(Collection<String> names) throws ComponentDefinitionException {
+        ExecutionContext oldContext = ExecutionContext.Holder.setContext(this);
+        try {
+            Map<String, Object> instances = createInstances(names);
+            for (String name : instances.keySet()) {
+                Object obj = instances.get(name);
+                instances.put(name, convert(name, obj));
+            }
+            return instances;
+        } finally {
+            ExecutionContext.Holder.setContext(oldContext);
+        }
+    }
+
+    public <T> List<T> getAllRecipes(Class<T> clazz, String... names) {
+        List<T> recipes = new ArrayList<T>();
+        for (Recipe r : getAllRecipes(names)) {
+            if (clazz.isInstance(r)) {
+                recipes.add(clazz.cast(r));
+            }
+        }
+        return recipes;
+    }
+
+    public Set<Recipe> getAllRecipes(String... names) {
+        ExecutionContext oldContext = ExecutionContext.Holder.setContext(this);
+        try {
+            Set<Recipe> allRecipes = new HashSet<Recipe>();
+            Collection<String> topLevel = names != null && names.length > 0 ? Arrays.asList(names) : recipes.keySet();
+            for (String name : topLevel) {
+                internalGetAllRecipes(allRecipes, getRecipe(name));
+            }
+            return allRecipes;
+        } finally {
+            ExecutionContext.Holder.setContext(oldContext);
+        }
+    }
+
+    /*
+     * This method should not be called directly, only from one of the getAllRecipes() methods.
+     */
+    private void internalGetAllRecipes(Set<Recipe> allRecipes, Recipe r) {
+        if (r != null) {
+            if (allRecipes.add(r)) {
+                for (Recipe c : r.getDependencies()) {
+                    internalGetAllRecipes(allRecipes, c);
+                }
+            }
+        }
+    }
+
+    private Object createInstance(String name) {
+        Object instance = getInstance(name);
+        if (instance == null) {
+            Map <String, Object> instances = createInstances(Arrays.asList(name));
+            instance = instances.get(name); 
+            if (instance == null) {
+                throw new NoSuchComponentException(name);
+            }
+        }
+        return instance;
+    }
+
+    private Map<String, Object> createInstances(Collection<String> names) {
+        // We need to synchronize recipe creation on the repository
+        // so that we don't end up with multiple threads creating the
+        // same instance at the same time.
+        synchronized (instanceLock) {
+            DependencyGraph graph = new DependencyGraph(this);
+            HashMap<String, Object> objects = new LinkedHashMap<String, Object>();
+            for (Map.Entry<String, Recipe> entry : graph.getSortedRecipes(names).entrySet()) {
+                String name = entry.getKey();
+                Object object = instances.get(name);
+                if (object == null) {
+                    Recipe recipe = entry.getValue();
+                    object = recipe.create();
+                }
+                objects.put(name, object);
+            }
+            return objects;
+        }
+    }
+        
+    public void validate() {
+        for (Recipe recipe : getAllRecipes()) {
+            // Check that references are satisfied
+            String ref = null;
+            if (recipe instanceof RefRecipe) {
+                ref = ((RefRecipe) recipe).getIdRef();
+            } else if (recipe instanceof IdRefRecipe) {
+                ref = ((IdRefRecipe) recipe).getIdRef();
+            }
+            if (ref != null && getRecipe(ref) == null) {
+                throw new ComponentDefinitionException("Unresolved ref/idref to component: " + ref);
+            }
+            // Check service
+            if (recipe instanceof ServiceRecipe) {
+                Recipe r = ((ServiceRecipe) recipe).getServiceRecipe();
+                if (r instanceof RefRecipe) {
+                    r = getRecipe(((RefRecipe) r).getIdRef());
+                }
+                if (r instanceof ServiceRecipe) {
+                    throw new ComponentDefinitionException("The target for a <service> element must not be <service> element");
+                }
+                if (r instanceof ReferenceListRecipe) {
+                    throw new ComponentDefinitionException("The target for a <service> element must not be <reference-list> element");
+                }
+                CollectionRecipe listeners = ((ServiceRecipe) recipe).getListenersRecipe();
+                for (Recipe lr : listeners.getDependencies()) {
+                    // The listener recipe is a bean recipe with the listener being set in a property
+                    for (Recipe l : lr.getDependencies()) {
+                        if (l instanceof RefRecipe) {
+                            l = getRecipe(((RefRecipe) l).getIdRef());
+                        }
+                        if (l instanceof ServiceRecipe) {
+                            throw new ComponentDefinitionException("The target for a <registration-listener> element must not be <service> element");
+                        }
+                        if (l instanceof ReferenceListRecipe) {
+                            throw new ComponentDefinitionException("The target for a <registration-listener> element must not be <reference-list> element");
+                        }
+                    }
+                }
+            }
+            // Check references
+            if (recipe instanceof AbstractServiceReferenceRecipe) {
+                CollectionRecipe listeners = ((AbstractServiceReferenceRecipe) recipe).getListenersRecipe();
+                for (Recipe lr : listeners.getDependencies()) {
+                    // The listener recipe is a bean recipe with the listener being set in a property
+                    for (Recipe l : lr.getDependencies()) {
+                        if (l instanceof RefRecipe) {
+                            l = getRecipe(((RefRecipe) l).getIdRef());
+                        }
+                        if (l instanceof ServiceRecipe) {
+                            throw new ComponentDefinitionException("The target for a <reference-listener> element must not be <service> element");
+                        }
+                        if (l instanceof ReferenceListRecipe) {
+                            throw new ComponentDefinitionException("The target for a <reference-listener> element must not be <reference-list> element");
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public void destroy() {
+        // destroy objects in reverse creation order
+        List<String> order = new ArrayList<String>(creationOrder);
+        Collections.reverse(order);
+        for (String name : order) {
+            Recipe recipe = recipes.get(name);
+            if (recipe != null) {
+                recipe.destroy(instances.get(name));
+            }
+        }
+        instances.clear();
+        creationOrder.clear();
+    }
+
+    public Object getInstanceLock() {
+        return instanceLock;
+    }
+
+    public void push(Recipe recipe) {
+        if (stack.contains(recipe)) {
+            ArrayList<Recipe> circularity = new ArrayList<Recipe>(stack.subList(stack.indexOf(recipe), stack.size()));
+
+            // remove anonymous nodes from circularity list
+            for (Iterator<Recipe> iterator = circularity.iterator(); iterator.hasNext();) {
+                Recipe item = iterator.next();
+                if (item != recipe && item.getName() == null) {
+                    iterator.remove();
+                }
+            }
+
+            // add ending node to list so a full circuit is shown
+            circularity.add(recipe);
+
+            throw new CircularDependencyException(circularity);
+        }
+        stack.add(recipe);
+    }
+
+    public Recipe pop() {
+        return stack.removeLast();
+    }
+
+    public LinkedList<Recipe> getStack() {
+        return new LinkedList<Recipe>(stack);
+    }
+
+    public boolean containsObject(String name) {
+        return getInstance(name) != null
+                || getRecipe(name) != null;
+    }
+
+    public Object getObject(String name) {
+        Object object = getInstance(name);
+        if (object == null) {
+            object = getRecipe(name);
+        }
+        return object;
+    }
+
+    public void addFullObject(String name, Object object) {
+        if (instances.get(name) != null) {
+            throw new ComponentDefinitionException("Name " + name + " is already registered to instance " + instances.get(name));
+        }
+        instances.put(name, object);
+        creationOrder.add(name); 
+        partialObjects.remove(name);
+    }
+    
+    public void addPartialObject(String name, Object object) {
+        partialObjects.put(name, object);
+    }
+    
+    public Object removePartialObject(String name) {
+        return partialObjects.remove(name);
+    }
+    
+    public Object getPartialObject(String name) {
+        Object obj = partialObjects.get(name);
+        if (obj == null) {
+            obj = getInstance(name);
+        }
+        return obj;
+    }
+
+    public Object convert(Object value, ReifiedType type) throws Exception {
+        return blueprintContainer.getConverter().convert(value, type);
+    }
+    
+    public boolean canConvert(Object value, ReifiedType type) {
+        return blueprintContainer.getConverter().canConvert(value, type);
+    }
+
+    public Class loadClass(String typeName) throws ClassNotFoundException {
+        return blueprintContainer.loadClass(typeName);
+    }
+}

Added: aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintThreadFactory.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintThreadFactory.java?rev=1075143&view=auto
==============================================================================
--- aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintThreadFactory.java (added)
+++ aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintThreadFactory.java Sun Feb 27 20:50:38 2011
@@ -0,0 +1,38 @@
+/*
+ * 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.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class BlueprintThreadFactory implements ThreadFactory {
+    private final ThreadFactory factory = Executors.defaultThreadFactory();
+    private final AtomicInteger count = new AtomicInteger();
+    private final String name;
+        
+    public BlueprintThreadFactory(String name) {
+        this.name = name;
+    }
+
+    public Thread newThread(Runnable r) {
+        final Thread t = factory.newThread(r);
+        t.setName(name + ": " + count.incrementAndGet());
+        t.setDaemon(true);
+        return t;
+    }
+}

Added: aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/DependencyGraph.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/DependencyGraph.java?rev=1075143&view=auto
==============================================================================
--- aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/DependencyGraph.java (added)
+++ aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/DependencyGraph.java Sun Feb 27 20:50:38 2011
@@ -0,0 +1,188 @@
+/**
+ *
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.aries.blueprint.di.CircularDependencyException;
+import org.apache.aries.blueprint.di.Recipe;
+import org.apache.aries.blueprint.di.RefRecipe;
+import org.osgi.service.blueprint.container.NoSuchComponentException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class DependencyGraph {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(DependencyGraph.class);
+
+    private BlueprintRepository repository;
+    
+    public DependencyGraph(BlueprintRepository repository) {
+        this.repository = repository;        
+    }
+    
+    public LinkedHashMap<String, Recipe> getSortedRecipes(Collection<String> names) {
+        // construct the graph
+        Map<String, Node> nodes = new LinkedHashMap<String, Node>();
+        for (String name : names) {
+            Object object = repository.getObject(name);
+            if (object == null) {
+                throw new NoSuchComponentException(name);
+            }
+            if (object instanceof Recipe) {
+                Recipe recipe = (Recipe) object;
+                if (!recipe.getName().equals(name)) {
+                    throw new RuntimeException("Recipe '" + name + "' returned from the repository has name '" + name + "'");
+                }
+                createNode(name, recipe,  nodes);
+            }
+        }
+
+        // find all initial leaf nodes (and islands)
+        List<Node> sortedNodes = new ArrayList<Node>(nodes.size());
+        LinkedList<Node> leafNodes = new LinkedList<Node>();
+        for (Node n : nodes.values()) {
+            if (n.referenceCount == 0) {
+                // if the node is totally isolated (no in or out refs),
+                // move it directly to the finished list, so they are first
+                if (n.references.size() == 0) {
+                    sortedNodes.add(n);
+                } else {
+                    leafNodes.add(n);
+                }
+            }
+        }
+
+        // pluck the leaves until there are no leaves remaining
+        while (!leafNodes.isEmpty()) {
+            Node node = leafNodes.removeFirst();
+            sortedNodes.add(node);
+            for (Node ref : node.references) {
+                ref.referenceCount--;
+                if (ref.referenceCount == 0) {
+                    leafNodes.add(ref);
+                }
+            }
+        }
+
+        // There are no more leaves so if there are there still
+        // unprocessed nodes in the graph, we have one or more curcuits
+        if (sortedNodes.size() != nodes.size()) {
+            findCircuit(nodes.values().iterator().next(), new ArrayList<Recipe>(nodes.size()));
+            // find circuit should never fail, if it does there is a programming error
+            throw new RuntimeException("Internal Error: expected a CircularDependencyException");
+        }
+        
+        // return the recipes
+        LinkedHashMap<String, Recipe> sortedRecipes = new LinkedHashMap<String, Recipe>();
+        for (Node node : sortedNodes) {
+            sortedRecipes.put(node.name, node.recipe);
+        }
+        
+        return sortedRecipes;
+    }
+
+    private void findCircuit(Node node, ArrayList<Recipe> stack) {
+        if (stack.contains(node.recipe)) {
+            ArrayList<Recipe> circularity = new ArrayList<Recipe>(stack.subList(stack.indexOf(node.recipe), stack.size()));
+
+            // remove anonymous nodes from circularity list
+            for (Iterator<Recipe> iterator = circularity.iterator(); iterator.hasNext();) {
+                Recipe recipe = iterator.next();
+                if (recipe != node.recipe && recipe.getName() == null) {
+                    iterator.remove();
+                }
+            }
+
+            // add ending node to list so a full circuit is shown
+            circularity.add(node.recipe);
+            
+            throw new CircularDependencyException(circularity);
+        }
+
+        stack.add(node.recipe);
+        for (Node reference : node.references) {
+            findCircuit(reference, stack);
+        }
+    }
+
+    private Node createNode(String name, Recipe recipe, Map<String, Node> nodes) {
+        // if node already exists, verify that the exact same recipe instnace is used for both
+        if (nodes.containsKey(name)) {
+            Node node = nodes.get(name);
+            if (node.recipe != recipe) {
+                throw new RuntimeException("The name '" + name +"' is assigned to multiple recipies");
+            }
+            return node;
+        }
+
+        // create the node
+        Node node = new Node();
+        node.name = name;
+        node.recipe = recipe;
+        nodes.put(name, node);
+
+        // link in the references
+        LinkedList<Recipe> constructorRecipes = new LinkedList<Recipe>(recipe.getConstructorDependencies());
+        while (!constructorRecipes.isEmpty()) {
+            Recipe nestedRecipe = constructorRecipes.removeFirst();            
+            if (nestedRecipe instanceof RefRecipe) {
+                nestedRecipe =  nestedRecipe.getDependencies().get(0);
+                String nestedName = nestedRecipe.getName();
+                Node nestedNode = createNode(nestedName, nestedRecipe, nodes);
+                node.referenceCount++;
+                nestedNode.references.add(node);                
+            } else {
+                constructorRecipes.addAll(nestedRecipe.getDependencies());
+            }
+        }
+        
+        return node;
+    }
+
+    private class Node {
+        String name;
+        Recipe recipe;
+        final List<Node> references = new ArrayList<Node>();
+        int referenceCount;
+        
+        public String toString() {
+            StringBuffer buf = new StringBuffer();
+            buf.append("Node[").append(name);
+            if (references.size() > 0) {
+                buf.append(" <- ");
+                Iterator<Node> iter = references.iterator();
+                while(iter.hasNext()) {
+                    buf.append(iter.next().name);
+                    if (iter.hasNext()) {
+                        buf.append(", ");
+                    }
+                }
+            }
+            buf.append("]");
+            return buf.toString();
+        }
+
+    }
+}

Added: aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/GenericType.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/GenericType.java?rev=1075143&view=auto
==============================================================================
--- aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/GenericType.java (added)
+++ aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/GenericType.java Sun Feb 27 20:50:38 2011
@@ -0,0 +1,253 @@
+/**
+ *
+ * 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.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.security.AccessController;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.aries.blueprint.ExtendedBlueprintContainer;
+import org.apache.aries.blueprint.di.ExecutionContext;
+import org.osgi.framework.Bundle;
+import org.osgi.service.blueprint.container.ReifiedType;
+
+/**
+ * XXXX: Currently, in case of arrays getActualTypeArgument(0) returns something similar to what
+ * Class.getComponentType() does for arrays.  I don't think this is quite right since getActualTypeArgument()
+ * should return the given parameterized type not the component type. Need to check this behavior with the spec.
+ */
+public class GenericType extends ReifiedType {
+
+	private static final GenericType[] EMPTY = new GenericType[0];
+
+    private static final Map<String, Class> primitiveClasses = new HashMap<String, Class>();
+
+    static {
+        primitiveClasses.put("int", int.class);
+        primitiveClasses.put("short", short.class);
+        primitiveClasses.put("long", long.class);
+        primitiveClasses.put("byte", byte.class);
+        primitiveClasses.put("char", char.class);
+        primitiveClasses.put("float", float.class);
+        primitiveClasses.put("double", double.class);
+        primitiveClasses.put("boolean", boolean.class);
+    }
+
+    private GenericType[] parameters;
+
+	public GenericType(Type type) {
+		this(getConcreteClass(type), parametersOf(type));
+	}
+
+    public GenericType(Class clazz, GenericType... parameters) {
+        super(clazz);
+        this.parameters = parameters;
+    }
+
+    public static GenericType parse(String rawType, final Object loader) throws ClassNotFoundException, IllegalArgumentException {
+        final String type = rawType.trim();
+        // Check if this is an array
+        if (type.endsWith("[]")) {
+            GenericType t = parse(type.substring(0, type.length() - 2), loader);
+            return new GenericType(Array.newInstance(t.getRawClass(), 0).getClass(), t);
+        }
+        // Check if this is a generic
+        int genericIndex = type.indexOf('<');
+        if (genericIndex > 0) {
+            if (!type.endsWith(">")) {
+                throw new IllegalArgumentException("Can not load type: " + type);
+            }
+            GenericType base = parse(type.substring(0, genericIndex), loader);
+            String[] params = type.substring(genericIndex + 1, type.length() - 1).split(",");
+            GenericType[] types = new GenericType[params.length];
+            for (int i = 0; i < params.length; i++) {
+                types[i] = parse(params[i], loader);
+            }
+            return new GenericType(base.getRawClass(), types);
+        }
+        // Primitive
+        if (primitiveClasses.containsKey(type)) {
+            return new GenericType(primitiveClasses.get(type));
+        }
+        // Class
+        if (loader instanceof ClassLoader) {
+            return new GenericType(((ClassLoader) loader).loadClass(type));
+        } else if (loader instanceof Bundle) {
+            try {
+              return AccessController.doPrivileged(new PrivilegedExceptionAction<GenericType>() {
+                public GenericType run() throws ClassNotFoundException {
+                  return new GenericType(((Bundle) loader).loadClass(type));
+                }
+              });
+            } catch (PrivilegedActionException pae) {
+              Exception e = pae.getException();
+              if (e instanceof ClassNotFoundException) 
+                throw (ClassNotFoundException) e;
+              else
+                throw (RuntimeException) e;
+            }
+        } else if (loader instanceof ExecutionContext) {
+            return new GenericType(((ExecutionContext) loader).loadClass(type));
+        } else if (loader instanceof ExtendedBlueprintContainer) {
+            return new GenericType(((ExtendedBlueprintContainer) loader).loadClass(type));
+        } else {
+            throw new IllegalArgumentException("Unsupported loader: " + loader);
+        }
+    }
+
+    @Override
+    public ReifiedType getActualTypeArgument(int i) {
+        if (parameters.length == 0) {
+            return super.getActualTypeArgument(i);
+        }
+        return parameters[i];
+    }
+
+    @Override
+    public int size() {
+        return parameters.length;
+    }
+
+    @Override
+    public String toString() {
+        Class cl = getRawClass();
+        if (cl.isArray()) {
+            if (parameters.length > 0) {
+                return parameters[0].toString() + "[]";
+            } else {
+                return cl.getComponentType().getName() + "[]";
+            }
+        }
+        if (parameters.length > 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(cl.getName());    
+            sb.append("<");
+            for (int i = 0; i < parameters.length; i++) {
+                if (i > 0) {
+                    sb.append(",");
+                }
+                sb.append(parameters[i].toString());
+            }
+            sb.append(">");   
+            return sb.toString();
+        }
+        return cl.getName();
+    }
+
+    public boolean equals(Object object) {
+        if (!(object instanceof GenericType)) {
+            return false;
+        }
+        GenericType other = (GenericType) object;
+        if (getRawClass() != other.getRawClass()) {
+            return false;
+        }
+        if (parameters == null) {
+            return (other.parameters == null);
+        } else {
+            if (other.parameters == null) {
+                return false;
+            }
+            if (parameters.length != other.parameters.length) {
+                return false;
+            }
+            for (int i = 0; i < parameters.length; i++) {
+                if (!parameters[i].equals(other.parameters[i])) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+    
+    static GenericType[] parametersOf(Type type) {
+		if (type instanceof Class) {
+		    Class clazz = (Class) type;
+		    if (clazz.isArray()) {
+                GenericType t = new GenericType(clazz.getComponentType());
+                if (t.size() > 0) {
+		            return new GenericType[] { t };
+                } else {
+                    return EMPTY;
+                }
+		    } else {
+		        return EMPTY;
+		    }
+		}
+        if (type instanceof ParameterizedType) {
+            ParameterizedType pt = (ParameterizedType) type;
+            Type [] parameters = pt.getActualTypeArguments();
+            GenericType[] gts = new GenericType[parameters.length];
+            for ( int i =0; i<gts.length; i++) {
+                gts[i] = new GenericType(parameters[i]);
+            }
+            return gts;
+        }
+        if (type instanceof GenericArrayType) {
+            return new GenericType[] { new GenericType(((GenericArrayType) type).getGenericComponentType()) };
+        }
+        if (type instanceof WildcardType) {
+            return EMPTY;
+        }
+        if (type instanceof TypeVariable) {
+            return EMPTY;
+        }
+        throw new IllegalStateException();
+	}
+
+	static Class<?> getConcreteClass(Type type) {
+		Type ntype = collapse(type);
+		if ( ntype instanceof Class )
+			return (Class<?>) ntype;
+
+		if ( ntype instanceof ParameterizedType )
+			return getConcreteClass(collapse(((ParameterizedType)ntype).getRawType()));
+
+		throw new RuntimeException("Unknown type " + type );
+	}
+
+	static Type collapse(Type target) {
+		if (target instanceof Class || target instanceof ParameterizedType ) {
+			return target;
+		} else if (target instanceof TypeVariable) {
+			return collapse(((TypeVariable<?>) target).getBounds()[0]);
+		} else if (target instanceof GenericArrayType) {
+			Type t = collapse(((GenericArrayType) target)
+					.getGenericComponentType());
+			while ( t instanceof ParameterizedType )
+				t = collapse(((ParameterizedType)t).getRawType());
+			return Array.newInstance((Class<?>)t, 0).getClass();
+		} else if (target instanceof WildcardType) {
+			WildcardType wct = (WildcardType) target;
+			if (wct.getLowerBounds().length == 0)
+				return collapse(wct.getUpperBounds()[0]);
+			else
+				return collapse(wct.getLowerBounds()[0]);
+		}
+		throw new RuntimeException("Huh? " + target);
+	}
+
+}

Added: aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/IdSpace.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/IdSpace.java?rev=1075143&view=auto
==============================================================================
--- aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/IdSpace.java (added)
+++ aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/IdSpace.java Sun Feb 27 20:50:38 2011
@@ -0,0 +1,28 @@
+/**
+ *
+ * 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.concurrent.atomic.AtomicLong;
+
+public class IdSpace {
+    private AtomicLong currentId = new AtomicLong(0);
+    
+    public long nextId() {
+        return currentId.getAndIncrement();
+    }
+}

Added: aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/NamespaceHandlerRegistry.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/NamespaceHandlerRegistry.java?rev=1075143&view=auto
==============================================================================
--- aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/NamespaceHandlerRegistry.java (added)
+++ aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/NamespaceHandlerRegistry.java Sun Feb 27 20:50:38 2011
@@ -0,0 +1,119 @@
+/*
+ * 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.util.Set;
+import java.io.IOException;
+
+import javax.xml.validation.Schema;
+
+import org.apache.aries.blueprint.NamespaceHandler;
+import org.osgi.framework.Bundle;
+import org.xml.sax.SAXException;
+
+/**
+ * Registry of NamespaceHandler.
+ *
+ * @version $Rev: 982158 $, $Date: 2010-08-04 09:32:15 +0100 (Wed, 04 Aug 2010) $
+ */
+public interface NamespaceHandlerRegistry {
+
+    /**
+     * Retrieve the <code>NamespaceHandler</code> for the specified URI. Make sure
+     *
+     * @param uri the namespace identifying the namespace handler
+     * @param bundle the blueprint bundle to be checked for class space consistency
+     *
+     * @return a set of registered <code>NamespaceHandler</code>s compatible with the class space of the given bundle
+     */
+    NamespaceHandlerSet getNamespaceHandlers(Set<URI> uri, Bundle bundle);
+
+    /**
+     * Destroy this registry
+     */
+    void destroy();
+
+    /**
+     * Interface used to managed a set of namespace handlers
+     */
+    public interface NamespaceHandlerSet {
+
+        Set<URI> getNamespaces();
+
+        boolean isComplete();
+
+        /**
+         * Retrieve the NamespaceHandler to use for the given namespace
+         *
+         * @return the NamespaceHandler to use or <code>null</code> if none is available at this time
+         */
+        NamespaceHandler getNamespaceHandler(URI namespace);
+
+        /**
+         * Obtain a schema to validate the XML for the given list of namespaces
+         *
+         * @return the schema to use to validate the XML
+         */
+        Schema getSchema() throws SAXException, IOException;
+
+        /**
+         * Add a new Listener to be called when namespace handlers are registerd or unregistered
+         *
+         * @param listener the listener to register
+         */
+        void addListener(Listener listener);
+
+        /**
+         * Remove a previously registered Listener
+         *
+         * @param listener the listener to unregister
+         */
+        void removeListener(Listener listener);
+
+        /**
+         * Destroy this handler set
+         */
+        void destroy();
+    }
+
+    /**
+     * Interface used to listen to registered or unregistered namespace handlers.
+     *
+     * @see NamespaceHandlerSet#addListener(org.apache.aries.blueprint.container.NamespaceHandlerRegistry.Listener)
+     * @see NamespaceHandlerSet#removeListener(org.apache.aries.blueprint.container.NamespaceHandlerRegistry.Listener) 
+     */
+    public interface Listener {
+
+        /**
+         * Called when a NamespaceHandler has been registered for the specified URI.
+         *
+         * @param uri the URI of the newly registered namespace handler
+         */
+        void namespaceHandlerRegistered(URI uri);
+
+        /**
+         * Called when a NamespaceHandler has been unregistered for the specified URI.
+         *
+         * @param uri the URI of the newly unregistered namespace handler
+         */
+        void namespaceHandlerUnregistered(URI uri);
+
+    }
+}