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
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 = new ConcurrentHashMap<ImmutablePair<String, Object>, Component>();
50 private ConcurrentMap<Object, Component> dbGlobalInstances = new ConcurrentHashMap<Object, Component>();
53 * Method that should be overriden by the derived class for customization
54 * during activation of the Component bundle in a container.
56 protected void init() {
61 * Method that should be overriden by the derived class for customization
62 * during DE-activation of the Component bundle in a container.
64 public void destroy() {
69 * Method which tells how many implementations are supported by
70 * the bundle. This way we can tune the number of components
74 * @return The list of implementations the bundle will support,
75 * this will be used to decide how many components need to be
76 * created per-container
78 protected Object[] getImplementations() {
83 * Method which tells how many Global implementations are
84 * supported by the bundle. This way we can tune the number of
85 * components created. This components will be created ONLY at the
86 * time of bundle startup and will be destroyed only at time of
87 * bundle destruction, this is the major difference with the
88 * implementation retrieved via getImplementations where all of
89 * them are assumed to be in a container!
92 * @return The list of implementations the bundle will support,
95 protected Object[] getGlobalImplementations() {
100 * Configure the dependency for a given instance inside a container
102 * @param c Component assigned for this instance, this will be
103 * what will be used for configuration
104 * @param imp implementation to be configured
105 * @param containerName container on which the configuration happens
107 protected void configureInstance(Component c, Object imp,
108 String containerName) {
109 // do nothing by default
113 * Configure the dependency for a given instance Global
115 * @param c Component assigned for this instance, this will be
116 * what will be used for configuration
117 * @param imp implementation to be configured
118 * @param containerName container on which the configuration happens
120 protected void configureGlobalInstance(Component c, Object imp) {
121 // Do nothing by default
124 // Private class used to listen to state transition so we can
125 // implement the necessary logic to call "started" and "stopping"
126 // methods on the component. Right now the framework natively
127 // support only the call of:
128 // - "init": Called after dependency are satisfied
129 // - "start": Called after init but before services are registered
130 // - "stop": Called after services are unregistered but before the
131 // component is going to be destroyed
132 // - "destroy": Called to destroy the component.
133 // There is still a need for two notifications:
134 // - "started" method to be called after "start" and after the
135 // services has been registered in the OSGi service registry
136 // - "stopping" method to be called before "stop" method and
137 // before the services of the component are removed from OSGi
139 class ListenerComponentStates implements ComponentStateListener {
141 public void starting(Component component) {
146 public void started(Component component) {
147 if (component == null) {
150 component.invokeCallbackMethod(new Object[] { component
151 .getService() }, "started", new Class[][] {
152 { Component.class }, {} }, new Object[][] { { component },
157 public void stopped(Component component) {
162 public void stopping(Component component) {
163 if (component == null) {
166 component.invokeCallbackMethod(new Object[] { component
167 .getService() }, "stopping", new Class[][] {
168 { Component.class }, {} }, new Object[][] { { component },
174 * Method of IContainerAware called when a new container is available
176 * @param containerName Container being created
179 public void containerCreate(String containerName) {
181 Object[] imps = getImplementations();
182 logger.trace("Creating instance {}", containerName);
184 for (int i = 0; i < imps.length; i++) {
185 ImmutablePair<String, Object> key = new ImmutablePair<String, Object>(
186 containerName, imps[i]);
187 Component c = this.dbInstances.get(key);
189 c = this.dm.createComponent();
190 c.addStateListener(new ListenerComponentStates());
191 // Now let the derived class to configure the
192 // dependencies it wants
193 configureInstance(c, imps[i], containerName);
194 // Set the implementation so the component can manage
196 if (c.getService() == null) {
197 logger.trace("Setting implementation to: {}",
199 c.setImplementation(imps[i]);
202 //Set the service properties to include the containerName
203 //in the service, that is fundamental for supporting
204 //multiple services just distinguished via a container
205 @SuppressWarnings("unchecked")
206 Dictionary<String, String> serviceProps = c
207 .getServiceProperties();
208 if (serviceProps != null) {
209 logger.trace("Adding new property for container");
210 serviceProps.put("containerName", containerName);
213 .trace("Create a new properties for the service");
214 serviceProps = new Hashtable<String, String>();
215 serviceProps.put("containerName", containerName);
217 c.setServiceProperties(serviceProps);
219 // Now add the component to the dependency Manager
220 // which will immediately start tracking the dependencies
223 //Now lets keep track in our shadow database of the
225 this.dbInstances.put(key, c);
228 .error("I have been asked again to create an instance "
233 + "when i already have it!!");
237 } catch (Exception ex) {
239 .error("During containerDestroy invocation caught exception: "
242 + stackToString(ex.getStackTrace()));
247 public void containerDestroy(String containerName) {
249 Object[] imps = getImplementations();
250 logger.trace("Destroying instance {}", containerName);
252 for (int i = 0; i < imps.length; i++) {
253 ImmutablePair<String, Object> key = new ImmutablePair<String, Object>(
254 containerName, imps[i]);
255 Component c = this.dbInstances.get(key);
257 if (c.getService() != null) {
258 c.invokeCallbackMethod(new Object[] { c.getService() }, "containerStop",
259 new Class[][] {{ Component.class}, {} },
260 new Object[][] { {c}, {} });
262 // Now remove the component from dependency manager,
263 // which will implicitely stop it first
267 .error("I have been asked again to remove an instance "
272 + "when i already have cleared it!!");
275 //Now lets remove the association from our shadow
276 //database so the component can be recycled, this is done
277 //unconditionally in case of spurious conditions
278 this.dbInstances.remove(key);
281 } catch (Exception ex) {
283 .error("During containerDestroy invocation caught exception: "
286 + stackToString(ex.getStackTrace()));
290 private String stackToString(StackTraceElement[] stack) {
292 return "<EmptyStack>";
294 StringBuffer buffer = new StringBuffer();
296 for (int i = 0; i < stack.length; i++) {
297 buffer.append("\n\t").append(stack[i].toString());
299 return buffer.toString();
303 * Method called by the OSGi framework when the OSGi bundle
304 * starts. The functionality we want to perform here are:
306 * 1) Register with the OSGi framework, that we are a provider of
307 * IContainerAware service and so in case of startup of a container we
310 * 2) Create data structures that allow to keep track of all the
311 * instances created per-container given the derived class of
312 * ComponentActivatorAbstractBase will act as a Factory manager
314 * @param context OSGi bundle context to interact with OSGi framework
317 public void start(BundleContext context) {
319 this.dm = new DependencyManager(context);
321 logger.trace("Activating");
323 // Now create Global components
324 Object[] imps = getGlobalImplementations();
326 for (int i = 0; i < imps.length; i++) {
327 Object key = imps[i];
328 Component c = this.dbGlobalInstances.get(key);
331 c = this.dm.createComponent();
332 c.addStateListener(new ListenerComponentStates());
333 // Now let the derived class to configure the
334 // dependencies it wants
335 configureGlobalInstance(c, imps[i]);
336 // Set the implementation so the component
337 // can manage its lifesycle
338 if (c.getService() == null) {
339 logger.trace("Setting implementation to: {}",
341 c.setImplementation(imps[i]);
344 // Now add the component to the dependency
345 // Manager which will immediately start
346 // tracking the dependencies
348 } catch (Exception nex) {
349 logger.error("During creation of a Global "
350 + "instance caught exception: " + nex
352 + stackToString(nex.getStackTrace()));
355 //Now lets keep track in our shadow database of the
358 this.dbGlobalInstances.put(key, c);
360 logger.error("I have been asked again to create an "
361 + "instance " + " Global for object: "
362 + imps[i] + "when i already have it!!");
367 // Register with OSGi the provider for the service IContainerAware
368 context.registerService(
369 IContainerAware.class.getName(), this, null);
371 // Now call the derived class init function
374 logger.trace("Activation DONE!");
375 } catch (Exception ex) {
376 logger.error("During Activator start caught exception: " + ex
377 + "\nStacktrace:" + stackToString(ex.getStackTrace()));
382 * Method called by the OSGi framework when the OSGi bundle
383 * stops. The functionality we want to perform here are:
385 * 1) Force all the instances to stop and do cleanup and
386 * unreference them so garbage collection can clean them up
388 * NOTE: UN-Register with the OSGi framework,is not needed because
389 * the framework will automatically do it
391 * @param context OSGi bundle context to interact with OSGi framework
394 public void stop(BundleContext context) {
396 logger.trace("DE-Activating");
398 // Now call the derived class destroy function
401 // Now remove all the components tracked for container components
402 for (ImmutablePair<String, Object> key : this.dbInstances.keySet()) {
404 Component c = this.dbInstances.get(key);
406 logger.trace("Remove component on container: {} Object: {}",
407 key.getLeft(), key.getRight());
410 } catch (Exception nex) {
411 logger.error("During removal of a container component "
412 + "instance caught exception: " + nex
414 + stackToString(nex.getStackTrace()));
416 this.dbInstances.remove(key);
419 // Now remove all the components tracked for Global Components
420 for (Object key : this.dbGlobalInstances.keySet()) {
422 Component c = this.dbGlobalInstances.get(key);
424 logger.trace("Remove component for Object: {}" , key);
427 } catch (Exception nex) {
428 logger.error("During removal of a Global "
429 + "instance caught exception: " + nex
431 + stackToString(nex.getStackTrace()));
434 this.dbGlobalInstances.remove(key);
437 // Detach Dependency Manager
440 logger.trace("Deactivation DONE!");
441 } catch (Exception ex) {
442 logger.error("During Activator stop caught exception: " + ex
443 + "\nStacktrace:" + stackToString(ex.getStackTrace()));
448 * Return a ServiceDependency customized ad hoc for slicing, this
449 * essentially the same org.apache.felix.dm.ServiceDependency just
450 * with some filters pre-set
452 * @param containerName containerName for which we want to create the dependency
454 * @return a ServiceDependency
456 protected ServiceDependency createContainerServiceDependency(
457 String containerName) {
458 return (new ContainerServiceDependency(this.dm, containerName));
462 * Return a ServiceDependency as provided by Dependency Manager as it's
465 * @return a ServiceDependency
467 protected ServiceDependency createServiceDependency() {
468 return this.dm.createServiceDependency();