Add Set<String> getAvailableModuleFactoryQNames() to config-api.
[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             @Override
80             public Set<String> getAvailableModuleFactoryQNames() {
81                 throw new UnsupportedOperationException();
82             }
83         };
84         return new ServiceReferenceRegistryImpl(Collections.<String, ModuleFactory>emptyMap(), lookupRegistry,
85                 Collections.<String /* qName */, Map<String /* refName */, ModuleIdentifier>>emptyMap());
86     }
87
88     /**
89      * Static constructor for transaction controller. Take current state as seen by config registry, allow writing new data.
90      */
91     public static ServiceReferenceWritableRegistry createSRWritableRegistry(ServiceReferenceReadableRegistry oldReadableRegistry,
92             LookupRegistry lookupRegistry, Map<String, Map.Entry<ModuleFactory, BundleContext>> currentlyRegisteredFactories) {
93
94         ServiceReferenceRegistryImpl old = (ServiceReferenceRegistryImpl) oldReadableRegistry;
95         Map<String, ModuleFactory> factories = extractFactoriesMap(currentlyRegisteredFactories);
96         return new ServiceReferenceRegistryImpl(factories, lookupRegistry, Collections.unmodifiableMap(old.refNames));
97     }
98
99     /**
100      * Copy back state to config registry after commit.
101      */
102     public static ServiceReferenceReadableRegistry createSRReadableRegistry(ServiceReferenceWritableRegistry oldWritableRegistry, LookupRegistry lookupRegistry) {
103         ServiceReferenceRegistryImpl old = (ServiceReferenceRegistryImpl) oldWritableRegistry;
104         // even if factories do change, nothing in the mapping can change between transactions
105         return new ServiceReferenceRegistryImpl(old.factories, lookupRegistry, Collections.unmodifiableMap(old.refNames));
106     }
107
108     private static Map<String, ModuleFactory> extractFactoriesMap(Map<String, Map.Entry<ModuleFactory, BundleContext>> currentlyRegisteredFactories) {
109         Map<String, ModuleFactory> result = new HashMap<>();
110         for (Entry<String, Entry<ModuleFactory, BundleContext>> entry : currentlyRegisteredFactories.entrySet()) {
111             result.put(entry.getKey(), entry.getValue().getKey());
112         }
113         return result;
114     }
115
116     private ServiceReferenceRegistryImpl(Map<String, ModuleFactory> factories, LookupRegistry lookupRegistry,
117                                          Map<String /* qName */, Map<String /* refName */, ModuleIdentifier>> refNamesToCopy) {
118         this.factories = factories;
119         this.lookupRegistry = lookupRegistry;
120         Map<String, Set<String /* QName */>> factoryNamesToQNames = new HashMap<>();
121         Set<ServiceInterfaceAnnotation> allAnnotations = new HashSet<>();
122         Set<String /* qName */> allQNames = new HashSet<>();
123         for (Entry<String, ModuleFactory> entry : factories.entrySet()) {
124             if (entry.getKey().equals(entry.getValue().getImplementationName()) == false) {
125                 logger.error("Possible error in code: Mismatch between supplied and actual name of {}", entry);
126                 throw new IllegalArgumentException("Possible error in code: Mismatch between supplied and actual name of " + entry);
127             }
128             Set<ServiceInterfaceAnnotation> siAnnotations = InterfacesHelper.getServiceInterfaceAnnotations(entry.getValue());
129             Set<String> qNames = new HashSet<>();
130             for (ServiceInterfaceAnnotation sia: siAnnotations) {
131                 qNames.add(sia.value());
132             }
133             allAnnotations.addAll(siAnnotations);
134             allQNames.addAll(qNames);
135             factoryNamesToQNames.put(entry.getKey(), Collections.unmodifiableSet(qNames));
136         }
137         this.factoryNamesToQNames = Collections.unmodifiableMap(factoryNamesToQNames);
138         this.allQNames = Collections.unmodifiableSet(allQNames);
139         // fill namespacesToAnnotations
140         Map<String /* namespace */, Map<String /* localName */, ServiceInterfaceAnnotation>> namespacesToAnnotations =
141                 new HashMap<>();
142         for (ServiceInterfaceAnnotation sia : allAnnotations) {
143             Map<String, ServiceInterfaceAnnotation> ofNamespace = namespacesToAnnotations.get(sia.namespace());
144             if (ofNamespace == null) {
145                 ofNamespace = new HashMap<>();
146                 namespacesToAnnotations.put(sia.namespace(), ofNamespace);
147             }
148             if (ofNamespace.containsKey(sia.localName())) {
149                 logger.error("Cannot construct namespacesToAnnotations map, conflict between local names in {}, offending local name: {}, map so far {}",
150                         sia.namespace(), sia.localName(), namespacesToAnnotations);
151                 throw new IllegalArgumentException("Conflict between local names in " + sia.namespace() + " : " + sia.localName());
152             }
153             ofNamespace.put(sia.localName(), sia);
154         }
155         this.namespacesToAnnotations = Collections.unmodifiableMap(namespacesToAnnotations);
156         // copy refNames
157         Map<String /* qName */, Map<String /* refName */, ModuleIdentifier>> deepCopy = new HashMap<>();
158         for (Entry<String, Map<String, ModuleIdentifier>> outerROEntry: refNamesToCopy.entrySet()) {
159             Map<String /* refName */, ModuleIdentifier> innerWritableMap = new HashMap<>();
160             deepCopy.put(outerROEntry.getKey(), innerWritableMap);
161             for (Entry<String, ModuleIdentifier> innerROEntry:  outerROEntry.getValue().entrySet()) {
162                 innerWritableMap.put(innerROEntry.getKey(), innerROEntry.getValue());
163             }
164         }
165         this.refNames = deepCopy;
166         logger.trace("factoryNamesToQNames:{}", this.factoryNamesToQNames);
167         logger.trace("refNames:{}", refNames);
168     }
169
170
171     @Override
172     public Set<String> lookupServiceInterfaceNames(ObjectName objectName) throws InstanceNotFoundException {
173         lookupRegistry.checkConfigBeanExists(objectName);
174
175         String factoryName = ObjectNameUtil.getFactoryName(objectName);
176         Set<String> serviceInterfaceAnnotations = factoryNamesToQNames.get(factoryName);
177         if (serviceInterfaceAnnotations == null) {
178             logger.error("Possible error in code: cannot find factory annotations of '{}' extracted from ON {} in {}",
179                     factoryName, objectName, factoryNamesToQNames);
180             throw new IllegalArgumentException("Cannot find factory with name " + factoryName);
181         }
182         return serviceInterfaceAnnotations;
183     }
184
185     @Override
186     public String getServiceInterfaceName(String namespace, String localName) {
187         Map<String /* localName */, ServiceInterfaceAnnotation> ofNamespace = namespacesToAnnotations.get(namespace);
188         if (ofNamespace == null) {
189             logger.error("Cannot find namespace {} in {}", namespace, namespacesToAnnotations);
190             throw new IllegalArgumentException("Cannot find namespace " + namespace);
191         }
192         ServiceInterfaceAnnotation sia = ofNamespace.get(localName);
193         if (sia == null) {
194             logger.error("Cannot find local name {} in namespace {}, found only {}", localName, namespace, ofNamespace);
195             throw new IllegalArgumentException("Cannot find local name " + localName + " in namespace " + namespace);
196         }
197         return sia.value();
198     }
199
200
201
202     // reading:
203
204     @Override
205     public Map<String /* serviceInterfaceName */, Map<String/* refName */, ObjectName>> getServiceMapping() {
206         Map<String /* serviceInterfaceName */, Map<String/* refName */, ObjectName>> result = new HashMap<>();
207         for (Entry<String /* qName */, Map<String, ModuleIdentifier>> outerEntry: refNames.entrySet()) {
208             String qName = outerEntry.getKey();
209             Map<String /* refName */, ObjectName> innerMap = new HashMap<>();
210             result.put(qName, innerMap);
211             for (Entry<String /* refName */, ModuleIdentifier> innerEntry: outerEntry.getValue().entrySet()) {
212                 ModuleIdentifier moduleIdentifier = innerEntry.getValue();
213                 ObjectName on;
214                 on = getObjectName(moduleIdentifier);
215                 innerMap.put(innerEntry.getKey(), on);
216             }
217         }
218         return result;
219     }
220
221     private ObjectName getObjectName(ModuleIdentifier moduleIdentifier) {
222         ObjectName on;
223         try {
224             on = lookupRegistry.lookupConfigBean(moduleIdentifier.getFactoryName(), moduleIdentifier.getInstanceName());
225         } catch (InstanceNotFoundException e) {
226             logger.error("Cannot find instance {}", moduleIdentifier);
227             throw new IllegalStateException("Cannot find instance " + moduleIdentifier, e);
228         }
229         return on;
230     }
231
232     @Override
233     public ObjectName lookupConfigBeanByServiceInterfaceName(String serviceInterfaceName, String refName) {
234         Map<String, ModuleIdentifier> innerMap = refNames.get(serviceInterfaceName);
235         if (innerMap == null) {
236             logger.error("Cannot find qname {} in {}", serviceInterfaceName, refName);
237             throw new IllegalArgumentException("Cannot find " + serviceInterfaceName);
238         }
239         ModuleIdentifier moduleIdentifier = innerMap.get(refName);
240         if (moduleIdentifier == null) {
241             logger.error("Cannot find refName {} in {}, using qname {}", refName, innerMap, serviceInterfaceName);
242             throw new IllegalArgumentException("Cannot find module based on service reference " + refName);
243         }
244         return getObjectName(moduleIdentifier);
245     }
246
247     @Override
248     public  Map<String /* refName */, ObjectName> lookupServiceReferencesByServiceInterfaceName(String serviceInterfaceName) {
249         Map<String, ModuleIdentifier> innerMap = refNames.get(serviceInterfaceName);
250         if (innerMap == null) {
251             logger.error("Cannot find qname {} in {}", serviceInterfaceName, refNames);
252             throw new IllegalArgumentException("Cannot find " + serviceInterfaceName);
253         }
254         Map<String /* refName */, ObjectName> result = new HashMap<>();
255         for (Entry<String/* refName */, ModuleIdentifier> entry: innerMap.entrySet()) {
256             ObjectName on = getObjectName(entry.getValue());
257             result.put(entry.getKey(), on);
258         }
259         return result;
260     }
261
262     // writing:
263
264     @Override
265     public void saveServiceReference(String serviceInterfaceName, String refName, ObjectName objectName)  throws InstanceNotFoundException {
266         // make sure it is found
267         lookupRegistry.checkConfigBeanExists(objectName);
268         String factoryName = ObjectNameUtil.getFactoryName(objectName);
269         // check that service interface name exist
270         Set<String> serviceInterfaceQNames = factoryNamesToQNames.get(factoryName);
271         if (serviceInterfaceQNames == null) {
272             logger.error("Possible error in code: cannot find factoryName {} in {}, object name {}", factoryName, factoryNamesToQNames, objectName);
273             throw new IllegalStateException("Possible error in code: cannot find annotations of existing factory " + factoryName);
274         }
275         // supplied serviceInterfaceName must exist in this collection
276         if (serviceInterfaceQNames.contains(serviceInterfaceName) == false) {
277             logger.error("Cannot find qname {} with factory name {}, found {}", serviceInterfaceName, factoryName, serviceInterfaceQNames);
278             throw new IllegalArgumentException("Cannot find service interface " + serviceInterfaceName + " within factory " + factoryName );
279         }
280         String instanceName = ObjectNameUtil.getInstanceName(objectName);
281         ModuleIdentifier moduleIdentifier = new ModuleIdentifier(factoryName, instanceName);
282         Map<String /* refName */, ModuleIdentifier> ofQName = refNames.get(serviceInterfaceName);
283         // might be null
284         if (ofQName == null) {
285             ofQName = new HashMap<>();
286             refNames.put(serviceInterfaceName, ofQName);
287         }
288         ofQName.put(refName, moduleIdentifier);
289     }
290
291     @Override
292     public boolean removeServiceReference(String serviceInterfaceName, String refName) {
293         // is the qname known?
294         if (allQNames.contains(serviceInterfaceName) == false) {
295             logger.error("Cannot find qname {} in {}", serviceInterfaceName, allQNames);
296             throw new IllegalArgumentException("Cannot find service interface " + serviceInterfaceName);
297         }
298         Map<String, ModuleIdentifier> ofQName = refNames.get(serviceInterfaceName);
299         if (ofQName == null) {
300             return false;
301         }
302         return ofQName.remove(refName) != null;
303     }
304
305     @Override
306     public void removeAllServiceReferences() {
307         refNames.clear();
308     }
309
310     @Override
311     public boolean removeServiceReferences(ObjectName objectName) throws InstanceNotFoundException {
312         lookupRegistry.checkConfigBeanExists(objectName);
313         String factoryName = ObjectNameUtil.getFactoryName(objectName);
314         // check that service interface name exist
315         Set<String> serviceInterfaceQNames = factoryNamesToQNames.get(factoryName);
316         if (serviceInterfaceQNames == null) {
317             logger.error("Possible error in code: cannot find factoryName {} in {}, object name {}", factoryName, factoryNamesToQNames, objectName);
318             throw new IllegalStateException("Possible error in code: cannot find annotations of existing factory " + factoryName);
319         }
320         String instanceName = ObjectNameUtil.getInstanceName(objectName);
321         ModuleIdentifier moduleIdentifier = new ModuleIdentifier(factoryName, instanceName);
322         boolean found = false;
323         for(String qName: serviceInterfaceQNames){
324             Map<String, ModuleIdentifier> ofQName = refNames.get(qName);
325             if (ofQName != null) {
326                 for(Iterator<Entry<String, ModuleIdentifier>> it = ofQName.entrySet ().iterator(); it.hasNext();){
327                     Entry<String, ModuleIdentifier> next = it.next();
328                     if (next.getValue().equals(moduleIdentifier)) {
329                         found = true;
330                         it.remove();
331                     }
332                 }
333             }
334         }
335         return found;
336     }
337
338     @Override
339     public String toString() {
340         return "ServiceReferenceRegistryImpl{" +
341                 "refNames=" + refNames +
342                 ", factoryNamesToQNames=" + factoryNamesToQNames +
343                 '}';
344     }
345 }