2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.config.manager.impl;
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.jmx.BaseJMXRegistrator;
17 import org.opendaylight.controller.config.manager.impl.jmx.ServiceReference;
18 import org.opendaylight.controller.config.manager.impl.jmx.ServiceReferenceMXBeanImpl;
19 import org.opendaylight.controller.config.manager.impl.jmx.ServiceReferenceRegistrator;
20 import org.opendaylight.controller.config.manager.impl.jmx.ServiceReferenceRegistrator.ServiceReferenceJMXRegistration;
21 import org.opendaylight.controller.config.manager.impl.jmx.ServiceReferenceRegistrator.ServiceReferenceTransactionRegistratorFactory;
22 import org.opendaylight.controller.config.manager.impl.jmx.ServiceReferenceRegistrator.ServiceReferenceTransactionRegistratorFactoryImpl;
23 import org.opendaylight.controller.config.manager.impl.util.InterfacesHelper;
24 import org.opendaylight.controller.config.spi.ModuleFactory;
25 import org.osgi.framework.BundleContext;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
29 import javax.management.InstanceAlreadyExistsException;
30 import javax.management.InstanceNotFoundException;
31 import javax.management.ObjectName;
32 import java.util.Collections;
33 import java.util.HashMap;
34 import java.util.HashSet;
36 import java.util.Map.Entry;
39 public class ServiceReferenceRegistryImpl implements CloseableServiceReferenceReadableRegistry, ServiceReferenceWritableRegistry {
40 private static final Logger logger = LoggerFactory.getLogger(ServiceReferenceRegistryImpl.class);
42 private final Map<String, ModuleFactory> factories;
43 private final Map<String, Set<String>> factoryNamesToQNames;
44 // validator of incoming ObjectNames - throws InstanceNotFoundException if not found either in registry or transaction
45 private final LookupRegistry lookupRegistry;
46 private final ServiceReferenceRegistrator serviceReferenceRegistrator;
47 // helper method for getting QName of SI from namespace + local name
48 private final Map<String /* namespace */, Map<String /* local name */, ServiceInterfaceAnnotation>> namespacesToAnnotations;
49 // all Service Interface qNames for sanity checking
50 private final Set<String /* qName */> allQNames;
52 // actual reference database
53 private final Map<ServiceReference, ModuleIdentifier> refNames = new HashMap<>();
54 private final boolean writable;
55 private final Map<ServiceReference, Entry<ServiceReferenceMXBeanImpl, ServiceReferenceJMXRegistration>> mBeans = new HashMap<>();
58 * Static constructor for config registry. Since only transaction can write to this registry, it will
61 public static CloseableServiceReferenceReadableRegistry createInitialSRLookupRegistry() {
62 // since this is initial state, just throw exception:
63 LookupRegistry lookupRegistry = new LookupRegistry() {
65 public Set<ObjectName> lookupConfigBeans() {
66 throw new UnsupportedOperationException();
70 public Set<ObjectName> lookupConfigBeans(String moduleName) {
71 throw new UnsupportedOperationException();
75 public Set<ObjectName> lookupConfigBeans(String moduleName, String instanceName) {
76 throw new UnsupportedOperationException();
80 public ObjectName lookupConfigBean(String moduleName, String instanceName) throws InstanceNotFoundException {
81 throw new UnsupportedOperationException();
85 public void checkConfigBeanExists(ObjectName objectName) throws InstanceNotFoundException {
86 throw new InstanceNotFoundException("Cannot find " + objectName + " - Tried to use mocking registry");
90 public Set<String> getAvailableModuleFactoryQNames() {
91 throw new UnsupportedOperationException();
95 public String toString() {
99 ServiceReferenceTransactionRegistratorFactory serviceReferenceRegistratorFactory = new ServiceReferenceTransactionRegistratorFactory(){
101 public ServiceReferenceRegistrator create() {
102 return new ServiceReferenceRegistrator() {
104 public String getNullableTransactionName() {
105 throw new UnsupportedOperationException();
109 public ServiceReferenceJMXRegistration registerMBean(ServiceReferenceMXBeanImpl object, ObjectName on) throws InstanceAlreadyExistsException {
110 throw new UnsupportedOperationException();
114 public void close() {
120 return new ServiceReferenceRegistryImpl(Collections.<String, ModuleFactory>emptyMap(), lookupRegistry,
121 serviceReferenceRegistratorFactory, false);
125 * Static constructor for transaction controller. Take current state as seen by config registry, allow writing new data.
127 public static ServiceReferenceWritableRegistry createSRWritableRegistry(ServiceReferenceReadableRegistry oldReadableRegistry,
128 ConfigTransactionLookupRegistry txLookupRegistry,
129 Map<String, Map.Entry<ModuleFactory, BundleContext>> currentlyRegisteredFactories) {
131 if (txLookupRegistry == null) {
132 throw new IllegalArgumentException("txLookupRegistry is null");
134 ServiceReferenceRegistryImpl old = (ServiceReferenceRegistryImpl) oldReadableRegistry;
135 Map<String, ModuleFactory> factories = extractFactoriesMap(currentlyRegisteredFactories);
136 ServiceReferenceTransactionRegistratorFactory serviceReferenceRegistratorFactory = new ServiceReferenceTransactionRegistratorFactoryImpl(
137 txLookupRegistry.getTxModuleJMXRegistrator(), txLookupRegistry.getTxModuleJMXRegistrator().getTransactionName());
138 ServiceReferenceRegistryImpl newRegistry = new ServiceReferenceRegistryImpl(factories, txLookupRegistry,
139 serviceReferenceRegistratorFactory, true);
140 copy(old, newRegistry, txLookupRegistry.getTransactionIdentifier().getName());
145 * Copy back state to config registry after commit.
147 public static CloseableServiceReferenceReadableRegistry createSRReadableRegistry(ServiceReferenceWritableRegistry oldWritableRegistry,
148 LookupRegistry lookupRegistry, BaseJMXRegistrator baseJMXRegistrator) {
149 ServiceReferenceRegistryImpl old = (ServiceReferenceRegistryImpl) oldWritableRegistry;
151 // even if factories do change, nothing in the mapping can change between transactions
152 ServiceReferenceTransactionRegistratorFactory serviceReferenceRegistratorFactory = new ServiceReferenceTransactionRegistratorFactoryImpl(baseJMXRegistrator);
153 ServiceReferenceRegistryImpl newRegistry = new ServiceReferenceRegistryImpl(old.factories, lookupRegistry,
154 serviceReferenceRegistratorFactory, false);
155 copy(old, newRegistry, null);
160 * Fill refNames and mBeans maps from old instance
162 private static void copy(ServiceReferenceRegistryImpl old, ServiceReferenceRegistryImpl newRegistry, String nullableDstTransactionName) {
163 for (Entry<ServiceReference, Entry<ServiceReferenceMXBeanImpl, ServiceReferenceJMXRegistration>> refNameEntry : old.mBeans.entrySet()) {
164 ObjectName currentImplementation;
165 ObjectName currentImplementationSrc = refNameEntry.getValue().getKey().getCurrentImplementation();
166 if (nullableDstTransactionName != null) {
167 currentImplementation = ObjectNameUtil.withTransactionName(currentImplementationSrc, nullableDstTransactionName);
169 currentImplementation = ObjectNameUtil.withoutTransactionName(currentImplementationSrc);
172 boolean skipChecks = true;
173 newRegistry.saveServiceReference(refNameEntry.getKey(), currentImplementation, skipChecks);
174 } catch (InstanceNotFoundException e) {
175 logger.error("Cannot save service reference({}, {})", refNameEntry.getKey(), currentImplementation);
176 throw new IllegalStateException("Possible code error", e);
181 private static Map<String, ModuleFactory> extractFactoriesMap(Map<String, Map.Entry<ModuleFactory, BundleContext>> currentlyRegisteredFactories) {
182 Map<String, ModuleFactory> result = new HashMap<>();
183 for (Entry<String, Entry<ModuleFactory, BundleContext>> entry : currentlyRegisteredFactories.entrySet()) {
184 result.put(entry.getKey(), entry.getValue().getKey());
189 private ServiceReferenceRegistryImpl(Map<String, ModuleFactory> factories, LookupRegistry lookupRegistry,
190 ServiceReferenceTransactionRegistratorFactory serviceReferenceRegistratorFactory,
192 this.factories = factories;
193 this.writable = writable;
194 this.lookupRegistry = lookupRegistry;
196 this.serviceReferenceRegistrator = serviceReferenceRegistratorFactory.create();
198 Map<String, Set<String /* QName */>> modifiableFactoryNamesToQNames = new HashMap<>();
199 Set<ServiceInterfaceAnnotation> allAnnotations = new HashSet<>();
200 Set<String /* qName */> allQNames = new HashSet<>();
201 for (Entry<String, ModuleFactory> entry : factories.entrySet()) {
202 if (entry.getKey().equals(entry.getValue().getImplementationName()) == false) {
203 logger.error("Possible error in code: Mismatch between supplied and actual name of {}", entry);
204 throw new IllegalArgumentException("Possible error in code: Mismatch between supplied and actual name of " + entry);
206 Set<ServiceInterfaceAnnotation> siAnnotations = InterfacesHelper.getServiceInterfaceAnnotations(entry.getValue());
207 Set<String> qNames = new HashSet<>();
208 for (ServiceInterfaceAnnotation sia: siAnnotations) {
209 qNames.add(sia.value());
211 allAnnotations.addAll(siAnnotations);
212 allQNames.addAll(qNames);
213 modifiableFactoryNamesToQNames.put(entry.getKey(), Collections.unmodifiableSet(qNames));
215 this.factoryNamesToQNames = Collections.unmodifiableMap(modifiableFactoryNamesToQNames);
216 this.allQNames = Collections.unmodifiableSet(allQNames);
217 // fill namespacesToAnnotations
218 Map<String /* namespace */, Map<String /* localName */, ServiceInterfaceAnnotation>> modifiableNamespacesToAnnotations =
220 for (ServiceInterfaceAnnotation sia : allAnnotations) {
221 Map<String, ServiceInterfaceAnnotation> ofNamespace = modifiableNamespacesToAnnotations.get(sia.namespace());
222 if (ofNamespace == null) {
223 ofNamespace = new HashMap<>();
224 modifiableNamespacesToAnnotations.put(sia.namespace(), ofNamespace);
226 if (ofNamespace.containsKey(sia.localName())) {
227 logger.error("Cannot construct namespacesToAnnotations map, conflict between local names in {}, offending local name: {}, map so far {}",
228 sia.namespace(), sia.localName(), modifiableNamespacesToAnnotations);
229 throw new IllegalArgumentException("Conflict between local names in " + sia.namespace() + " : " + sia.localName());
231 ofNamespace.put(sia.localName(), sia);
233 this.namespacesToAnnotations = Collections.unmodifiableMap(modifiableNamespacesToAnnotations);
235 logger.trace("factoryNamesToQNames:{}", this.factoryNamesToQNames);
240 public synchronized Set<String> lookupServiceInterfaceNames(ObjectName objectName) throws InstanceNotFoundException {
241 lookupRegistry.checkConfigBeanExists(objectName);
243 String factoryName = ObjectNameUtil.getFactoryName(objectName);
244 Set<String> serviceInterfaceAnnotations = factoryNamesToQNames.get(factoryName);
245 if (serviceInterfaceAnnotations == null) {
246 logger.error("Possible error in code: cannot find factory annotations of '{}' extracted from ON {} in {}",
247 factoryName, objectName, factoryNamesToQNames);
248 throw new IllegalArgumentException("Cannot find factory with name " + factoryName);
250 return serviceInterfaceAnnotations;
254 public synchronized String getServiceInterfaceName(String namespace, String localName) {
255 Map<String /* localName */, ServiceInterfaceAnnotation> ofNamespace = namespacesToAnnotations.get(namespace);
256 if (ofNamespace == null) {
257 logger.error("Cannot find namespace {} in {}", namespace, namespacesToAnnotations);
258 throw new IllegalArgumentException("Cannot find namespace " + namespace);
260 ServiceInterfaceAnnotation sia = ofNamespace.get(localName);
262 logger.error("Cannot find local name {} in namespace {}, found only {}", localName, namespace, ofNamespace);
263 throw new IllegalArgumentException("Cannot find local name " + localName + " in namespace " + namespace);
271 public synchronized Map<String /* serviceInterfaceName */, Map<String/* refName */, ObjectName>> getServiceMapping() {
272 Map<String /* serviceInterfaceName */, Map<String/* refName */, ObjectName>> result = new HashMap<>();
273 for (Entry<ServiceReference, ModuleIdentifier> entry: refNames.entrySet()) {
274 String qName = entry.getKey().getServiceInterfaceName();
275 Map<String /* refName */, ObjectName> innerMap = result.get(qName);
276 if (innerMap == null) {
277 innerMap = new HashMap<>();
278 result.put(qName, innerMap);
280 innerMap.put(entry.getKey().getRefName(), getObjectName(entry.getValue()));
285 private ObjectName getObjectName(ModuleIdentifier moduleIdentifier) {
288 on = lookupRegistry.lookupConfigBean(moduleIdentifier.getFactoryName(), moduleIdentifier.getInstanceName());
289 } catch (InstanceNotFoundException e) {
290 logger.error("Cannot find instance {}", moduleIdentifier);
291 throw new IllegalStateException("Cannot find instance " + moduleIdentifier, e);
297 public synchronized ObjectName lookupConfigBeanByServiceInterfaceName(String serviceInterfaceQName, String refName) {
298 ServiceReference serviceReference = new ServiceReference(serviceInterfaceQName, refName);
299 ModuleIdentifier moduleIdentifier = refNames.get(serviceReference);
300 if (moduleIdentifier == null) {
301 logger.error("Cannot find qname {} and refName {} in {}", serviceInterfaceQName, refName, refName);
302 throw new IllegalArgumentException("Cannot find " + serviceReference);
304 return getObjectName(moduleIdentifier);
308 public synchronized Map<String /* refName */, ObjectName> lookupServiceReferencesByServiceInterfaceName(String serviceInterfaceQName) {
309 Map<String, Map<String, ObjectName>> serviceMapping = getServiceMapping();
310 Map<String, ObjectName> innerMap = serviceMapping.get(serviceInterfaceQName);
311 if (innerMap == null) {
312 logger.error("Cannot find qname {} in {}", serviceInterfaceQName, refNames);
313 throw new IllegalArgumentException("Cannot find " + serviceInterfaceQName);
319 public synchronized ObjectName getServiceReference(String serviceInterfaceQName, String refName) throws InstanceNotFoundException {
320 ServiceReference serviceReference = new ServiceReference(serviceInterfaceQName, refName);
321 if (mBeans.containsKey(serviceReference) == false) {
322 throw new InstanceNotFoundException("Cannot find " + serviceReference);
324 return getServiceON(serviceReference);
328 public synchronized void checkServiceReferenceExists(ObjectName objectName) throws InstanceNotFoundException {
329 String actualTransactionName = ObjectNameUtil.getTransactionName(objectName);
330 String expectedTransactionName = serviceReferenceRegistrator.getNullableTransactionName();
331 if (writable & actualTransactionName == null || (writable && actualTransactionName.equals(expectedTransactionName) == false)) {
332 throw new IllegalArgumentException("Mismatched transaction name in " + objectName);
334 String serviceQName = ObjectNameUtil.getServiceQName(objectName);
335 String referenceName = ObjectNameUtil.getReferenceName(objectName);
336 ServiceReference serviceReference = new ServiceReference(serviceQName, referenceName);
337 if (refNames.containsKey(serviceReference) == false) {
338 logger.warn("Cannot find {} in {}", serviceReference, refNames);
339 throw new InstanceNotFoundException("Service reference not found:" + objectName);
345 private void assertWritable() {
346 if (writable == false) {
347 throw new IllegalStateException("Cannot write to readable registry");
352 public synchronized ObjectName saveServiceReference(String serviceInterfaceName, String refName, ObjectName moduleON) throws InstanceNotFoundException {
354 ServiceReference serviceReference = new ServiceReference(serviceInterfaceName, refName);
355 return saveServiceReference(serviceReference, moduleON);
358 private synchronized ObjectName saveServiceReference(ServiceReference serviceReference, ObjectName moduleON)
359 throws InstanceNotFoundException{
360 return saveServiceReference(serviceReference, moduleON, false);
363 private synchronized ObjectName saveServiceReference(ServiceReference serviceReference, ObjectName moduleON,
364 boolean skipChecks) throws InstanceNotFoundException {
366 // make sure it is found
367 if (skipChecks == false) {
368 lookupRegistry.checkConfigBeanExists(moduleON);
370 String factoryName = ObjectNameUtil.getFactoryName(moduleON);
371 String instanceName = ObjectNameUtil.getInstanceName(moduleON);
372 ModuleIdentifier moduleIdentifier = new ModuleIdentifier(factoryName, instanceName);
374 // check that service interface name exist
375 Set<String> serviceInterfaceQNames = factoryNamesToQNames.get(moduleIdentifier.getFactoryName());
376 if (serviceInterfaceQNames == null) {
377 logger.error("Possible error in code: cannot find factoryName {} in {}, {}", moduleIdentifier.getFactoryName(),
378 factoryNamesToQNames, moduleIdentifier);
379 throw new IllegalStateException("Possible error in code: cannot find annotations of existing factory " + moduleIdentifier.getFactoryName());
381 // supplied serviceInterfaceName must exist in this collection
382 if (serviceInterfaceQNames.contains(serviceReference.getServiceInterfaceName()) == false) {
383 logger.error("Cannot find qName {} with factory name {}, found {}", serviceReference.getServiceInterfaceName(), moduleIdentifier.getFactoryName(), serviceInterfaceQNames);
384 throw new IllegalArgumentException("Cannot find service interface " + serviceReference.getServiceInterfaceName() + " within factory " + moduleIdentifier.getFactoryName());
388 // create service reference object name, put to mBeans
389 ObjectName result = getServiceON(serviceReference);
390 Entry<ServiceReferenceMXBeanImpl, ServiceReferenceJMXRegistration> mxBeanEntry = mBeans.get(serviceReference);
391 if (mxBeanEntry == null) {
392 // create dummy mx bean
393 ServiceReferenceMXBeanImpl dummyMXBean = new ServiceReferenceMXBeanImpl(moduleON);
394 ServiceReferenceJMXRegistration dummyMXBeanRegistration;
396 dummyMXBeanRegistration = serviceReferenceRegistrator.registerMBean(dummyMXBean, result);
397 } catch (InstanceAlreadyExistsException e) {
398 throw new IllegalStateException("Possible error in code. Cannot register " + result, e);
400 mBeans.put(serviceReference, createMXBeanEntry(dummyMXBean, dummyMXBeanRegistration));
403 mxBeanEntry.getKey().setCurrentImplementation(moduleON);
406 refNames.put(serviceReference, moduleIdentifier);
410 private Entry<ServiceReferenceMXBeanImpl, ServiceReferenceJMXRegistration> createMXBeanEntry(
411 final ServiceReferenceMXBeanImpl mxBean, final ServiceReferenceJMXRegistration registration) {
412 return new Entry<ServiceReferenceMXBeanImpl, ServiceReferenceJMXRegistration>() {
414 public ServiceReferenceMXBeanImpl getKey() {
419 public ServiceReferenceJMXRegistration getValue() {
424 public ServiceReferenceJMXRegistration setValue(ServiceReferenceJMXRegistration value) {
425 throw new UnsupportedOperationException();
430 private ObjectName getServiceON(ServiceReference serviceReference) {
432 return ObjectNameUtil.createTransactionServiceON(serviceReferenceRegistrator.getNullableTransactionName(),
433 serviceReference.getServiceInterfaceName(), serviceReference.getRefName());
435 return ObjectNameUtil.createReadOnlyServiceON(serviceReference.getServiceInterfaceName(), serviceReference.getRefName());
440 public synchronized void removeServiceReference(String serviceInterfaceName, String refName) throws InstanceNotFoundException{
441 ServiceReference serviceReference = new ServiceReference(serviceInterfaceName, refName);
442 removeServiceReference(serviceReference);
445 private synchronized void removeServiceReference(ServiceReference serviceReference) throws InstanceNotFoundException {
446 logger.debug("Removing service reference {} from {}", serviceReference, this);
448 // is the qName known?
449 if (allQNames.contains(serviceReference.getServiceInterfaceName()) == false) {
450 logger.error("Cannot find qname {} in {}", serviceReference.getServiceInterfaceName(), allQNames);
451 throw new IllegalArgumentException("Cannot find service interface " + serviceReference.getServiceInterfaceName());
453 ModuleIdentifier removed = refNames.remove(serviceReference);
454 if (removed == null){
455 throw new InstanceNotFoundException("Cannot find " + serviceReference.getServiceInterfaceName());
457 Entry<ServiceReferenceMXBeanImpl, ServiceReferenceJMXRegistration> entry = mBeans.remove(serviceReference);
459 throw new IllegalStateException("Possible code error: cannot remove from mBeans: " + serviceReference);
461 entry.getValue().close();
465 public synchronized void removeAllServiceReferences() {
467 for (ServiceReference serviceReference: mBeans.keySet()) {
469 removeServiceReference(serviceReference);
470 } catch (InstanceNotFoundException e) {
471 throw new IllegalStateException("Possible error in code", e);
477 public synchronized boolean removeServiceReferences(ObjectName moduleObjectName) throws InstanceNotFoundException {
479 Set<ServiceReference> serviceReferencesLinkingTo = findServiceReferencesLinkingTo(moduleObjectName);
480 for (ServiceReference sr : serviceReferencesLinkingTo) {
481 removeServiceReference(sr);
483 return serviceReferencesLinkingTo.isEmpty() == false;
486 private synchronized Set<ServiceReference> findServiceReferencesLinkingTo(ObjectName moduleObjectName) throws InstanceNotFoundException {
487 lookupRegistry.checkConfigBeanExists(moduleObjectName);
488 String factoryName = ObjectNameUtil.getFactoryName(moduleObjectName);
489 // check that service interface name exist
490 Set<String> serviceInterfaceQNames = factoryNamesToQNames.get(factoryName);
491 if (serviceInterfaceQNames == null) {
492 logger.error("Possible error in code: cannot find factoryName {} in {}, object name {}", factoryName, factoryNamesToQNames, moduleObjectName);
493 throw new IllegalStateException("Possible error in code: cannot find annotations of existing factory " + factoryName);
495 String instanceName = ObjectNameUtil.getInstanceName(moduleObjectName);
496 ModuleIdentifier moduleIdentifier = new ModuleIdentifier(factoryName, instanceName);
497 Set<ServiceReference> result = new HashSet<>();
498 for (Entry<ServiceReference, ModuleIdentifier> entry : refNames.entrySet()) {
499 if (entry.getValue().equals(moduleIdentifier)) {
500 result.add(entry.getKey());
508 public String toString() {
509 return "ServiceReferenceRegistryImpl{" +
510 "lookupRegistry=" + lookupRegistry +
511 "refNames=" + refNames +
512 ", factoryNamesToQNames=" + factoryNamesToQNames +
517 public void close() {
518 serviceReferenceRegistrator.close();