Merge changes I85b49247,Icca28a4a
[controller.git] / opendaylight / sal / api / src / main / java / org / opendaylight / controller / sal / core / ComponentActivatorAbstractBase.java
1
2 /*
3  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
4  *
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
8  */
9
10 package org.opendaylight.controller.sal.core;
11
12 /**
13  * @file   ComponentActivatorAbstractBase.java
14  *
15  * @brief  Abstract class which need to be subclassed in order to
16  * track and register dependencies per-container
17  *
18  * Abstract class which need to be subclassed in order to
19  * track and register dependencies per-container
20  *
21  */
22
23 import java.util.Dictionary;
24 import java.util.Hashtable;
25 import java.util.concurrent.ConcurrentHashMap;
26 import java.util.concurrent.ConcurrentMap;
27
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;
37
38 /**
39  * Abstract class which need to be subclassed in order to track and
40  * register dependencies per-container
41  *
42  */
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>();
50
51     /**
52      * Method that should be overriden by the derived class for customization
53      * during activation of the Component bundle in a container.
54      */
55     protected void init() {
56
57     }
58
59     /**
60      * Method that should be overriden by the derived class for customization
61      * during DE-activation of the Component bundle in a container.
62      */
63     public void destroy() {
64
65     }
66
67     /**
68      * Method which tells how many implementations are supported by
69      * the bundle. This way we can tune the number of components
70      * created.
71      *
72      *
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
76      */
77     protected Object[] getImplementations() {
78         return null;
79     }
80
81     /**
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!
89      *
90      *
91      * @return The list of implementations the bundle will support,
92      * in Global version
93      */
94     protected Object[] getGlobalImplementations() {
95         return null;
96     }
97
98     /**
99      * Configure the dependency for a given instance inside a container
100      *
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
105      */
106     protected void configureInstance(Component c, Object imp,
107             String containerName) {
108         // do nothing by default
109     }
110
111     /**
112      * Configure the dependency for a given instance Global
113      *
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
118      */
119     protected void configureGlobalInstance(Component c, Object imp) {
120         // Do nothing by default
121     }
122
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
137     // service registry
138     class ListenerComponentStates implements ComponentStateListener {
139         @Override
140         public void starting(Component component) {
141             // do nothing
142         }
143
144         @Override
145         public void started(Component component) {
146             if (component == null) {
147                 return;
148             }
149             component.invokeCallbackMethod(new Object[] { component
150                     .getService() }, "started", new Class[][] {
151                     { Component.class }, {} }, new Object[][] { { component },
152                     {} });
153         }
154
155         @Override
156         public void stopped(Component component) {
157             // do nothing
158         }
159
160         @Override
161         public void stopping(Component component) {
162             if (component == null) {
163                 return;
164             }
165             component.invokeCallbackMethod(new Object[] { component
166                     .getService() }, "stopping", new Class[][] {
167                     { Component.class }, {} }, new Object[][] { { component },
168                     {} });
169         }
170     }
171
172     /**
173      * Method of IContainerAware called when a new container is available
174      *
175      * @param containerName Container being created
176      */
177     @Override
178     public void containerCreate(String containerName) {
179         try {
180             Object[] imps = getImplementations();
181             logger.trace("Creating instance {}", containerName);
182             if (imps != null) {
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);
187                     if (c == null) {
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
194                         // its lifecycle
195                         if (c.getService() == null) {
196                             logger.trace("Setting implementation to: {}",
197                                           imps[i]);
198                             c.setImplementation(imps[i]);
199                         }
200
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);
210                         } else {
211                             logger
212                                     .trace("Create a new properties for the service");
213                             serviceProps = new Hashtable<String, String>();
214                             serviceProps.put("containerName", containerName);
215                         }
216                         c.setServiceProperties(serviceProps);
217
218                         // Now add the component to the dependency Manager
219                         // which will immediately start tracking the dependencies
220                         this.dm.add(c);
221
222                         //Now lets keep track in our shadow database of the
223                         //association
224                         this.dbInstances.put(key, c);
225                     } else {
226                         logger
227                                 .error("I have been asked again to create an instance "
228                                         + "on: "
229                                         + containerName
230                                         + "for object: "
231                                         + imps[i]
232                                         + "when i already have it!!");
233                     }
234                 }
235             }
236         } catch (Exception ex) {
237             logger
238                     .error("During containerDestroy invocation caught exception: "
239                             + ex
240                             + "\nStacktrace:"
241                             + stackToString(ex.getStackTrace()));
242         }
243     }
244
245     @Override
246     public void containerDestroy(String containerName) {
247         try {
248             Object[] imps = getImplementations();
249             logger.trace("Destroying instance {}", containerName);
250             if (imps != null) {
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);
255                     if (c != null) {
256                         if (c.getService() != null) {
257                             c.invokeCallbackMethod(new Object[] { c.getService() }, "containerStop",
258                                                    new Class[][] {{ Component.class}, {} },
259                                                    new Object[][] { {c}, {} });
260                         }
261                         // Now remove the component from dependency manager,
262                         // which will implicitely stop it first
263                         this.dm.remove(c);
264                     } else {
265                         logger
266                                 .error("I have been asked again to remove an instance "
267                                         + "on: "
268                                         + containerName
269                                         + "for object: "
270                                         + imps[i]
271                                         + "when i already have cleared it!!");
272                     }
273
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);
278                 }
279             }
280         } catch (Exception ex) {
281             logger
282                     .error("During containerDestroy invocation caught exception: "
283                             + ex
284                             + "\nStacktrace:"
285                             + stackToString(ex.getStackTrace()));
286         }
287     }
288
289     private String stackToString(StackTraceElement[] stack) {
290         if (stack == null) {
291             return "<EmptyStack>";
292         }
293         StringBuffer buffer = new StringBuffer();
294
295         for (int i = 0; i < stack.length; i++) {
296             buffer.append("\n\t").append(stack[i].toString());
297         }
298         return buffer.toString();
299     }
300
301     /**
302      * Method called by the OSGi framework when the OSGi bundle
303      * starts. The functionality we want to perform here are:
304      *
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
307      * want to be called
308      *
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
312      *
313      * @param context OSGi bundle context to interact with OSGi framework
314      */
315     @Override
316     public void start(BundleContext context) {
317         try {
318             this.dm = new DependencyManager(context);
319
320             logger.trace("Activating");
321
322             // Now create Global components
323             Object[] imps = getGlobalImplementations();
324             if (imps != null) {
325                 for (int i = 0; i < imps.length; i++) {
326                     Object key = imps[i];
327                     Component c = this.dbGlobalInstances.get(key);
328                     if (c == null) {
329                         try {
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: {}",
339                                         imps[i]);
340                                 c.setImplementation(imps[i]);
341                             }
342
343                             // Now add the component to the dependency
344                             // Manager which will immediately start
345                             // tracking the dependencies
346                             this.dm.add(c);
347                         } catch (Exception nex) {
348                             logger.error("During creation of a Global "
349                                     + "instance caught exception: " + nex
350                                     + "\nStacktrace:"
351                                     + stackToString(nex.getStackTrace()));
352                         }
353
354                         //Now lets keep track in our shadow database of the
355                         //association
356                         if (c != null)
357                             this.dbGlobalInstances.put(key, c);
358                     } else {
359                         logger.error("I have been asked again to create an "
360                                 + "instance " + " Global for object: "
361                                 + imps[i] + "when i already have it!!");
362                     }
363                 }
364             }
365
366             // Register with OSGi the provider for the service IContainerAware
367             context.registerService(
368                     IContainerAware.class.getName(), this, null);
369
370             // Now call the derived class init function
371             this.init();
372
373             logger.trace("Activation DONE!");
374         } catch (Exception ex) {
375             logger.error("During Activator start caught exception: " + ex
376                     + "\nStacktrace:" + stackToString(ex.getStackTrace()));
377         }
378     }
379
380     /**
381      * Method called by the OSGi framework when the OSGi bundle
382      * stops. The functionality we want to perform here are:
383      *
384      * 1) Force all the instances to stop and do cleanup and
385      * unreference them so garbage collection can clean them up
386      *
387      * NOTE: UN-Register with the OSGi framework,is not needed because
388      * the framework will automatically do it
389      *
390      * @param context OSGi bundle context to interact with OSGi framework
391      */
392     @Override
393     public void stop(BundleContext context) {
394         try {
395             logger.trace("DE-Activating");
396
397             // Now call the derived class destroy function
398             this.destroy();
399
400             // Now remove all the components tracked for container components
401             for (ImmutablePair<String, Object> key : this.dbInstances.keySet()) {
402                 try {
403                     Component c = this.dbInstances.get(key);
404                     if (c != null) {
405                         logger.trace("Remove component on container: {} Object: {}",
406                                 key.getLeft(), key.getRight());
407                         this.dm.remove(c);
408                     }
409                 } catch (Exception nex) {
410                     logger.error("During removal of a container component "
411                             + "instance caught exception: " + nex
412                             + "\nStacktrace:"
413                             + stackToString(nex.getStackTrace()));
414                 }
415                 this.dbInstances.remove(key);
416             }
417
418             // Now remove all the components tracked for Global Components
419             for (Object key : this.dbGlobalInstances.keySet()) {
420                 try {
421                     Component c = this.dbGlobalInstances.get(key);
422                     if (c != null) {
423                         logger.trace("Remove component for Object: {}" , key);
424                         this.dm.remove(c);
425                     }
426                 } catch (Exception nex) {
427                     logger.error("During removal of a Global "
428                             + "instance caught exception: " + nex
429                             + "\nStacktrace:"
430                             + stackToString(nex.getStackTrace()));
431                 }
432
433                 this.dbGlobalInstances.remove(key);
434             }
435
436             // Detach Dependency Manager
437             this.dm = null;
438
439             logger.trace("Deactivation DONE!");
440         } catch (Exception ex) {
441             logger.error("During Activator stop caught exception: " + ex
442                     + "\nStacktrace:" + stackToString(ex.getStackTrace()));
443         }
444     }
445
446     /**
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
450      *
451      * @param containerName containerName for which we want to create the dependency
452      *
453      * @return a ServiceDependency
454      */
455     protected ServiceDependency createContainerServiceDependency(
456             String containerName) {
457         return (new ContainerServiceDependency(this.dm, containerName));
458     }
459
460     /**
461      * Return a ServiceDependency as provided by Dependency Manager as it's
462      *
463      *
464      * @return a ServiceDependency
465      */
466     protected ServiceDependency createServiceDependency() {
467         return this.dm.createServiceDependency();
468     }
469 }