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