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