Package org.springframework.osgi.extender.internal.dependencies.startup

Source Code of org.springframework.osgi.extender.internal.dependencies.startup.DependencyServiceManager$DependencyServiceListener

package org.springframework.osgi.extender.internal.dependencies.startup;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.osgi.context.DelegatedExecutionOsgiBundleApplicationContext;
import org.springframework.osgi.extender.OsgiServiceDependencyFactory;
import org.springframework.osgi.extender.event.BootstrappingDependencyEvent;
import org.springframework.osgi.service.importer.OsgiServiceDependency;
import org.springframework.osgi.service.importer.event.OsgiServiceDependencyEvent;
import org.springframework.osgi.service.importer.event.OsgiServiceDependencyWaitEndedEvent;
import org.springframework.osgi.service.importer.event.OsgiServiceDependencyWaitStartingEvent;
import org.springframework.osgi.util.OsgiListenerUtils;
import org.springframework.osgi.util.OsgiStringUtils;

/**
* ServiceListener used for tracking dependent services. Even if the
* ServiceListener receives event synchronously, mutable properties should be
* synchronized to guarantee safe publishing between threads.
*
* @author Costin Leau
* @author Hal Hildebrand
* @author Andy Piper
*/
public class DependencyServiceManager {

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

  protected final Map dependencies = Collections.synchronizedMap(new LinkedHashMap());

  protected final Map unsatisfiedDependencies = Collections.synchronizedMap(new LinkedHashMap());

  private final ContextExecutorStateAccessor contextStateAccessor;

  private final BundleContext bundleContext;

  private final ServiceListener listener;

  private final DelegatedExecutionOsgiBundleApplicationContext context;

  /**
   * Task to execute if all dependencies are met.
   */
  private final Runnable executeIfDone;

  /** Maximum waiting time used in events when waiting for dependencies */
  private final long waitTime;

  /** dependency factories */
  private List dependencyFactories;


  /**
   * Actual ServiceListener.
   *
   * @author Costin Leau
   * @author Hal Hildebrand
   */
  private class DependencyServiceListener implements ServiceListener {

    /**
     * Process serviceChanged events, completing context initialization if
     * all the required dependencies are satisfied.
     *
     * @param serviceEvent
     */
    public void serviceChanged(ServiceEvent serviceEvent) {
      boolean trace = log.isTraceEnabled();

      try {
        if (unsatisfiedDependencies.isEmpty()) {

          // already completed but likely called due to threading
          if (trace) {
            log.trace("Handling service event, but no unsatisfied dependencies exist for "
                + context.getDisplayName());
          }

          return;
        }

        ServiceReference ref = serviceEvent.getServiceReference();
        if (trace) {
          log.trace("Handling service event [" + OsgiStringUtils.nullSafeToString(serviceEvent) + ":"
              + OsgiStringUtils.nullSafeToString(ref) + "] for " + context.getDisplayName());
        }

        updateDependencies(serviceEvent);

        ContextState state = contextStateAccessor.getContextState();

        // already resolved (closed or timed-out)
        if (state.isResolved()) {
          deregister();
          return;
        }

        // Good to go!
        if (unsatisfiedDependencies.isEmpty()) {
          deregister();
          // context.listener = null;
          log.info("No unsatisfied OSGi service dependencies; completing initialization for "
              + context.getDisplayName());

          // execute task to complete initialization
          // NOTE: the runnable should be able to delegate any long
          // process to a
          // different thread.
          executeIfDone.run();
        }
      }
      catch (Throwable e) {
        // frameworks will simply not log exception for event handlers
        log.error("Exception during dependency processing for " + context.getDisplayName(), e);
      }
    }

