Package org.springframework.osgi.extender.internal.activator

Source Code of org.springframework.osgi.extender.internal.activator.ContextLoaderListener$ContextBundleListener

/*
* Copyright 2006-2008 the original author or authors.
*
* Licensed 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.springframework.osgi.extender.internal.activator;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Timer;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.SynchronousBundleListener;
import org.osgi.framework.Version;
import org.osgi.service.packageadmin.PackageAdmin;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.core.CollectionFactory;
import org.springframework.core.task.SyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.osgi.context.ConfigurableOsgiBundleApplicationContext;
import org.springframework.osgi.context.DelegatedExecutionOsgiBundleApplicationContext;
import org.springframework.osgi.context.event.OsgiBundleApplicationContextEventMulticaster;
import org.springframework.osgi.context.event.OsgiBundleApplicationContextListener;
import org.springframework.osgi.extender.OsgiApplicationContextCreator;
import org.springframework.osgi.extender.internal.dependencies.shutdown.ComparatorServiceDependencySorter;
import org.springframework.osgi.extender.internal.dependencies.shutdown.ServiceDependencySorter;
import org.springframework.osgi.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor;
import org.springframework.osgi.extender.internal.support.ExtenderConfiguration;
import org.springframework.osgi.extender.internal.support.NamespaceManager;
import org.springframework.osgi.extender.internal.support.OsgiBeanFactoryPostProcessorAdapter;
import org.springframework.osgi.extender.internal.util.concurrent.Counter;
import org.springframework.osgi.extender.internal.util.concurrent.RunnableTimedExecution;
import org.springframework.osgi.extender.support.ApplicationContextConfiguration;
import org.springframework.osgi.extender.support.internal.ConfigUtils;
import org.springframework.osgi.service.importer.support.Cardinality;
import org.springframework.osgi.service.importer.support.CollectionType;
import org.springframework.osgi.service.importer.support.OsgiServiceCollectionProxyFactoryBean;
import org.springframework.osgi.util.OsgiBundleUtils;
import org.springframework.osgi.util.OsgiStringUtils;
import org.springframework.util.Assert;

/**
* Osgi Extender that bootstraps 'Spring powered bundles'.
*
* <p/> The class listens to bundle events and manages the creation and
* destruction of application contexts for bundles that have one or both of:
* <ul>
* <li> A manifest header entry Spring-Context
* <li> XML files in META-INF/spring folder
* </ul>
*
* <p/> The extender also discovers any Spring namespace/schema handlers in
* resolved bundles and makes them available through a dedicated OSGi service.
*
* <p/> The extender behaviour can be customized by attaching fragments to the
* extender bundle. On startup, the extender will look for
* <code>META-INF/spring/*.xml</code> files and merge them into an application
* context. From the resulting context, the context will look for beans with
* predefined names to determine its configuration. The current version
* recognises the following bean names:
*
* <table border="1">
* <tr>
* <th>Bean Name</th>
* <th>Bean Type</th>
* <th>Description</th>
* </tr>
* <tr>
* <td><code>taskExecutor</code></td>
* <td><code>org.springframework.core.task.TaskExecutor</code></td>
* <td>Task executor used for creating the discovered application contexts.</td>
* </tr>
* <tr>
* <td><code>shutdownTaskExecutor</code></td>
* <td><code>org.springframework.core.task.TaskExecutor</code></td>
* <td>Task executor used for shutting down various application contexts.</td>
* </tr>
* <tr>
* <td><code>extenderProperties</code></td>
* <td><code>java.util.Properties</code></td>
* <td>Various properties for configuring the extender behaviour (see below)</td>
* </tr>
* </table>
*
* <p/> <code>extenderProperties</code> recognises the following properties:
*
* <table border="1">
* <tr>
* <th>Name</th>
* <th>Type</th>
* <th>Description</th>
* </tr>
* <tr>
* <td><code>shutdown.wait.time</code></td>
* <td>Number</td>
* <td>The amount of time the extender will wait for each application context
* to shutdown gracefully. Expressed in milliseconds.</td>
* </tr>
* <tr>
* <td><code>process.annotations</code></td>
* <td>Boolean</td>
* <td>Whether or not, the extender will process SpringOSGi annotations.</td>
* </tr>
* </table>
*
* <p/> Note: The extender configuration context is created during the bundle
* activation (a synchronous OSGi lifecycle callback) and should contain only
* simple bean definitions that will not delay context initialisation.
* </p>
*
* @author Bill Gallagher
* @author Andy Piper
* @author Hal Hildebrand
* @author Adrian Colyer
* @author Costin Leau
*/
public class ContextLoaderListener implements BundleActivator {

