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);
+
+ }
+}