Resolve Bug:681 - Fix config module registration to Service Registry.
[controller.git] / opendaylight / config / config-manager / src / main / java / org / opendaylight / controller / config / manager / impl / osgi / BeanToOsgiServiceManager.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.config.manager.impl.osgi;
9
10 import org.opendaylight.controller.config.api.ModuleIdentifier;
11 import org.opendaylight.controller.config.api.annotations.ServiceInterfaceAnnotation;
12 import org.osgi.framework.BundleContext;
13 import org.osgi.framework.ServiceRegistration;
14 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory;
16
17 import javax.annotation.concurrent.GuardedBy;
18 import java.util.Dictionary;
19 import java.util.HashSet;
20 import java.util.Hashtable;
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import java.util.Set;
24
25 import static com.google.common.base.Preconditions.checkState;
26
27 /**
28  * Registers instantiated beans as OSGi services and unregisters these services
29  * if beans are destroyed.
30  */
31 public class BeanToOsgiServiceManager {
32     private static final String SERVICE_NAME_OSGI_PROP = "name";
33
34     /**
35      * To be called for every created, reconfigured and recreated config bean.
36      * It is expected that before using this method OSGi service registry will
37      * be cleaned from previous registrations.
38      */
39     public OsgiRegistration registerToOsgi(AutoCloseable instance, ModuleIdentifier moduleIdentifier,
40                                            BundleContext bundleContext,
41                                            Map<String, ServiceInterfaceAnnotation> serviceNamesToAnnotations) {
42         return new OsgiRegistration(instance, moduleIdentifier, bundleContext, serviceNamesToAnnotations);
43     }
44
45     private static Dictionary<String, String> createProps(String serviceName) {
46         Hashtable<String, String> result = new Hashtable<>();
47         result.put(SERVICE_NAME_OSGI_PROP, serviceName);
48         return result;
49     }
50
51
52     public static class OsgiRegistration implements AutoCloseable {
53         private static final Logger logger = LoggerFactory.getLogger(OsgiRegistration.class);
54
55         @GuardedBy("this")
56         private AutoCloseable instance;
57         private final ModuleIdentifier moduleIdentifier;
58         @GuardedBy("this")
59         private final Set<ServiceRegistration<?>> serviceRegistrations;
60         @GuardedBy("this")
61         private final Map<String, ServiceInterfaceAnnotation> serviceNamesToAnnotations;
62
63         public OsgiRegistration(AutoCloseable instance, ModuleIdentifier moduleIdentifier,
64                                 BundleContext bundleContext,
65                                 Map<String, ServiceInterfaceAnnotation> serviceNamesToAnnotations) {
66             this.instance = instance;
67             this.moduleIdentifier = moduleIdentifier;
68             this.serviceNamesToAnnotations = serviceNamesToAnnotations;
69             this.serviceRegistrations = registerToSR(instance, bundleContext, serviceNamesToAnnotations);
70         }
71
72         private static Set<ServiceRegistration<?>> registerToSR(AutoCloseable instance, BundleContext bundleContext,
73                                                                 Map<String, ServiceInterfaceAnnotation> serviceNamesToAnnotations) {
74             Set<ServiceRegistration<?>> serviceRegistrations = new HashSet<>();
75             for (Entry<String, ServiceInterfaceAnnotation> entry : serviceNamesToAnnotations.entrySet()) {
76                 Class<?> requiredInterface = entry.getValue().osgiRegistrationType();
77                 checkState(requiredInterface.isInstance(instance), instance.getClass().getName() +
78                         " instance should implement " + requiredInterface.getName());
79                 Dictionary<String, String> propertiesForOsgi = createProps(entry.getKey());
80                 ServiceRegistration<?> serviceRegistration = bundleContext
81                         .registerService(requiredInterface.getName(), instance, propertiesForOsgi);
82                 serviceRegistrations.add(serviceRegistration);
83             }
84             return serviceRegistrations;
85         }
86
87         @Override
88         public synchronized void close() {
89             for (ServiceRegistration<?> serviceRegistration : serviceRegistrations) {
90                 serviceRegistration.unregister();
91             }
92             serviceRegistrations.clear();
93         }
94
95         public synchronized void updateRegistrations(Map<String, ServiceInterfaceAnnotation> newAnnotationMapping,
96                                                      BundleContext bundleContext, AutoCloseable newInstance) {
97             boolean notEquals = this.instance != newInstance;
98             notEquals |= newAnnotationMapping.equals(serviceNamesToAnnotations) == false;
99             if (notEquals) {
100                 // FIXME: changing from old state to new state can be improved by computing the diff
101                 logger.debug("Detected change in service registrations for {}: old: {}, new: {}", moduleIdentifier,
102                         serviceNamesToAnnotations, newAnnotationMapping);
103                 close();
104                 this.instance = newInstance;
105                 Set<ServiceRegistration<?>> newRegs = registerToSR(instance, bundleContext, newAnnotationMapping);
106                 serviceRegistrations.clear();
107                 serviceRegistrations.addAll(newRegs);
108             }
109         }
110     }
111 }