3 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
5 * This program and the accompanying materials are made available under the
6 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
7 * and is available at http://www.eclipse.org/legal/epl-v10.html
10 package org.opendaylight.controller.sal.core;
13 * @file ComponentActivatorAbstractBase.java
15 * @brief Abstract class which need to be subclassed in order to
16 * track and register dependencies per-container
18 * Abstract class which need to be subclassed in order to
19 * track and register dependencies per-container
23 import java.util.Dictionary;
24 import java.util.Hashtable;
25 import java.util.concurrent.ConcurrentHashMap;
26 import java.util.concurrent.ConcurrentMap;
28 import org.apache.commons.lang3.tuple.ImmutablePair;
29 import org.apache.felix.dm.Component;
30 import org.apache.felix.dm.ComponentStateListener;
31 import org.apache.felix.dm.DependencyManager;
32 import org.apache.felix.dm.ServiceDependency;
33 import org.osgi.framework.BundleActivator;
34 import org.osgi.framework.BundleContext;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
39 * Abstract class which need to be subclassed in order to track and
40 * register dependencies per-container
43 abstract public class ComponentActivatorAbstractBase implements
44 BundleActivator, IContainerAware {
45 Logger logger = LoggerFactory
46 .getLogger(ComponentActivatorAbstractBase.class);
47 private DependencyManager dm;
48 private ConcurrentMap<ImmutablePair<String, Object>, Component> dbInstances = new ConcurrentHashMap<ImmutablePair<String, Object>, Component>();
49 private ConcurrentMap<Object, Component> dbGlobalInstances = new ConcurrentHashMap<Object, Component>();
52 * Method that should be overriden by the derived class for customization
53 * during activation of the Component bundle in a container.
55 protected void init() {
60 * Method that should be overriden by the derived class for customization
61 * during DE-activation of the Component bundle in a container.
63 public void destroy() {
68 * Method which tells how many implementations are supported by
69 * the bundle. This way we can tune the number of components
73 * @return The list of implementations the bundle will support,
74 * this will be used to decide how many components need to be
75 * created per-container
77 protected Object[] getImplementations() {
82 * Method which tells how many Global implementations are
83 * supported by the bundle. This way we can tune the number of
84 * components created. This components will be created ONLY at the
85 * time of bundle startup and will be destroyed only at time of
86 * bundle destruction, this is the major difference with the
87 * implementation retrieved via getImplementations where all of
88 * them are assumed to be in a container!
91 * @return The list of implementations the bundle will support,
94 protected Object[] getGlobalImplementations() {
99 * Configure the dependency for a given instance inside a container
101 * @param c Component assigned for this instance, this will be
102 * what will be used for configuration
103 * @param imp implementation to be configured
104 * @param containerName container on which the configuration happens
106 protected void configureInstance(Component c, Object imp,
107 String containerName) {
108 // do nothing by default
112 * Configure the dependency for a given instance Global
114 * @param c Component assigned for this instance, this will be
115 * what will be used for configuration
116 * @param imp implementation to be configured
117 * @param containerName container on which the configuration happens
119 protected void configureGlobalInstance(Component c, Object imp) {
120 // Do nothing by default
123 // Private class used to listen to state transition so we can
124 // implement the necessary logic to call "started" and "stopping"
125 // methods on the component. Right now the framework natively
126 // support only the call of:
127 // - "init": Called after dependency are satisfied
128 // - "start": Called after init but before services are registered
129 // - "stop": Called after services are unregistered but before the
130 // component is going to be destroyed
131 // - "destroy": Called to destroy the component.
132 // There is still a need for two notifications:
133 // - "started" method to be called after "start" and after the
134 // services has been registered in the OSGi service registry
135 // - "stopping" method to be called before "stop" method and
136 // before the services of the component are removed from OSGi
138 class ListenerComponentStates implements ComponentStateListener {
140 public void starting(Component component) {
145 public void started(Component component) {
146 if (component == null) {
149 component.invokeCallbackMethod(new Object[] { component
150 .getService() }, "started", new Class[][] {
151 { Component.class }, {} }, new Object[][] { { component },
156 public void stopped(Component component) {
161 public void stopping(Component component) {
162 if (component == null) {
165 component.invokeCallbackMethod(new Object[] { component
166 .getService() }, "stopping", new Class[][] {
167 { Component.class }, {} }, new Object[][] { { component },
173 * Method of IContainerAware called when a new container is available
175 * @param containerName Container being created
178 public void containerCreate(String containerName) {
180 Object[] imps = getImplementations();
181 logger.trace("Creating instance {}", containerName);
183 for (int i = 0; i < imps.length; i++) {
184 ImmutablePair<String, Object> key = new ImmutablePair<String, Object>(
185 containerName, imps[i]);
186 Component c = this.dbInstances.get(key);
188 c = this.dm.createComponent();
189 c.addStateListener(new ListenerComponentStates());
190 // Now let the derived class to configure the
191 // dependencies it wants
192 configureInstance(c, imps[i], containerName);
193 // Set the implementation so the component can manage
195 if (c.getService() == null) {
196 logger.trace("Setting implementation to: {}",
198 c.setImplementation(imps[i]);
201 //Set the service properties to include the containerName
202 //in the service, that is fundamental for supporting
203 //multiple services just distinguished via a container
204 @SuppressWarnings("unchecked")
205 Dictionary<String, String> serviceProps = c
206 .getServiceProperties();
207 if (serviceProps != null) {
208 logger.trace("Adding new property for container");
209 serviceProps.put("containerName", containerName);
212 .trace("Create a new properties for the service");
213 serviceProps = new Hashtable<String, String>();
214 serviceProps.put("containerName", containerName);
216 c.setServiceProperties(serviceProps);
218 // Now add the component to the dependency Manager
219 // which will immediately start tracking the dependencies
222 //Now lets keep track in our shadow database of the
224 this.dbInstances.put(key, c);
227 .error("I have been asked again to create an instance "
232 + "when i already have it!!");
236 } catch (Exception ex) {
238 .error("During containerDestroy invocation caught exception: "
241 + stackToString(ex.getStackTrace()));
246 public void containerDestroy(String containerName) {
248 Object[] imps = getImplementations();
249 logger.trace("Destroying instance {}", containerName);
251 for (int i = 0; i < imps.length; i++) {
252 ImmutablePair<String, Object> key = new ImmutablePair<String, Object>(
253 containerName, imps[i]);
254 Component c = this.dbInstances.get(key);
256 if (c.getService() != null) {
257 c.invokeCallbackMethod(new Object[] { c.getService() }, "containerStop",
258 new Class[][] {{ Component.class}, {} },
259 new Object[][] { {c}, {} });
261 // Now remove the component from dependency manager,
262 // which will implicitely stop it first
266 .error("I have been asked again to remove an instance "
271 + "when i already have cleared it!!");
274 //Now lets remove the association from our shadow
275 //database so the component can be recycled, this is done
276 //unconditionally in case of spurious conditions
277 this.dbInstances.remove(key);
280 } catch (Exception ex) {
282 .error("During containerDestroy invocation caught exception: "
285 + stackToString(ex.getStackTrace()));
289 private String stackToString(StackTraceElement[] stack) {
291 return "<EmptyStack>";
293 StringBuffer buffer = new StringBuffer();
295 for (int i = 0; i < stack.length; i++) {
296 buffer.append("\n\t").append(stack[i].toString());
298 return buffer.toString();
302 * Method called by the OSGi framework when the OSGi bundle
303 * starts. The functionality we want to perform here are:
305 * 1) Register with the OSGi framework, that we are a provider of
306 * IContainerAware service and so in case of startup of a container we
309 * 2) Create data structures that allow to keep track of all the
310 * instances created per-container given the derived class of
311 * ComponentActivatorAbstractBase will act as a Factory manager
313 * @param context OSGi bundle context to interact with OSGi framework
316 public void start(BundleContext context) {
318 this.dm = new DependencyManager(context);
320 logger.trace("Activating");
322 // Now create Global components
323 Object[] imps = getGlobalImplementations();
325 for (int i = 0; i < imps.length; i++) {
326 Object key = imps[i];
327 Component c = this.dbGlobalInstances.get(key);
330 c = this.dm.createComponent();
331 c.addStateListener(new ListenerComponentStates());
332 // Now let the derived class to configure the
333 // dependencies it wants
334 configureGlobalInstance(c, imps[i]);
335 // Set the implementation so the component
336 // can manage its lifesycle
337 if (c.getService() == null) {
338 logger.trace("Setting implementation to: {}",
340 c.setImplementation(imps[i]);
343 // Now add the component to the dependency
344 // Manager which will immediately start
345 // tracking the dependencies
347 } catch (Exception nex) {
348 logger.error("During creation of a Global "
349 + "instance caught exception: " + nex
351 + stackToString(nex.getStackTrace()));
354 //Now lets keep track in our shadow database of the
357 this.dbGlobalInstances.put(key, c);
359 logger.error("I have been asked again to create an "
360 + "instance " + " Global for object: "
361 + imps[i] + "when i already have it!!");
366 // Register with OSGi the provider for the service IContainerAware
367 context.registerService(
368 IContainerAware.class.getName(), this, null);
370 // Now call the derived class init function
373 logger.trace("Activation DONE!");
374 } catch (Exception ex) {
375 logger.error("During Activator start caught exception: " + ex
376 + "\nStacktrace:" + stackToString(ex.getStackTrace()));
381 * Method called by the OSGi framework when the OSGi bundle
382 * stops. The functionality we want to perform here are:
384 * 1) Force all the instances to stop and do cleanup and
385 * unreference them so garbage collection can clean them up
387 * NOTE: UN-Register with the OSGi framework,is not needed because
388 * the framework will automatically do it
390 * @param context OSGi bundle context to interact with OSGi framework
393 public void stop(BundleContext context) {
395 logger.trace("DE-Activating");
397 // Now call the derived class destroy function
400 // Now remove all the components tracked for container components
401 for (ImmutablePair<String, Object> key : this.dbInstances.keySet()) {
403 Component c = this.dbInstances.get(key);
405 logger.trace("Remove component on container: {} Object: {}",
406 key.getLeft(), key.getRight());
409 } catch (Exception nex) {
410 logger.error("During removal of a container component "
411 + "instance caught exception: " + nex
413 + stackToString(nex.getStackTrace()));
415 this.dbInstances.remove(key);
418 // Now remove all the components tracked for Global Components
419 for (Object key : this.dbGlobalInstances.keySet()) {
421 Component c = this.dbGlobalInstances.get(key);
423 logger.trace("Remove component for Object: {}" , key);
426 } catch (Exception nex) {
427 logger.error("During removal of a Global "
428 + "instance caught exception: " + nex
430 + stackToString(nex.getStackTrace()));
433 this.dbGlobalInstances.remove(key);
436 // Detach Dependency Manager
439 logger.trace("Deactivation DONE!");
440 } catch (Exception ex) {
441 logger.error("During Activator stop caught exception: " + ex
442 + "\nStacktrace:" + stackToString(ex.getStackTrace()));
447 * Return a ServiceDependency customized ad hoc for slicing, this
448 * essentially the same org.apache.felix.dm.ServiceDependency just
449 * with some filters pre-set
451 * @param containerName containerName for which we want to create the dependency
453 * @return a ServiceDependency
455 protected ServiceDependency createContainerServiceDependency(
456 String containerName) {
457 return (new ContainerServiceDependency(this.dm, containerName));
461 * Return a ServiceDependency as provided by Dependency Manager as it's
464 * @return a ServiceDependency
466 protected ServiceDependency createServiceDependency() {
467 return this.dm.createServiceDependency();