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.osgi.framework.ServiceRegistration;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
40 * Abstract class which need to be subclassed in order to track and
41 * register dependencies per-container
44 abstract public class ComponentActivatorAbstractBase implements
45 BundleActivator, IContainerAware {
46 Logger logger = LoggerFactory
47 .getLogger(ComponentActivatorAbstractBase.class);
48 private DependencyManager dm;
49 private ConcurrentMap<ImmutablePair<String, Object>, Component> dbInstances = (ConcurrentMap<ImmutablePair<String, Object>, Component>) new ConcurrentHashMap<ImmutablePair<String, Object>, Component>();
50 private ConcurrentMap<Object, Component> dbGlobalInstances = (ConcurrentMap<Object, Component>) new ConcurrentHashMap<Object, Component>();
53 * Abstract method that MUST be implemented by the derived class
54 * that wants to activate the Component bundle in a container. Here
55 * customization for the component are expected
57 abstract protected void init();
60 * Abstract method that MUST be implemented by the derived class
61 * that wants to DE-activate the Component bundle in a container. Here
62 * customization for the component are expected
64 abstract protected void destroy();
67 * Method which tells how many implementations are supported by
68 * the bundle. This way we can tune the number of components
72 * @return The list of implementations the bundle will support,
73 * this will be used to decide how many components need to be
74 * created per-container
76 protected Object[] getImplementations() {
81 * Method which tells how many Global implementations are
82 * supported by the bundle. This way we can tune the number of
83 * components created. This components will be created ONLY at the
84 * time of bundle startup and will be destroyed only at time of
85 * bundle destruction, this is the major difference with the
86 * implementation retrieved via getImplementations where all of
87 * them are assumed to be in a container!
90 * @return The list of implementations the bundle will support,
93 protected Object[] getGlobalImplementations() {
98 * Configure the dependency for a given instance inside a container
100 * @param c Component assigned for this instance, this will be
101 * what will be used for configuration
102 * @param imp implementation to be configured
103 * @param containerName container on which the configuration happens
105 protected void configureInstance(Component c, Object imp,
106 String containerName) {
107 // do nothing by default
111 * Configure the dependency for a given instance Global
113 * @param c Component assigned for this instance, this will be
114 * what will be used for configuration
115 * @param imp implementation to be configured
116 * @param containerName container on which the configuration happens
118 protected void configureGlobalInstance(Component c, Object imp) {
119 // Do nothing by default
122 // Private class used to listen to state transition so we can
123 // implement the necessary logic to call "started" and "stopping"
124 // methods on the component. Right now the framework natively
125 // support only the call of:
126 // - "init": Called after dependency are satisfied
127 // - "start": Called after init but before services are registered
128 // - "stop": Called after services are unregistered but before the
129 // component is going to be destroyed
130 // - "destroy": Called to destroy the component.
131 // There is still a need for two notifications:
132 // - "started" method to be called after "start" and after the
133 // services has been registered in the OSGi service registry
134 // - "stopping" method to be called before "stop" method and
135 // before the services of the component are removed from OSGi
137 class ListenerComponentStates implements ComponentStateListener {
139 public void starting(Component component) {
144 public void started(Component component) {
145 if (component == null) {
148 component.invokeCallbackMethod(new Object[] { component
149 .getService() }, "started", new Class[][] {
150 { Component.class }, {} }, new Object[][] { { component },
155 public void stopped(Component component) {
156 if (component == null) {
159 component.invokeCallbackMethod(new Object[] { component
160 .getService() }, "stopping", new Class[][] {
161 { Component.class }, {} }, new Object[][] { { component },
166 public void stopping(Component component) {
172 * Method of IContainerAware called when a new container is available
174 * @param containerName Container being created
177 public void containerCreate(String containerName) {
179 Object[] imps = getImplementations();
180 logger.trace("Creating instance {}", containerName);
182 for (int i = 0; i < imps.length; i++) {
183 ImmutablePair<String, Object> key = new ImmutablePair<String, Object>(
184 containerName, imps[i]);
185 Component c = this.dbInstances.get(key);
187 c = this.dm.createComponent();
188 c.addStateListener(new ListenerComponentStates());
189 // Now let the derived class to configure the
190 // dependencies it wants
191 configureInstance(c, imps[i], containerName);
192 // Set the implementation so the component can manage
194 if (c.getService() == null) {
195 logger.trace("Setting implementation to: {}",
197 c.setImplementation(imps[i]);
200 //Set the service properties to include the containerName
201 //in the service, that is fundamental for supporting
202 //multiple services just distinguished via a container
203 Dictionary<String, String> serviceProps = c
204 .getServiceProperties();
205 if (serviceProps != null) {
206 logger.trace("Adding new property for container");
207 serviceProps.put("containerName", containerName);
210 .trace("Create a new properties for the service");
211 serviceProps = new Hashtable<String, String>();
212 serviceProps.put("containerName", containerName);
214 c.setServiceProperties(serviceProps);
216 // Now add the component to the dependency Manager
217 // which will immediately start tracking the dependencies
220 //Now lets keep track in our shadow database of the
222 this.dbInstances.put(key, c);
225 .error("I have been asked again to create an instance "
230 + "when i already have it!!");
234 } catch (Exception ex) {
236 .error("During containerDestroy invocation caught exception: "
239 + stackToString(ex.getStackTrace()));
244 public void containerDestroy(String containerName) {
246 Object[] imps = getImplementations();
247 logger.trace("Destroying instance {}", containerName);
249 for (int i = 0; i < imps.length; i++) {
250 ImmutablePair<String, Object> key = new ImmutablePair<String, Object>(
251 containerName, imps[i]);
252 Component c = this.dbInstances.get(key);
254 // Now remove the component from dependency manager,
255 // which will implicitely stop it first
259 .error("I have been asked again to remove an instance "
264 + "when i already have cleared it!!");
267 //Now lets remove the association from our shadow
268 //database so the component can be recycled, this is done
269 //unconditionally in case of spurious conditions
270 this.dbInstances.remove(key);
273 } catch (Exception ex) {
275 .error("During containerDestroy invocation caught exception: "
278 + stackToString(ex.getStackTrace()));
282 private String stackToString(StackTraceElement[] stack) {
284 return "<EmptyStack>";
286 StringBuffer buffer = new StringBuffer();
288 for (int i = 0; i < stack.length; i++) {
289 buffer.append("\n\t" + stack[i].toString());
291 return buffer.toString();
295 * Method called by the OSGi framework when the OSGi bundle
296 * starts. The functionality we want to perform here are:
298 * 1) Register with the OSGi framework, that we are a provider of
299 * IContainerAware service and so in case of startup of a container we
302 * 2) Create data structures that allow to keep track of all the
303 * instances created per-container given the derived class of
304 * ComponentActivatorAbstractBase will act as a Factory manager
306 * @param context OSGi bundle context to interact with OSGi framework
309 public void start(BundleContext context) {
311 this.dm = new DependencyManager(context);
313 logger.trace("Activating");
315 // Now create Global components
316 Object[] imps = getGlobalImplementations();
318 for (int i = 0; i < imps.length; i++) {
319 Object key = imps[i];
320 Component c = this.dbGlobalInstances.get(key);
323 c = this.dm.createComponent();
324 c.addStateListener(new ListenerComponentStates());
325 // Now let the derived class to configure the
326 // dependencies it wants
327 configureGlobalInstance(c, imps[i]);
328 // Set the implementation so the component
329 // can manage its lifesycle
330 if (c.getService() == null) {
331 logger.trace("Setting implementation to: {}",
333 c.setImplementation(imps[i]);
336 // Now add the component to the dependency
337 // Manager which will immediately start
338 // tracking the dependencies
340 } catch (Exception nex) {
341 logger.error("During creation of a Global "
342 + "instance caught exception: " + nex
344 + stackToString(nex.getStackTrace()));
347 //Now lets keep track in our shadow database of the
350 this.dbGlobalInstances.put(key, c);
352 logger.error("I have been asked again to create an "
353 + "instance " + " Global for object: "
354 + imps[i] + "when i already have it!!");
359 // Register with OSGi the provider for the service IContainerAware
360 context.registerService(
361 IContainerAware.class.getName(), this, null);
363 // Now call the derived class init function
366 logger.trace("Activation DONE!");
367 } catch (Exception ex) {
368 logger.error("During Activator start caught exception: " + ex
369 + "\nStacktrace:" + stackToString(ex.getStackTrace()));
374 * Method called by the OSGi framework when the OSGi bundle
375 * stops. The functionality we want to perform here are:
377 * 1) Force all the instances to stop and do cleanup and
378 * unreference them so garbage collection can clean them up
380 * NOTE: UN-Register with the OSGi framework,is not needed because
381 * the framework will automatically do it
383 * @param context OSGi bundle context to interact with OSGi framework
386 public void stop(BundleContext context) {
388 logger.trace("DE-Activating");
390 // Now call the derived class destroy function
393 // Now remove all the components tracked for container components
394 for (ImmutablePair<String, Object> key : this.dbInstances.keySet()) {
396 Component c = this.dbInstances.get(key);
398 logger.trace("Remove component on container: {} Object: {}",
399 key.getLeft(), key.getRight());
402 } catch (Exception nex) {
403 logger.error("During removal of a container component "
404 + "instance caught exception: " + nex
406 + stackToString(nex.getStackTrace()));
408 this.dbInstances.remove(key);
411 // Now remove all the components tracked for Global Components
412 for (Object key : this.dbGlobalInstances.keySet()) {
414 Component c = this.dbGlobalInstances.get(key);
416 logger.trace("Remove component for Object: {}" , key);
419 } catch (Exception nex) {
420 logger.error("During removal of a Global "
421 + "instance caught exception: " + nex
423 + stackToString(nex.getStackTrace()));
426 this.dbGlobalInstances.remove(key);
429 // Detach Dependency Manager
432 logger.trace("Deactivation DONE!");
433 } catch (Exception ex) {
434 logger.error("During Activator stop caught exception: " + ex
435 + "\nStacktrace:" + stackToString(ex.getStackTrace()));
440 * Return a ServiceDependency customized ad hoc for slicing, this
441 * essentially the same org.apache.felix.dm.ServiceDependency just
442 * with some filters pre-set
444 * @param containerName containerName for which we want to create the dependency
446 * @return a ServiceDependency
448 protected ServiceDependency createContainerServiceDependency(
449 String containerName) {
450 return (new ContainerServiceDependency(this.dm, containerName));
454 * Return a ServiceDependency as provided by Dependency Manager as it's
457 * @return a ServiceDependency
459 protected ServiceDependency createServiceDependency() {
460 return this.dm.createServiceDependency();