Fixed netconf monitoring.
[controller.git] / opendaylight / config / config-manager / src / main / java / org / opendaylight / controller / config / manager / impl / ServiceReferenceRegistryImpl.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;
9
10 import org.opendaylight.controller.config.api.LookupRegistry;
11 import org.opendaylight.controller.config.api.ModuleIdentifier;
12 import org.opendaylight.controller.config.api.ServiceReferenceReadableRegistry;
13 import org.opendaylight.controller.config.api.ServiceReferenceWritableRegistry;
14 import org.opendaylight.controller.config.api.annotations.ServiceInterfaceAnnotation;
15 import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
16 import org.opendaylight.controller.config.manager.impl.util.InterfacesHelper;
17 import org.opendaylight.controller.config.spi.ModuleFactory;
18 import org.osgi.framework.BundleContext;
19 import org.slf4j.Logger;
20 import org.slf4j.LoggerFactory;
21
22 import javax.management.InstanceNotFoundException;
23 import javax.management.ObjectName;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.Iterator;
28 import java.util.Map;
29 import java.util.Map.Entry;
30 import java.util.Set;
31
32 public class ServiceReferenceRegistryImpl implements ServiceReferenceReadableRegistry, ServiceReferenceWritableRegistry {
33     private static final Logger logger = LoggerFactory.getLogger(ServiceReferenceRegistryImpl.class);
34
35     private final Map<String, ModuleFactory> factories;
36     private final Map<String, Set<String>> factoryNamesToQNames;
37     // validator of incoming ObjectNames - throws InstanceNotFoundException if not found either in registry or transaction
38     private final LookupRegistry lookupRegistry;
39     // helper method for getting QName of SI from namespace + local name
40     private final Map<String /* namespace */, Map<String /* local name */, ServiceInterfaceAnnotation>> namespacesToAnnotations;
41     // all Service Interface qNames for sanity checking
42     private final Set<String /* qName */> allQNames;
43
44     // actual reference database
45     private final Map<String /* qName */, Map<String /* refName */, ModuleIdentifier>> refNames;
46
47     /**
48      * Static constructor for config registry. Since only transaction can write to this registry, it will
49      * return blank state.
50      */
51     public static ServiceReferenceReadableRegistry createInitialSRLookupRegistry() {
52         // since this is initial state, just throw exception:
53         LookupRegistry lookupRegistry = new LookupRegistry() {
54             @Override
55             public Set<ObjectName> lookupConfigBeans() {
56                 throw new UnsupportedOperationException();
57             }
58
59             @Override
60             public Set<ObjectName> lookupConfigBeans(String moduleName) {
61                 throw new UnsupportedOperationException();
62             }
63
64             @Override
65             public Set<ObjectName> lookupConfigBeans(String moduleName, String instanceName) {
66                 throw new UnsupportedOperationException();
67             }
68
69             @Override
70             public ObjectName lookupConfigBean(String moduleName, String instanceName) throws InstanceNotFoundException {
71                 throw new UnsupportedOperationException();
72             }
73
74             @Override
75             public void checkConfigBeanExists(ObjectName objectName) throws InstanceNotFoundException {
76                 throw new InstanceNotFoundException("Cannot find " + objectName);
77             }
78         };
79         return new ServiceReferenceRegistryImpl(Collections.<String, ModuleFactory>emptyMap(), lookupRegistry,
80                 Collections.<String /* qName */, Map<String /* refName */, ModuleIdentifier>>emptyMap());
81     }
82
83     /**
84      * Static constructor for transaction controller. Take current state as seen by config registry, allow writing new data.
85      */
86     public static ServiceReferenceWritableRegistry createSRWritableRegistry(ServiceReferenceReadableRegistry oldReadableRegistry,
87             LookupRegistry lookupRegistry, Map<String, Map.Entry<ModuleFactory, BundleContext>> currentlyRegisteredFactories) {
88
89         ServiceReferenceRegistryImpl old = (ServiceReferenceRegistryImpl) oldReadableRegistry;
90         Map<String, ModuleFactory> factories = extractFactoriesMap(currentlyRegisteredFactories);
91         return new ServiceReferenceRegistryImpl(factories, lookupRegistry, Collections.unmodifiableMap(old.refNames));
92     }
93
94     /**
95      * Copy back state to config registry after commit.
96      */
97     public static ServiceReferenceReadableRegistry createSRReadableRegistry(ServiceReferenceWritableRegistry oldWritableRegistry, LookupRegistry lookupRegistry) {
98         ServiceReferenceRegistryImpl old = (ServiceReferenceRegistryImpl) oldWritableRegistry;
99         // even if factories do change, nothing in the mapping can change between transactions
100         return new ServiceReferenceRegistryImpl(old.factories, lookupRegistry, Collections.unmodifiableMap(old.refNames));
101     }
102
103     private static Map<String, ModuleFactory> extractFactoriesMap(Map<String, Map.Entry<ModuleFactory, BundleContext>> currentlyRegisteredFactories) {
104         Map<String, ModuleFactory> result = new HashMap<>();
105         for (Entry<String, Entry<ModuleFactory, BundleContext>> entry : currentlyRegisteredFactories.entrySet()) {
106             result.put(entry.getKey(), entry.getValue().getKey());
107         }
108         return result;
109     }
110
111     private ServiceReferenceRegistryImpl(Map<String, ModuleFactory> factories, LookupRegistry lookupRegistry,
112                                          Map<String /* qName */, Map<String /* refName */, ModuleIdentifier>> refNamesToCopy) {
113         this.factories = factories;
114         this.lookupRegistry = lookupRegistry;
115         Map<String, Set<String /* QName */>> factoryNamesToQNames = new HashMap<>();
116         Set<ServiceInterfaceAnnotation> allAnnotations = new HashSet<>();
117         Set<String /* qName */> allQNames = new HashSet<>();
118         for (Entry<String, ModuleFactory> entry : factories.entrySet()) {
119             if (entry.getKey().equals(entry.getValue().getImplementationName()) == false) {
120                 logger.error("Possible error in code: Mismatch between supplied and actual name of {}", entry);
121                 throw new IllegalArgumentException("Possible error in code: Mismatch between supplied and actual name of " + entry);
122             }
123             Set<ServiceInterfaceAnnotation> siAnnotations = InterfacesHelper.getServiceInterfaceAnnotations(entry.getValue());
124             Set<String> qNames = new HashSet<>();
125             for (ServiceInterfaceAnnotation sia: siAnnotations) {
126                 qNames.add(sia.value());
127             }
128             allAnnotations.addAll(siAnnotations);
129             allQNames.addAll(qNames);
130             factoryNamesToQNames.put(entry.getKey(), Collections.unmodifiableSet(qNames));
131         }
132         this.factoryNamesToQNames = Collections.unmodifiableMap(factoryNamesToQNames);
133         this.allQNames = Collections.unmodifiableSet(allQNames);
134         // fill namespacesToAnnotations
135         Map<String /* namespace */, Map<String /* localName */, ServiceInterfaceAnnotation>> namespacesToAnnotations =
136                 new HashMap<>();
137         for (ServiceInterfaceAnnotation sia : allAnnotations) {
138             Map<String, ServiceInterfaceAnnotation> ofNamespace = namespacesToAnnotations.get(sia.namespace());
139             if (ofNamespace == null) {
140                 ofNamespace = new HashMap<>();
141                 namespacesToAnnotations.put(sia.namespace(), ofNamespace);
142             }
143             if (ofNamespace.containsKey(sia.localName())) {
144                 logger.error("Cannot construct namespacesToAnnotations map, conflict between local names in {}, offending local name: {}, map so far {}",
145                         sia.namespace(), sia.localName(), namespacesToAnnotations);
146                 throw new IllegalArgumentException("Conflict between local names in " + sia.namespace() + " : " + sia.localName());
147             }
148             ofNamespace.put(sia.localName(), sia);
149         }
150         this.namespacesToAnnotations = Collections.unmodifiableMap(namespacesToAnnotations);
151         // copy refNames
152         Map<String /* qName */, Map<String /* refName */, ModuleIdentifier>> deepCopy = new HashMap<>();
153         for (Entry<String, Map<String, ModuleIdentifier>> outerROEntry: refNamesToCopy.entrySet()) {
154             Map<String /* refName */, ModuleIdentifier> innerWritableMap = new HashMap<>();
155             deepCopy.put(outerROEntry.getKey(), innerWritableMap);
156             for (Entry<String, ModuleIdentifier> innerROEntry:  outerROEntry.getValue().entrySet()) {
157                 innerWritableMap.put(innerROEntry.getKey(), innerROEntry.getValue());
158             }
159         }
160         this.refNames = deepCopy;
161         logger.trace("factoryNamesToQNames:{}", this.factoryNamesToQNames);
162         logger.trace("refNames:{}", refNames);
163     }
164
165
166     @Override
167     public Set<String> lookupServiceInterfaceNames(ObjectName objectName) throws InstanceNotFoundException {
168         lookupRegistry.checkConfigBeanExists(objectName);
169
170         String factoryName = ObjectNameUtil.getFactoryName(objectName);
171         Set<String> serviceInterfaceAnnotations = factoryNamesToQNames.get(factoryName);
172         if (serviceInterfaceAnnotations == null) {
173             logger.error("Possible error in code: cannot find factory annotations of '{}' extracted from ON {} in {}",
174                     factoryName, objectName, factoryNamesToQNames);
175             throw new IllegalArgumentException("Cannot find factory with name " + factoryName);
176         }
177         return serviceInterfaceAnnotations;
178     }
179
180     @Override
181     public String getServiceInterfaceName(String namespace, String localName) {
182         Map<String /* localName */, ServiceInterfaceAnnotation> ofNamespace = namespacesToAnnotations.get(namespace);
183         if (ofNamespace == null) {
184             logger.error("Cannot find namespace {} in {}", namespace, namespacesToAnnotations);
185             throw new IllegalArgumentException("Cannot find namespace " + namespace);
186         }
187         ServiceInterfaceAnnotation sia = ofNamespace.get(localName);
188         if (sia == null) {
189             logger.error("Cannot find local name {} in namespace {}, found only {}", localName, namespace, ofNamespace);
190             throw new IllegalArgumentException("Cannot find local name " + localName + " in namespace " + namespace);
191         }
192         return sia.value();
193     }
194
195
196
197     // reading:
198
199     @Override
200     public Map<String /* serviceInterfaceName */, Map<String/* refName */, ObjectName>> getServiceMapping() {
201         Map<String /* serviceInterfaceName */, Map<String/* refName */, ObjectName>> result = new HashMap<>();
202         for (Entry<String /* qName */, Map<String, ModuleIdentifier>> outerEntry: refNames.entrySet()) {
203             String qName = outerEntry.getKey();
204             Map<String /* refName */, ObjectName> innerMap = new HashMap<>();
205             result.put(qName, innerMap);
206             for (Entry<String /* refName */, ModuleIdentifier> innerEntry: outerEntry.getValue().entrySet()) {
207                 ModuleIdentifier moduleIdentifier = innerEntry.getValue();
208                 ObjectName on;
209                 on = getObjectName(moduleIdentifier);
210                 innerMap.put(innerEntry.getKey(), on);
211             }
212         }
213         return result;
214     }
215
216     private ObjectName getObjectName(ModuleIdentifier moduleIdentifier) {
217         ObjectName on;
218         try {
219             on = lookupRegistry.lookupConfigBean(moduleIdentifier.getFactoryName(), moduleIdentifier.getInstanceName());
220         } catch (InstanceNotFoundException e) {
221             logger.error("Cannot find instance {}", moduleIdentifier);
222             throw new IllegalStateException("Cannot find instance " + moduleIdentifier, e);
223         }
224         return on;
225     }
226
227     @Override
228     public ObjectName lookupConfigBeanByServiceInterfaceName(String serviceInterfaceName, String refName) {
229         Map<String, ModuleIdentifier> innerMap = refNames.get(serviceInterfaceName);
230         if (innerMap == null) {
231             logger.error("Cannot find qname {} in {}", serviceInterfaceName, refName);
232             throw new IllegalArgumentException("Cannot find " + serviceInterfaceName);
233         }
234         ModuleIdentifier moduleIdentifier = innerMap.get(refName);
235         if (moduleIdentifier == null) {
236             logger.error("Cannot find refName {} in {}, using qname {}", refName, innerMap, serviceInterfaceName);
237             throw new IllegalArgumentException("Cannot find module based on service reference " + refName);
238         }
239         return getObjectName(moduleIdentifier);
240     }
241
242     @Override
243     public  Map<String /* refName */, ObjectName> lookupServiceReferencesByServiceInterfaceName(String serviceInterfaceName) {
244         Map<String, ModuleIdentifier> innerMap = refNames.get(serviceInterfaceName);
245         if (innerMap == null) {
246             logger.error("Cannot find qname {} in {}", serviceInterfaceName, refNames);
247             throw new IllegalArgumentException("Cannot find " + serviceInterfaceName);
248         }
249         Map<String /* refName */, ObjectName> result = new HashMap<>();
250         for (Entry<String/* refName */, ModuleIdentifier> entry: innerMap.entrySet()) {
251             ObjectName on = getObjectName(entry.getValue());
252             result.put(entry.getKey(), on);
253         }
254         return result;
255     }
256
257     // writing:
258
259     @Override
260     public void saveServiceReference(String serviceInterfaceName, String refName, ObjectName objectName)  throws InstanceNotFoundException {
261         // make sure it is found
262         lookupRegistry.checkConfigBeanExists(objectName);
263         String factoryName = ObjectNameUtil.getFactoryName(objectName);
264         // check that service interface name exist
265         Set<String> serviceInterfaceQNames = factoryNamesToQNames.get(factoryName);
266         if (serviceInterfaceQNames == null) {
267             logger.error("Possible error in code: cannot find factoryName {} in {}, object name {}", factoryName, factoryNamesToQNames, objectName);
268             throw new IllegalStateException("Possible error in code: cannot find annotations of existing factory " + factoryName);
269         }
270         // supplied serviceInterfaceName must exist in this collection
271         if (serviceInterfaceQNames.contains(serviceInterfaceName) == false) {
272             logger.error("Cannot find qname {} with factory name {}, found {}", serviceInterfaceName, factoryName, serviceInterfaceQNames);
273             throw new IllegalArgumentException("Cannot find service interface " + serviceInterfaceName + " within factory " + factoryName );
274         }
275         String instanceName = ObjectNameUtil.getInstanceName(objectName);
276         ModuleIdentifier moduleIdentifier = new ModuleIdentifier(factoryName, instanceName);
277         Map<String /* refName */, ModuleIdentifier> ofQName = refNames.get(serviceInterfaceName);
278         // might be null
279         if (ofQName == null) {
280             ofQName = new HashMap<>();
281             refNames.put(serviceInterfaceName, ofQName);
282         }
283         ofQName.put(refName, moduleIdentifier);
284     }
285
286     @Override
287     public boolean removeServiceReference(String serviceInterfaceName, String refName) {
288         // is the qname known?
289         if (allQNames.contains(serviceInterfaceName) == false) {
290             logger.error("Cannot find qname {} in {}", serviceInterfaceName, allQNames);
291             throw new IllegalArgumentException("Cannot find service interface " + serviceInterfaceName);
292         }
293         Map<String, ModuleIdentifier> ofQName = refNames.get(serviceInterfaceName);
294         if (ofQName == null) {
295             return false;
296         }
297         return ofQName.remove(refName) != null;
298     }
299
300     @Override
301     public void removeAllServiceReferences() {
302         refNames.clear();
303     }
304
305     @Override
306     public boolean removeServiceReferences(ObjectName objectName) throws InstanceNotFoundException {
307         lookupRegistry.checkConfigBeanExists(objectName);
308         String factoryName = ObjectNameUtil.getFactoryName(objectName);
309         // check that service interface name exist
310         Set<String> serviceInterfaceQNames = factoryNamesToQNames.get(factoryName);
311         if (serviceInterfaceQNames == null) {
312             logger.error("Possible error in code: cannot find factoryName {} in {}, object name {}", factoryName, factoryNamesToQNames, objectName);
313             throw new IllegalStateException("Possible error in code: cannot find annotations of existing factory " + factoryName);
314         }
315         String instanceName = ObjectNameUtil.getInstanceName(objectName);
316         ModuleIdentifier moduleIdentifier = new ModuleIdentifier(factoryName, instanceName);
317         boolean found = false;
318         for(String qName: serviceInterfaceQNames){
319             Map<String, ModuleIdentifier> ofQName = refNames.get(qName);
320             if (ofQName != null) {
321                 for(Iterator<Entry<String, ModuleIdentifier>> it = ofQName.entrySet ().iterator(); it.hasNext();){
322                     Entry<String, ModuleIdentifier> next = it.next();
323                     if (next.getValue().equals(moduleIdentifier)) {
324                         found = true;
325                         it.remove();
326                     }
327                 }
328             }
329         }
330         return found;
331     }
332
333     @Override
334     public String toString() {
335         return "ServiceReferenceRegistryImpl{" +
336                 "refNames=" + refNames +
337                 ", factoryNamesToQNames=" + factoryNamesToQNames +
338                 '}';
339     }
340 }