  /**
   * Common base class for {@link ContextLoaderListener} listeners.
   *
   * @author Costin Leau
   */
  private abstract class BaseListener implements SynchronousBundleListener {

    /**
     * A bundle has been started, stopped, resolved, or unresolved. This
     * method is a synchronous callback, do not do any long-running work in
     * this thread.
     *
     * @see org.osgi.framework.SynchronousBundleListener#bundleChanged
     */
    public void bundleChanged(BundleEvent event) {

      boolean trace = log.isTraceEnabled();

      // check if the listener is still alive
      synchronized (monitor) {
        if (isClosed) {
          if (trace)
            log.trace("Listener is closed; events are being ignored");
          return;
        }
      }
      if (trace) {
        log.debug("Processing bundle event [" + OsgiStringUtils.nullSafeToString(event) + "] for bundle ["
            + OsgiStringUtils.nullSafeSymbolicName(event.getBundle()) + "]");
      }
      try {
        handleEvent(event);
      }
      catch (Exception ex) {
        /* log exceptions before swallowing */
        log.warn("Got exception while handling event " + event, ex);
      }
    }

    protected abstract void handleEvent(BundleEvent event);
  }

  /**
   * Bundle listener used for detecting namespace handler/resolvers. Exists as
   * a separate listener so that it can be registered early to avoid race
   * conditions with bundles in INSTALLING state but still to avoid premature
   * context creation before the Spring {@link ContextLoaderListener} is not
   * fully initialized.
   *
   * @author Costin Leau
   */
  private class NamespaceBundleLister extends BaseListener {

    protected void handleEvent(BundleEvent event) {

      Bundle bundle = event.getBundle();

      switch (event.getType()) {
        case BundleEvent.RESOLVED: {
          maybeAddNamespaceHandlerFor(bundle);
          break;
        }
        case BundleEvent.UNRESOLVED: {
          maybeRemoveNameSpaceHandlerFor(bundle);
          break;
        }
        default:
          break;
      }
    }
  }

  /**
   * Bundle listener used for context creation/destruction.
   */
  private class ContextBundleListener extends BaseListener {

    protected void handleEvent(BundleEvent event) {

      Bundle bundle = event.getBundle();

      // ignore current bundle for context creation
      if (bundle.getBundleId() == bundleId) {
        return;
      }

      switch (event.getType()) {
        case BundleEvent.STARTED: {
          maybeCreateApplicationContextFor(bundle);
          break;
        }
        case BundleEvent.STOPPING: {
          if (OsgiBundleUtils.isSystemBundle(bundle)) {
            if (log.isDebugEnabled()) {
              log.debug("System bundle stopping");
            }
            // System bundle is shutting down; Special handling for
            // framework shutdown
            shutdown();
          }
          else {
            maybeCloseApplicationContextFor(bundle);
          }
          break;
        }
        default:
          break;
      }
    }
  }


  private static final Log log = LogFactory.getLog(ContextLoaderListener.class);

  // "Spring Application Context Creation Timer"
  private Timer timer = new Timer(true);

  /** extender bundle id */
  private long bundleId;

  /** extender configuration */
  private ExtenderConfiguration extenderConfiguration;

  /**
   * The contexts we are currently managing. Keys are bundle ids, values are
   * ServiceDependentOsgiApplicationContexts for the application context
   */
  private final Map managedContexts;

  /** Task executor used for bootstraping the Spring contexts in async mode */
  private TaskExecutor taskExecutor;

  /** ApplicationContext Creator */
  private OsgiApplicationContextCreator contextCreator;

  /** BFPP list */
  private List postProcessors;

  /**
   * Task executor which uses the same thread for running tasks. Used when
   * doing a synchronous wait-for-dependencies.
   */
  private TaskExecutor sameThreadTaskExecutor = new SyncTaskExecutor();

  /** listener counter - used to properly synchronize shutdown */
  private Counter contextsStarted = new Counter("contextsStarted");

  /** Spring namespace/resolver manager */
  private NamespaceManager nsManager;

  /** The bundle's context */
  private BundleContext bundleContext;

  /** Bundle listener interested in context creation */
  private SynchronousBundleListener contextListener;

  /** Bundle listener interested in namespace resolvers/parsers discovery */
  private SynchronousBundleListener nsListener;

