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