Removed unused private variable containerAwareRegistration - please
[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.osgi.framework.ServiceRegistration;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 /**
40  * Abstract class which need to be subclassed in order to track and
41  * register dependencies per-container
42  *
43  */
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>();
51
52     /**
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
56      */
57     abstract protected void init();
58
59     /**
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
63      */
64     abstract protected void destroy();
65
66     /**
67      * Method which tells how many implementations are supported by
68      * the bundle. This way we can tune the number of components
69      * created.
70      *
71      *
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
75      */
76     protected Object[] getImplementations() {
77         return null;
78     }
79
80     /**
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!
88      *
89      *
90      * @return The list of implementations the bundle will support,
91      * in Global version
92      */
93     protected Object[] getGlobalImplementations() {
94         return null;
95     }
96
97     /**
98      * Configure the dependency for a given instance inside a container
99      *
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
104      */
105     protected void configureInstance(Component c, Object imp,
106             String containerName) {
107         // do nothing by default
108     }
109
110     /**
111      * Configure the dependency for a given instance Global
112      *
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
117      */
118     protected void configureGlobalInstance(Component c, Object imp) {
119         // Do nothing by default
120     }
121
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
136     // service registry
137     class ListenerComponentStates implements ComponentStateListener {
138         @Override
139         public void starting(Component component) {
140             // do nothing
141         }
142
143         @Override
144         public void started(Component component) {
145             if (component == null) {
146                 return;
147             }
148             component.invokeCallbackMethod(new Object[] { component
149                     .getService() }, "started", new Class[][] {
150                     { Component.class }, {} }, new Object[][] { { component },
151                     {} });
152         }
153
154         @Override
155         public void stopped(Component component) {
156             if (component == null) {
157                 return;
158             }
159             component.invokeCallbackMethod(new Object[] { component
160                     .getService() }, "stopping", new Class[][] {
161                     { Component.class }, {} }, new Object[][] { { component },
162                     {} });
163         }
164
165         @Override
166         public void stopping(Component component) {
167             // do nothing
168         }
169     }
170
171     /**
172      * Method of IContainerAware called when a new container is available
173      *
174      * @param containerName Container being created
175      */
176     @Override
177     public void containerCreate(String containerName) {
178         try {
179             Object[] imps = getImplementations();
180             logger.trace("Creating instance {}", containerName);
181             if (imps != null) {
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);
186                     if (c == null) {
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
193                         // its lifecycle
194                         if (c.getService() == null) {
195                             logger.trace("Setting implementation to: {}",
196                                           imps[i]);
197                             c.setImplementation(imps[i]);
198                         }
199
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);
208                         } else {
209                             logger
210                                     .trace("Create a new properties for the service");
211                             serviceProps = new Hashtable<String, String>();
212                             serviceProps.put("containerName", containerName);
213                         }
214                         c.setServiceProperties(serviceProps);
215
216                         // Now add the component to the dependency Manager
217                         // which will immediately start tracking the dependencies
218                         this.dm.add(c);
219
220                         //Now lets keep track in our shadow database of the
221                         //association
222                         this.dbInstances.put(key, c);
223                     } else {
224                         logger
225                                 .error("I have been asked again to create an instance "
226                                         + "on: "
227                                         + containerName
228                                         + "for object: "
229                                         + imps[i]
230                                         + "when i already have it!!");
231                     }
232                 }
233             }
234         } catch (Exception ex) {
235             logger
236                     .error("During containerDestroy invocation caught exception: "
237                             + ex
238                             + "\nStacktrace:"
239                             + stackToString(ex.getStackTrace()));
240         }
241     }
242
243     @Override
244     public void containerDestroy(String containerName) {
245         try {
246             Object[] imps = getImplementations();
247             logger.trace("Destroying instance {}", containerName);
248             if (imps != null) {
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);
253                     if (c != null) {
254                         // Now remove the component from dependency manager,
255                         // which will implicitely stop it first
256                         this.dm.remove(c);
257                     } else {
258                         logger
259                                 .error("I have been asked again to remove an instance "
260                                         + "on: "
261                                         + containerName
262                                         + "for object: "
263                                         + imps[i]
264                                         + "when i already have cleared it!!");
265                     }
266
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);
271                 }
272             }
273         } catch (Exception ex) {
274             logger
275                     .error("During containerDestroy invocation caught exception: "
276                             + ex
277                             + "\nStacktrace:"
278                             + stackToString(ex.getStackTrace()));
279         }
280     }
281
282     private String stackToString(StackTraceElement[] stack) {
283         if (stack == null) {
284             return "<EmptyStack>";
285         }
286         StringBuffer buffer = new StringBuffer();
287
288         for (int i = 0; i < stack.length; i++) {
289             buffer.append("\n\t" + stack[i].toString());
290         }
291         return buffer.toString();
292     }
293
294     /**
295      * Method called by the OSGi framework when the OSGi bundle
296      * starts. The functionality we want to perform here are:
297      *
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
300      * want to be called
301      *
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
305      *
306      * @param context OSGi bundle context to interact with OSGi framework
307      */
308     @Override
309     public void start(BundleContext context) {
310         try {
311             this.dm = new DependencyManager(context);
312
313             logger.trace("Activating");
314
315             // Now create Global components
316             Object[] imps = getGlobalImplementations();
317             if (imps != null) {
318                 for (int i = 0; i < imps.length; i++) {
319                     Object key = imps[i];
320                     Component c = this.dbGlobalInstances.get(key);
321                     if (c == null) {
322                         try {
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: {}",
332                                         imps[i]);
333                                 c.setImplementation(imps[i]);
334                             }
335
336                             // Now add the component to the dependency
337                             // Manager which will immediately start
338                             // tracking the dependencies
339                             this.dm.add(c);
340                         } catch (Exception nex) {
341                             logger.error("During creation of a Global "
342                                     + "instance caught exception: " + nex
343                                     + "\nStacktrace:"
344                                     + stackToString(nex.getStackTrace()));
345                         }
346
347                         //Now lets keep track in our shadow database of the
348                         //association
349                         if (c != null)
350                             this.dbGlobalInstances.put(key, c);
351                     } else {
352                         logger.error("I have been asked again to create an "
353                                 + "instance " + " Global for object: "
354                                 + imps[i] + "when i already have it!!");
355                     }
356                 }
357             }
358             
359             // Register with OSGi the provider for the service IContainerAware
360             context.registerService(
361                     IContainerAware.class.getName(), this, null);
362
363             // Now call the derived class init function
364             this.init();
365
366             logger.trace("Activation DONE!");
367         } catch (Exception ex) {
368             logger.error("During Activator start caught exception: " + ex
369                     + "\nStacktrace:" + stackToString(ex.getStackTrace()));
370         }
371     }
372
373     /**
374      * Method called by the OSGi framework when the OSGi bundle
375      * stops. The functionality we want to perform here are:
376      *
377      * 1) Force all the instances to stop and do cleanup and
378      * unreference them so garbage collection can clean them up
379      *
380      * NOTE: UN-Register with the OSGi framework,is not needed because
381      * the framework will automatically do it
382      *
383      * @param context OSGi bundle context to interact with OSGi framework
384      */
385     @Override
386     public void stop(BundleContext context) {
387         try {
388             logger.trace("DE-Activating");
389
390             // Now call the derived class destroy function
391             this.destroy();
392
393             // Now remove all the components tracked for container components
394             for (ImmutablePair<String, Object> key : this.dbInstances.keySet()) {
395                 try {
396                     Component c = this.dbInstances.get(key);
397                     if (c != null) {
398                         logger.trace("Remove component on container: {} Object: {}",
399                                 key.getLeft(), key.getRight());
400                         this.dm.remove(c);
401                     }
402                 } catch (Exception nex) {
403                     logger.error("During removal of a container component "
404                             + "instance caught exception: " + nex
405                             + "\nStacktrace:"
406                             + stackToString(nex.getStackTrace()));
407                 }
408                 this.dbInstances.remove(key);
409             }
410
411             // Now remove all the components tracked for Global Components
412             for (Object key : this.dbGlobalInstances.keySet()) {
413                 try {
414                     Component c = this.dbGlobalInstances.get(key);
415                     if (c != null) {
416                         logger.trace("Remove component for Object: {}" , key);
417                         this.dm.remove(c);
418                     }
419                 } catch (Exception nex) {
420                     logger.error("During removal of a Global "
421                             + "instance caught exception: " + nex
422                             + "\nStacktrace:"
423                             + stackToString(nex.getStackTrace()));
424                 }
425
426                 this.dbGlobalInstances.remove(key);
427             }
428
429             // Detach Dependency Manager
430             this.dm = null;
431
432             logger.trace("Deactivation DONE!");
433         } catch (Exception ex) {
434             logger.error("During Activator stop caught exception: " + ex
435                     + "\nStacktrace:" + stackToString(ex.getStackTrace()));
436         }
437     }
438
439     /**
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
443      *
444      * @param containerName containerName for which we want to create the dependency
445      *
446      * @return a ServiceDependency
447      */
448     protected ServiceDependency createContainerServiceDependency(
449             String containerName) {
450         return (new ContainerServiceDependency(this.dm, containerName));
451     }
452
453     /**
454      * Return a ServiceDependency as provided by Dependency Manager as it's
455      *
456      *
457      * @return a ServiceDependency
458      */
459     protected ServiceDependency createServiceDependency() {
460         return this.dm.createServiceDependency();
461     }
462 }