2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.controller.config.manager.impl;
10 import static com.google.common.base.Preconditions.checkNotNull;
11 import static java.lang.String.format;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.HashSet;
16 import java.util.List;
18 import java.util.Map.Entry;
20 import java.util.concurrent.atomic.AtomicBoolean;
21 import javax.annotation.Nullable;
22 import javax.annotation.concurrent.GuardedBy;
23 import javax.management.DynamicMBean;
24 import javax.management.InstanceAlreadyExistsException;
25 import javax.management.InstanceNotFoundException;
26 import javax.management.MBeanServer;
27 import javax.management.ObjectName;
28 import org.opendaylight.controller.config.api.DependencyResolver;
29 import org.opendaylight.controller.config.api.ModuleIdentifier;
30 import org.opendaylight.controller.config.api.ValidationException;
31 import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
32 import org.opendaylight.controller.config.manager.impl.dependencyresolver.DependencyResolverManager;
33 import org.opendaylight.controller.config.manager.impl.dependencyresolver.ModuleInternalTransactionalInfo;
34 import org.opendaylight.controller.config.manager.impl.dynamicmbean.DynamicWritableWrapper;
35 import org.opendaylight.controller.config.manager.impl.dynamicmbean.ReadOnlyAtomicBoolean;
36 import org.opendaylight.controller.config.manager.impl.dynamicmbean.ReadOnlyAtomicBoolean.ReadOnlyAtomicBooleanImpl;
37 import org.opendaylight.controller.config.manager.impl.factoriesresolver.HierarchicalConfigMBeanFactoriesHolder;
38 import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator;
39 import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator.TransactionModuleJMXRegistration;
40 import org.opendaylight.controller.config.spi.Module;
41 import org.opendaylight.controller.config.spi.ModuleFactory;
42 import org.opendaylight.yangtools.concepts.Identifiable;
43 import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry;
44 import org.osgi.framework.BundleContext;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
48 * This is a JMX bean representing current transaction. It contains
49 * transaction identifier, unique version and parent version for
52 class ConfigTransactionControllerImpl implements
53 ConfigTransactionControllerInternal,
54 ConfigTransactionControllerImplMXBean,
55 Identifiable<TransactionIdentifier> {
56 private static final Logger logger = LoggerFactory.getLogger(ConfigTransactionControllerImpl.class);
58 private final ConfigTransactionLookupRegistry txLookupRegistry;
59 private final ObjectName controllerON;
61 private final long parentVersion, currentVersion;
62 private final HierarchicalConfigMBeanFactoriesHolder factoriesHolder;
63 private final DependencyResolverManager dependencyResolverManager;
64 private final TransactionStatus transactionStatus;
65 private final MBeanServer transactionsMBeanServer;
66 private final Map<String, Map.Entry<ModuleFactory, BundleContext>> currentlyRegisteredFactories;
69 * Disables ability of {@link DynamicWritableWrapper} to change attributes
73 private final AtomicBoolean configBeanModificationDisabled = new AtomicBoolean(
75 private final ReadOnlyAtomicBoolean readOnlyAtomicBoolean = new ReadOnlyAtomicBooleanImpl(
76 configBeanModificationDisabled);
77 private final MBeanServer configMBeanServer;
79 private final boolean blankTransaction;
82 private final SearchableServiceReferenceWritableRegistry writableSRRegistry;
84 public ConfigTransactionControllerImpl(ConfigTransactionLookupRegistry txLookupRegistry,
85 long parentVersion, CodecRegistry codecRegistry, long currentVersion,
86 Map<String, Entry<ModuleFactory, BundleContext>> currentlyRegisteredFactories,
87 MBeanServer transactionsMBeanServer, MBeanServer configMBeanServer,
88 boolean blankTransaction, SearchableServiceReferenceWritableRegistry writableSRRegistry) {
89 this.txLookupRegistry = txLookupRegistry;
90 String transactionName = txLookupRegistry.getTransactionIdentifier().getName();
91 this.controllerON = ObjectNameUtil.createTransactionControllerON(transactionName);
92 this.parentVersion = parentVersion;
93 this.currentVersion = currentVersion;
94 this.currentlyRegisteredFactories = currentlyRegisteredFactories;
95 this.factoriesHolder = new HierarchicalConfigMBeanFactoriesHolder(currentlyRegisteredFactories);
96 this.transactionStatus = new TransactionStatus();
97 this.dependencyResolverManager = new DependencyResolverManager(txLookupRegistry.getTransactionIdentifier(),
98 transactionStatus, writableSRRegistry, codecRegistry, transactionsMBeanServer);
99 this.transactionsMBeanServer = transactionsMBeanServer;
100 this.configMBeanServer = configMBeanServer;
101 this.blankTransaction = blankTransaction;
102 this.writableSRRegistry = writableSRRegistry;
106 public void copyExistingModulesAndProcessFactoryDiff(Collection<ModuleInternalInfo> existingModules, List<ModuleFactory> lastListOfFactories) {
107 // copy old configuration to this server
108 for (ModuleInternalInfo oldConfigInfo : existingModules) {
110 copyExistingModule(oldConfigInfo);
111 } catch (InstanceAlreadyExistsException e) {
112 throw new IllegalStateException("Error while copying " + oldConfigInfo, e);
115 processDefaultBeans(lastListOfFactories);
118 private synchronized void processDefaultBeans(List<ModuleFactory> lastListOfFactories) {
119 transactionStatus.checkNotCommitStarted();
120 transactionStatus.checkNotAborted();
122 Set<ModuleFactory> oldSet = new HashSet<>(lastListOfFactories);
123 Set<ModuleFactory> newSet = new HashSet<>(factoriesHolder.getModuleFactories());
125 List<ModuleFactory> toBeAdded = new ArrayList<>();
126 List<ModuleFactory> toBeRemoved = new ArrayList<>();
127 for (ModuleFactory moduleFactory : factoriesHolder.getModuleFactories()) {
128 if (oldSet.contains(moduleFactory) == false) {
129 toBeAdded.add(moduleFactory);
132 for (ModuleFactory moduleFactory : lastListOfFactories) {
133 if (newSet.contains(moduleFactory) == false) {
134 toBeRemoved.add(moduleFactory);
137 // add default modules
138 for (ModuleFactory moduleFactory : toBeAdded) {
139 BundleContext bundleContext = getModuleFactoryBundleContext(moduleFactory.getImplementationName());
140 Set<? extends Module> defaultModules = moduleFactory.getDefaultModules(dependencyResolverManager,
142 for (Module module : defaultModules) {
143 // ensure default module to be registered to jmx even if its module factory does not use dependencyResolverFactory
144 DependencyResolver dependencyResolver = dependencyResolverManager.getOrCreate(module.getIdentifier());
146 boolean defaultBean = true;
147 putConfigBeanToJMXAndInternalMaps(module.getIdentifier(), module, moduleFactory, null,
148 dependencyResolver, defaultBean, bundleContext);
149 } catch (InstanceAlreadyExistsException e) {
150 throw new IllegalStateException(e);
155 // remove modules belonging to removed factories
156 for (ModuleFactory removedFactory : toBeRemoved) {
157 List<ModuleIdentifier> modulesOfRemovedFactory = dependencyResolverManager.findAllByFactory(removedFactory);
158 for (ModuleIdentifier name : modulesOfRemovedFactory) {
165 private synchronized void copyExistingModule(ModuleInternalInfo oldConfigBeanInfo) throws InstanceAlreadyExistsException {
167 transactionStatus.checkNotCommitStarted();
168 transactionStatus.checkNotAborted();
169 ModuleIdentifier moduleIdentifier = oldConfigBeanInfo.getIdentifier();
170 dependencyResolverManager.assertNotExists(moduleIdentifier);
172 ModuleFactory moduleFactory;
175 moduleFactory = factoriesHolder.findByModuleName(moduleIdentifier.getFactoryName());
176 bc = getModuleFactoryBundleContext(moduleFactory.getImplementationName());
177 } catch (InstanceNotFoundException e) {
178 throw new IllegalStateException(e);
182 DependencyResolver dependencyResolver = dependencyResolverManager.getOrCreate(moduleIdentifier);
185 module = moduleFactory.createModule(
186 moduleIdentifier.getInstanceName(), dependencyResolver,
187 oldConfigBeanInfo.getReadableModule(), bc);
188 } catch (Exception e) {
189 throw new IllegalStateException(format(
190 "Error while copying old configuration from %s to %s",
191 oldConfigBeanInfo, moduleFactory), e);
193 putConfigBeanToJMXAndInternalMaps(moduleIdentifier, module, moduleFactory, oldConfigBeanInfo, dependencyResolver,
194 oldConfigBeanInfo.isDefaultBean(), bc);
198 public synchronized ObjectName createModule(String factoryName,
199 String instanceName) throws InstanceAlreadyExistsException {
201 transactionStatus.checkNotCommitStarted();
202 transactionStatus.checkNotAborted();
203 ModuleIdentifier moduleIdentifier = new ModuleIdentifier(factoryName, instanceName);
204 dependencyResolverManager.assertNotExists(moduleIdentifier);
207 ModuleFactory moduleFactory;
209 moduleFactory = factoriesHolder.findByModuleName(factoryName);
210 } catch (InstanceNotFoundException e) {
211 throw new IllegalArgumentException(e);
213 DependencyResolver dependencyResolver = dependencyResolverManager.getOrCreate(moduleIdentifier);
214 BundleContext bundleContext = getModuleFactoryBundleContext(moduleFactory.getImplementationName());
215 Module module = moduleFactory.createModule(instanceName, dependencyResolver,
217 boolean defaultBean = false;
218 return putConfigBeanToJMXAndInternalMaps(moduleIdentifier, module,
219 moduleFactory, null, dependencyResolver, defaultBean, bundleContext);
222 private synchronized ObjectName putConfigBeanToJMXAndInternalMaps(
223 ModuleIdentifier moduleIdentifier, Module module,
224 ModuleFactory moduleFactory,
225 @Nullable ModuleInternalInfo maybeOldConfigBeanInfo, DependencyResolver dependencyResolver,
226 boolean isDefaultBean, BundleContext bundleContext)
227 throws InstanceAlreadyExistsException {
229 logger.debug("Adding module {} to transaction {}", moduleIdentifier, this);
230 if (moduleIdentifier.equals(module.getIdentifier()) == false) {
231 throw new IllegalStateException("Incorrect name reported by module. Expected "
232 + moduleIdentifier + ", got " + module.getIdentifier());
234 if (dependencyResolver.getIdentifier().equals(moduleIdentifier) == false) {
235 throw new IllegalStateException("Incorrect name reported by dependency resolver. Expected "
236 + moduleIdentifier + ", got " + dependencyResolver.getIdentifier());
238 DynamicMBean writableDynamicWrapper = new DynamicWritableWrapper(
239 module, moduleIdentifier, getTransactionIdentifier(),
240 readOnlyAtomicBoolean, transactionsMBeanServer,
243 ObjectName writableON = ObjectNameUtil.createTransactionModuleON(
244 getTransactionIdentifier().getName(), moduleIdentifier);
245 // put wrapper to jmx
246 TransactionModuleJMXRegistration transactionModuleJMXRegistration = getTxModuleJMXRegistrator()
247 .registerMBean(writableDynamicWrapper, writableON);
249 dependencyResolverManager.put(
250 moduleIdentifier, module, moduleFactory,
251 maybeOldConfigBeanInfo, transactionModuleJMXRegistration, isDefaultBean, bundleContext);
256 public synchronized void destroyModule(ObjectName objectName) throws InstanceNotFoundException {
257 checkTransactionName(objectName);
258 ObjectNameUtil.checkDomain(objectName);
259 ModuleIdentifier moduleIdentifier = ObjectNameUtil.fromON(objectName,
260 ObjectNameUtil.TYPE_MODULE);
261 destroyModule(moduleIdentifier);
264 private void checkTransactionName(ObjectName objectName) {
265 String foundTransactionName = ObjectNameUtil
266 .getTransactionName(objectName);
267 if (getTransactionIdentifier().getName().equals(foundTransactionName) == false) {
268 throw new IllegalArgumentException("Wrong transaction name "
273 private synchronized void destroyModule(ModuleIdentifier moduleIdentifier) {
274 logger.debug("Destroying module {} in transaction {}", moduleIdentifier, this);
275 transactionStatus.checkNotAborted();
277 ModuleInternalTransactionalInfo found = dependencyResolverManager.findModuleInternalTransactionalInfo(moduleIdentifier);
278 if (blankTransaction == false) {
280 if (found.isDefaultBean()) {
281 logger.warn("Warning: removing default bean. This will be forbidden in next version of config-subsystem");
284 // first remove refNames, it checks for objectname existence
287 writableSRRegistry.removeServiceReferences(
288 ObjectNameUtil.createTransactionModuleON(getTransactionName(), moduleIdentifier));
289 } catch (InstanceNotFoundException e) {
290 logger.error("Possible code error: cannot find {} in {}", moduleIdentifier, writableSRRegistry);
291 throw new IllegalStateException("Possible code error: cannot find " + moduleIdentifier, e);
294 ModuleInternalTransactionalInfo removedTInfo = dependencyResolverManager.destroyModule(moduleIdentifier);
296 removedTInfo.getTransactionModuleJMXRegistration().close();
300 public long getParentVersion() {
301 return parentVersion;
305 public long getVersion() {
306 return currentVersion;
310 public synchronized void validateConfig() throws ValidationException {
311 if (configBeanModificationDisabled.get()) {
312 throw new IllegalStateException("Cannot start validation");
314 configBeanModificationDisabled.set(true);
318 configBeanModificationDisabled.set(false);
322 private void validate_noLocks() throws ValidationException {
323 transactionStatus.checkNotAborted();
324 logger.trace("Validating transaction {}", getTransactionIdentifier());
326 List<ValidationException> collectedExceptions = new ArrayList<>();
327 for (Entry<ModuleIdentifier, Module> entry : dependencyResolverManager
328 .getAllModules().entrySet()) {
329 ModuleIdentifier name = entry.getKey();
330 Module module = entry.getValue();
333 } catch (Exception e) {
334 logger.warn("Validation exception in {}", getTransactionName(),
336 collectedExceptions.add(ValidationException
337 .createForSingleException(name, e));
340 if (collectedExceptions.size() > 0) {
341 throw ValidationException
342 .createFromCollectedValidationExceptions(collectedExceptions);
344 logger.trace("Validated transaction {}", getTransactionIdentifier());
348 * If this method passes validation, it will grab
349 * {@link TransactionStatus#secondPhaseCommitStarted} lock. This lock will
350 * prevent calling @{link #validateBeforeCommitAndLockTransaction},
351 * effectively only allowing to call {@link #secondPhaseCommit} after
352 * successful return of this method.
355 public synchronized CommitInfo validateBeforeCommitAndLockTransaction()
356 throws ValidationException {
357 transactionStatus.checkNotAborted();
358 transactionStatus.checkNotCommitStarted();
359 configBeanModificationDisabled.set(true);
362 } catch (ValidationException e) {
363 logger.trace("Commit failed on validation");
364 configBeanModificationDisabled.set(false); // recoverable error
367 // errors in this state are not recoverable. modules are not mutable
369 transactionStatus.setSecondPhaseCommitStarted();
370 return dependencyResolverManager.toCommitInfo();
377 public synchronized List<ModuleIdentifier> secondPhaseCommit() {
378 transactionStatus.checkNotAborted();
379 transactionStatus.checkCommitStarted();
380 if (configBeanModificationDisabled.get() == false) {
381 throw new IllegalStateException(
382 "Internal error - validateBeforeCommitAndLockTransaction should be called "
383 + "to obtain a lock");
386 logger.trace("Committing transaction {}", getTransactionIdentifier());
388 // call getInstance()
389 for (Entry<ModuleIdentifier, Module> entry : dependencyResolverManager
390 .getAllModules().entrySet()) {
391 Module module = entry.getValue();
392 ModuleIdentifier name = entry.getKey();
394 logger.debug("About to commit {} in transaction {}",
395 name, getTransactionIdentifier());
396 AutoCloseable instance = module.getInstance();
397 checkNotNull(instance, "Instance is null:{} in transaction {}", name, getTransactionIdentifier());
398 } catch (Exception e) {
399 logger.error("Commit failed on {} in transaction {}", name,
400 getTransactionIdentifier(), e);
402 throw new IllegalStateException(
403 format("Error - getInstance() failed for %s in transaction %s",
404 name, getTransactionIdentifier()), e);
408 // count dependency order
410 logger.trace("Committed configuration {}", getTransactionIdentifier());
411 transactionStatus.setCommitted();
413 return dependencyResolverManager.getSortedModuleIdentifiers();
417 public synchronized void abortConfig() {
418 transactionStatus.checkNotCommitStarted();
419 transactionStatus.checkNotAborted();
423 private void internalAbort() {
424 logger.trace("Aborting {}", this);
425 transactionStatus.setAborted();
429 public void close() {
430 dependencyResolverManager.close();
431 txLookupRegistry.close();
435 public ObjectName getControllerObjectName() {
440 public String getTransactionName() {
441 return getTransactionIdentifier().getName();
448 public Set<ObjectName> lookupConfigBeans() {
449 return txLookupRegistry.lookupConfigBeans();
456 public Set<ObjectName> lookupConfigBeans(String moduleName) {
457 return txLookupRegistry.lookupConfigBeans(moduleName);
464 public ObjectName lookupConfigBean(String moduleName, String instanceName)
465 throws InstanceNotFoundException {
466 return txLookupRegistry.lookupConfigBean(moduleName, instanceName);
473 public Set<ObjectName> lookupConfigBeans(String moduleName, String instanceName) {
474 return txLookupRegistry.lookupConfigBeans(moduleName, instanceName);
481 public void checkConfigBeanExists(ObjectName objectName) throws InstanceNotFoundException {
482 txLookupRegistry.checkConfigBeanExists(objectName);
490 public Set<String> getAvailableModuleNames() {
491 return factoriesHolder.getModuleNames();
495 public boolean isClosed() {
496 return transactionStatus.isAbortedOrCommitted();
500 public String toString() {
501 StringBuilder sb = new StringBuilder();
502 sb.append("transactionName=");
503 sb.append(getTransactionName());
504 return sb.toString();
507 // @VisibleForTesting
509 TransactionModuleJMXRegistrator getTxModuleJMXRegistrator() {
510 return txLookupRegistry.getTxModuleJMXRegistrator();
513 public TransactionIdentifier getName() {
514 return getTransactionIdentifier();
518 public List<ModuleFactory> getCurrentlyRegisteredFactories() {
519 return new ArrayList<>(factoriesHolder.getModuleFactories());
523 public TransactionIdentifier getIdentifier() {
524 return getTransactionIdentifier();
528 public BundleContext getModuleFactoryBundleContext(String factoryName) {
529 Map.Entry<ModuleFactory, BundleContext> factoryBundleContextEntry = this.currentlyRegisteredFactories.get(factoryName);
530 if (factoryBundleContextEntry == null || factoryBundleContextEntry.getValue() == null) {
531 throw new NullPointerException("Bundle context of " + factoryName + " ModuleFactory not found.");
533 return factoryBundleContextEntry.getValue();
536 // service reference functionality:
540 public synchronized ObjectName lookupConfigBeanByServiceInterfaceName(String serviceInterfaceQName, String refName) {
541 return writableSRRegistry.lookupConfigBeanByServiceInterfaceName(serviceInterfaceQName, refName);
545 public synchronized Map<String, Map<String, ObjectName>> getServiceMapping() {
546 return writableSRRegistry.getServiceMapping();
550 public synchronized Map<String, ObjectName> lookupServiceReferencesByServiceInterfaceName(String serviceInterfaceQName) {
551 return writableSRRegistry.lookupServiceReferencesByServiceInterfaceName(serviceInterfaceQName);
555 public synchronized Set<String> lookupServiceInterfaceNames(ObjectName objectName) throws InstanceNotFoundException {
556 return writableSRRegistry.lookupServiceInterfaceNames(objectName);
560 public synchronized String getServiceInterfaceName(String namespace, String localName) {
561 return writableSRRegistry.getServiceInterfaceName(namespace, localName);
565 public synchronized ObjectName saveServiceReference(String serviceInterfaceName, String refName, ObjectName moduleON) throws InstanceNotFoundException {
566 return writableSRRegistry.saveServiceReference(serviceInterfaceName, refName, moduleON);
570 public synchronized void removeServiceReference(String serviceInterfaceName, String refName) throws InstanceNotFoundException {
571 writableSRRegistry.removeServiceReference(serviceInterfaceName, refName);
575 public synchronized void removeAllServiceReferences() {
576 writableSRRegistry.removeAllServiceReferences();
580 public boolean removeServiceReferences(ObjectName objectName) throws InstanceNotFoundException {
581 return writableSRRegistry.removeServiceReferences(objectName);
585 public SearchableServiceReferenceWritableRegistry getWritableRegistry() {
586 return writableSRRegistry;
590 public TransactionIdentifier getTransactionIdentifier() {
591 return txLookupRegistry.getTransactionIdentifier();
595 public Set<String> getAvailableModuleFactoryQNames() {
596 return txLookupRegistry.getAvailableModuleFactoryQNames();
600 public void checkServiceReferenceExists(ObjectName objectName) throws InstanceNotFoundException {
601 writableSRRegistry.checkServiceReferenceExists(objectName);
605 public ObjectName getServiceReference(String serviceInterfaceQName, String refName) throws InstanceNotFoundException {
606 return writableSRRegistry.getServiceReference(serviceInterfaceQName, refName);