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 = (ConcurrentMap<ImmutablePair<String, Object>, Component>) new ConcurrentHashMap<ImmutablePair<String, Object>, Component>();
49 private ConcurrentMap<Object, Component> dbGlobalInstances = (ConcurrentMap<Object, Component>) new ConcurrentHashMap<Object, Component>();
52 * Abstract method that MUST be implemented by the derived class
53 * that wants to activate the Component bundle in a container. Here
54 * customization for the component are expected
56 abstract protected void init();
59 * Abstract method that MUST be implemented by the derived class
60 * that wants to DE-activate the Component bundle in a container. Here
61 * customization for the component are expected
63 abstract protected void destroy();
66 * Method which tells how many implementations are supported by
67 * the bundle. This way we can tune the number of components
71 * @return The list of implementations the bundle will support,
72 * this will be used to decide how many components need to be
73 * created per-container
75 protected Object[] getImplementations() {
80 * Method which tells how many Global implementations are
81 * supported by the bundle. This way we can tune the number of
82 * components created. This components will be created ONLY at the
83 * time of bundle startup and will be destroyed only at time of
84 * bundle destruction, this is the major difference with the
85 * implementation retrieved via getImplementations where all of
86 * them are assumed to be in a container!
89 * @return The list of implementations the bundle will support,
92 protected Object[] getGlobalImplementations() {
97 * Configure the dependency for a given instance inside a container
99 * @param c Component assigned for this instance, this will be
100 * what will be used for configuration
101 * @param imp implementation to be configured
102 * @param containerName container on which the configuration happens
104 protected void configureInstance(Component c, Object imp,
105 String containerName) {
106 // do nothing by default
110 * Configure the dependency for a given instance Global
112 * @param c Component assigned for this instance, this will be
113 * what will be used for configuration
114 * @param imp implementation to be configured
115 * @param containerName container on which the configuration happens
117 protected void configureGlobalInstance(Component c, Object imp) {
118 // Do nothing by default
121 // Private class used to listen to state transition so we can
122 // implement the necessary logic to call "started" and "stopping"
123 // methods on the component. Right now the framework natively
124 // support only the call of:
125 // - "init": Called after dependency are satisfied
126 // - "start": Called after init but before services are registered
127 // - "stop": Called after services are unregistered but before the
128 // component is going to be destroyed
129 // - "destroy": Called to destroy the component.
130 // There is still a need for two notifications:
131 // - "started" method to be called after "start" and after the
132 // services has been registered in the OSGi service registry
133 // - "stopping" method to be called before "stop" method and
134 // before the services of the component are removed from OSGi
136 class ListenerComponentStates implements ComponentStateListener {
138 public void starting(Component component) {
143 public void started(Component component) {
144 if (component == null) {
147 component.invokeCallbackMethod(new Object[] { component
148 .getService() }, "started", new Class[][] {
149 { Component.class }, {} }, new Object[][] { { component },
154 public void stopped(Component component) {
159 public void stopping(Component component) {
160 if (component == null) {
163 component.invokeCallbackMethod(new Object[] { component
164 .getService() }, "stopping", new Class[][] {
165 { Component.class }, {} }, new Object[][] { { component },
171 * Method of IContainerAware called when a new container is available
173 * @param containerName Container being created
176 public void containerCreate(String containerName) {
178 Object[] imps = getImplementations();
179 logger.trace("Creating instance {}", containerName);
181 for (int i = 0; i < imps.length; i++) {
182 ImmutablePair<String, Object> key = new ImmutablePair<String, Object>(
183 containerName, imps[i]);
184 Component c = this.dbInstances.get(key);
186 c = this.dm.createComponent();
187 c.addStateListener(new ListenerComponentStates());
188 // Now let the derived class to configure the
189 // dependencies it wants
190 configureInstance(c, imps[i], containerName);
191 // Set the implementation so the component can manage
193 if (c.getService() == null) {
194 logger.trace("Setting implementation to: {}",
196 c.setImplementation(imps[i]);
199 //Set the service properties to include the containerName
200 //in the service, that is fundamental for supporting
201 //multiple services just distinguished via a container
202 Dictionary<String, String> serviceProps = c
203 .getServiceProperties();
204 if (serviceProps != null) {
205 logger.trace("Adding new property for container");
206 serviceProps.put("containerName", containerName);
209 .trace("Create a new properties for the service");
210 serviceProps = new Hashtable<String, String>();
211 serviceProps.put("containerName", containerName);
213 c.setServiceProperties(serviceProps);
215 // Now add the component to the dependency Manager
216 // which will immediately start tracking the dependencies
219 //Now lets keep track in our shadow database of the
221 this.dbInstances.put(key, c);
224 .error("I have been asked again to create an instance "
229 + "when i already have it!!");
233 } catch (Exception ex) {
235 .error("During containerDestroy invocation caught exception: "
238 + stackToString(ex.getStackTrace()));
243 public void containerDestroy(String containerName) {
245 Object[] imps = getImplementations();
246 logger.trace("Destroying instance {}", containerName);
248 for (int i = 0; i < imps.length; i++) {
249 ImmutablePair<String, Object> key = new ImmutablePair<String, Object>(
250 containerName, imps[i]);
251 Component c = this.dbInstances.get(key);
253 // Now remove the component from dependency manager,
254 // which will implicitely stop it first
258 .error("I have been asked again to remove an instance "
263 + "when i already have cleared it!!");
266 //Now lets remove the association from our shadow
267 //database so the component can be recycled, this is done
268 //unconditionally in case of spurious conditions
269 this.dbInstances.remove(key);
272 } catch (Exception ex) {
274 .error("During containerDestroy invocation caught exception: "
277 + stackToString(ex.getStackTrace()));
281 private String stackToString(StackTraceElement[] stack) {
283 return "<EmptyStack>";
285 StringBuffer buffer = new StringBuffer();
287 for (int i = 0; i < stack.length; i++) {
288 buffer.append("\n\t" + stack[i].toString());
290 return buffer.toString();
294 * Method called by the OSGi framework when the OSGi bundle
295 * starts. The functionality we want to perform here are:
297 * 1) Register with the OSGi framework, that we are a provider of
298 * IContainerAware service and so in case of startup of a container we
301 * 2) Create data structures that allow to keep track of all the
302 * instances created per-container given the derived class of
303 * ComponentActivatorAbstractBase will act as a Factory manager
305 * @param context OSGi bundle context to interact with OSGi framework
308 public void start(BundleContext context) {
310 this.dm = new DependencyManager(context);
312 logger.trace("Activating");
314 // Now create Global components
315 Object[] imps = getGlobalImplementations();
317 for (int i = 0; i < imps.length; i++) {
318 Object key = imps[i];
319 Component c = this.dbGlobalInstances.get(key);
322 c = this.dm.createComponent();
323 c.addStateListener(new ListenerComponentStates());
324 // Now let the derived class to configure the
325 // dependencies it wants
326 configureGlobalInstance(c, imps[i]);
327 // Set the implementation so the component
328 // can manage its lifesycle
329 if (c.getService() == null) {
330 logger.trace("Setting implementation to: {}",
332 c.setImplementation(imps[i]);
335 // Now add the component to the dependency
336 // Manager which will immediately start
337 // tracking the dependencies
339 } catch (Exception nex) {
340 logger.error("During creation of a Global "
341 + "instance caught exception: " + nex
343 + stackToString(nex.getStackTrace()));
346 //Now lets keep track in our shadow database of the
349 this.dbGlobalInstances.put(key, c);
351 logger.error("I have been asked again to create an "
352 + "instance " + " Global for object: "
353 + imps[i] + "when i already have it!!");
358 // Register with OSGi the provider for the service IContainerAware
359 context.registerService(
360 IContainerAware.class.getName(), this, null);
362 // Now call the derived class init function
365 logger.trace("Activation DONE!");
366 } catch (Exception ex) {
367 logger.error("During Activator start caught exception: " + ex
368 + "\nStacktrace:" + stackToString(ex.getStackTrace()));
373 * Method called by the OSGi framework when the OSGi bundle
374 * stops. The functionality we want to perform here are:
376 * 1) Force all the instances to stop and do cleanup and
377 * unreference them so garbage collection can clean them up
379 * NOTE: UN-Register with the OSGi framework,is not needed because
380 * the framework will automatically do it
382 * @param context OSGi bundle context to interact with OSGi framework
385 public void stop(BundleContext context) {
387 logger.trace("DE-Activating");
389 // Now call the derived class destroy function
392 // Now remove all the components tracked for container components
393 for (ImmutablePair<String, Object> key : this.dbInstances.keySet()) {
395 Component c = this.dbInstances.get(key);
397 logger.trace("Remove component on container: {} Object: {}",
398 key.getLeft(), key.getRight());
401 } catch (Exception nex) {
402 logger.error("During removal of a container component "
403 + "instance caught exception: " + nex
405 + stackToString(nex.getStackTrace()));
407 this.dbInstances.remove(key);
410 // Now remove all the components tracked for Global Components
411 for (Object key : this.dbGlobalInstances.keySet()) {
413 Component c = this.dbGlobalInstances.get(key);
415 logger.trace("Remove component for Object: {}" , key);
418 } catch (Exception nex) {
419 logger.error("During removal of a Global "
420 + "instance caught exception: " + nex
422 + stackToString(nex.getStackTrace()));
425 this.dbGlobalInstances.remove(key);
428 // Detach Dependency Manager
431 logger.trace("Deactivation DONE!");
432 } catch (Exception ex) {
433 logger.error("During Activator stop caught exception: " + ex
434 + "\nStacktrace:" + stackToString(ex.getStackTrace()));
439 * Return a ServiceDependency customized ad hoc for slicing, this
440 * essentially the same org.apache.felix.dm.ServiceDependency just
441 * with some filters pre-set
443 * @param containerName containerName for which we want to create the dependency
445 * @return a ServiceDependency
447 protected ServiceDependency createContainerServiceDependency(
448 String containerName) {
449 return (new ContainerServiceDependency(this.dm, containerName));
453 * Return a ServiceDependency as provided by Dependency Manager as it's
456 * @return a ServiceDependency
458 protected ServiceDependency createServiceDependency() {
459 return this.dm.createServiceDependency();