Package org.springframework.osgi.service.dependency.internal

Source Code of org.springframework.osgi.service.dependency.internal.DefaultMandatoryDependencyManager$ImporterDependencyListener

/*
* 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.service.dependency.internal;

import java.util.ArrayList;
import java.util.Collection;
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.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.core.CollectionFactory;
import org.springframework.core.ConcurrentMap;
import org.springframework.osgi.service.exporter.support.internal.controller.ExporterControllerUtils;
import org.springframework.osgi.service.exporter.support.internal.controller.ExporterInternalActions;
import org.springframework.osgi.service.importer.OsgiServiceDependency;
import org.springframework.osgi.service.importer.support.OsgiServiceCollectionProxyFactoryBean;
import org.springframework.osgi.service.importer.support.OsgiServiceProxyFactoryBean;
import org.springframework.osgi.service.importer.support.internal.controller.ImporterControllerUtils;
import org.springframework.osgi.service.importer.support.internal.controller.ImporterInternalActions;
import org.springframework.osgi.service.importer.support.internal.dependency.ImporterStateListener;
import org.springframework.osgi.util.internal.BeanFactoryUtils;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

/**
* Default implementation of {@link MandatoryServiceDependencyManager} which
* determines the relationship between importers and exporters and unpublishes
* exported service if they dependent, transitively, on imported OSGi services
* that are mandatory and cannot be satisfied.
*
* <strong>Note:</strong> aimed for singleton beans only
*
* @author Costin Leau
*
*/
public class DefaultMandatoryDependencyManager implements MandatoryServiceDependencyManager, BeanFactoryAware,
    DisposableBean {

  /**
   * Importer state listener that gets associated with each exporter.
   *
   * @author Costin Leau
   */
  private class ImporterDependencyListener implements ImporterStateListener {

    private final Object exporter;
    private final String exporterName;


    private ImporterDependencyListener(Object exporter) {
      this.exporter = exporter;
      this.exporterName = (String) exporterToName.get(exporter);

    }

    public void importerSatisfied(Object importer, OsgiServiceDependency dependency) {

      boolean trace = log.isTraceEnabled();
      boolean exporterRemoved = false;

      // update importer status
      synchronized (exporter) {
        Map importers = (Map) exporterToImporterDeps.get(exporter);
        exporterRemoved = !(importers != null);

        // if the list is not present (exporter was removed), bail out
        if (!exporterRemoved) {
          importers.put(importer, Boolean.TRUE);
          if (trace)
            log.trace("Importer [" + importerToName.get(importer)
                + "] is satisfied; checking the rest of the dependencies for exporter "
                + exporterToName.get(exporter));

          checkIfExporterShouldStart(exporter, importers);
        }
      }

      if (exporterRemoved && trace)
        log.trace("Exporter [" + exporterName + "] removed; ignoring dependency [" + dependency.getBeanName()
            + "] update");
    }

    public void importerUnsatisfied(Object importer, OsgiServiceDependency dependency) {

      boolean exporterRemoved = false;

      synchronized (exporter) {
        Map importers = (Map) exporterToImporterDeps.get(exporter);
        exporterRemoved = !(importers != null);
        if (!exporterRemoved) {
          // record the importer status
          importers.put(importer, Boolean.FALSE);
        }
      }

      boolean trace = log.isTraceEnabled();

      if (!exporterRemoved) {
        if (trace)
          log.trace("Exporter [" + exporterName + "] stopped; transitive OSGi dependency ["
              + dependency.getBeanName() + "] is unsatifised");

        // if the importer goes down, simply shut down the exporter
        stopExporter(exporter);
      }
      else {
        if (trace) {
          log.trace("Exporter [" + exporterName + "] removed; ignoring dependency ["
              + dependency.getBeanName() + "] update");
        }
      }
    }
  }


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

  /** cache map - useful for avoiding double registration */
  private final ConcurrentMap exportersSeen = CollectionFactory.createConcurrentMap(4);

  private static final Object VALUE = new Object();

  /**
   * Importers on which an exporter depends. The exporter instance is used as
   * a key, while the value is represented by a list of importers name and
   * their status (up or down).
   */
  private final Map exporterToImporterDeps = CollectionFactory.createConcurrentMap(8);

  /** exporter -> importer listener map */
  private final Map exporterListener = CollectionFactory.createConcurrentMap(8);

  /** importer -> name map */
  private final ConcurrentMap importerToName = CollectionFactory.createConcurrentMap(8);

  /** exporter name map */
  private final Map exporterToName = CollectionFactory.createConcurrentMap(8);

  /** owning bean factory */
  private ConfigurableListableBeanFactory beanFactory;


  public void addServiceExporter(Object exporter, String exporterBeanName) {
    Assert.hasText(exporterBeanName);

    if (exportersSeen.putIfAbsent(exporterBeanName, VALUE) == null) {

      String beanName = exporterBeanName;

      if (beanFactory.isFactoryBean(exporterBeanName))
        beanName = BeanFactory.FACTORY_BEAN_PREFIX + exporterBeanName;

      // check if it's factory bean (no need to check for abstract
      // definition since we're called by a BPP)
      if (!beanFactory.isSingleton(beanName)) {
        log.info("Exporter [" + beanName + "] is not singleton and will not be tracked");
      }

      else {
        exporterToName.put(exporter, exporterBeanName);
        // retrieve associated controller
        ExporterInternalActions controller = ExporterControllerUtils.getControllerFor(exporter);

        // disable publication at startup
        controller.registerServiceAtStartup(false);

        // populate the dependency maps
        discoverDependentImporterFor(exporterBeanName, exporter);
      }
    }
  }

  /**
   * Discover all the importers for the given exporter. Since the importers
   * are already created before the exporter instance is created, this method
   * only does filtering based on the mandatory imports.
   */
  protected void discoverDependentImporterFor(String exporterBeanName, Object exporter) {

    boolean trace = log.isTraceEnabled();

    // determine exporters
    String[] importerA = BeanFactoryUtils.getTransitiveDependenciesForBean(beanFactory, exporterBeanName, true,
      OsgiServiceProxyFactoryBean.class);

    String[] importerB = BeanFactoryUtils.getTransitiveDependenciesForBean(beanFactory, exporterBeanName, true,
      OsgiServiceCollectionProxyFactoryBean.class);

    String[] importerNames = StringUtils.concatenateStringArrays(importerA, importerB);

    // create map of associated importers
    Map dependingImporters = new LinkedHashMap(importerNames.length);

    if (trace)
      log.trace("Exporter [" + exporterBeanName + "] depends (transitively) on the following importers:"
          + ObjectUtils.nullSafeToString(importerNames));

    // first create a listener for the exporter
    ImporterStateListener listener = new ImporterDependencyListener(exporter);
    exporterListener.put(exporter, listener);

    // exclude non-singletons and non-mandatory importers
    for (int i = 0; i < importerNames.length; i++) {
      if (beanFactory.isSingleton(importerNames[i])) {
        Object importer = beanFactory.getBean(importerNames[i]);

        // create an importer -> exporter association
        if (isSatisfied(importer)) {
          dependingImporters.put(importer, importerNames[i]);
          importerToName.putIfAbsent(importer, importerNames[i]);
        }

        else if (trace)
          log.trace("Importer [" + importerNames[i] + "] is optional; skipping it");
      }
      else if (trace)
        log.trace("Importer [" + importerNames[i] + "] is a non-singleton; ignoring it");
    }

    if (trace)
      log.trace("After filtering, exporter [" + exporterBeanName + "] depends on importers:"
          + dependingImporters.values());

    Collection filteredImporters = dependingImporters.keySet();

    // add the importers and their status to the collection
    synchronized (exporter) {
      Map importerStatuses = new LinkedHashMap(filteredImporters.size());

      for (Iterator iter = filteredImporters.iterator(); iter.hasNext();) {
        Object importer = iter.next();
        importerStatuses.put(importer, Boolean.valueOf(isSatisfied(importer)));
        // add the listener after the importer status has been recorded
        addListener(importer, listener);
      }
      exporterToImporterDeps.put(exporter, importerStatuses);
      checkIfExporterShouldStart(exporter, importerStatuses);
    }
  }

  private void checkIfExporterShouldStart(Object exporter, Map importers) {

    if (!importers.containsValue(Boolean.FALSE)) {
      startExporter(exporter);

      if (log.isDebugEnabled())
        log.trace("Exporter [" + exporterToName.get(exporter) + "] started; all its dependencies are satisfied");
    }
    else {
      List unsatisfiedDependencies = new ArrayList(importers.size());

      for (Iterator iterator = importers.entrySet().iterator(); iterator.hasNext();) {
        Map.Entry entry = (Map.Entry) iterator.next();
        if (Boolean.FALSE.equals(entry.getValue()))
          unsatisfiedDependencies.add(importerToName.get(entry.getKey()));
      }

      if (log.isTraceEnabled()) {
        log.trace("Exporter [" + exporterToName.get(exporter)
            + "] not started; there are still has unsatisfied dependencies " + unsatisfiedDependencies);
      }
    }
  }

  public void removeServiceExporter(Object bean, String beanName) {
    if (log.isTraceEnabled()) {
      log.trace("Removing exporter [" + beanName + "]");
    }

    // remove the exporter and its listeners from the map
    ImporterStateListener stateListener = (ImporterStateListener) exporterListener.remove(bean);

    Map importers;

    synchronized (bean) {
      importers = (Map) exporterToImporterDeps.remove(bean);
    }

    // no need to do synchronization anymore since no other threads will find the collection
    if (importers != null)
      for (Iterator iterator = importers.keySet().iterator(); iterator.hasNext();) {
        Object importer = iterator.next();
        // get associated controller
        removeListener(importer, stateListener);
      }
  }

  public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    Assert.isInstanceOf(ConfigurableListableBeanFactory.class, beanFactory);
    this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
  }

  public void destroy() {
    exportersSeen.clear();
    exporterListener.clear();
    exporterToImporterDeps.clear();
    exporterToName.clear();
    importerToName.clear();
  }

  private void startExporter(Object exporter) {
    ExporterControllerUtils.getControllerFor(exporter).registerService();
  }

  private void stopExporter(Object exporter) {
    ExporterControllerUtils.getControllerFor(exporter).unregisterService();
  }

  private void addListener(Object importer, ImporterStateListener stateListener) {
    ImporterInternalActions controller = ImporterControllerUtils.getControllerFor(importer);
    controller.addStateListener(stateListener);
  }

  private void removeListener(Object importer, ImporterStateListener stateListener) {
    ImporterInternalActions controller = ImporterControllerUtils.getControllerFor(importer);
    controller.removeStateListener(stateListener);
  }

  private boolean isSatisfied(Object importer) {
    return ImporterControllerUtils.getControllerFor(importer).isSatisfied();
  }
}
TOP

Related Classes of org.springframework.osgi.service.dependency.internal.DefaultMandatoryDependencyManager$ImporterDependencyListener

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.