  /** Service-based dependency sorter for shutdown */
  private ServiceDependencySorter shutdownDependencySorter = new ComparatorServiceDependencySorter();

  /**
   * Monitor used for dealing with the bundle activator and synchronous bundle
   * threads
   */
  private transient final Object monitor = new Object();

  /**
   * flag indicating whether the context is down or not - useful during
   * shutdown
   */
  private boolean isClosed = false;

  /** This extender version */
  private Version extenderVersion;

  private OsgiBundleApplicationContextEventMulticaster multicaster;

  /** listeners interested in monitoring managed OSGi appCtxs */
  private List applicationListeners;

  /** dynamicList clean up hook */
  private DisposableBean applicationListenersCleaner;
  /** Spring compatibility checker */
  private SpringTypeCompatibilityChecker compatibilityChecker;
  /** Spring version used */
  private Bundle wiredSpringBundle;
  /** shutdown task executor */
  private TaskExecutor shutdownTaskExecutor;


  /** Required by the BundleActivator contract */
  public ContextLoaderListener() {
    this.managedContexts = CollectionFactory.createConcurrentMap(16);
  }

  /**
   * <p/> Called by OSGi when this bundle is started. Finds all previously
   * resolved bundles and adds namespace handlers for them if necessary.
   * </p>
   * <p/> Creates application contexts for bundles started before the extender
   * was started.
   * </p>
   * <p/> Registers a namespace/entity resolving service for use by web app
   * contexts.
   * </p>
   *
   * @see org.osgi.framework.BundleActivator#start
   */
  public void start(BundleContext context) throws Exception {

    this.bundleContext = context;
    this.bundleId = context.getBundle().getBundleId();

    this.extenderVersion = OsgiBundleUtils.getBundleVersion(context.getBundle());
    log.info("Starting [" + bundleContext.getBundle().getSymbolicName() + "] bundle v.[" + extenderVersion + "]");

    detectSpringVersion(context);

    compatibilityChecker = new SpringTypeCompatibilityChecker(bundleContext);

    // Step 1 : discover existing namespaces (in case there are fragments with custom XML definitions)
    nsManager = new NamespaceManager(context);

    // register listener first to make sure any bundles in INSTALLED state
    // are not lost
    nsListener = new NamespaceBundleLister();
    context.addBundleListener(nsListener);

    Bundle[] previousBundles = context.getBundles();

    for (int i = 0; i < previousBundles.length; i++) {
      Bundle bundle = previousBundles[i];
      if (OsgiBundleUtils.isBundleResolved(bundle)) {
        maybeAddNamespaceHandlerFor(bundle);
      }
    }

    // discovery finished, publish the resolvers/parsers in the OSGi space
    nsManager.afterPropertiesSet();

    // Step 2: initialize the extender configuration
    extenderConfiguration = new ExtenderConfiguration(context);

    // initialize the configuration once namespace handlers have been detected
    this.taskExecutor = extenderConfiguration.getTaskExecutor();
    this.shutdownTaskExecutor = extenderConfiguration.getShutdownTaskExecutor();

    this.contextCreator = extenderConfiguration.getContextCreator();
    this.postProcessors = extenderConfiguration.getPostProcessors();

    // init the OSGi event dispatch/listening system
    initListenerService();

    // Step 3: discover the bundles that are started
    // and require context creation

    // register the context creation listener
    contextListener = new ContextBundleListener();
    // listen to any changes in bundles
    context.addBundleListener(contextListener);
    // get the bundles again to get an updated view
    previousBundles = context.getBundles();

    // Instantiate all previously resolved bundles which are Spring
    // powered
    for (int i = 0; i < previousBundles.length; i++) {
      if (OsgiBundleUtils.isBundleActive(previousBundles[i])) {
        try {
          maybeCreateApplicationContextFor(previousBundles[i]);
        }
        catch (Throwable e) {
          log.warn("Cannot start bundle " + OsgiStringUtils.nullSafeSymbolicName(previousBundles[i])
              + " due to", e);
        }
      }
    }

  }

