+++ /dev/null
-
-/*
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,
- * and is available at http://www.eclipse.org/legal/epl-v10.html
- */
-
-package org.opendaylight.controller.sal.core;
-
-/**
- * @file ComponentActivatorAbstractBase.java
- *
- * @brief Abstract class which need to be subclassed in order to
- * track and register dependencies per-container
- *
- * Abstract class which need to be subclassed in order to
- * track and register dependencies per-container
- *
- */
-
-import java.util.Dictionary;
-import java.util.Hashtable;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import org.apache.commons.lang3.tuple.ImmutablePair;
-import org.apache.felix.dm.Component;
-import org.apache.felix.dm.ComponentStateListener;
-import org.apache.felix.dm.DependencyManager;
-import org.apache.felix.dm.ServiceDependency;
-import org.osgi.framework.BundleActivator;
-import org.osgi.framework.BundleContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Abstract class which need to be subclassed in order to track and
- * register dependencies per-container
- *
- */
-abstract public class ComponentActivatorAbstractBase implements
- BundleActivator, IContainerAware {
- Logger logger = LoggerFactory
- .getLogger(ComponentActivatorAbstractBase.class);
- private DependencyManager dm;
- private ConcurrentMap<ImmutablePair<String, Object>, Component> dbInstances = new ConcurrentHashMap<ImmutablePair<String, Object>, Component>();
- private ConcurrentMap<Object, Component> dbGlobalInstances = new ConcurrentHashMap<Object, Component>();
-
- /**
- * Method that should be overriden by the derived class for customization
- * during activation of the Component bundle in a container.
- */
- protected void init() {
-
- }
-
- /**
- * Method that should be overriden by the derived class for customization
- * during DE-activation of the Component bundle in a container.
- */
- public void destroy() {
-
- }
-
- /**
- * Method which tells how many implementations are supported by
- * the bundle. This way we can tune the number of components
- * created.
- *
- *
- * @return The list of implementations the bundle will support,
- * this will be used to decide how many components need to be
- * created per-container
- */
- protected Object[] getImplementations() {
- return null;
- }
-
- /**
- * Method which tells how many Global implementations are
- * supported by the bundle. This way we can tune the number of
- * components created. This components will be created ONLY at the
- * time of bundle startup and will be destroyed only at time of
- * bundle destruction, this is the major difference with the
- * implementation retrieved via getImplementations where all of
- * them are assumed to be in a container!
- *
- *
- * @return The list of implementations the bundle will support,
- * in Global version
- */
- protected Object[] getGlobalImplementations() {
- return null;
- }
-
- /**
- * Configure the dependency for a given instance inside a container
- *
- * @param c Component assigned for this instance, this will be
- * what will be used for configuration
- * @param imp implementation to be configured
- * @param containerName container on which the configuration happens
- */
- protected void configureInstance(Component c, Object imp,
- String containerName) {
- // do nothing by default
- }
-
- /**
- * Configure the dependency for a given instance Global
- *
- * @param c Component assigned for this instance, this will be
- * what will be used for configuration
- * @param imp implementation to be configured
- * @param containerName container on which the configuration happens
- */
- protected void configureGlobalInstance(Component c, Object imp) {
- // Do nothing by default
- }
-
- // Private class used to listen to state transition so we can
- // implement the necessary logic to call "started" and "stopping"
- // methods on the component. Right now the framework natively
- // support only the call of:
- // - "init": Called after dependency are satisfied
- // - "start": Called after init but before services are registered
- // - "stop": Called after services are unregistered but before the
- // component is going to be destroyed
- // - "destroy": Called to destroy the component.
- // There is still a need for two notifications:
- // - "started" method to be called after "start" and after the
- // services has been registered in the OSGi service registry
- // - "stopping" method to be called before "stop" method and
- // before the services of the component are removed from OSGi
- // service registry
- class ListenerComponentStates implements ComponentStateListener {
- @Override
- public void starting(Component component) {
- // do nothing
- }
-
- @Override
- public void started(Component component) {
- if (component == null) {
- return;
- }
- component.invokeCallbackMethod(new Object[] { component
- .getService() }, "started", new Class[][] {
- { Component.class }, {} }, new Object[][] { { component },
- {} });
- }
-
- @Override
- public void stopped(Component component) {
- // do nothing
- }
-
- @Override
- public void stopping(Component component) {
- if (component == null) {
- return;
- }
- component.invokeCallbackMethod(new Object[] { component
- .getService() }, "stopping", new Class[][] {
- { Component.class }, {} }, new Object[][] { { component },
- {} });
- }
- }
-
- /**
- * Method of IContainerAware called when a new container is available
- *
- * @param containerName Container being created
- */
- @Override
- public void containerCreate(String containerName) {
- try {
- Object[] imps = getImplementations();
- logger.trace("Creating instance {}", containerName);
- if (imps != null) {
- for (int i = 0; i < imps.length; i++) {
- ImmutablePair<String, Object> key = new ImmutablePair<String, Object>(
- containerName, imps[i]);
- Component c = this.dbInstances.get(key);
- if (c == null) {
- c = this.dm.createComponent();
- c.addStateListener(new ListenerComponentStates());
- // Now let the derived class to configure the
- // dependencies it wants
- configureInstance(c, imps[i], containerName);
- // Set the implementation so the component can manage
- // its lifecycle
- if (c.getService() == null) {
- logger.trace("Setting implementation to: {}",
- imps[i]);
- c.setImplementation(imps[i]);
- }
-
- //Set the service properties to include the containerName
- //in the service, that is fundamental for supporting
- //multiple services just distinguished via a container
- @SuppressWarnings("unchecked")
- Dictionary<String, String> serviceProps = c
- .getServiceProperties();
- if (serviceProps != null) {
- logger.trace("Adding new property for container");
- serviceProps.put("containerName", containerName);
- } else {
- logger
- .trace("Create a new properties for the service");
- serviceProps = new Hashtable<String, String>();
- serviceProps.put("containerName", containerName);
- }
- c.setServiceProperties(serviceProps);
-
- // Now add the component to the dependency Manager
- // which will immediately start tracking the dependencies
- this.dm.add(c);
-
- //Now lets keep track in our shadow database of the
- //association
- this.dbInstances.put(key, c);
- } else {
- logger
- .error("I have been asked again to create an instance "
- + "on: "
- + containerName
- + "for object: "
- + imps[i]
- + "when i already have it!!");
- }
- }
- }
- } catch (Exception ex) {
- logger
- .error("During containerDestroy invocation caught exception: "
- + ex
- + "\nStacktrace:"
- + stackToString(ex.getStackTrace()));
- }
- }
-
- @Override
- public void containerDestroy(String containerName) {
- try {
- Object[] imps = getImplementations();
- logger.trace("Destroying instance {}", containerName);
- if (imps != null) {
- for (int i = 0; i < imps.length; i++) {
- ImmutablePair<String, Object> key = new ImmutablePair<String, Object>(
- containerName, imps[i]);
- Component c = this.dbInstances.get(key);
- if (c != null) {
- if (c.getService() != null) {
- c.invokeCallbackMethod(new Object[] { c.getService() }, "containerStop",
- new Class[][] {{ Component.class}, {} },
- new Object[][] { {c}, {} });
- }
- // Now remove the component from dependency manager,
- // which will implicitely stop it first
- this.dm.remove(c);
- } else {
- logger
- .error("I have been asked again to remove an instance "
- + "on: "
- + containerName
- + "for object: "
- + imps[i]
- + "when i already have cleared it!!");
- }
-
- //Now lets remove the association from our shadow
- //database so the component can be recycled, this is done
- //unconditionally in case of spurious conditions
- this.dbInstances.remove(key);
- }
- }
- } catch (Exception ex) {
- logger
- .error("During containerDestroy invocation caught exception: "
- + ex
- + "\nStacktrace:"
- + stackToString(ex.getStackTrace()));
- }
- }
-
- private String stackToString(StackTraceElement[] stack) {
- if (stack == null) {
- return "<EmptyStack>";
- }
- StringBuffer buffer = new StringBuffer();
-
- for (int i = 0; i < stack.length; i++) {
- buffer.append("\n\t").append(stack[i].toString());
- }
- return buffer.toString();
- }
-
- /**
- * Method called by the OSGi framework when the OSGi bundle
- * starts. The functionality we want to perform here are:
- *
- * 1) Register with the OSGi framework, that we are a provider of
- * IContainerAware service and so in case of startup of a container we
- * want to be called
- *
- * 2) Create data structures that allow to keep track of all the
- * instances created per-container given the derived class of
- * ComponentActivatorAbstractBase will act as a Factory manager
- *
- * @param context OSGi bundle context to interact with OSGi framework
- */
- @Override
- public void start(BundleContext context) {
- try {
- this.dm = new DependencyManager(context);
-
- logger.trace("Activating");
-
- // Now create Global components
- Object[] imps = getGlobalImplementations();
- if (imps != null) {
- for (int i = 0; i < imps.length; i++) {
- Object key = imps[i];
- Component c = this.dbGlobalInstances.get(key);
- if (c == null) {
- try {
- c = this.dm.createComponent();
- c.addStateListener(new ListenerComponentStates());
- // Now let the derived class to configure the
- // dependencies it wants
- configureGlobalInstance(c, imps[i]);
- // Set the implementation so the component
- // can manage its lifesycle
- if (c.getService() == null) {
- logger.trace("Setting implementation to: {}",
- imps[i]);
- c.setImplementation(imps[i]);
- }
-
- // Now add the component to the dependency
- // Manager which will immediately start
- // tracking the dependencies
- this.dm.add(c);
- } catch (Exception nex) {
- logger.error("During creation of a Global "
- + "instance caught exception: " + nex
- + "\nStacktrace:"
- + stackToString(nex.getStackTrace()));
- }
-
- //Now lets keep track in our shadow database of the
- //association
- if (c != null)
- this.dbGlobalInstances.put(key, c);
- } else {
- logger.error("I have been asked again to create an "
- + "instance " + " Global for object: "
- + imps[i] + "when i already have it!!");
- }
- }
- }
-
- // Register with OSGi the provider for the service IContainerAware
- context.registerService(
- IContainerAware.class.getName(), this, null);
-
- // Now call the derived class init function
- this.init();
-
- logger.trace("Activation DONE!");
- } catch (Exception ex) {
- logger.error("During Activator start caught exception: " + ex
- + "\nStacktrace:" + stackToString(ex.getStackTrace()));
- }
- }
-
- /**
- * Method called by the OSGi framework when the OSGi bundle
- * stops. The functionality we want to perform here are:
- *
- * 1) Force all the instances to stop and do cleanup and
- * unreference them so garbage collection can clean them up
- *
- * NOTE: UN-Register with the OSGi framework,is not needed because
- * the framework will automatically do it
- *
- * @param context OSGi bundle context to interact with OSGi framework
- */
- @Override
- public void stop(BundleContext context) {
- try {
- logger.trace("DE-Activating");
-
- // Now call the derived class destroy function
- this.destroy();
-
- // Now remove all the components tracked for container components
- for (ImmutablePair<String, Object> key : this.dbInstances.keySet()) {
- try {
- Component c = this.dbInstances.get(key);
- if (c != null) {
- logger.trace("Remove component on container: {} Object: {}",
- key.getLeft(), key.getRight());
- this.dm.remove(c);
- }
- } catch (Exception nex) {
- logger.error("During removal of a container component "
- + "instance caught exception: " + nex
- + "\nStacktrace:"
- + stackToString(nex.getStackTrace()));
- }
- this.dbInstances.remove(key);
- }
-
- // Now remove all the components tracked for Global Components
- for (Object key : this.dbGlobalInstances.keySet()) {
- try {
- Component c = this.dbGlobalInstances.get(key);
- if (c != null) {
- logger.trace("Remove component for Object: {}" , key);
- this.dm.remove(c);
- }
- } catch (Exception nex) {
- logger.error("During removal of a Global "
- + "instance caught exception: " + nex
- + "\nStacktrace:"
- + stackToString(nex.getStackTrace()));
- }
-
- this.dbGlobalInstances.remove(key);
- }
-
- // Detach Dependency Manager
- this.dm = null;
-
- logger.trace("Deactivation DONE!");
- } catch (Exception ex) {
- logger.error("During Activator stop caught exception: " + ex
- + "\nStacktrace:" + stackToString(ex.getStackTrace()));
- }
- }
-
- /**
- * Return a ServiceDependency customized ad hoc for slicing, this
- * essentially the same org.apache.felix.dm.ServiceDependency just
- * with some filters pre-set
- *
- * @param containerName containerName for which we want to create the dependency
- *
- * @return a ServiceDependency
- */
- protected ServiceDependency createContainerServiceDependency(
- String containerName) {
- return (new ContainerServiceDependency(this.dm, containerName));
- }
-
- /**
- * Return a ServiceDependency as provided by Dependency Manager as it's
- *
- *
- * @return a ServiceDependency
- */
- protected ServiceDependency createServiceDependency() {
- return this.dm.createServiceDependency();
- }
-}