Resolve Bug:807 - Keep ModuleFactory references shutdown purposes.
[controller.git] / opendaylight / config / config-manager / src / main / java / org / opendaylight / controller / config / manager / impl / ConfigTransactionControllerImpl.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 static java.lang.String.format;
11
12 import java.util.ArrayList;
13 import java.util.Collection;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Map.Entry;
18 import java.util.Set;
19 import java.util.concurrent.atomic.AtomicBoolean;
20 import javax.annotation.Nullable;
21 import javax.annotation.concurrent.GuardedBy;
22 import javax.management.DynamicMBean;
23 import javax.management.InstanceAlreadyExistsException;
24 import javax.management.InstanceNotFoundException;
25 import javax.management.MBeanServer;
26 import javax.management.ObjectName;
27 import org.opendaylight.controller.config.api.DependencyResolver;
28 import org.opendaylight.controller.config.api.ModuleIdentifier;
29 import org.opendaylight.controller.config.api.ValidationException;
30 import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
31 import org.opendaylight.controller.config.manager.impl.dependencyresolver.DependencyResolverManager;
32 import org.opendaylight.controller.config.manager.impl.dependencyresolver.ModuleInternalTransactionalInfo;
33 import org.opendaylight.controller.config.manager.impl.dynamicmbean.DynamicWritableWrapper;
34 import org.opendaylight.controller.config.manager.impl.dynamicmbean.ReadOnlyAtomicBoolean;
35 import org.opendaylight.controller.config.manager.impl.dynamicmbean.ReadOnlyAtomicBoolean.ReadOnlyAtomicBooleanImpl;
36 import org.opendaylight.controller.config.manager.impl.factoriesresolver.HierarchicalConfigMBeanFactoriesHolder;
37 import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator;
38 import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator.TransactionModuleJMXRegistration;
39 import org.opendaylight.controller.config.spi.Module;
40 import org.opendaylight.controller.config.spi.ModuleFactory;
41 import org.opendaylight.yangtools.concepts.Identifiable;
42 import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry;
43 import org.osgi.framework.BundleContext;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 import static com.google.common.base.Preconditions.checkNotNull;
48 /**
49  * This is a JMX bean representing current transaction. It contains
50  * transaction identifier, unique version and parent version for
51  * optimistic locking.
52  */
53 class ConfigTransactionControllerImpl implements
54         ConfigTransactionControllerInternal,
55         ConfigTransactionControllerImplMXBean,
56         Identifiable<TransactionIdentifier> {
57     private static final Logger logger = LoggerFactory.getLogger(ConfigTransactionControllerImpl.class);
58
59     private final ConfigTransactionLookupRegistry txLookupRegistry;
60     private final ObjectName controllerON;
61
62     private final long parentVersion, currentVersion;
63     private final HierarchicalConfigMBeanFactoriesHolder factoriesHolder;
64     private final DependencyResolverManager dependencyResolverManager;
65     private final TransactionStatus transactionStatus;
66     private final MBeanServer transactionsMBeanServer;
67     private final Map<String, Map.Entry<ModuleFactory, BundleContext>> currentlyRegisteredFactories;
68
69     /**
70      * Disables ability of {@link DynamicWritableWrapper} to change attributes
71      * during validation.
72      */
73     @GuardedBy("this")
74     private final AtomicBoolean configBeanModificationDisabled = new AtomicBoolean(
75             false);
76     private final ReadOnlyAtomicBoolean readOnlyAtomicBoolean = new ReadOnlyAtomicBooleanImpl(
77             configBeanModificationDisabled);
78     private final MBeanServer configMBeanServer;
79
80     private final boolean blankTransaction;
81
82     @GuardedBy("this")
83     private final SearchableServiceReferenceWritableRegistry writableSRRegistry;
84
85     public ConfigTransactionControllerImpl(ConfigTransactionLookupRegistry txLookupRegistry,
86                                            long parentVersion, CodecRegistry codecRegistry, long currentVersion,
87                                            Map<String, Entry<ModuleFactory, BundleContext>> currentlyRegisteredFactories,
88                                            MBeanServer transactionsMBeanServer, MBeanServer configMBeanServer,
89                                            boolean blankTransaction, SearchableServiceReferenceWritableRegistry  writableSRRegistry) {
90         this.txLookupRegistry = txLookupRegistry;
91         String transactionName = txLookupRegistry.getTransactionIdentifier().getName();
92         this.controllerON = ObjectNameUtil.createTransactionControllerON(transactionName);
93         this.parentVersion = parentVersion;
94         this.currentVersion = currentVersion;
95         this.currentlyRegisteredFactories = currentlyRegisteredFactories;
96         this.factoriesHolder = new HierarchicalConfigMBeanFactoriesHolder(currentlyRegisteredFactories);
97         this.transactionStatus = new TransactionStatus();
98         this.dependencyResolverManager = new DependencyResolverManager(txLookupRegistry.getTransactionIdentifier(),
99                 transactionStatus, writableSRRegistry, codecRegistry);
100         this.transactionsMBeanServer = transactionsMBeanServer;
101         this.configMBeanServer = configMBeanServer;
102         this.blankTransaction = blankTransaction;
103         this.writableSRRegistry = writableSRRegistry;
104     }
105
106     @Override
107     public void copyExistingModulesAndProcessFactoryDiff(Collection<ModuleInternalInfo> existingModules, List<ModuleFactory> lastListOfFactories) {
108         // copy old configuration to this server
109         for (ModuleInternalInfo oldConfigInfo : existingModules) {
110             try {
111                 copyExistingModule(oldConfigInfo);
112             } catch (InstanceAlreadyExistsException e) {
113                 throw new IllegalStateException("Error while copying " + oldConfigInfo, e);
114             }
115         }
116         processDefaultBeans(lastListOfFactories);
117     }
118
119     private synchronized void processDefaultBeans(List<ModuleFactory> lastListOfFactories) {
120         transactionStatus.checkNotCommitStarted();
121         transactionStatus.checkNotAborted();
122
123         Set<ModuleFactory> oldSet = new HashSet<>(lastListOfFactories);
124         Set<ModuleFactory> newSet = new HashSet<>(factoriesHolder.getModuleFactories());
125
126         List<ModuleFactory> toBeAdded = new ArrayList<>();
127         List<ModuleFactory> toBeRemoved = new ArrayList<>();
128         for (ModuleFactory moduleFactory : factoriesHolder.getModuleFactories()) {
129             if (oldSet.contains(moduleFactory) == false) {
130                 toBeAdded.add(moduleFactory);
131             }
132         }
133         for (ModuleFactory moduleFactory : lastListOfFactories) {
134             if (newSet.contains(moduleFactory) == false) {
135                 toBeRemoved.add(moduleFactory);
136             }
137         }
138         // add default modules
139         for (ModuleFactory moduleFactory : toBeAdded) {
140             BundleContext bundleContext = getModuleFactoryBundleContext(moduleFactory.getImplementationName());
141             Set<? extends Module> defaultModules = moduleFactory.getDefaultModules(dependencyResolverManager,
142                     bundleContext);
143             for (Module module : defaultModules) {
144                 // ensure default module to be registered to jmx even if its module factory does not use dependencyResolverFactory
145                 DependencyResolver dependencyResolver = dependencyResolverManager.getOrCreate(module.getIdentifier());
146                 try {
147                     boolean defaultBean = true;
148                     putConfigBeanToJMXAndInternalMaps(module.getIdentifier(), module, moduleFactory, null,
149                             dependencyResolver, defaultBean, bundleContext);
150                 } catch (InstanceAlreadyExistsException e) {
151                     throw new IllegalStateException(e);
152                 }
153             }
154         }
155
156         // remove modules belonging to removed factories
157         for (ModuleFactory removedFactory : toBeRemoved) {
158             List<ModuleIdentifier> modulesOfRemovedFactory = dependencyResolverManager.findAllByFactory(removedFactory);
159             for (ModuleIdentifier name : modulesOfRemovedFactory) {
160                 destroyModule(name);
161             }
162         }
163     }
164
165
166     private synchronized void copyExistingModule(ModuleInternalInfo oldConfigBeanInfo) throws InstanceAlreadyExistsException {
167
168         transactionStatus.checkNotCommitStarted();
169         transactionStatus.checkNotAborted();
170         ModuleIdentifier moduleIdentifier = oldConfigBeanInfo.getIdentifier();
171         dependencyResolverManager.assertNotExists(moduleIdentifier);
172
173         ModuleFactory moduleFactory;
174         BundleContext bc;
175         try {
176             moduleFactory = factoriesHolder.findByModuleName(moduleIdentifier.getFactoryName());
177             bc = getModuleFactoryBundleContext(moduleFactory.getImplementationName());
178         } catch (InstanceNotFoundException e) {
179             throw new IllegalStateException(e);
180         }
181
182         Module module;
183         DependencyResolver dependencyResolver = dependencyResolverManager.getOrCreate(moduleIdentifier);
184         try {
185
186             module = moduleFactory.createModule(
187                     moduleIdentifier.getInstanceName(), dependencyResolver,
188                     oldConfigBeanInfo.getReadableModule(), bc);
189         } catch (Exception e) {
190             throw new IllegalStateException(format(
191                     "Error while copying old configuration from %s to %s",
192                     oldConfigBeanInfo, moduleFactory), e);
193         }
194         putConfigBeanToJMXAndInternalMaps(moduleIdentifier, module, moduleFactory, oldConfigBeanInfo, dependencyResolver,
195                 oldConfigBeanInfo.isDefaultBean(), bc);
196     }
197
198     @Override
199     public synchronized ObjectName createModule(String factoryName,
200                                                 String instanceName) throws InstanceAlreadyExistsException {
201
202         transactionStatus.checkNotCommitStarted();
203         transactionStatus.checkNotAborted();
204         ModuleIdentifier moduleIdentifier = new ModuleIdentifier(factoryName, instanceName);
205         dependencyResolverManager.assertNotExists(moduleIdentifier);
206
207         // find factory
208         ModuleFactory moduleFactory;
209         try {
210             moduleFactory = factoriesHolder.findByModuleName(factoryName);
211         } catch (InstanceNotFoundException e) {
212             throw new IllegalArgumentException(e);
213         }
214         DependencyResolver dependencyResolver = dependencyResolverManager.getOrCreate(moduleIdentifier);
215         BundleContext bundleContext = getModuleFactoryBundleContext(moduleFactory.getImplementationName());
216         Module module = moduleFactory.createModule(instanceName, dependencyResolver,
217                 bundleContext);
218         boolean defaultBean = false;
219         return putConfigBeanToJMXAndInternalMaps(moduleIdentifier, module,
220                 moduleFactory, null, dependencyResolver, defaultBean, bundleContext);
221     }
222
223     private synchronized ObjectName putConfigBeanToJMXAndInternalMaps(
224             ModuleIdentifier moduleIdentifier, Module module,
225             ModuleFactory moduleFactory,
226             @Nullable ModuleInternalInfo maybeOldConfigBeanInfo, DependencyResolver dependencyResolver,
227             boolean isDefaultBean, BundleContext bundleContext)
228             throws InstanceAlreadyExistsException {
229
230         logger.debug("Adding module {} to transaction {}", moduleIdentifier, this);
231         if (moduleIdentifier.equals(module.getIdentifier()) == false) {
232             throw new IllegalStateException("Incorrect name reported by module. Expected "
233                     + moduleIdentifier + ", got " + module.getIdentifier());
234         }
235         if (dependencyResolver.getIdentifier().equals(moduleIdentifier) == false) {
236             throw new IllegalStateException("Incorrect name reported by dependency resolver. Expected "
237                     + moduleIdentifier + ", got " + dependencyResolver.getIdentifier());
238         }
239         DynamicMBean writableDynamicWrapper = new DynamicWritableWrapper(
240                 module, moduleIdentifier, getTransactionIdentifier(),
241                 readOnlyAtomicBoolean, transactionsMBeanServer,
242                 configMBeanServer);
243
244         ObjectName writableON = ObjectNameUtil.createTransactionModuleON(
245                 getTransactionIdentifier().getName(), moduleIdentifier);
246         // put wrapper to jmx
247         TransactionModuleJMXRegistration transactionModuleJMXRegistration = getTxModuleJMXRegistrator()
248                 .registerMBean(writableDynamicWrapper, writableON);
249
250         dependencyResolverManager.put(
251                 moduleIdentifier, module, moduleFactory,
252                 maybeOldConfigBeanInfo, transactionModuleJMXRegistration, isDefaultBean, bundleContext);
253         return writableON;
254     }
255
256     @Override
257     public synchronized void destroyModule(ObjectName objectName) throws InstanceNotFoundException {
258         checkTransactionName(objectName);
259         ObjectNameUtil.checkDomain(objectName);
260         ModuleIdentifier moduleIdentifier = ObjectNameUtil.fromON(objectName,
261                 ObjectNameUtil.TYPE_MODULE);
262         destroyModule(moduleIdentifier);
263     }
264
265     private void checkTransactionName(ObjectName objectName) {
266         String foundTransactionName = ObjectNameUtil
267                 .getTransactionName(objectName);
268         if (getTransactionIdentifier().getName().equals(foundTransactionName) == false) {
269             throw new IllegalArgumentException("Wrong transaction name "
270                     + objectName);
271         }
272     }
273
274     private synchronized void destroyModule(ModuleIdentifier moduleIdentifier) {
275         logger.debug("Destroying module {} in transaction {}", moduleIdentifier, this);
276         transactionStatus.checkNotAborted();
277
278         ModuleInternalTransactionalInfo found = dependencyResolverManager.findModuleInternalTransactionalInfo(moduleIdentifier);
279         if (blankTransaction == false) {
280
281             if (found.isDefaultBean()) {
282                 logger.warn("Warning: removing default bean. This will be forbidden in next version of config-subsystem");
283             }
284         }
285         // first remove refNames, it checks for objectname existence
286
287         try {
288             writableSRRegistry.removeServiceReferences(
289                     ObjectNameUtil.createTransactionModuleON(getTransactionName(), moduleIdentifier));
290         } catch (InstanceNotFoundException e) {
291             logger.error("Possible code error: cannot find {} in {}", moduleIdentifier, writableSRRegistry);
292             throw new IllegalStateException("Possible code error: cannot find " + moduleIdentifier, e);
293         }
294
295         ModuleInternalTransactionalInfo removedTInfo = dependencyResolverManager.destroyModule(moduleIdentifier);
296         // remove from jmx
297         removedTInfo.getTransactionModuleJMXRegistration().close();
298     }
299
300     @Override
301     public long getParentVersion() {
302         return parentVersion;
303     }
304
305     @Override
306     public long getVersion() {
307         return currentVersion;
308     }
309
310     @Override
311     public synchronized void validateConfig() throws ValidationException {
312         if (configBeanModificationDisabled.get()) {
313             throw new IllegalStateException("Cannot start validation");
314         }
315         configBeanModificationDisabled.set(true);
316         try {
317             validate_noLocks();
318         } finally {
319             configBeanModificationDisabled.set(false);
320         }
321     }
322
323     private void validate_noLocks() throws ValidationException {
324         transactionStatus.checkNotAborted();
325         logger.trace("Validating transaction {}", getTransactionIdentifier());
326         // call validate()
327         List<ValidationException> collectedExceptions = new ArrayList<>();
328         for (Entry<ModuleIdentifier, Module> entry : dependencyResolverManager
329                 .getAllModules().entrySet()) {
330             ModuleIdentifier name = entry.getKey();
331             Module module = entry.getValue();
332             try {
333                 module.validate();
334             } catch (Exception e) {
335                 logger.warn("Validation exception in {}", getTransactionName(),
336                         e);
337                 collectedExceptions.add(ValidationException
338                         .createForSingleException(name, e));
339             }
340         }
341         if (collectedExceptions.size() > 0) {
342             throw ValidationException
343                     .createFromCollectedValidationExceptions(collectedExceptions);
344         }
345         logger.trace("Validated transaction {}", getTransactionIdentifier());
346     }
347
348     /**
349      * If this method passes validation, it will grab
350      * {@link TransactionStatus#secondPhaseCommitStarted} lock. This lock will
351      * prevent calling @{link #validateBeforeCommitAndLockTransaction},
352      * effectively only allowing to call {@link #secondPhaseCommit} after
353      * successful return of this method.
354      */
355     @Override
356     public synchronized CommitInfo validateBeforeCommitAndLockTransaction()
357             throws ValidationException {
358         transactionStatus.checkNotAborted();
359         transactionStatus.checkNotCommitStarted();
360         configBeanModificationDisabled.set(true);
361         try {
362             validate_noLocks();
363         } catch (ValidationException e) {
364             logger.trace("Commit failed on validation");
365             configBeanModificationDisabled.set(false); // recoverable error
366             throw e;
367         }
368         // errors in this state are not recoverable. modules are not mutable
369         // anymore.
370         transactionStatus.setSecondPhaseCommitStarted();
371         return dependencyResolverManager.toCommitInfo();
372     }
373
374     /**
375      * {@inheritDoc}
376      */
377     @Override
378     public synchronized List<ModuleIdentifier> secondPhaseCommit() {
379         transactionStatus.checkNotAborted();
380         transactionStatus.checkCommitStarted();
381         if (configBeanModificationDisabled.get() == false) {
382             throw new IllegalStateException(
383                     "Internal error - validateBeforeCommitAndLockTransaction should be called "
384                             + "to obtain a lock");
385         }
386
387         logger.trace("Committing transaction {}", getTransactionIdentifier());
388
389         // call getInstance()
390         for (Entry<ModuleIdentifier, Module> entry : dependencyResolverManager
391                 .getAllModules().entrySet()) {
392             Module module = entry.getValue();
393             ModuleIdentifier name = entry.getKey();
394             try {
395                 logger.debug("About to commit {} in transaction {}",
396                         name, getTransactionIdentifier());
397                 AutoCloseable instance = module.getInstance();
398                 checkNotNull(instance, "Instance is null:{} in transaction {}", name, getTransactionIdentifier());
399             } catch (Exception e) {
400                 logger.error("Commit failed on {} in transaction {}", name,
401                         getTransactionIdentifier(), e);
402                 internalAbort();
403                 throw new IllegalStateException(
404                         format("Error - getInstance() failed for %s in transaction %s",
405                                 name, getTransactionIdentifier()), e);
406             }
407         }
408
409         // count dependency order
410
411         logger.trace("Committed configuration {}", getTransactionIdentifier());
412         transactionStatus.setCommitted();
413
414         return dependencyResolverManager.getSortedModuleIdentifiers();
415     }
416
417     @Override
418     public synchronized void abortConfig() {
419         transactionStatus.checkNotCommitStarted();
420         transactionStatus.checkNotAborted();
421         internalAbort();
422     }
423
424     private void internalAbort() {
425         logger.trace("Aborting {}", this);
426         transactionStatus.setAborted();
427         close();
428     }
429
430     public void close() {
431         dependencyResolverManager.close();
432         txLookupRegistry.close();
433     }
434
435     @Override
436     public ObjectName getControllerObjectName() {
437         return controllerON;
438     }
439
440     @Override
441     public String getTransactionName() {
442         return getTransactionIdentifier().getName();
443     }
444
445     /**
446      * {@inheritDoc}
447      */
448     @Override
449     public Set<ObjectName> lookupConfigBeans() {
450         return txLookupRegistry.lookupConfigBeans();
451     }
452
453     /**
454      * {@inheritDoc}
455      */
456     @Override
457     public Set<ObjectName> lookupConfigBeans(String moduleName) {
458         return txLookupRegistry.lookupConfigBeans(moduleName);
459     }
460
461     /**
462      * {@inheritDoc}
463      */
464     @Override
465     public ObjectName lookupConfigBean(String moduleName, String instanceName)
466             throws InstanceNotFoundException {
467         return txLookupRegistry.lookupConfigBean(moduleName, instanceName);
468     }
469
470     /**
471      * {@inheritDoc}
472      */
473     @Override
474     public Set<ObjectName> lookupConfigBeans(String moduleName, String instanceName) {
475         return txLookupRegistry.lookupConfigBeans(moduleName, instanceName);
476     }
477
478     /**
479      * {@inheritDoc}
480      */
481     @Override
482     public void checkConfigBeanExists(ObjectName objectName) throws InstanceNotFoundException {
483         txLookupRegistry.checkConfigBeanExists(objectName);
484     }
485     // --
486
487     /**
488      * {@inheritDoc}
489      */
490     @Override
491     public Set<String> getAvailableModuleNames() {
492         return factoriesHolder.getModuleNames();
493     }
494
495     @Override
496     public boolean isClosed() {
497         return transactionStatus.isAbortedOrCommitted();
498     }
499
500     @Override
501     public String toString() {
502         StringBuilder sb = new StringBuilder();
503         sb.append("transactionName=");
504         sb.append(getTransactionName());
505         return sb.toString();
506     }
507
508     // @VisibleForTesting
509
510     TransactionModuleJMXRegistrator getTxModuleJMXRegistrator() {
511         return txLookupRegistry.getTxModuleJMXRegistrator();
512     }
513
514     public TransactionIdentifier getName() {
515         return getTransactionIdentifier();
516     }
517
518     @Override
519     public List<ModuleFactory> getCurrentlyRegisteredFactories() {
520         return new ArrayList<>(factoriesHolder.getModuleFactories());
521     }
522
523     @Override
524     public TransactionIdentifier getIdentifier() {
525         return getTransactionIdentifier();
526     }
527
528     @Override
529     public BundleContext getModuleFactoryBundleContext(String factoryName) {
530         Map.Entry<ModuleFactory, BundleContext> factoryBundleContextEntry = this.currentlyRegisteredFactories.get(factoryName);
531         if (factoryBundleContextEntry == null || factoryBundleContextEntry.getValue() == null) {
532             throw new NullPointerException("Bundle context of " + factoryName + " ModuleFactory not found.");
533         }
534         return factoryBundleContextEntry.getValue();
535     }
536
537     // service reference functionality:
538
539
540     @Override
541     public synchronized ObjectName lookupConfigBeanByServiceInterfaceName(String serviceInterfaceQName, String refName) {
542         return writableSRRegistry.lookupConfigBeanByServiceInterfaceName(serviceInterfaceQName, refName);
543     }
544
545     @Override
546     public synchronized Map<String, Map<String, ObjectName>> getServiceMapping() {
547         return writableSRRegistry.getServiceMapping();
548     }
549
550     @Override
551     public synchronized Map<String, ObjectName> lookupServiceReferencesByServiceInterfaceName(String serviceInterfaceQName) {
552         return writableSRRegistry.lookupServiceReferencesByServiceInterfaceName(serviceInterfaceQName);
553     }
554
555     @Override
556     public synchronized Set<String> lookupServiceInterfaceNames(ObjectName objectName) throws InstanceNotFoundException {
557         return writableSRRegistry.lookupServiceInterfaceNames(objectName);
558     }
559
560     @Override
561     public synchronized String getServiceInterfaceName(String namespace, String localName) {
562         return writableSRRegistry.getServiceInterfaceName(namespace, localName);
563     }
564
565     @Override
566     public synchronized ObjectName saveServiceReference(String serviceInterfaceName, String refName, ObjectName moduleON) throws InstanceNotFoundException {
567         return writableSRRegistry.saveServiceReference(serviceInterfaceName, refName, moduleON);
568     }
569
570     @Override
571     public synchronized void removeServiceReference(String serviceInterfaceName, String refName) throws InstanceNotFoundException {
572         writableSRRegistry.removeServiceReference(serviceInterfaceName, refName);
573     }
574
575     @Override
576     public synchronized void removeAllServiceReferences() {
577         writableSRRegistry.removeAllServiceReferences();
578     }
579
580     @Override
581     public boolean removeServiceReferences(ObjectName objectName) throws InstanceNotFoundException {
582         return writableSRRegistry.removeServiceReferences(objectName);
583     }
584
585     @Override
586     public SearchableServiceReferenceWritableRegistry  getWritableRegistry() {
587         return writableSRRegistry;
588     }
589
590     @Override
591     public TransactionIdentifier getTransactionIdentifier() {
592         return txLookupRegistry.getTransactionIdentifier();
593     }
594
595     @Override
596     public Set<String> getAvailableModuleFactoryQNames() {
597         return txLookupRegistry.getAvailableModuleFactoryQNames();
598     }
599
600     @Override
601     public void checkServiceReferenceExists(ObjectName objectName) throws InstanceNotFoundException {
602         writableSRRegistry.checkServiceReferenceExists(objectName);
603     }
604
605     @Override
606     public ObjectName getServiceReference(String serviceInterfaceQName, String refName) throws InstanceNotFoundException {
607         return writableSRRegistry.getServiceReference(serviceInterfaceQName, refName);
608     }
609 }