  private void detectSpringVersion(BundleContext context) {
    boolean debug = log.isDebugEnabled();

    // use PackageAdmin internally to determine the wired Spring version
    ServiceReference ref = bundleContext.getServiceReference(PackageAdmin.class.getName());
    if (ref != null) {
      PackageAdmin pa = (PackageAdmin) bundleContext.getService(ref);
      wiredSpringBundle = pa.getBundle(Assert.class);
    }
    else {
      if (debug) {
        log.debug("PackageAdmin not available; falling back to raw class loading for detecting the wired Spring bundle");
      }
      wiredSpringBundle = SpringTypeCompatibilityChecker.findOriginatingBundle(context, Assert.class);
      if (wiredSpringBundle == null) {
        throw new IllegalStateException("Impossible to find the originating Spring bundle for " + Assert.class
            + "; bailing out");
      }
    }

    if (debug)
      log.debug("Spring-DM v.[" + extenderVersion + "] is wired to Spring core bundle "
          + OsgiStringUtils.nullSafeSymbolicName(wiredSpringBundle) + " version ["
          + OsgiBundleUtils.getBundleVersion(wiredSpringBundle) + "]");
  }

  /**
   * Called by OSGi when this bundled is stopped. Unregister the
   * namespace/entity resolving service and clear all state. No further
   * management of application contexts created by this extender prior to
   * stopping the bundle occurs after this point (even if the extender bundle
   * is subsequently restarted).
   *
   * @see org.osgi.framework.BundleActivator#stop
   */
  public void stop(BundleContext context) throws Exception {
    shutdown();
  }

  /**
   * Shutdown the extender and all bundled managed by it. Shutdown of contexts
   * is in the topological order of the dependency graph formed by the service
   * references.
   */
  protected void shutdown() {
    synchronized (monitor) {
      // if already closed, bail out
      if (isClosed)
        return;
      else
        isClosed = true;
    }
    log.info("Stopping [" + bundleContext.getBundle().getSymbolicName() + "] bundle v.[" + extenderVersion + "]");

    // first stop the watchdog
    stopTimer();

    // remove the bundle listeners (we are closing down)
    if (contextListener != null) {
      bundleContext.removeBundleListener(contextListener);
      contextListener = null;
    }

    if (nsListener != null) {
      bundleContext.removeBundleListener(nsListener);
      nsListener = null;
    }

    // destroy bundles
    Bundle[] bundles = new Bundle[managedContexts.size()];

    int i = 0;
    for (Iterator it = managedContexts.values().iterator(); it.hasNext();) {
      ConfigurableOsgiBundleApplicationContext context = (ConfigurableOsgiBundleApplicationContext) it.next();
      bundles[i++] = context.getBundle();
    }

    bundles = shutdownDependencySorter.computeServiceDependencyGraph(bundles);

    boolean debug = log.isDebugEnabled();

    StringBuffer buffer = new StringBuffer();
    if (debug) {
      buffer.append("Shutdown order is: {");
      for (i = 0; i < bundles.length; i++) {
        buffer.append("\nBundle [" + bundles[i].getSymbolicName() + "]");
      }
      buffer.append("\n}");
      log.debug(buffer);
    }

    final List taskList = new ArrayList(managedContexts.size());
    final List closedContexts = Collections.synchronizedList(new ArrayList());
    final Object[] contextClosingDown = new Object[1];

    for (i = 0; i < bundles.length; i++) {
      Long id = new Long(bundles[i].getBundleId());
      final ConfigurableOsgiBundleApplicationContext context = (ConfigurableOsgiBundleApplicationContext) managedContexts.get(id);
      if (context != null) {
        closedContexts.add(context);
        // add a new runnable
        taskList.add(new Runnable() {

          private final String toString = "Closing runnable for context " + context.getDisplayName();


          public void run() {
            contextClosingDown[0] = context;
            // eliminate context
            closedContexts.remove(context);
            if (log.isDebugEnabled())
              log.debug("Closing appCtx " + context.getDisplayName());
            context.close();
          }

          public String toString() {
            return toString;
          }
        });
      }
    }

    // tasks
    final Runnable[] tasks = (Runnable[]) taskList.toArray(new Runnable[taskList.size()]);

    // start the ripper >:)
    for (int j = 0; j < tasks.length; j++) {
      if (RunnableTimedExecution.execute(tasks[j], extenderConfiguration.getShutdownWaitTime(),
        shutdownTaskExecutor)) {
        if (debug) {
          log.debug(contextClosingDown[0] + " context did not close successfully; forcing shutdown...");
        }
      }
    }

    this.managedContexts.clear();
    // clear the namespace registry
    nsManager.destroy();

    // release listeners
    if (applicationListeners != null) {
      applicationListeners = null;
      try {
        applicationListenersCleaner.destroy();
      }
      catch (Exception ex) {
        log.warn("exception thrown while releasing OSGi event listeners", ex);
      }
    }

    // release multicaster
    if (multicaster != null) {
      multicaster.removeAllListeners();
      multicaster = null;
    }

    // before bailing out; wait for the threads that might be left by
    // the task executor
    stopTaskExecutor();

    extenderConfiguration.destroy();
  }

