Remove yang-test
[controller.git] / opendaylight / config / config-manager / src / main / java / org / opendaylight / controller / config / manager / impl / ConfigRegistryImpl.java
1 /*
2  * Copyright (c) 2013, 2017 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 com.google.common.collect.Maps;
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.HashMap;
15 import java.util.Iterator;
16 import java.util.LinkedList;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Map.Entry;
20 import java.util.Set;
21 import java.util.concurrent.ConcurrentHashMap;
22 import java.util.concurrent.ConcurrentMap;
23 import java.util.concurrent.TimeUnit;
24 import java.util.concurrent.atomic.AtomicBoolean;
25 import java.util.concurrent.locks.Lock;
26 import java.util.concurrent.locks.ReentrantLock;
27 import javax.annotation.concurrent.GuardedBy;
28 import javax.annotation.concurrent.ThreadSafe;
29 import javax.management.InstanceAlreadyExistsException;
30 import javax.management.InstanceNotFoundException;
31 import javax.management.MBeanServer;
32 import javax.management.MBeanServerFactory;
33 import javax.management.ObjectName;
34 import org.opendaylight.controller.config.api.ConflictingVersionException;
35 import org.opendaylight.controller.config.api.ModuleIdentifier;
36 import org.opendaylight.controller.config.api.RuntimeBeanRegistratorAwareModule;
37 import org.opendaylight.controller.config.api.ValidationException;
38 import org.opendaylight.controller.config.api.annotations.ServiceInterfaceAnnotation;
39 import org.opendaylight.controller.config.api.jmx.CommitStatus;
40 import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
41 import org.opendaylight.controller.config.manager.impl.ConfigTransactionLookupRegistry.TransactionJMXRegistratorFactory;
42 import org.opendaylight.controller.config.manager.impl.dependencyresolver.DestroyedModule;
43 import org.opendaylight.controller.config.manager.impl.dependencyresolver.ModuleInternalTransactionalInfo;
44 import org.opendaylight.controller.config.manager.impl.dynamicmbean.DynamicReadableWrapper;
45 import org.opendaylight.controller.config.manager.impl.factoriesresolver.HierarchicalConfigMBeanFactoriesHolder;
46 import org.opendaylight.controller.config.manager.impl.factoriesresolver.ModuleFactoriesResolver;
47 import org.opendaylight.controller.config.manager.impl.jmx.BaseJMXRegistrator;
48 import org.opendaylight.controller.config.manager.impl.jmx.ModuleJMXRegistrator;
49 import org.opendaylight.controller.config.manager.impl.jmx.RootRuntimeBeanRegistratorImpl;
50 import org.opendaylight.controller.config.manager.impl.osgi.BeanToOsgiServiceManager;
51 import org.opendaylight.controller.config.manager.impl.osgi.BeanToOsgiServiceManager.OsgiRegistration;
52 import org.opendaylight.controller.config.manager.impl.osgi.mapping.BindingContextProvider;
53 import org.opendaylight.controller.config.manager.impl.util.LookupBeansUtil;
54 import org.opendaylight.controller.config.manager.impl.util.ModuleQNameUtil;
55 import org.opendaylight.controller.config.spi.Module;
56 import org.opendaylight.controller.config.spi.ModuleFactory;
57 import org.osgi.framework.BundleContext;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 /**
62  * Singleton that is responsible for creating and committing Config
63  * Transactions. It is registered in Platform MBean Server.
64  */
65 @ThreadSafe
66 public class ConfigRegistryImpl implements AutoCloseable, ConfigRegistryImplMXBean {
67     private static final Logger LOG = LoggerFactory.getLogger(ConfigRegistryImpl.class);
68     private static final ObjectName NOOP_TX_NAME = ObjectNameUtil.createTransactionControllerON("noop");
69
70     private final ModuleFactoriesResolver resolver;
71     private final MBeanServer configMBeanServer;
72     private final BindingContextProvider bindingContextProvider;
73
74     private volatile long version = 0;
75     private volatile long versionCounter = 0;
76
77     /**
78      * Contains current configuration in form of {moduleName:{instanceName,read only
79      * module}} for copying state to new transaction. Each running module is present
80      * just once, no matter how many interfaces it exposes.
81      */
82     private final ConfigHolder currentConfig = new ConfigHolder();
83
84     /**
85      * Will return true unless there was a transaction that succeeded during
86      * validation but failed in second phase of commit. In this case the server is
87      * unstable and its state is undefined.
88      */
89     private volatile boolean isHealthy = true;
90
91     /**
92      * Holds the map of transactions and purges it each time its content is
93      * requested.
94      */
95     private final TransactionsHolder transactionsHolder = new TransactionsHolder();
96
97     private final BaseJMXRegistrator baseJMXRegistrator;
98
99     private final BeanToOsgiServiceManager beanToOsgiServiceManager;
100
101     // internal jmx server for read only beans
102     private final MBeanServer registryMBeanServer;
103     // internal jmx server shared by all transactions
104     private final MBeanServer transactionsMBeanServer;
105
106     private final AtomicBoolean closed = new AtomicBoolean();
107
108     private final Object readableSRRegistryLock = new Object();
109
110     private final Lock configTransactionLock = new ReentrantLock();
111
112     // Used for finding new factory instances for default module functionality
113     @GuardedBy("configTransactionLock")
114     private List<ModuleFactory> lastListOfFactories = Collections.emptyList();
115
116     // switched in every 2ndPC
117     @GuardedBy("readableSRRegistryLock")
118     private CloseableServiceReferenceReadableRegistry readableSRRegistry = ServiceReferenceRegistryImpl
119             .createInitialSRLookupRegistry();
120
121     // constructor
122     public ConfigRegistryImpl(final ModuleFactoriesResolver resolver, final MBeanServer configMBeanServer,
123             final BindingContextProvider bindingContextProvider) {
124         this(resolver, configMBeanServer, new BaseJMXRegistrator(configMBeanServer), bindingContextProvider);
125     }
126
127     // constructor
128     public ConfigRegistryImpl(final ModuleFactoriesResolver resolver, final MBeanServer configMBeanServer,
129             final BaseJMXRegistrator baseJMXRegistrator, final BindingContextProvider bindingContextProvider) {
130         this.resolver = resolver;
131         this.beanToOsgiServiceManager = new BeanToOsgiServiceManager();
132         this.configMBeanServer = configMBeanServer;
133         this.baseJMXRegistrator = baseJMXRegistrator;
134         this.bindingContextProvider = bindingContextProvider;
135         this.registryMBeanServer = MBeanServerFactory
136                 .createMBeanServer("ConfigRegistry" + configMBeanServer.getDefaultDomain());
137         this.transactionsMBeanServer = MBeanServerFactory
138                 .createMBeanServer("ConfigTransactions" + configMBeanServer.getDefaultDomain());
139     }
140
141     /**
142      * Create new {@link ConfigTransactionControllerImpl }.
143      */
144     @Override
145     public ObjectName beginConfig() {
146         return beginConfig(false);
147     }
148
149     /**
150      * It returns true if this transaction is created automatically by
151      * org.opendaylight.controller.config.manager.impl.osgi.BlankTransactionServiceTracker.
152      *
153      * @param blankTransaction
154      *            true if this transaction is created automatically by
155      *            org.opendaylight.controller.config.manager.impl.osgi.BlankTransactionServiceTracker
156      *
157      * @return object name
158      */
159     public ObjectName beginConfig(final boolean blankTransaction) {
160         // If we're closed or in the process of closing then all modules are destroyed
161         // or being destroyed
162         // so there's no point in trying to acquire the lock and beginning an actual
163         // transaction. Also we want
164         // to avoid trying to lock as it may block the shutdown process if there is an
165         // outstanding transaction
166         // attempting to be committed.
167         //
168         // We could throw an exception here to indicate this but that's not part of the
169         // original API contract
170         // and callers may not be expecting an unchecked exception. Therefore we return
171         // a special transaction
172         // handle that signifies a "no-op".
173         if (closed.get()) {
174             return NOOP_TX_NAME;
175         }
176
177         if (blankTransaction) {
178             try {
179                 // For a "blank" transaction, we'll try to obtain the config lock but "blank"
180                 // transactions
181                 // are initiated via OSGi events so we don't want to block indefinitely or for a
182                 // long period
183                 // of time.
184                 if (!configTransactionLock.tryLock(5, TimeUnit.SECONDS)) {
185                     LOG.debug("Timed out trying to obtain configTransactionLock");
186                     return NOOP_TX_NAME;
187                 }
188             } catch (final InterruptedException e) {
189                 LOG.debug("Interrupted trying to obtain configTransactionLock", e);
190                 Thread.currentThread().interrupt();
191                 return NOOP_TX_NAME;
192             }
193         } else {
194             configTransactionLock.lock();
195         }
196
197         try {
198             return beginConfigSafe(blankTransaction).getControllerObjectName();
199         } finally {
200             configTransactionLock.unlock();
201         }
202     }
203
204     @GuardedBy("configTransactionLock")
205     private ConfigTransactionControllerInternal beginConfigSafe(final boolean blankTransaction) {
206         versionCounter++;
207         final String transactionName = "ConfigTransaction-" + version + "-" + versionCounter;
208
209         TransactionJMXRegistratorFactory factory = () -> baseJMXRegistrator
210                 .createTransactionJMXRegistrator(transactionName);
211
212         Map<String, Map.Entry<ModuleFactory, BundleContext>> allCurrentFactories = new HashMap<>(
213                 resolver.getAllFactories());
214
215         // add all factories that disappeared from SR but are still committed
216         for (ModuleInternalInfo moduleInternalInfo : currentConfig.getEntries()) {
217             String name = moduleInternalInfo.getModuleFactory().getImplementationName();
218             if (!allCurrentFactories.containsKey(name)) {
219                 LOG.trace("Factory {} not found in SR, using reference from previous commit", name);
220                 allCurrentFactories.put(name, Maps.immutableEntry(moduleInternalInfo.getModuleFactory(),
221                         moduleInternalInfo.getBundleContext()));
222             }
223         }
224         allCurrentFactories = Collections.unmodifiableMap(allCurrentFactories);
225
226         // closed by transaction controller
227         ConfigTransactionLookupRegistry txLookupRegistry = new ConfigTransactionLookupRegistry(
228                 new TransactionIdentifier(transactionName), factory, allCurrentFactories);
229         SearchableServiceReferenceWritableRegistry writableRegistry = ServiceReferenceRegistryImpl
230                 .createSRWritableRegistry(readableSRRegistry, txLookupRegistry, allCurrentFactories);
231
232         ConfigTransactionControllerInternal transactionController = new ConfigTransactionControllerImpl(
233                 txLookupRegistry, version, bindingContextProvider, versionCounter, allCurrentFactories,
234                 transactionsMBeanServer, configMBeanServer, blankTransaction, writableRegistry);
235         try {
236             txLookupRegistry.registerMBean(transactionController, transactionController.getControllerObjectName());
237         } catch (final InstanceAlreadyExistsException e) {
238             throw new IllegalStateException(e);
239         }
240         transactionController.copyExistingModulesAndProcessFactoryDiff(currentConfig.getEntries(), lastListOfFactories);
241         transactionsHolder.add(transactionName, transactionController, txLookupRegistry);
242         return transactionController;
243     }
244
245     @Override
246     public CommitStatus commitConfig(final ObjectName transactionControllerON)
247             throws ValidationException, ConflictingVersionException {
248         if (NOOP_TX_NAME.equals(transactionControllerON) || closed.get()) {
249             return new CommitStatus(Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
250         }
251
252         configTransactionLock.lock();
253         try {
254             return commitConfigSafe(transactionControllerON);
255         } finally {
256             configTransactionLock.unlock();
257         }
258     }
259
260     @GuardedBy("configTransactionLock")
261     private CommitStatus commitConfigSafe(final ObjectName transactionControllerON)
262             throws ConflictingVersionException, ValidationException {
263         final String transactionName = ObjectNameUtil.getTransactionName(transactionControllerON);
264         LOG.trace("About to commit {}. Current parentVersion: {}, versionCounter {}", transactionName, version,
265                 versionCounter);
266
267         // find ConfigTransactionController
268         Map<String, Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>> transactions =
269                 transactionsHolder.getCurrentTransactions();
270         Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry> configTransactionControllerEntry =
271                 transactions.get(transactionName);
272         if (configTransactionControllerEntry == null) {
273             throw new IllegalArgumentException(String.format("Transaction with name '%s' not found", transactionName));
274         }
275         ConfigTransactionControllerInternal configTransactionController = configTransactionControllerEntry.getKey();
276         // check optimistic lock
277         if (version != configTransactionController.getParentVersion()) {
278             throw new ConflictingVersionException(
279                     String.format("Optimistic lock failed. Expected parent version %d, was %d", version,
280                             configTransactionController.getParentVersion()));
281         }
282         // optimistic lock ok
283
284         CommitInfo commitInfo = configTransactionController.validateBeforeCommitAndLockTransaction();
285         lastListOfFactories = Collections
286                 .unmodifiableList(configTransactionController.getCurrentlyRegisteredFactories());
287         return secondPhaseCommit(configTransactionController, commitInfo, configTransactionControllerEntry.getValue());
288     }
289
290     @GuardedBy("configTransactionLock")
291     private CommitStatus secondPhaseCommit(final ConfigTransactionControllerInternal configTransactionController,
292             final CommitInfo commitInfo, final ConfigTransactionLookupRegistry txLookupRegistry) {
293
294         // close instances which were destroyed by the user, including
295         // (hopefully) runtime beans
296         for (DestroyedModule toBeDestroyed : commitInfo.getDestroyedFromPreviousTransactions()) {
297             // closes instance (which should close
298             // runtime jmx registrator),
299             // also closes osgi registration and ModuleJMXRegistrator
300             // registration
301             toBeDestroyed.close();
302             currentConfig.remove(toBeDestroyed.getIdentifier());
303         }
304
305         // set RuntimeBeanRegistrators on beans implementing
306         // RuntimeBeanRegistratorAwareModule
307         Map<ModuleIdentifier, RootRuntimeBeanRegistratorImpl> runtimeRegistrators = new HashMap<>();
308         for (ModuleInternalTransactionalInfo entry : commitInfo.getCommitted().values()) {
309             // set runtime jmx registrator if required
310             Module module = entry.getProxiedModule();
311             RootRuntimeBeanRegistratorImpl runtimeBeanRegistrator = null;
312
313             if (module instanceof RuntimeBeanRegistratorAwareModule) {
314
315                 if (entry.hasOldModule()) {
316
317                     if (module.canReuse(entry.getOldInternalInfo().getReadableModule().getModule())) {
318                         runtimeBeanRegistrator = entry.getOldInternalInfo().getRuntimeBeanRegistrator();
319                         ((RuntimeBeanRegistratorAwareModule) module).setRuntimeBeanRegistrator(runtimeBeanRegistrator);
320                     } else {
321                         runtimeBeanRegistrator = baseJMXRegistrator.createRuntimeBeanRegistrator(entry.getIdentifier());
322                         entry.getOldInternalInfo().getRuntimeBeanRegistrator().close();
323                         ((RuntimeBeanRegistratorAwareModule) module).setRuntimeBeanRegistrator(runtimeBeanRegistrator);
324                     }
325                 } else {
326                     runtimeBeanRegistrator = baseJMXRegistrator.createRuntimeBeanRegistrator(entry.getIdentifier());
327                     ((RuntimeBeanRegistratorAwareModule) module).setRuntimeBeanRegistrator(runtimeBeanRegistrator);
328                 }
329             }
330             // save it to info so it is accessible afterwards
331             if (runtimeBeanRegistrator != null) {
332                 runtimeRegistrators.put(entry.getIdentifier(), runtimeBeanRegistrator);
333             }
334         }
335
336         // can register runtime beans
337         List<ModuleIdentifier> orderedModuleIdentifiers = configTransactionController.secondPhaseCommit();
338         txLookupRegistry.close();
339         configTransactionController.close();
340
341         // copy configuration to read only mode
342         List<ObjectName> newInstances = new LinkedList<>();
343         List<ObjectName> reusedInstances = new LinkedList<>();
344         List<ObjectName> recreatedInstances = new LinkedList<>();
345
346         Map<Module, ModuleInternalInfo> newConfigEntries = new HashMap<>();
347
348         int orderingIdx = 0;
349         for (ModuleIdentifier moduleIdentifier : orderedModuleIdentifiers) {
350             LOG.trace("Registering {}", moduleIdentifier);
351             ModuleInternalTransactionalInfo entry = commitInfo.getCommitted().get(moduleIdentifier);
352             if (entry == null) {
353                 throw new NullPointerException("Module not found " + moduleIdentifier);
354             }
355
356             ObjectName primaryReadOnlyON = ObjectNameUtil.createReadOnlyModuleON(moduleIdentifier);
357
358             // determine if current instance was recreated or reused or is new
359
360             // rules for closing resources:
361             // osgi registration - will be reused if possible.
362             // module jmx registration - will be (re)created every time, needs
363             // to be closed here
364             // runtime jmx registration - should be taken care of by module
365             // itself
366             // instance - is closed only if it was destroyed
367             ModuleJMXRegistrator newModuleJMXRegistrator = baseJMXRegistrator.createModuleJMXRegistrator();
368
369             OsgiRegistration osgiRegistration = null;
370             AutoCloseable instance = entry.getProxiedModule().getInstance();
371             if (entry.hasOldModule()) {
372                 ModuleInternalInfo oldInternalInfo = entry.getOldInternalInfo();
373                 DynamicReadableWrapper oldReadableConfigBean = oldInternalInfo.getReadableModule();
374                 currentConfig.remove(entry.getIdentifier());
375
376                 // test if old instance == new instance
377                 if (oldReadableConfigBean.getInstance().equals(instance)) {
378                     // reused old instance:
379                     // wrap in readable dynamic mbean
380                     reusedInstances.add(primaryReadOnlyON);
381                     osgiRegistration = oldInternalInfo.getOsgiRegistration();
382                 } else {
383                     // recreated instance:
384                     // it is responsibility of module to call the old instance -
385                     // we just need to unregister configbean
386                     recreatedInstances.add(primaryReadOnlyON);
387
388                     // close old osgi registration
389                     oldInternalInfo.getOsgiRegistration().close();
390                 }
391
392                 // close old module jmx registrator
393                 oldInternalInfo.getModuleJMXRegistrator().close();
394
395                 // We no longer need old internal info. Clear it out, so we do not create a
396                 // serial leak evidenced
397                 // by BUG-4514. The reason is that modules retain their resolver, which retains
398                 // modules. If we retain
399                 // the old module, we would have the complete reconfiguration history held in
400                 // heap for no good reason.
401                 entry.clearOldInternalInfo();
402             } else {
403                 // new instance:
404                 // wrap in readable dynamic mbean
405                 newInstances.add(primaryReadOnlyON);
406             }
407             Module realModule = entry.getRealModule();
408
409             DynamicReadableWrapper newReadableConfigBean = new DynamicReadableWrapper(realModule, instance,
410                     moduleIdentifier, registryMBeanServer, configMBeanServer);
411
412             // register to JMX
413             try {
414                 newModuleJMXRegistrator.registerMBean(newReadableConfigBean, primaryReadOnlyON);
415             } catch (final InstanceAlreadyExistsException e) {
416                 throw new IllegalStateException("Possible code error, already registered:" + primaryReadOnlyON, e);
417             }
418
419             // register services to OSGi
420             Map<ServiceInterfaceAnnotation, String> annotationMapping = configTransactionController
421                     .getWritableRegistry().findServiceInterfaces(moduleIdentifier);
422             BundleContext bc = configTransactionController
423                     .getModuleFactoryBundleContext(entry.getModuleFactory().getImplementationName());
424             if (osgiRegistration == null) {
425                 osgiRegistration = beanToOsgiServiceManager.registerToOsgi(newReadableConfigBean.getInstance(),
426                         moduleIdentifier, bc, annotationMapping);
427             } else {
428                 osgiRegistration.updateRegistrations(annotationMapping, bc, instance);
429             }
430
431             RootRuntimeBeanRegistratorImpl runtimeBeanRegistrator = runtimeRegistrators.get(entry.getIdentifier());
432             ModuleInternalInfo newInfo = new ModuleInternalInfo(entry.getIdentifier(), newReadableConfigBean,
433                     osgiRegistration, runtimeBeanRegistrator, newModuleJMXRegistrator, orderingIdx,
434                     entry.isDefaultBean(), entry.getModuleFactory(), entry.getBundleContext());
435
436             newConfigEntries.put(realModule, newInfo);
437             orderingIdx++;
438         }
439         currentConfig.addAll(newConfigEntries.values());
440
441         // update version
442         version = configTransactionController.getVersion();
443
444         // switch readable Service Reference Registry
445         synchronized (readableSRRegistryLock) {
446             readableSRRegistry.close();
447             readableSRRegistry = ServiceReferenceRegistryImpl.createSRReadableRegistry(
448                     configTransactionController.getWritableRegistry(), this, baseJMXRegistrator);
449         }
450
451         return new CommitStatus(newInstances, reusedInstances, recreatedInstances);
452     }
453
454     /**
455      * {@inheritDoc}
456      */
457     @Override
458     public List<ObjectName> getOpenConfigs() {
459         Map<String, Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>> transactions =
460                 transactionsHolder.getCurrentTransactions();
461         List<ObjectName> result = new ArrayList<>(transactions.size());
462         for (Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>
463             configTransactionControllerEntry : transactions.values()) {
464             result.add(configTransactionControllerEntry.getKey().getControllerObjectName());
465         }
466         return result;
467     }
468
469     /**
470      * Abort open transactions and unregister read only modules. Since this class is
471      * not responsible for registering itself under
472      * {@link org.opendaylight.controller.config.api.ConfigRegistry#OBJECT_NAME}, it
473      * will not unregister itself here.
474      */
475     @Override
476     public void close() {
477         if (!closed.compareAndSet(false, true)) {
478             return;
479         }
480
481         // abort transactions
482         Map<String, Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>> transactions =
483                 transactionsHolder.getCurrentTransactions();
484         for (Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>
485             configTransactionControllerEntry : transactions.values()) {
486             ConfigTransactionControllerInternal configTransactionController = configTransactionControllerEntry.getKey();
487             configTransactionControllerEntry.getValue().close();
488             configTransactionController.abortConfig();
489         }
490
491         // destroy all live objects one after another in order of the dependency
492         // hierarchy, from top to bottom
493         List<DestroyedModule> destroyedModules = currentConfig.getModulesToBeDestroyed();
494
495         LOG.info("ConfigRegistry closing - destroying {} modules", destroyedModules.size());
496
497         for (DestroyedModule destroyedModule : destroyedModules) {
498             destroyedModule.close();
499         }
500         // unregister MBeans that failed to unregister properly
501         baseJMXRegistrator.close();
502         // remove jmx servers
503         MBeanServerFactory.releaseMBeanServer(registryMBeanServer);
504         MBeanServerFactory.releaseMBeanServer(transactionsMBeanServer);
505
506         LOG.info("ConfigRegistry closed");
507     }
508
509     /**
510      * {@inheritDoc}
511      */
512     @Override
513     public long getVersion() {
514         return version;
515     }
516
517     /**
518      * {@inheritDoc}
519      */
520     @Override
521     public Set<String> getAvailableModuleNames() {
522         return new HierarchicalConfigMBeanFactoriesHolder(resolver.getAllFactories()).getModuleNames();
523     }
524
525     /**
526      * {@inheritDoc}
527      */
528     @Override
529     public boolean isHealthy() {
530         return isHealthy;
531     }
532
533     // filtering methods
534
535     /**
536      * {@inheritDoc}
537      */
538     @Override
539     public Set<ObjectName> lookupConfigBeans() {
540         return lookupConfigBeans("*", "*");
541     }
542
543     /**
544      * {@inheritDoc}
545      */
546     @Override
547     public Set<ObjectName> lookupConfigBeans(final String moduleName) {
548         return lookupConfigBeans(moduleName, "*");
549     }
550
551     /**
552      * {@inheritDoc}
553      */
554     @Override
555     public Set<ObjectName> lookupConfigBeans(final String moduleName, final String instanceName) {
556         ObjectName namePattern = ObjectNameUtil.createModulePattern(moduleName, instanceName);
557         return baseJMXRegistrator.queryNames(namePattern, null);
558     }
559
560     /**
561      * {@inheritDoc}
562      */
563     @Override
564     public ObjectName lookupConfigBean(final String moduleName, final String instanceName)
565             throws InstanceNotFoundException {
566         return LookupBeansUtil.lookupConfigBean(this, moduleName, instanceName);
567     }
568
569     /**
570      * {@inheritDoc}
571      */
572     @Override
573     public Set<ObjectName> lookupRuntimeBeans() {
574         return lookupRuntimeBeans("*", "*");
575     }
576
577     /**
578      * {@inheritDoc}
579      */
580     @Override
581     public Set<ObjectName> lookupRuntimeBeans(final String moduleName, final String instanceName) {
582         String finalModuleName = moduleName == null ? "*" : moduleName;
583         String finalInstanceName = instanceName == null ? "*" : instanceName;
584         ObjectName namePattern = ObjectNameUtil.createRuntimeBeanPattern(finalModuleName, finalInstanceName);
585         return baseJMXRegistrator.queryNames(namePattern, null);
586     }
587
588     @Override
589     public void checkConfigBeanExists(final ObjectName objectName) throws InstanceNotFoundException {
590         ObjectNameUtil.checkDomain(objectName);
591         ObjectNameUtil.checkType(objectName, ObjectNameUtil.TYPE_MODULE);
592         String transactionName = ObjectNameUtil.getTransactionName(objectName);
593         if (transactionName != null) {
594             throw new IllegalArgumentException(
595                     "Transaction attribute not supported in registry, wrong ObjectName: " + objectName);
596         }
597         // make sure exactly one match is found:
598         LookupBeansUtil.lookupConfigBean(this, ObjectNameUtil.getFactoryName(objectName),
599                 ObjectNameUtil.getInstanceName(objectName));
600     }
601
602     // service reference functionality:
603     @Override
604     public ObjectName lookupConfigBeanByServiceInterfaceName(final String serviceInterfaceQName, final String refName) {
605         synchronized (readableSRRegistryLock) {
606             return readableSRRegistry.lookupConfigBeanByServiceInterfaceName(serviceInterfaceQName, refName);
607         }
608     }
609
610     @Override
611     public Map<String, Map<String, ObjectName>> getServiceMapping() {
612         synchronized (readableSRRegistryLock) {
613             return readableSRRegistry.getServiceMapping();
614         }
615     }
616
617     @Override
618     public Map<String, ObjectName> lookupServiceReferencesByServiceInterfaceName(final String serviceInterfaceQName) {
619         synchronized (readableSRRegistryLock) {
620             return readableSRRegistry.lookupServiceReferencesByServiceInterfaceName(serviceInterfaceQName);
621         }
622     }
623
624     @Override
625     public Set<String> lookupServiceInterfaceNames(final ObjectName objectName) throws InstanceNotFoundException {
626         synchronized (readableSRRegistryLock) {
627             return readableSRRegistry.lookupServiceInterfaceNames(objectName);
628         }
629     }
630
631     @Override
632     public String getServiceInterfaceName(final String namespace, final String localName) {
633         synchronized (readableSRRegistryLock) {
634             return readableSRRegistry.getServiceInterfaceName(namespace, localName);
635         }
636     }
637
638     @Override
639     public void checkServiceReferenceExists(final ObjectName objectName) throws InstanceNotFoundException {
640         synchronized (readableSRRegistryLock) {
641             readableSRRegistry.checkServiceReferenceExists(objectName);
642         }
643     }
644
645     @Override
646     public ObjectName getServiceReference(final String serviceInterfaceQName, final String refName)
647             throws InstanceNotFoundException {
648         synchronized (readableSRRegistryLock) {
649             return readableSRRegistry.getServiceReference(serviceInterfaceQName, refName);
650         }
651     }
652
653     @Override
654     public Set<String> getAvailableModuleFactoryQNames() {
655         return ModuleQNameUtil.getQNames(resolver.getAllFactories());
656     }
657
658     @Override
659     public String toString() {
660         return "ConfigRegistryImpl{" + "versionCounter=" + versionCounter + ", version=" + version + '}';
661     }
662
663     /**
664      * Inner class holding transactions and purges it each time its content is
665      * requested.
666      */
667     class TransactionsHolder {
668         /**
669          * This map keeps transaction names and
670          * {@link ConfigTransactionControllerInternal} instances, because platform
671          * MBeanServer transforms mbeans into another representation. Map is cleaned
672          * every time current transactions are requested.
673          */
674         private final ConcurrentMap<String /* transactionName */,
675             Entry<ConfigTransactionControllerInternal,
676             ConfigTransactionLookupRegistry>> transactions = new ConcurrentHashMap<>();
677
678         public void add(final String transactionName, final ConfigTransactionControllerInternal transactionController,
679                 final ConfigTransactionLookupRegistry txLookupRegistry) {
680             Object oldValue = transactions.putIfAbsent(transactionName,
681                     Maps.immutableEntry(transactionController, txLookupRegistry));
682             if (oldValue != null) {
683                 throw new IllegalStateException("Error: two transactions with same name");
684             }
685         }
686
687         /**
688          * Purges closed transactions from transactions map. Calling this method more
689          * than once within the method can modify the resulting map that was obtained in
690          * previous calls.
691          *
692          * @return current view on transactions map.
693          */
694         public Map<String, Entry<ConfigTransactionControllerInternal,
695             ConfigTransactionLookupRegistry>> getCurrentTransactions() {
696             // first, remove closed transaction
697             for (Iterator<Entry<String, Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>>>
698                 it = transactions.entrySet().iterator(); it.hasNext();) {
699                 Entry<String, Entry<ConfigTransactionControllerInternal, ConfigTransactionLookupRegistry>> entry = it
700                         .next();
701                 if (entry.getValue().getKey().isClosed()) {
702                     it.remove();
703                 }
704             }
705             return Collections.unmodifiableMap(transactions);
706         }
707     }
708
709     /**
710      * Inner class that holds currently running modules.
711      */
712     class ConfigHolder {
713         private final ConcurrentMap<ModuleIdentifier, ModuleInternalInfo> currentConfig = new ConcurrentHashMap<>();
714
715         /**
716          * Add all modules to the internal map. Also add service instance to OSGi
717          * Service Registry.
718          */
719         public void addAll(final Collection<ModuleInternalInfo> configInfos) {
720             if (!currentConfig.isEmpty()) {
721                 throw new IllegalStateException("Error - some config entries were not removed: " + currentConfig);
722             }
723             for (ModuleInternalInfo configInfo : configInfos) {
724                 add(configInfo);
725             }
726         }
727
728         private void add(final ModuleInternalInfo configInfo) {
729             ModuleInternalInfo oldValue = currentConfig.putIfAbsent(configInfo.getIdentifier(), configInfo);
730             if (oldValue != null) {
731                 throw new IllegalStateException(
732                         "Cannot overwrite module with same name:" + configInfo.getIdentifier() + ":" + configInfo);
733             }
734         }
735
736         /**
737          * Remove entry from current config.
738          */
739         public void remove(final ModuleIdentifier name) {
740             ModuleInternalInfo removed = currentConfig.remove(name);
741             if (removed == null) {
742                 throw new IllegalStateException("Cannot remove from ConfigHolder - name not found:" + name);
743             }
744         }
745
746         public Collection<ModuleInternalInfo> getEntries() {
747             return currentConfig.values();
748         }
749
750         public List<DestroyedModule> getModulesToBeDestroyed() {
751             List<DestroyedModule> result = new ArrayList<>();
752             for (ModuleInternalInfo moduleInternalInfo : getEntries()) {
753                 result.add(moduleInternalInfo.toDestroyedModule());
754             }
755             Collections.sort(result);
756             return result;
757         }
758     }
759 }