    private void updateDependencies(ServiceEvent serviceEvent) {
      boolean trace = log.isTraceEnabled();
      boolean debug = log.isDebugEnabled();

      for (Iterator i = dependencies.keySet().iterator(); i.hasNext();) {
        MandatoryServiceDependency dependency = (MandatoryServiceDependency) i.next();

        // check if there is a match on the service
        if (dependency.matches(serviceEvent)) {
          switch (serviceEvent.getType()) {

            case ServiceEvent.REGISTERED:
            case ServiceEvent.MODIFIED:
              unsatisfiedDependencies.remove(dependency);
              if (debug) {
                log.debug("Found service for " + context.getDisplayName() + "; eliminating "
                    + dependency + ", remaining [" + unsatisfiedDependencies + "]");
              }

              sendDependencySatisfiedEvent(dependency);
              break;

            case ServiceEvent.UNREGISTERING:
              unsatisfiedDependencies.put(dependency, dependency.getBeanName());
              if (debug) {
                log.debug("Service unregistered; adding " + dependency);
              }
              sendDependencyUnsatisfiedEvent(dependency);
              break;
            default: // do nothing
              if (debug) {
                log.debug("Unknown service event type for: " + dependency);
              }
              break;
          }
        }
        else {
          if (trace) {
            log.trace(dependency + " does not match: "
                + OsgiStringUtils.nullSafeToString(serviceEvent.getServiceReference()));
          }
        }
      }
    }
  }


  /**
   * Create a dependency manager, indicating the executor bound to, the
   * context that contains the dependencies and the task to execute if all
   * dependencies are met.
   *
   * @param executor
   * @param context
   * @param executeIfDone
   */
  public DependencyServiceManager(ContextExecutorStateAccessor executor,
      DelegatedExecutionOsgiBundleApplicationContext context, List dependencyFactories, Runnable executeIfDone,
      long maxWaitTime) {
    this.contextStateAccessor = executor;
    this.context = context;
    this.dependencyFactories = new ArrayList(8);

    if (dependencyFactories != null)
      this.dependencyFactories.addAll(dependencyFactories);

    this.waitTime = maxWaitTime;
    this.bundleContext = context.getBundleContext();
    this.listener = new DependencyServiceListener();

    this.executeIfDone = executeIfDone;
  }

  protected void findServiceDependencies() throws Exception {
    Thread currentThread = Thread.currentThread();
    ClassLoader oldTCCL = currentThread.getContextClassLoader();

    boolean debug = log.isDebugEnabled();
    boolean trace = log.isTraceEnabled();

    try {
      currentThread.setContextClassLoader(context.getClassLoader());

      ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();

      if (trace)
        log.trace("Looking for dependency factories inside bean factory [" + beanFactory.toString() + "]");

      Map localFactories = BeanFactoryUtils.beansOfTypeIncludingAncestors(beanFactory,
        OsgiServiceDependencyFactory.class, true, false);

      if (debug)
        log.debug("Discovered local dependency factories: " + localFactories.keySet());

      dependencyFactories.addAll(localFactories.values());

      for (Iterator iterator = dependencyFactories.iterator(); iterator.hasNext();) {
        OsgiServiceDependencyFactory dependencyFactory = (OsgiServiceDependencyFactory) iterator.next();
        Collection discoveredDependencies = null;

        try {
          discoveredDependencies = dependencyFactory.getServiceDependencies(bundleContext, beanFactory);
        }
        catch (Exception ex) {
          log.warn("Dependency factory " + dependencyFactory
              + " threw exception while detecting dependencies for beanFactory " + beanFactory + " in "
              + context.getDisplayName(), ex);
          throw ex;
        }
        // add the dependencies one by one
        if (discoveredDependencies != null)
          for (Iterator dependencyIterator = discoveredDependencies.iterator(); dependencyIterator.hasNext();) {
            OsgiServiceDependency dependency = (OsgiServiceDependency) dependencyIterator.next();
            MandatoryServiceDependency msd = new MandatoryServiceDependency(bundleContext, dependency);
            dependencies.put(msd, dependency.getBeanName());

            if (!msd.isServicePresent()) {
              log.info("Adding OSGi service dependency for importer [" + msd.getBeanName()
                  + "] matching OSGi filter [" + msd.filterAsString + "]");
              unsatisfiedDependencies.put(msd, dependency.getBeanName());
            }
          }
      }
    }
    finally {
      currentThread.setContextClassLoader(oldTCCL);
    }

    if (debug) {
      log.debug(dependencies.size() + " OSGi service dependencies, " + unsatisfiedDependencies.size()
          + " unsatisfied (for beans " + unsatisfiedDependencies.values() + ") in "
          + context.getDisplayName());
    }

    if (!unsatisfiedDependencies.isEmpty()) {
      log.info(context.getDisplayName() + " is waiting for unsatisfied dependencies ["
          + unsatisfiedDependencies.values() + "]");
    }
    if (log.isTraceEnabled()) {
      log.trace("Total OSGi service dependencies beans " + dependencies.values());
      log.trace("Unsatified OSGi service dependencies beans " + unsatisfiedDependencies.values());
    }
  }