  /**
   * Cancel any tasks scheduled for the timer.
   */
  private void stopTimer() {
    if (timer != null) {
      if (log.isDebugEnabled())
        log.debug("Canceling timer tasks");
      timer.cancel();
    }
    timer = null;
  }

  /**
   * Do some additional waiting so the service dependency listeners detect the
   * shutdown.
   */
  private void stopTaskExecutor() {
    boolean debug = log.isDebugEnabled();

    if (debug)
      log.debug("Waiting for " + contextsStarted + " service dependency listener(s) to stop...");

    contextsStarted.waitForZero(extenderConfiguration.getShutdownWaitTime());

    if (!contextsStarted.isZero()) {
      if (debug)
        log.debug(contextsStarted.getValue()
            + " service dependency listener(s) did not responded in time; forcing them to shutdown...");
      extenderConfiguration.setForceThreadShutdown(true);
    }

    else
      log.debug("All listeners closed");
  }

  /**
   * Utility method that does extender range versioning and approapriate
   * logging.
   *
   * @param bundle
   */
  private boolean handlerBundleMatchesExtenderVersion(Bundle bundle) {
    if (!ConfigUtils.matchExtenderVersionRange(bundle, extenderVersion)) {
      if (log.isDebugEnabled())
        log.debug("Bundle [" + OsgiStringUtils.nullSafeNameAndSymName(bundle)
            + "] expects an extender w/ version["
            + OsgiBundleUtils.getHeaderAsVersion(bundle, ConfigUtils.EXTENDER_VERSION)
            + "] which does not match current extender w/ version[" + extenderVersion
            + "]; skipping bundle from handler detection");
      return false;
    }
    return true;
  }

  private void maybeAddNamespaceHandlerFor(Bundle bundle) {
    if (handlerBundleMatchesExtenderVersion(bundle))
      nsManager.maybeAddNamespaceHandlerFor(bundle);
  }

  private void maybeRemoveNameSpaceHandlerFor(Bundle bundle) {
    if (handlerBundleMatchesExtenderVersion(bundle))
      nsManager.maybeRemoveNameSpaceHandlerFor(bundle);
  }

