You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by zo...@apache.org on 2011/02/27 22:21:22 UTC
svn commit: r1075149 [11/23] - in /aries/tags/blueprint-0.3.1: ./
blueprint-annotation-api/ blueprint-annotation-api/src/
blueprint-annotation-api/src/main/ blueprint-annotation-api/src/main/java/
blueprint-annotation-api/src/main/java/org/ blueprint-a...
Added: aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintContainerImpl.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintContainerImpl.java?rev=1075149&view=auto
==============================================================================
--- aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintContainerImpl.java (added)
+++ aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintContainerImpl.java Sun Feb 27 21:21:05 2011
@@ -0,0 +1,875 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.blueprint.container;
+
+import java.io.FileNotFoundException;
+import java.net.URI;
+import java.net.URL;
+import java.security.AccessControlContext;
+import java.security.AccessController;
+import java.security.DomainCombiner;
+import java.security.Permission;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.ProtectionDomain;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.aries.blueprint.BlueprintConstants;
+import org.apache.aries.blueprint.ComponentDefinitionRegistryProcessor;
+import org.apache.aries.blueprint.ExtendedBeanMetadata;
+import org.apache.aries.blueprint.ExtendedBlueprintContainer;
+import org.apache.aries.blueprint.NamespaceHandler;
+import org.apache.aries.blueprint.Processor;
+import org.apache.aries.blueprint.di.Recipe;
+import org.apache.aries.blueprint.di.Repository;
+import org.apache.aries.blueprint.namespace.ComponentDefinitionRegistryImpl;
+import org.apache.aries.blueprint.namespace.NamespaceHandlerRegistryImpl;
+import org.apache.aries.blueprint.reflect.MetadataUtil;
+import org.apache.aries.blueprint.reflect.PassThroughMetadataImpl;
+import org.apache.aries.blueprint.utils.HeaderParser;
+import org.apache.aries.blueprint.utils.JavaUtils;
+import org.apache.aries.blueprint.utils.HeaderParser.PathElement;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.blueprint.container.BlueprintContainer;
+import org.osgi.service.blueprint.container.BlueprintEvent;
+import org.osgi.service.blueprint.container.BlueprintListener;
+import org.osgi.service.blueprint.container.ComponentDefinitionException;
+import org.osgi.service.blueprint.container.Converter;
+import org.osgi.service.blueprint.container.NoSuchComponentException;
+import org.osgi.service.blueprint.reflect.BeanArgument;
+import org.osgi.service.blueprint.reflect.BeanMetadata;
+import org.osgi.service.blueprint.reflect.BeanProperty;
+import org.osgi.service.blueprint.reflect.CollectionMetadata;
+import org.osgi.service.blueprint.reflect.ComponentMetadata;
+import org.osgi.service.blueprint.reflect.MapEntry;
+import org.osgi.service.blueprint.reflect.MapMetadata;
+import org.osgi.service.blueprint.reflect.Metadata;
+import org.osgi.service.blueprint.reflect.PropsMetadata;
+import org.osgi.service.blueprint.reflect.RefMetadata;
+import org.osgi.service.blueprint.reflect.ReferenceListener;
+import org.osgi.service.blueprint.reflect.RegistrationListener;
+import org.osgi.service.blueprint.reflect.ServiceMetadata;
+import org.osgi.service.blueprint.reflect.ServiceReferenceMetadata;
+import org.osgi.service.blueprint.reflect.Target;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * TODO: javadoc
+ *
+ * @version $Rev: 1072952 $, $Date: 2011-02-21 12:41:31 +0000 (Mon, 21 Feb 2011) $
+ */
+public class BlueprintContainerImpl implements ExtendedBlueprintContainer, NamespaceHandlerRegistry.Listener, Runnable, SatisfiableRecipe.SatisfactionListener {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BlueprintContainerImpl.class);
+
+ private enum State {
+ Unknown,
+ WaitForNamespaceHandlers,
+ Populated,
+ WaitForInitialReferences,
+ InitialReferencesSatisfied,
+ WaitForInitialReferences2,
+ Create,
+ Created,
+ Failed,
+ }
+
+ private final BundleContext bundleContext;
+ private final Bundle extenderBundle;
+ private final BlueprintListener eventDispatcher;
+ private final NamespaceHandlerRegistry handlers;
+ private final List<Object> pathList;
+ private final ComponentDefinitionRegistryImpl componentDefinitionRegistry;
+ private final AggregateConverter converter;
+ private final ScheduledExecutorService executors;
+ private Set<URI> namespaces;
+ private State state = State.Unknown;
+ private NamespaceHandlerRegistry.NamespaceHandlerSet handlerSet;
+ private boolean destroyed;
+ private Parser parser;
+ private BlueprintRepository repository;
+ private ServiceRegistration registration;
+ private List<Processor> processors;
+ private final Object satisfiablesLock = new Object();
+ private Map<String, List<SatisfiableRecipe>> satisfiables;
+ private long timeout = 5 * 60 * 1000;
+ private boolean waitForDependencies = true;
+ private boolean xmlValidation = true;
+ private ScheduledFuture timeoutFuture;
+ private final AtomicBoolean scheduled = new AtomicBoolean();
+ private final AtomicBoolean running = new AtomicBoolean();
+ private List<ServiceRecipe> services;
+ private AccessControlContext accessControlContext;
+ private final IdSpace tempRecipeIdSpace = new IdSpace();
+
+ public BlueprintContainerImpl(BundleContext bundleContext, Bundle extenderBundle, BlueprintListener eventDispatcher, NamespaceHandlerRegistry handlers, ScheduledExecutorService executors, List<Object> pathList) {
+ this.bundleContext = bundleContext;
+ this.extenderBundle = extenderBundle;
+ this.eventDispatcher = eventDispatcher;
+ this.handlers = handlers;
+ this.pathList = pathList;
+ this.converter = new AggregateConverter(this);
+ this.componentDefinitionRegistry = new ComponentDefinitionRegistryImpl();
+ this.executors = executors;
+ this.processors = new ArrayList<Processor>();
+ if (System.getSecurityManager() != null) {
+ this.accessControlContext = createAccessControlContext();
+ }
+ }
+
+ public Bundle getExtenderBundle() {
+ return extenderBundle;
+ }
+
+ public <T extends Processor> List<T> getProcessors(Class<T> clazz) {
+ List<T> p = new ArrayList<T>();
+ for (Processor processor : processors) {
+ if (clazz.isInstance(processor)) {
+ p.add(clazz.cast(processor));
+ }
+ }
+ return p;
+ }
+
+ public BlueprintListener getEventDispatcher() {
+ return eventDispatcher;
+ }
+
+ private void checkDirectives() {
+ Bundle bundle = bundleContext.getBundle();
+ Dictionary headers = bundle.getHeaders();
+ String symbolicName = (String)headers.get(Constants.BUNDLE_SYMBOLICNAME);
+ List<PathElement> paths = HeaderParser.parseHeader(symbolicName);
+
+ String timeoutDirective = paths.get(0).getDirective(BlueprintConstants.TIMEOUT_DIRECTIVE);
+ if (timeoutDirective != null) {
+ LOGGER.debug("Timeout directive: {}", timeoutDirective);
+ timeout = Integer.parseInt(timeoutDirective);
+ }
+
+ String graceperiod = paths.get(0).getDirective(BlueprintConstants.GRACE_PERIOD);
+ if (graceperiod != null) {
+ LOGGER.debug("Grace-period directive: {}", graceperiod);
+ waitForDependencies = Boolean.parseBoolean(graceperiod);
+ }
+
+ String xmlValidationDirective = paths.get(0).getDirective(BlueprintConstants.XML_VALIDATION);
+ if (xmlValidationDirective != null) {
+ LOGGER.debug("Xml-validation directive: {}", xmlValidationDirective);
+ xmlValidation = Boolean.parseBoolean(xmlValidationDirective);
+ }
+ }
+
+ public void schedule() {
+ if (scheduled.compareAndSet(false, true)) {
+ executors.submit(this);
+ }
+ }
+
+ public void reload() {
+ unregisterServices();
+ untrackServiceReferences();
+ destroyComponents();
+ this.componentDefinitionRegistry.reset();
+ this.repository = null;
+ this.processors = new ArrayList<Processor>();
+ timeout = 5 * 60 * 1000;
+ waitForDependencies = true;
+ xmlValidation = true;
+ state = State.Unknown;
+ schedule();
+ }
+
+ public void run() {
+ scheduled.set(false);
+ synchronized (scheduled) {
+ synchronized (running) {
+ running.set(true);
+ try {
+ doRun();
+ } finally {
+ running.set(false);
+ running.notifyAll();
+ }
+ }
+ }
+ }
+
+ /**
+ * This method must be called inside a synchronized block to ensure this method is not run concurrently
+ */
+ private void doRun() {
+ try {
+ for (;;) {
+ if (destroyed) {
+ return;
+ }
+ LOGGER.debug("Running blueprint container for bundle {} in state {}", bundleContext.getBundle().getSymbolicName(), state);
+ switch (state) {
+ case Unknown:
+ checkDirectives();
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.CREATING, getBundleContext().getBundle(), getExtenderBundle()));
+ parser = new Parser();
+ parser.parse(getResources());
+ namespaces = parser.getNamespaces();
+ handlerSet = handlers.getNamespaceHandlers(namespaces, getBundleContext().getBundle());
+ handlerSet.addListener(this);
+ state = State.WaitForNamespaceHandlers;
+ break;
+ case WaitForNamespaceHandlers:
+ {
+ List<String> missing = new ArrayList<String>();
+ for (URI ns : namespaces) {
+ if (handlerSet.getNamespaceHandler(ns) == null) {
+ missing.add("(&(" + Constants.OBJECTCLASS + "=" + NamespaceHandler.class.getName() + ")(" + NamespaceHandlerRegistryImpl.NAMESPACE + "=" + ns + "))");
+ }
+ }
+ if (missing.size() > 0) {
+ LOGGER.info("Bundle {} is waiting for namespace handlers ", bundleContext.getBundle().getSymbolicName(), missing);
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.GRACE_PERIOD, getBundleContext().getBundle(), getExtenderBundle(), missing.toArray(new String[missing.size()])));
+ return;
+ }
+ componentDefinitionRegistry.registerComponentDefinition(new PassThroughMetadataImpl("blueprintContainer", this));
+ componentDefinitionRegistry.registerComponentDefinition(new PassThroughMetadataImpl("blueprintBundle", bundleContext.getBundle()));
+ componentDefinitionRegistry.registerComponentDefinition(new PassThroughMetadataImpl("blueprintBundleContext", bundleContext));
+ componentDefinitionRegistry.registerComponentDefinition(new PassThroughMetadataImpl("blueprintConverter", converter));
+ if (xmlValidation) {
+ parser.validate(handlerSet.getSchema());
+ }
+ parser.populate(handlerSet, componentDefinitionRegistry);
+ state = State.Populated;
+ break;
+ }
+ case Populated:
+ getRepository();
+ trackServiceReferences();
+ Runnable r = new Runnable() {
+ public void run() {
+ synchronized (scheduled) {
+ Throwable t = new TimeoutException();
+ state = State.Failed;
+ String[] missingDependecies = getMissingDependencies();
+ unregisterServices();
+ untrackServiceReferences();
+ destroyComponents();
+ LOGGER.error("Unable to start blueprint container for bundle " + bundleContext.getBundle().getSymbolicName() + " due to unresolved dependencies " + Arrays.asList(missingDependecies), t);
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.FAILURE, getBundleContext().getBundle(), getExtenderBundle(), missingDependecies, t));
+ }
+ }
+ };
+ timeoutFuture = executors.schedule(r, timeout, TimeUnit.MILLISECONDS);
+ state = State.WaitForInitialReferences;
+ break;
+ case WaitForInitialReferences:
+ if (waitForDependencies) {
+ String[] missingDependencies = getMissingDependencies();
+ if (missingDependencies.length > 0) {
+ LOGGER.info("Bundle {} is waiting for dependencies {}", bundleContext.getBundle().getSymbolicName(), Arrays.asList(missingDependencies));
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.GRACE_PERIOD, getBundleContext().getBundle(), getExtenderBundle(), missingDependencies));
+ return;
+ }
+ }
+ state = State.InitialReferencesSatisfied;
+ break;
+ case InitialReferencesSatisfied:
+ processTypeConverters();
+ processProcessors();
+ state = State.WaitForInitialReferences2;
+ break;
+ case WaitForInitialReferences2:
+ if (waitForDependencies) {
+ String[] missingDependencies = getMissingDependencies();
+ if (missingDependencies.length > 0) {
+ LOGGER.info("Bundle {} is waiting for dependencies {}", bundleContext.getBundle().getSymbolicName(), Arrays.asList(missingDependencies));
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.GRACE_PERIOD, getBundleContext().getBundle(), getExtenderBundle(), missingDependencies));
+ return;
+ }
+ }
+ state = State.Create;
+ break;
+ case Create:
+ timeoutFuture.cancel(false);
+ registerServices();
+ instantiateEagerComponents();
+ // Register the BlueprintContainer in the OSGi registry
+ int bs = bundleContext.getBundle().getState();
+ if (registration == null && (bs == Bundle.ACTIVE || bs == Bundle.STARTING)) {
+ Properties props = new Properties();
+ props.put(BlueprintConstants.CONTAINER_SYMBOLIC_NAME_PROPERTY,
+ bundleContext.getBundle().getSymbolicName());
+ props.put(BlueprintConstants.CONTAINER_VERSION_PROPERTY,
+ JavaUtils.getBundleVersion(bundleContext.getBundle()));
+ registration = registerService(new String [] { BlueprintContainer.class.getName() }, this, props);
+ }
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.CREATED, getBundleContext().getBundle(), getExtenderBundle()));
+ state = State.Created;
+ break;
+ case Created:
+ case Failed:
+ return;
+ }
+ }
+ } catch (Throwable t) {
+ state = State.Failed;
+ if (timeoutFuture != null) {
+ timeoutFuture.cancel(false);
+ }
+ unregisterServices();
+ untrackServiceReferences();
+ destroyComponents();
+ LOGGER.error("Unable to start blueprint container for bundle " + bundleContext.getBundle().getSymbolicName(), t);
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.FAILURE, getBundleContext().getBundle(), getExtenderBundle(), t));
+ }
+ }
+
+ private List<URL> getResources() throws FileNotFoundException {
+ List<URL> resources = new ArrayList<URL>();
+ for (Object path : pathList) {
+ if (path instanceof URL) {
+ resources.add((URL) path);
+ } else if (path instanceof String) {
+ URL url = bundleContext.getBundle().getEntry((String) path);
+ if (url == null) {
+ throw new FileNotFoundException("Unable to find configuration file for " + path);
+ } else {
+ resources.add(url);
+ }
+ } else {
+ throw new IllegalArgumentException("Unexpected path type: " + path.getClass());
+ }
+ }
+ return resources;
+ }
+
+ public Class loadClass(final String name) throws ClassNotFoundException {
+ if (accessControlContext == null) {
+ return bundleContext.getBundle().loadClass(name);
+ } else {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Class>() {
+ public Class run() throws Exception {
+ return bundleContext.getBundle().loadClass(name);
+ }
+ }, accessControlContext);
+ } catch (PrivilegedActionException e) {
+ Exception cause = e.getException();
+ if (cause instanceof ClassNotFoundException) {
+ throw (ClassNotFoundException) cause;
+ }
+ throw new IllegalStateException("Unexpected checked exception", cause);
+ }
+ }
+ }
+
+ public ServiceRegistration registerService(final String[] classes, final Object service, final Dictionary properties) {
+ if (accessControlContext == null) {
+ return bundleContext.registerService(classes, service, properties);
+ } else {
+ return AccessController.doPrivileged(new PrivilegedAction<ServiceRegistration>() {
+ public ServiceRegistration run() {
+ return bundleContext.registerService(classes, service, properties);
+ }
+ }, accessControlContext);
+ }
+ }
+
+ public Object getService(final ServiceReference reference) {
+ if (accessControlContext == null) {
+ return bundleContext.getService(reference);
+ } else {
+ return AccessController.doPrivileged(new PrivilegedAction<Object>() {
+ public Object run() {
+ return bundleContext.getService(reference);
+ }
+ }, accessControlContext);
+ }
+ }
+
+ private AccessControlContext createAccessControlContext() {
+ return new AccessControlContext(AccessController.getContext(),
+ new DomainCombiner() {
+ public ProtectionDomain[] combine(ProtectionDomain[] arg0,
+ ProtectionDomain[] arg1) {
+ return new ProtectionDomain[] { new ProtectionDomain(null, null) {
+ public boolean implies(Permission permission) {
+ return bundleContext.getBundle().hasPermission(permission);
+ }
+ }
+ };
+ }
+ });
+ }
+
+ public AccessControlContext getAccessControlContext() {
+ return accessControlContext;
+ }
+
+ public BlueprintRepository getRepository() {
+ if (repository == null) {
+ repository = new RecipeBuilder(this, tempRecipeIdSpace).createRepository();
+ }
+ return repository;
+ }
+
+ private void processTypeConverters() throws Exception {
+ List<String> typeConverters = new ArrayList<String>();
+ for (Target target : componentDefinitionRegistry.getTypeConverters()) {
+ if (target instanceof ComponentMetadata) {
+ typeConverters.add(((ComponentMetadata) target).getId());
+ } else if (target instanceof RefMetadata) {
+ typeConverters.add(((RefMetadata) target).getComponentId());
+ } else {
+ throw new ComponentDefinitionException("Unexpected metadata for type converter: " + target);
+ }
+ }
+
+ Map<String, Object> objects = repository.createAll(typeConverters);
+ for (String name : typeConverters) {
+ Object obj = objects.get(name);
+ if (obj instanceof Converter) {
+ converter.registerConverter((Converter) obj);
+ } else {
+ throw new ComponentDefinitionException("Type converter " + obj + " does not implement the " + Converter.class.getName() + " interface");
+ }
+ }
+ }
+
+ private void processProcessors() throws Exception {
+ // Instanciate ComponentDefinitionRegistryProcessor and BeanProcessor
+ for (BeanMetadata bean : getMetadata(BeanMetadata.class)) {
+ if (bean instanceof ExtendedBeanMetadata && !((ExtendedBeanMetadata) bean).isProcessor()) {
+ continue;
+ }
+
+ Class clazz = null;
+ if (bean instanceof ExtendedBeanMetadata) {
+ clazz = ((ExtendedBeanMetadata) bean).getRuntimeClass();
+ }
+ if (clazz == null && bean.getClassName() != null) {
+ clazz = loadClass(bean.getClassName());
+ }
+ if (clazz == null) {
+ continue;
+ }
+
+ if (ComponentDefinitionRegistryProcessor.class.isAssignableFrom(clazz)) {
+ Object obj = repository.create(bean.getId());
+ ((ComponentDefinitionRegistryProcessor) obj).process(componentDefinitionRegistry);
+ } else if (Processor.class.isAssignableFrom(clazz)) {
+ Object obj = repository.create(bean.getId());
+ this.processors.add((Processor) obj);
+ } else {
+ continue;
+ }
+ // Update repository with recipes processed by the processors
+ untrackServiceReferences();
+ Repository tmpRepo = new RecipeBuilder(this, tempRecipeIdSpace).createRepository();
+
+ LOGGER.debug("Updating blueprint repository");
+
+ for (String name : repository.getNames()) {
+ if (repository.getInstance(name) == null) {
+ LOGGER.debug("Removing uninstantiated recipe {}", new Object[] { name });
+ repository.removeRecipe(name);
+ } else {
+ LOGGER.debug("Recipe {} is already instantiated", new Object[] { name });
+ }
+ }
+
+ for (String name : tmpRepo.getNames()) {
+ if (repository.getInstance(name) == null) {
+ LOGGER.debug("Adding new recipe {}", new Object[] { name });
+ Recipe r = tmpRepo.getRecipe(name);
+ if (r != null) {
+ repository.putRecipe(name, r);
+ }
+ } else {
+ LOGGER.debug("Recipe {} is already instantiated and cannot be updated", new Object[] { name });
+ }
+ }
+
+ getSatisfiableDependenciesMap(true);
+ trackServiceReferences();
+ }
+ }
+
+ private Map<String, List<SatisfiableRecipe>> getSatisfiableDependenciesMap() {
+ return getSatisfiableDependenciesMap(false);
+ }
+
+ private Map<String, List<SatisfiableRecipe>> getSatisfiableDependenciesMap(boolean recompute) {
+ synchronized (satisfiablesLock) {
+ if ((recompute || satisfiables == null) && repository != null) {
+ satisfiables = new HashMap<String, List<SatisfiableRecipe>>();
+ for (Recipe r : repository.getAllRecipes()) {
+ List<SatisfiableRecipe> recipes = repository.getAllRecipes(SatisfiableRecipe.class, r.getName());
+ if (!recipes.isEmpty()) {
+ satisfiables.put(r.getName(), recipes);
+ }
+ }
+ }
+ return satisfiables;
+ }
+ }
+
+ private void trackServiceReferences() {
+ Map<String, List<SatisfiableRecipe>> dependencies = getSatisfiableDependenciesMap();
+ Set<String> satisfiables = new HashSet<String>();
+ for (List<SatisfiableRecipe> recipes : dependencies.values()) {
+ for (SatisfiableRecipe satisfiable : recipes) {
+ if (satisfiables.add(satisfiable.getName())) {
+ satisfiable.start(this);
+ }
+ }
+ }
+ LOGGER.debug("Tracking service references: {}", satisfiables);
+ }
+
+ private void untrackServiceReferences() {
+ Map<String, List<SatisfiableRecipe>> dependencies = getSatisfiableDependenciesMap();
+ if (dependencies != null) {
+ Set<String> stopped = new HashSet<String>();
+ for (List<SatisfiableRecipe> recipes : dependencies.values()) {
+ for (SatisfiableRecipe satisfiable : recipes) {
+ untrackServiceReference(satisfiable, stopped, dependencies);
+ }
+ }
+ }
+ }
+
+ private void untrackServiceReference(SatisfiableRecipe recipe, Set<String> stopped, Map<String, List<SatisfiableRecipe>> dependencies) {
+ if (stopped.add(recipe.getName())) {
+ for (Map.Entry<String, List<SatisfiableRecipe>> entry : dependencies.entrySet()) {
+ if (entry.getValue().contains(recipe)) {
+ Recipe r = getRepository().getRecipe(entry.getKey());
+ if (r instanceof SatisfiableRecipe) {
+ untrackServiceReference((SatisfiableRecipe) r, stopped, dependencies);
+ }
+ }
+ }
+ recipe.stop();
+ }
+ }
+
+ public void notifySatisfaction(SatisfiableRecipe satisfiable) {
+ LOGGER.debug("Notified satisfaction {} in bundle {}: {}",
+ new Object[] { satisfiable.getName(), bundleContext.getBundle().getSymbolicName(), satisfiable.isSatisfied() });
+ if (state == State.Create || state == State.Created ) {
+ Map<String, List<SatisfiableRecipe>> dependencies = getSatisfiableDependenciesMap();
+ for (Map.Entry<String, List<SatisfiableRecipe>> entry : dependencies.entrySet()) {
+ String name = entry.getKey();
+ ComponentMetadata metadata = componentDefinitionRegistry.getComponentDefinition(name);
+ if (metadata instanceof ServiceMetadata) {
+ ServiceRecipe reg = (ServiceRecipe) repository.getRecipe(name);
+ synchronized (reg) {
+ boolean satisfied = true;
+ for (SatisfiableRecipe recipe : entry.getValue()) {
+ if (!recipe.isSatisfied()) {
+ satisfied = false;
+ break;
+ }
+ }
+ if (satisfied && !reg.isRegistered()) {
+ LOGGER.debug("Registering service {} due to satisfied references", name);
+ reg.register();
+ } else if (!satisfied && reg.isRegistered()) {
+ LOGGER.debug("Unregistering service {} due to unsatisfied references", name);
+ reg.unregister();
+ }
+ }
+ }
+ }
+ } else {
+ schedule();
+ }
+ }
+
+ private void instantiateEagerComponents() {
+ List<String> components = new ArrayList<String>();
+ for (String name : componentDefinitionRegistry.getComponentDefinitionNames()) {
+ ComponentMetadata component = componentDefinitionRegistry.getComponentDefinition(name);
+ boolean eager = component.getActivation() == ComponentMetadata.ACTIVATION_EAGER;
+ if (component instanceof BeanMetadata) {
+ BeanMetadata local = (BeanMetadata) component;
+ eager &= MetadataUtil.isSingletonScope(local);
+ }
+ if (eager) {
+ components.add(name);
+ }
+ }
+ LOGGER.debug("Instantiating components: {}", components);
+ try {
+ repository.createAll(components);
+ } catch (ComponentDefinitionException e) {
+ throw e;
+ } catch (Throwable t) {
+ throw new ComponentDefinitionException("Unable to instantiate components", t);
+ }
+ }
+
+ private void registerServices() {
+ services = repository.getAllRecipes(ServiceRecipe.class);
+ for (ServiceRecipe r : services) {
+ List<SatisfiableRecipe> dependencies = getSatisfiableDependenciesMap().get(r.getName());
+ boolean enabled = true;
+ if (dependencies != null) {
+ for (SatisfiableRecipe recipe : dependencies) {
+ if (!recipe.isSatisfied()) {
+ enabled = false;
+ break;
+ }
+ }
+ }
+ if (enabled) {
+ r.register();
+ }
+ }
+ }
+
+ protected void unregisterServices() {
+ if (repository != null) {
+ List<ServiceRecipe> recipes = this.services;
+ this.services = null;
+ if (recipes != null) {
+ for (ServiceRecipe r : recipes) {
+ r.unregister();
+ }
+ }
+ }
+ }
+
+ private void destroyComponents() {
+ if (repository != null) {
+ repository.destroy();
+ }
+ }
+
+ private String[] getMissingDependencies() {
+ List<String> missing = new ArrayList<String>();
+ Map<String, List<SatisfiableRecipe>> dependencies = getSatisfiableDependenciesMap();
+ Set<SatisfiableRecipe> recipes = new HashSet<SatisfiableRecipe>();
+ for (List<SatisfiableRecipe> deps : dependencies.values()) {
+ for (SatisfiableRecipe recipe : deps) {
+ if (!recipe.isSatisfied()) {
+ recipes.add(recipe);
+ }
+ }
+ }
+ for (SatisfiableRecipe recipe : recipes) {
+ missing.add(recipe.getOsgiFilter());
+ }
+ return missing.toArray(new String[missing.size()]);
+ }
+
+ public Set<String> getComponentIds() {
+ Set<String> set = new LinkedHashSet<String>();
+ set.addAll(componentDefinitionRegistry.getComponentDefinitionNames());
+ set.add("blueprintContainer");
+ set.add("blueprintBundle");
+ set.add("blueprintBundleContext");
+ set.add("blueprintConverter");
+ return set;
+ }
+
+ public Object getComponentInstance(String id) throws NoSuchComponentException {
+ if (repository == null) {
+ throw new NoSuchComponentException(id);
+ }
+ try {
+ LOGGER.debug("Instantiating component {}", id);
+ return repository.create(id);
+ } catch (NoSuchComponentException e) {
+ throw e;
+ } catch (ComponentDefinitionException e) {
+ throw e;
+ } catch (Throwable t) {
+ throw new ComponentDefinitionException("Cound not create component instance for " + id, t);
+ }
+ }
+
+ public ComponentMetadata getComponentMetadata(String id) {
+ ComponentMetadata metadata = componentDefinitionRegistry.getComponentDefinition(id);
+ if (metadata == null) {
+ throw new NoSuchComponentException(id);
+ }
+ return metadata;
+ }
+
+ public <T extends ComponentMetadata> Collection<T> getMetadata(Class<T> clazz) {
+ Collection<T> metadatas = new ArrayList<T>();
+ for (String name : componentDefinitionRegistry.getComponentDefinitionNames()) {
+ ComponentMetadata component = componentDefinitionRegistry.getComponentDefinition(name);
+ getMetadata(clazz, component, metadatas);
+ }
+ metadatas = Collections.unmodifiableCollection(metadatas);
+ return metadatas;
+ }
+
+ private <T extends ComponentMetadata> void getMetadata(Class<T> clazz, Metadata component, Collection<T> metadatas) {
+ if (component == null) {
+ return;
+ }
+ if (clazz.isInstance(component)) {
+ metadatas.add(clazz.cast(component));
+ }
+ if (component instanceof BeanMetadata) {
+ getMetadata(clazz, ((BeanMetadata) component).getFactoryComponent(), metadatas);
+ for (BeanArgument arg : ((BeanMetadata) component).getArguments()) {
+ getMetadata(clazz, arg.getValue(), metadatas);
+ }
+ for (BeanProperty prop : ((BeanMetadata) component).getProperties()) {
+ getMetadata(clazz, prop.getValue(), metadatas);
+ }
+ }
+ if (component instanceof CollectionMetadata) {
+ for (Metadata m : ((CollectionMetadata) component).getValues()) {
+ getMetadata(clazz, m, metadatas);
+ }
+ }
+ if (component instanceof MapMetadata) {
+ for (MapEntry m : ((MapMetadata) component).getEntries()) {
+ getMetadata(clazz, m.getKey(), metadatas);
+ getMetadata(clazz, m.getValue(), metadatas);
+ }
+ }
+ if (component instanceof PropsMetadata) {
+ for (MapEntry m : ((PropsMetadata) component).getEntries()) {
+ getMetadata(clazz, m.getKey(), metadatas);
+ getMetadata(clazz, m.getValue(), metadatas);
+ }
+ }
+ if (component instanceof ServiceReferenceMetadata) {
+ for (ReferenceListener l : ((ServiceReferenceMetadata) component).getReferenceListeners()) {
+ getMetadata(clazz, l.getListenerComponent(), metadatas);
+ }
+ }
+ if (component instanceof ServiceMetadata) {
+ getMetadata(clazz, ((ServiceMetadata) component).getServiceComponent(), metadatas);
+ for (MapEntry m : ((ServiceMetadata) component).getServiceProperties()) {
+ getMetadata(clazz, m.getKey(), metadatas);
+ getMetadata(clazz, m.getValue(), metadatas);
+ }
+ for (RegistrationListener l : ((ServiceMetadata) component).getRegistrationListeners()) {
+ getMetadata(clazz, l.getListenerComponent(), metadatas);
+ }
+ }
+ }
+
+ public Converter getConverter() {
+ return converter;
+ }
+
+ public ComponentDefinitionRegistryImpl getComponentDefinitionRegistry() {
+ return componentDefinitionRegistry;
+ }
+
+ public BundleContext getBundleContext() {
+ return bundleContext;
+ }
+
+ public void destroy() {
+ destroyed = true;
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.DESTROYING, getBundleContext().getBundle(), getExtenderBundle()));
+
+ if (timeoutFuture != null) {
+ timeoutFuture.cancel(false);
+ }
+ if (registration != null) {
+ registration.unregister();
+ }
+ if (handlerSet != null) {
+ handlerSet.removeListener(this);
+ handlerSet.destroy();
+ }
+ unregisterServices();
+ untrackServiceReferences();
+
+ synchronized (running) {
+ while (running.get()) {
+ try {
+ running.wait();
+ } catch (InterruptedException e) {
+ // Ignore
+ }
+ }
+ }
+
+ destroyComponents();
+
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.DESTROYED, getBundleContext().getBundle(), getExtenderBundle()));
+ LOGGER.debug("Blueprint container destroyed: {}", this.bundleContext);
+ }
+
+ protected void quiesce() {
+ destroyed = true;
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.DESTROYING, getBundleContext().getBundle(), getExtenderBundle()));
+
+ if (timeoutFuture != null) {
+ timeoutFuture.cancel(false);
+ }
+ if (registration != null) {
+ registration.unregister();
+ }
+ if (handlerSet != null) {
+ handlerSet.removeListener(this);
+ handlerSet.destroy();
+ }
+ LOGGER.debug("Blueprint container quiesced: {}", this.bundleContext);
+ }
+
+ public void namespaceHandlerRegistered(URI uri) {
+ if (namespaces != null && namespaces.contains(uri)) {
+ schedule();
+ }
+ }
+
+ public void namespaceHandlerUnregistered(URI uri) {
+ if (namespaces != null && namespaces.contains(uri)) {
+ unregisterServices();
+ untrackServiceReferences();
+ destroyComponents();
+ state = State.WaitForNamespaceHandlers;
+ schedule();
+ }
+ }
+
+}
+
Added: aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintEventDispatcher.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintEventDispatcher.java?rev=1075149&view=auto
==============================================================================
--- aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintEventDispatcher.java (added)
+++ aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintEventDispatcher.java Sun Feb 27 21:21:05 2011
@@ -0,0 +1,296 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.blueprint.container;
+
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.blueprint.container.BlueprintEvent;
+import org.osgi.service.blueprint.container.BlueprintListener;
+import org.osgi.service.blueprint.container.EventConstants;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.aries.blueprint.utils.JavaUtils;
+
+/**
+ * The delivery of {@link BlueprintEvent}s is complicated. The blueprint extender and its containers use this class to
+ * deliver {@link BlueprintEvent}s.
+ *
+ * @version $Rev: 982158 $, $Date: 2010-08-04 09:32:15 +0100 (Wed, 04 Aug 2010) $
+ */
+class BlueprintEventDispatcher implements BlueprintListener {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BlueprintEventDispatcher.class);
+
+ private final Set<BlueprintListener> listeners = new CopyOnWriteArraySet<BlueprintListener>();
+ private final Map<Bundle, BlueprintEvent> states = new ConcurrentHashMap<Bundle, BlueprintEvent>();
+ private final ExecutorService executor = Executors.newSingleThreadExecutor(new BlueprintThreadFactory("Blueprint Event Dispatcher"));
+ private final ExecutorService sharedExecutor;
+ private final EventAdminListener eventAdminListener;
+ private final ServiceTracker containerListenerTracker;
+
+ BlueprintEventDispatcher(final BundleContext bundleContext, ExecutorService sharedExecutor) {
+
+ assert bundleContext != null;
+ assert sharedExecutor != null;
+
+ this.sharedExecutor = sharedExecutor;
+
+ EventAdminListener listener = null;
+ try {
+ getClass().getClassLoader().loadClass("org.osgi.service.event.EventAdmin");
+ listener = new EventAdminListener(bundleContext);
+ } catch (Throwable t) {
+ // Ignore, if the EventAdmin package is not available, just don't use it
+ LOGGER.debug("EventAdmin package is not available, just don't use it");
+ }
+ this.eventAdminListener = listener;
+
+ this.containerListenerTracker = new ServiceTracker(bundleContext, BlueprintListener.class.getName(), new ServiceTrackerCustomizer() {
+ public Object addingService(ServiceReference reference) {
+ BlueprintListener listener = (BlueprintListener) bundleContext.getService(reference);
+
+ synchronized (listeners) {
+ sendInitialEvents(listener);
+ listeners.add(listener);
+ }
+
+ return listener;
+ }
+
+ public void modifiedService(ServiceReference reference, Object service) {
+ }
+
+ public void removedService(ServiceReference reference, Object service) {
+ listeners.remove(service);
+ bundleContext.ungetService(reference);
+ }
+ });
+ this.containerListenerTracker.open();
+ }
+
+ private void sendInitialEvents(BlueprintListener listener) {
+ for (Map.Entry<Bundle, BlueprintEvent> entry : states.entrySet()) {
+ try {
+ callListener(listener, new BlueprintEvent(entry.getValue(), true));
+ } catch (RejectedExecutionException ree) {
+ LOGGER.warn("Executor shut down", ree);
+ break;
+ }
+ }
+ }
+
+ public void blueprintEvent(final BlueprintEvent event) {
+ if (LOGGER.isDebugEnabled()) {
+ LOGGER.debug("Sending blueprint container event {} for bundle {}", toString(event), event.getBundle().getSymbolicName());
+ }
+
+ synchronized (listeners) {
+ callListeners(event);
+ states.put(event.getBundle(), event);
+ }
+
+ if (eventAdminListener != null) {
+ try {
+ sharedExecutor.submit(new Runnable() {
+ public void run() {
+ eventAdminListener.blueprintEvent(event);
+ }
+ });
+ } catch (RejectedExecutionException ree) {
+ LOGGER.warn("Executor shut down", ree);
+ }
+ }
+ }
+
+ @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"})
+ private static String toString(BlueprintEvent event) {
+ return "BlueprintEvent[type=" + getEventType(event.getType())
+ + (event.getDependencies() != null ? ", dependencies=" + Arrays.asList(event.getDependencies()) : "")
+ + (event.getCause() != null ? ", exception=" + event.getCause().getMessage() : "")
+ + "]";
+ }
+
+ private static String getEventType(int type) {
+ switch (type) {
+ case BlueprintEvent.CREATING:
+ return "CREATING";
+ case BlueprintEvent.CREATED:
+ return "CREATED";
+ case BlueprintEvent.DESTROYING:
+ return "DESTROYING";
+ case BlueprintEvent.DESTROYED:
+ return "DESTROYED";
+ case BlueprintEvent.FAILURE:
+ return "FAILURE";
+ case BlueprintEvent.GRACE_PERIOD:
+ return "GRACE_PERIOD";
+ case BlueprintEvent.WAITING:
+ return "WAITING";
+ default:
+ return "UNKNOWN";
+ }
+ }
+
+ private void callListeners(BlueprintEvent event) {
+ for (final BlueprintListener listener : listeners) {
+ try {
+ callListener(listener, event);
+ } catch (RejectedExecutionException ree) {
+ LOGGER.warn("Executor shut down", ree);
+ break;
+ }
+ }
+ }
+
+ private void callListener(final BlueprintListener listener, final BlueprintEvent event) throws RejectedExecutionException {
+ try {
+ executor.invokeAny(Collections.<Callable<Void>>singleton(new Callable<Void>() {
+ public Void call() throws Exception {
+ listener.blueprintEvent(event);
+ return null;
+ }
+ }), 60L, TimeUnit.SECONDS);
+ } catch (InterruptedException ie) {
+ LOGGER.warn("Thread interrupted", ie);
+ Thread.currentThread().interrupt();
+ } catch (TimeoutException te) {
+ LOGGER.warn("Listener timed out, will be ignored", te);
+ listeners.remove(listener);
+ } catch (ExecutionException ee) {
+ LOGGER.warn("Listener caused an exception, will be ignored", ee);
+ listeners.remove(listener);
+ }
+ }
+
+ void destroy() {
+ executor.shutdown();
+ // wait for the queued tasks to execute
+ try {
+ executor.awaitTermination(60, TimeUnit.SECONDS);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+ containerListenerTracker.close();
+ // clean up the EventAdmin tracker if we're using that
+ if (eventAdminListener != null) {
+ eventAdminListener.destroy();
+ }
+ }
+
+ public void removeBlueprintBundle(Bundle bundle) {
+ states.remove(bundle);
+ }
+
+ private static class EventAdminListener implements BlueprintListener {
+
+ private final ServiceTracker tracker;
+
+ EventAdminListener(BundleContext context) {
+ tracker = new ServiceTracker(context, EventAdmin.class.getName(), null);
+ tracker.open();
+ }
+
+ @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"})
+ public void blueprintEvent(BlueprintEvent event) {
+ EventAdmin eventAdmin = (EventAdmin) tracker.getService();
+ if (eventAdmin == null) {
+ return;
+ }
+
+ Dictionary<String, Object> props = new Hashtable<String, Object>();
+ props.put(EventConstants.TYPE, event.getType());
+ props.put(EventConstants.EVENT, event);
+ props.put(EventConstants.TIMESTAMP, event.getTimestamp());
+ props.put(EventConstants.BUNDLE, event.getBundle());
+ props.put(EventConstants.BUNDLE_SYMBOLICNAME, event.getBundle().getSymbolicName());
+ props.put(EventConstants.BUNDLE_ID, event.getBundle().getBundleId());
+ props.put(EventConstants.BUNDLE_VERSION, JavaUtils.getBundleVersion(event.getBundle()));
+ props.put(EventConstants.EXTENDER_BUNDLE, event.getExtenderBundle());
+ props.put(EventConstants.EXTENDER_BUNDLE_ID, event.getExtenderBundle().getBundleId());
+ props.put(EventConstants.EXTENDER_BUNDLE_SYMBOLICNAME, event.getExtenderBundle().getSymbolicName());
+ props.put(EventConstants.EXTENDER_BUNDLE_VERSION, JavaUtils.getBundleVersion(event.getExtenderBundle()));
+
+ if (event.getCause() != null) {
+ props.put(EventConstants.CAUSE, event.getCause());
+ }
+ if (event.getDependencies() != null) {
+ props.put(EventConstants.DEPENDENCIES, event.getDependencies());
+ }
+ String topic;
+ switch (event.getType()) {
+ case BlueprintEvent.CREATING:
+ topic = EventConstants.TOPIC_CREATING;
+ break;
+ case BlueprintEvent.CREATED:
+ topic = EventConstants.TOPIC_CREATED;
+ break;
+ case BlueprintEvent.DESTROYING:
+ topic = EventConstants.TOPIC_DESTROYING;
+ break;
+ case BlueprintEvent.DESTROYED:
+ topic = EventConstants.TOPIC_DESTROYED;
+ break;
+ case BlueprintEvent.FAILURE:
+ topic = EventConstants.TOPIC_FAILURE;
+ break;
+ case BlueprintEvent.GRACE_PERIOD:
+ topic = EventConstants.TOPIC_GRACE_PERIOD;
+ break;
+ case BlueprintEvent.WAITING:
+ topic = EventConstants.TOPIC_WAITING;
+ break;
+ default:
+ throw new IllegalStateException("Unknown blueprint event type: " + event.getType());
+ }
+ eventAdmin.postEvent(new Event(topic, props));
+ }
+
+ /**
+ * Perform cleanup at Blueprint extender shutdown.
+ */
+ public void destroy() {
+ tracker.close();
+ }
+
+ }
+
+}
Added: aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintExtender.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintExtender.java?rev=1075149&view=auto
==============================================================================
--- aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintExtender.java (added)
+++ aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintExtender.java Sun Feb 27 21:21:05 2011
@@ -0,0 +1,446 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.blueprint.container;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.atomic.AtomicReference;
+
+import org.apache.aries.blueprint.BlueprintConstants;
+import org.apache.aries.blueprint.ParserService;
+import org.apache.aries.blueprint.annotation.service.BlueprintAnnotationScanner;
+import org.apache.aries.blueprint.namespace.NamespaceHandlerRegistryImpl;
+import org.apache.aries.blueprint.utils.HeaderParser;
+import org.apache.aries.blueprint.utils.HeaderParser.PathElement;
+import org.apache.aries.proxy.ProxyManager;
+import org.apache.aries.util.SingleServiceTracker;
+import org.apache.aries.util.SingleServiceTracker.SingleServiceListener;
+import org.apache.aries.util.tracker.RecursiveBundleTracker;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.SynchronousBundleListener;
+import org.osgi.service.blueprint.container.BlueprintContainer;
+import org.osgi.service.blueprint.container.BlueprintEvent;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This is the blueprint extender that listens to blueprint bundles.
+ *
+ * @version $Rev: 1030761 $, $Date: 2010-11-03 23:48:47 +0000 (Wed, 03 Nov 2010) $
+ */
+public class BlueprintExtender implements BundleActivator, SynchronousBundleListener {
+
+ /** The QuiesceParticipant implementation class name */
+ private static final String QUIESCE_PARTICIPANT_CLASS = "org.apache.aries.quiesce.participant.QuiesceParticipant";
+ private static final Logger LOGGER = LoggerFactory.getLogger(BlueprintExtender.class);
+
+ private BundleContext context;
+ private ScheduledExecutorService executors;
+ private Map<Bundle, BlueprintContainerImpl> containers;
+ private BlueprintEventDispatcher eventDispatcher;
+ private NamespaceHandlerRegistry handlers;
+ private RecursiveBundleTracker bt;
+ private ServiceRegistration parserServiceReg;
+ private ServiceRegistration quiesceParticipantReg;
+ private static SingleServiceTracker<ProxyManager> proxyManager;
+
+ public void start(BundleContext ctx) {
+ LOGGER.debug("Starting blueprint extender...");
+
+ this.context = ctx;
+ handlers = new NamespaceHandlerRegistryImpl(ctx);
+ executors = Executors.newScheduledThreadPool(3, new BlueprintThreadFactory("Blueprint Extender"));
+ eventDispatcher = new BlueprintEventDispatcher(ctx, executors);
+ containers = new HashMap<Bundle, BlueprintContainerImpl>();
+
+ int stateMask = Bundle.INSTALLED | Bundle.RESOLVED | Bundle.STARTING | Bundle.ACTIVE
+ | Bundle.STOPPING;
+ bt = new RecursiveBundleTracker(ctx, stateMask, new BlueprintBundleTrackerCustomizer());
+
+ proxyManager = new SingleServiceTracker<ProxyManager>(ctx, ProxyManager.class, new SingleServiceListener() {
+ public void serviceFound() {
+ LOGGER.debug("Found ProxyManager service, starting to process blueprint bundles");
+ bt.open();
+ }
+ public void serviceLost() {
+ // TODO we should probably close here, not sure.
+ }
+ public void serviceReplaced() {
+ }
+ });
+ proxyManager.open();
+
+ // Create and publish a ParserService
+ parserServiceReg = ctx.registerService(ParserService.class.getName(),
+ new ParserServiceImpl (handlers),
+ new Hashtable<Object, Object>());
+
+ try{
+ ctx.getBundle().loadClass(QUIESCE_PARTICIPANT_CLASS);
+ //Class was loaded, register
+
+ quiesceParticipantReg = ctx.registerService(QUIESCE_PARTICIPANT_CLASS,
+ new BlueprintQuiesceParticipant(ctx, this),
+ new Hashtable<Object, Object>());
+ }
+ catch (ClassNotFoundException e)
+ {
+ LOGGER.info("No quiesce support is available, so blueprint components will not participate in quiesce operations");
+ }
+
+ LOGGER.debug("Blueprint extender started");
+ }
+
+ /**
+ * this method checks the initial bundle that are installed/active before
+ * bundle tracker is opened.
+ *
+ * @param b the bundle to check
+ */
+ private void checkInitialBundle(Bundle b) {
+ // If the bundle is active, check it
+ if (b.getState() == Bundle.ACTIVE) {
+ checkBundle(b);
+ // Also check bundles in the starting state with a lazy activation
+ // policy
+ } else if (b.getState() == Bundle.STARTING) {
+ String activationPolicyHeader = (String) b.getHeaders().get(
+ Constants.BUNDLE_ACTIVATIONPOLICY);
+ if (activationPolicyHeader != null
+ && activationPolicyHeader
+ .startsWith(Constants.ACTIVATION_LAZY)) {
+ checkBundle(b);
+ }
+ }
+
+ }
+
+ public void stop(BundleContext context) {
+ LOGGER.debug("Stopping blueprint extender...");
+ if (bt != null) {
+ bt.close();
+ }
+
+ parserServiceReg.unregister();
+
+ if (quiesceParticipantReg != null)
+ quiesceParticipantReg.unregister();
+
+ // Orderly shutdown of containers
+ while (!containers.isEmpty()) {
+ for (Bundle bundle : getBundlesToDestroy()) {
+ destroyContext(bundle);
+ }
+ }
+ this.eventDispatcher.destroy();
+ this.handlers.destroy();
+ executors.shutdown();
+ LOGGER.debug("Blueprint extender stopped");
+ }
+
+ /**
+ * @return the proxy manager. This will return null if the blueprint is not yet managing bundles.
+ */
+ public static ProxyManager getProxyManager()
+ {
+ return proxyManager.getService();
+ }
+
+ private List<Bundle> getBundlesToDestroy() {
+ List<Bundle> bundlesToDestroy = new ArrayList<Bundle>();
+ for (Bundle bundle : containers.keySet()) {
+ ServiceReference[] references = bundle.getRegisteredServices();
+ int usage = 0;
+ if (references != null) {
+ for (ServiceReference reference : references) {
+ usage += getServiceUsage(reference);
+ }
+ }
+ LOGGER.debug("Usage for bundle {} is {}", bundle, usage);
+ if (usage == 0) {
+ bundlesToDestroy.add(bundle);
+ }
+ }
+ if (!bundlesToDestroy.isEmpty()) {
+ Collections.sort(bundlesToDestroy, new Comparator<Bundle>() {
+ public int compare(Bundle b1, Bundle b2) {
+ return (int) (b2.getLastModified() - b1.getLastModified());
+ }
+ });
+ LOGGER.debug("Selected bundles {} for destroy (no services in use)", bundlesToDestroy);
+ } else {
+ ServiceReference ref = null;
+ for (Bundle bundle : containers.keySet()) {
+ ServiceReference[] references = bundle.getRegisteredServices();
+ for (ServiceReference reference : references) {
+ if (getServiceUsage(reference) == 0) {
+ continue;
+ }
+ if (ref == null || reference.compareTo(ref) < 0) {
+ LOGGER.debug("Currently selecting bundle {} for destroy (with reference {})", bundle, reference);
+ ref = reference;
+ }
+ }
+ }
+ bundlesToDestroy.add(ref.getBundle());
+ LOGGER.debug("Selected bundle {} for destroy (lowest ranking service)", bundlesToDestroy);
+ }
+ return bundlesToDestroy;
+ }
+
+ private static int getServiceUsage(ServiceReference ref) {
+ Bundle[] usingBundles = ref.getUsingBundles();
+ return (usingBundles != null) ? usingBundles.length : 0;
+ }
+
+ public void bundleChanged(BundleEvent event) {
+ Bundle bundle = event.getBundle();
+ if (event.getType() == BundleEvent.LAZY_ACTIVATION) {
+ checkBundle(bundle);
+ } else if (event.getType() == BundleEvent.STARTED) {
+ BlueprintContainerImpl blueprintContainer = containers.get(bundle);
+ if (blueprintContainer == null) {
+ checkBundle(bundle);
+ }
+ } else if (event.getType() == BundleEvent.STOPPING) {
+ destroyContext(bundle);
+ }
+ }
+
+ private void destroyContext(Bundle bundle) {
+ BlueprintContainerImpl blueprintContainer = containers.remove(bundle);
+ if (blueprintContainer != null) {
+ LOGGER.debug("Destroying BlueprintContainer for bundle {}", bundle.getSymbolicName());
+ blueprintContainer.destroy();
+ }
+ eventDispatcher.removeBlueprintBundle(bundle);
+ }
+
+ private void checkBundle(Bundle bundle) {
+ LOGGER.debug("Scanning bundle {} for blueprint application", bundle.getSymbolicName());
+ try {
+ List<Object> pathList = new ArrayList<Object>();
+ String blueprintHeader = (String) bundle.getHeaders().get(BlueprintConstants.BUNDLE_BLUEPRINT_HEADER);
+ String blueprintHeaderAnnotation = (String) bundle.getHeaders().get(BlueprintConstants.BUNDLE_BLUEPRINT_ANNOTATION_HEADER);
+ if (blueprintHeader == null) {
+ blueprintHeader = "OSGI-INF/blueprint/";
+ }
+ List<PathElement> paths = HeaderParser.parseHeader(blueprintHeader);
+ for (PathElement path : paths) {
+ String name = path.getName();
+ if (name.endsWith("/")) {
+ addEntries(bundle, name, "*.xml", pathList);
+ } else {
+ String baseName;
+ String filePattern;
+ int pos = name.lastIndexOf('/');
+ if (pos < 0) {
+ baseName = "/";
+ filePattern = name;
+ } else {
+ baseName = name.substring(0, pos + 1);
+ filePattern = name.substring(pos + 1);
+ }
+ if (hasWildcards(filePattern)) {
+ addEntries(bundle, baseName, filePattern, pathList);
+ } else {
+ addEntry(bundle, name, pathList);
+ }
+ }
+ }
+
+ if (pathList.isEmpty() && blueprintHeaderAnnotation != null && blueprintHeaderAnnotation.trim().equalsIgnoreCase("true")) {
+ LOGGER.debug("Scanning bundle {} for blueprint annotations", bundle.getSymbolicName());
+ ServiceReference sr = this.context.getServiceReference("org.apache.aries.blueprint.annotation.service.BlueprintAnnotationScanner");
+
+ if (sr != null) {
+ BlueprintAnnotationScanner bas = (BlueprintAnnotationScanner)this.context.getService(sr);
+ // try to generate the blueprint definition XML
+ URL url = bas.createBlueprintModel(bundle);
+
+ if (url != null) {
+ pathList.add(url);
+ }
+
+ this.context.ungetService(sr);
+ }
+
+ }
+
+ if (!pathList.isEmpty()) {
+ LOGGER.debug("Found blueprint application in bundle {} with paths: {}", bundle.getSymbolicName(), pathList);
+ // Check compatibility
+ // TODO: For lazy bundles, the class is either loaded from an imported package or not found, so it should
+ // not trigger the activation. If it does, we need to use something else like package admin or
+ // ServiceReference, or just not do this check, which could be quite harmful.
+ boolean compatible = isCompatible(bundle);
+ if (compatible) {
+ final BlueprintContainerImpl blueprintContainer = new BlueprintContainerImpl(bundle.getBundleContext(), context.getBundle(), eventDispatcher, handlers, executors, pathList);
+ containers.put(bundle, blueprintContainer);
+ blueprintContainer.schedule();
+ } else {
+ LOGGER.info("Bundle {} is not compatible with this blueprint extender", bundle.getSymbolicName());
+ }
+
+ } else {
+ LOGGER.debug("No blueprint application found in bundle {}", bundle.getSymbolicName());
+ }
+ } catch (Throwable t) {
+ eventDispatcher.blueprintEvent(new BlueprintEvent(BlueprintEvent.FAILURE, bundle, context.getBundle(), t));
+ }
+ }
+
+ private boolean isCompatible(Bundle bundle) {
+ // Check compatibility
+ boolean compatible;
+ if (bundle.getState() == Bundle.ACTIVE) {
+ try {
+ Class<?> clazz = bundle.getBundleContext().getBundle().loadClass(BlueprintContainer.class.getName());
+ compatible = (clazz == BlueprintContainer.class);
+ } catch (ClassNotFoundException e) {
+ compatible = true;
+ }
+ } else {
+ // for lazy bundle, we can't load the class, so just assume it's ok
+ compatible = true;
+ }
+ return compatible;
+ }
+
+ private boolean hasWildcards(String path) {
+ return path.indexOf("*") >= 0;
+ }
+
+ private String getFilePart(URL url) {
+ String path = url.getPath();
+ int index = path.lastIndexOf('/');
+ return path.substring(index + 1);
+ }
+
+ private String cachePath(Bundle bundle, String filePath)
+ {
+ return Integer.toHexString(bundle.hashCode()) + "/" + filePath;
+ }
+
+ private URL getOverrideURLForCachePath(String privatePath){
+ URL override = null;
+ File privateDataVersion = context.getDataFile(privatePath);
+ if (privateDataVersion != null
+ && privateDataVersion.exists()) {
+ try {
+ override = privateDataVersion.toURI().toURL();
+ } catch (MalformedURLException e) {
+ LOGGER.error("Unexpected URL Conversion Issue", e);
+ }
+ }
+ return override;
+ }
+
+ private URL getOverrideURL(Bundle bundle, String path){
+ String cachePath = cachePath(bundle, path);
+ return getOverrideURLForCachePath(cachePath);
+ }
+
+ private URL getOverrideURL(Bundle bundle, URL path, String basePath){
+ String cachePath = cachePath(bundle, basePath + getFilePart(path));
+ return getOverrideURLForCachePath(cachePath);
+ }
+
+ private void addEntry(Bundle bundle, String path, List<Object> pathList) {
+ URL override = getOverrideURL(bundle, path);
+ if(override == null) {
+ pathList.add(path);
+ } else {
+ pathList.add(override);
+ }
+ }
+
+ private void addEntries(Bundle bundle, String path, String filePattern, List<Object> pathList) {
+ Enumeration<?> e = bundle.findEntries(path, filePattern, false);
+ while (e != null && e.hasMoreElements()) {
+ URL u = (URL) e.nextElement();
+ URL override = getOverrideURL(bundle, u, path);
+ if(override == null) {
+ pathList.add(u);
+ } else {
+ pathList.add(override);
+ }
+ }
+ }
+
+ // blueprint bundle tracker calls bundleChanged to minimize changes.
+ private class BlueprintBundleTrackerCustomizer implements
+ BundleTrackerCustomizer {
+
+ public BlueprintBundleTrackerCustomizer() {
+ }
+
+ public Object addingBundle(Bundle b, BundleEvent event) {
+ if (event == null) {
+ // existing bundles first added to the tracker with no event change
+ checkInitialBundle(b);
+ } else {
+ bundleChanged(event);
+ }
+
+ return b;
+ }
+
+ public void modifiedBundle(Bundle b, BundleEvent event, Object arg2) {
+ if (event == null) {
+ // cannot think of why we would be interested in a modified bundle with no bundle event
+ return;
+ }
+
+ bundleChanged(event);
+
+ }
+
+ // don't think we would be interested in removedBundle, as that is
+ // called when bundle is removed from the tracker
+ public void removedBundle(Bundle b, BundleEvent event, Object arg2) {
+ }
+ }
+
+ protected BlueprintContainerImpl getBlueprintContainerImpl(Bundle bundle)
+ {
+ return containers.get(bundle);
+ }
+
+}
Added: aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintQuiesceParticipant.java
URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintQuiesceParticipant.java?rev=1075149&view=auto
==============================================================================
--- aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintQuiesceParticipant.java (added)
+++ aries/tags/blueprint-0.3.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/BlueprintQuiesceParticipant.java Sun Feb 27 21:21:05 2011
@@ -0,0 +1,169 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.aries.blueprint.container;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.aries.blueprint.di.Recipe;
+import org.apache.aries.quiesce.manager.QuiesceCallback;
+import org.apache.aries.quiesce.participant.QuiesceParticipant;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+
+public class BlueprintQuiesceParticipant implements QuiesceParticipant
+{
+ private final BundleContext ctx;
+ private final BlueprintExtender extender;
+
+ public BlueprintQuiesceParticipant(BundleContext context, BlueprintExtender extender)
+ {
+ this.ctx = context;
+ this.extender = extender;
+ }
+
+ /**
+ * A Threadpool for running quiesce operations
+ */
+ private final ExecutorService executor = new ThreadPoolExecutor(0, 10, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), new ThreadFactory()
+ {
+ public Thread newThread(Runnable r)
+ {
+ Thread t = new Thread(r, "Blueprint-Container-ThreadPool");
+ t.setDaemon(true);
+ return t;
+ }
+ });
+
+ public void quiesce(QuiesceCallback callback, List<Bundle> bundlesToQuiesce)
+ {
+ boolean shutdownMe = false;
+ for(Bundle b : bundlesToQuiesce)
+ {
+ try
+ {
+ executor.execute(new QuiesceBundle(callback, b, extender));
+ }
+ catch (RejectedExecutionException re) {
+ }
+
+ //If we are quiescing, then we need to quiesce this threadpool!
+ shutdownMe |= b.equals(ctx.getBundle());
+ }
+
+ if (shutdownMe) executor.shutdown();
+ }
+
+ /**
+ * A runnable Quiesce operation for a single bundle
+ */
+ private static final class QuiesceBundle implements Runnable
+ {
+ /** The bundle being quiesced */
+ private final Bundle bundleToQuiesce;
+ private final QuiesceCallback callback;
+ private final BlueprintExtender extender;
+
+ public QuiesceBundle(QuiesceCallback callback, Bundle bundleToQuiesce,
+ BlueprintExtender extender)
+ {
+ super();
+ this.callback = callback;
+ this.bundleToQuiesce = bundleToQuiesce;
+ this.extender = extender;
+ }
+
+ public void run()
+ {
+ BlueprintContainerImpl container = extender.getBlueprintContainerImpl(bundleToQuiesce);
+
+ // have we got an actual blueprint bundle
+ if (container != null) {
+ BlueprintRepository repository = container.getRepository();
+ Set<String> names = repository.getNames();
+ container.quiesce();
+ boolean hasServices = false;
+
+ for (String name: names)
+ {
+ Recipe recipe = repository.getRecipe(name);
+ if (recipe instanceof ServiceRecipe)
+ {
+ hasServices = true;
+ ((ServiceRecipe)recipe).quiesce(new QuiesceDelegatingCallback(callback, bundleToQuiesce));
+ }
+ }
+ //If the bundle has no services we can quiesce immediately
+ if (!hasServices)
+ {
+ callback.bundleQuiesced(bundleToQuiesce);
+ }
+ } else {
+ // for non-Blueprint bundles just call return completed
+
+ callback.bundleQuiesced(bundleToQuiesce);
+ }
+ }
+ }
+
+ /**
+ * A wrapper to protect our internals from the Quiesce API so that we can make it
+ * an optional dependency
+ */
+ private static final class QuiesceDelegatingCallback implements DestroyCallback
+ {
+
+ /** The callback to delegate to */
+ private final QuiesceCallback callback;
+
+ /** The single bundle being quiesced by this DestroyCallback */
+ private final Bundle toQuiesce;
+
+ private final Set<String> services = new HashSet<String>();
+
+ public QuiesceDelegatingCallback(QuiesceCallback cbk, Bundle b)
+ {
+ callback = cbk;
+ toQuiesce = b;
+
+ ServiceReference[] serviceRefs = b.getRegisteredServices();
+
+ for (ServiceReference ref : serviceRefs)
+ {
+ services.add(b.getBundleContext().getService(ref).toString());
+ }
+ }
+
+ public void callback(Object key)
+ {
+ if (key != null && services.remove(key.toString()) && services.isEmpty())
+ {
+ callback.bundleQuiesced(toQuiesce);
+ }
+ }
+ }
+}