  protected boolean isSatisfied() {
    return unsatisfiedDependencies.isEmpty();
  }

  public Map getUnsatisfiedDependencies() {
    return unsatisfiedDependencies;
  }

  protected void register() {
    String filter = createDependencyFilter();
    if (log.isDebugEnabled()) {
      log.debug(context.getDisplayName() + " has registered service dependency dependencyDetector with filter: "
          + filter);
    }

    // send dependency event before registering the filter
    sendInitialDependencyEvents();
    OsgiListenerUtils.addServiceListener(bundleContext, listener, filter);
  }

  /**
   * Look at the existing dependencies and create an appropriate filter. This
   * method concatenates the filters into one.
   *
   * @return
   */
  protected String createDependencyFilter() {
    boolean multiple = unsatisfiedDependencies.size() > 1;
    StringBuffer sb = new StringBuffer(100 * unsatisfiedDependencies.size());
    if (multiple) {
      sb.append("(|");
    }
    for (Iterator i = unsatisfiedDependencies.keySet().iterator(); i.hasNext();) {
      sb.append(((MandatoryServiceDependency) i.next()).filterAsString);
    }
    if (multiple) {
      sb.append(')');
    }
    return sb.toString();
  }

  protected void deregister() {
    if (log.isDebugEnabled()) {
      log.debug("Deregistering service dependency dependencyDetector for " + context.getDisplayName());
    }

    OsgiListenerUtils.removeServiceListener(bundleContext, listener);
  }

  // event notification
  private void sendInitialDependencyEvents() {
    for (Iterator iterator = unsatisfiedDependencies.keySet().iterator(); iterator.hasNext();) {
      MandatoryServiceDependency entry = (MandatoryServiceDependency) iterator.next();
      OsgiServiceDependencyEvent nestedEvent = new OsgiServiceDependencyWaitStartingEvent(context,
        entry.getServiceDependency(), waitTime);
      BootstrappingDependencyEvent dependencyEvent = new BootstrappingDependencyEvent(context,
        context.getBundle(), nestedEvent);
      publishEvent(dependencyEvent);
    }
  }

  private void sendDependencyUnsatisfiedEvent(MandatoryServiceDependency dependency) {
    OsgiServiceDependencyEvent nestedEvent = new OsgiServiceDependencyWaitStartingEvent(context,
      dependency.getServiceDependency(), waitTime);
    BootstrappingDependencyEvent dependencyEvent = new BootstrappingDependencyEvent(context, context.getBundle(),
      nestedEvent);
    publishEvent(dependencyEvent);
  }

  private void sendDependencySatisfiedEvent(MandatoryServiceDependency dependency) {
    OsgiServiceDependencyEvent nestedEvent = new OsgiServiceDependencyWaitEndedEvent(context,
      dependency.getServiceDependency(), waitTime);
    BootstrappingDependencyEvent dependencyEvent = new BootstrappingDependencyEvent(context, context.getBundle(),
      nestedEvent);
    publishEvent(dependencyEvent);
  }

  private void publishEvent(BootstrappingDependencyEvent dependencyEvent) {
    this.contextStateAccessor.getEventMulticaster().multicastEvent(dependencyEvent);
  }
}
TOP

Related Classes of org.springframework.osgi.extender.internal.dependencies.startup.DependencyServiceManager$DependencyServiceListener

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.