  /**
   * Context creation is a potentially long-running activity (certainly more
   * than we want to do on the synchronous event callback).
   *
   * <p/>Based on our configuration, the context can be started on the same
   * thread or on a different one.
   *
   * <p/> Kick off a background activity to create an application context for
   * the given bundle if needed.
   *
   * <b>Note:</b> Make sure to do the fastest filtering first to avoid
   * slowdowns on platforms with a big number of plugins and wiring (i.e.
   * Eclipse platform).
   *
   * @param bundle
   */
  protected void maybeCreateApplicationContextFor(Bundle bundle) {

    boolean debug = log.isDebugEnabled();
    String bundleString = "[" + OsgiStringUtils.nullSafeNameAndSymName(bundle) + "]";

    final Long bundleId = new Long(bundle.getBundleId());

    if (managedContexts.containsKey(bundleId)) {
      if (debug) {
        log.debug("Bundle " + bundleString + " is already managed; ignoring...");
      }
      return;
    }

    if (!ConfigUtils.matchExtenderVersionRange(bundle, extenderVersion)) {
      if (debug)
        log.debug("Bundle " + bundleString + " expects an extender w/ version["
            + OsgiBundleUtils.getHeaderAsVersion(bundle, ConfigUtils.EXTENDER_VERSION)
            + "] which does not match current extender w/ version[" + extenderVersion
            + "]; skipping bundle from context creation");
      return;
    }

    BundleContext localBundleContext = OsgiBundleUtils.getBundleContext(bundle);

    if (debug)
      log.debug("Scanning bundle " + bundleString + " for configurations...");

    // initialize context
    final DelegatedExecutionOsgiBundleApplicationContext localApplicationContext;

    if (debug)
      log.debug("Creating an application context for bundle " + bundleString);

    try {
      localApplicationContext = contextCreator.createApplicationContext(localBundleContext);
    }
    catch (Exception ex) {
      log.error("Cannot create application context for bundle " + bundleString, ex);
      return;
    }

    if (localApplicationContext == null) {
      log.debug("No application context created for bundle " + bundleString);
      return;
    }

    // an application context has been created - do type filtering
    // filtering could be applied before creating the application context but then user might disable this by accident
    // so its best to do this inside the extender itself (this could change in the future)

    if (compatibilityChecker.checkCompatibility(bundle)) {
      log.debug("Bundle " + bundleString + " is Spring type compatible with Spring-DM");

    }
    else {
      log.debug("Ignoring bundle " + bundleString + " as it's Spring incompatible with Spring-DM...");
      return;
    }

    // create a dedicated hook for this application context
    BeanFactoryPostProcessor processingHook = new OsgiBeanFactoryPostProcessorAdapter(localBundleContext,
      postProcessors);

    // add in the post processors
    localApplicationContext.addBeanFactoryPostProcessor(processingHook);

    // add the context to the tracker
    managedContexts.put(bundleId, localApplicationContext);

    localApplicationContext.setDelegatedEventMulticaster(multicaster);

    // create refresh runnable
    Runnable contextRefresh = new Runnable() {

      public void run() {
        localApplicationContext.refresh();
      }
    };

    // executor used for creating the appCtx
    // chosen based on the sync/async configuration
    TaskExecutor executor = null;

    ApplicationContextConfiguration config = new ApplicationContextConfiguration(bundle);

    String creationType;

    // synch/asynch context creation
    if (config.isCreateAsynchronously()) {
      // for the async stuff use the executor
      executor = taskExecutor;
      creationType = "Asynchronous";
    }
    else {
      // for the sync stuff, use this thread
      executor = sameThreadTaskExecutor;
      creationType = "Synchronous";
    }

    if (debug) {
      log.debug(creationType + " context creation for bundle " + bundleString);
    }

    // wait/no wait for dependencies behaviour
    if (config.isWaitForDependencies()) {
      DependencyWaiterApplicationContextExecutor appCtxExecutor = new DependencyWaiterApplicationContextExecutor(
        localApplicationContext, !config.isCreateAsynchronously(),
        extenderConfiguration.getDependencyFactories());

      appCtxExecutor.setTimeout(config.getTimeout());
      appCtxExecutor.setWatchdog(timer);
      appCtxExecutor.setTaskExecutor(executor);
      appCtxExecutor.setMonitoringCounter(contextsStarted);
      // set events publisher
      appCtxExecutor.setDelegatedMulticaster(this.multicaster);

      contextsStarted.increment();
    }
    else {
      // do nothing; by default contexts do not wait for services.
    }

    executor.execute(contextRefresh);
  }

  /**
   * Closing an application context is a potentially long-running activity,
   * however, we *have* to do it synchronously during the event process as the
   * BundleContext object is not valid once we return from this method.
   *
   * @param bundle
   */
  protected void maybeCloseApplicationContextFor(Bundle bundle) {
    final ConfigurableOsgiBundleApplicationContext context = (ConfigurableOsgiBundleApplicationContext) managedContexts.remove(new Long(
      bundle.getBundleId()));
    if (context == null) {
      return;
    }

    RunnableTimedExecution.execute(new Runnable() {

      private final String toString = "Closing runnable for context " + context.getDisplayName();


      public void run() {
        if (context.isActive()) {
          context.close();
        }
      }

      public String toString() {
        return toString;
      }

    }, extenderConfiguration.getShutdownWaitTime(), shutdownTaskExecutor);
  }

  private void initListenerService() {
    multicaster = extenderConfiguration.getEventMulticaster();

    createListenersList();
    // register the listener that does the dispatching
    multicaster.addApplicationListener(new ListListenerAdapter(applicationListeners));

    if (log.isDebugEnabled())
      log.debug("Initialization of OSGi listeners service completed...");
  }

  /**
   * Creates a dynamic OSGi list of OSGi services interested in receiving
   * events for OSGi application contexts.
   */
  private void createListenersList() {
    OsgiServiceCollectionProxyFactoryBean fb = new OsgiServiceCollectionProxyFactoryBean();
    fb.setBundleContext(bundleContext);
    fb.setCardinality(Cardinality.C_0__N);
    fb.setCollectionType(CollectionType.LIST);
    fb.setInterfaces(new Class[] { OsgiBundleApplicationContextListener.class });
    fb.setBeanClassLoader(extenderConfiguration.getClassLoader());
    fb.afterPropertiesSet();

    applicationListenersCleaner = fb;
    applicationListeners = (List) fb.getObject();
  }
}
TOP

Related Classes of org.springframework.osgi.extender.internal.activator.ContextLoaderListener$ContextBundleListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.