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 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;
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;
43 import java.util.Map.Entry;
45 import java.util.concurrent.atomic.AtomicBoolean;
47 import static java.lang.String.format;
50 * This is a JMX bean representing current transaction. It contains
51 * transaction identifier, unique version and parent version for
54 class ConfigTransactionControllerImpl implements
55 ConfigTransactionControllerInternal,
56 ConfigTransactionControllerImplMXBean,
57 Identifiable<TransactionIdentifier> {
58 private static final Logger logger = LoggerFactory.getLogger(ConfigTransactionControllerImpl.class);
60 private final ConfigTransactionLookupRegistry txLookupRegistry;
61 private final ObjectName controllerON;
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;
71 * Disables ability of {@link DynamicWritableWrapper} to change attributes
75 private final AtomicBoolean configBeanModificationDisabled = new AtomicBoolean(
77 private final ReadOnlyAtomicBoolean readOnlyAtomicBoolean = new ReadOnlyAtomicBooleanImpl(
78 configBeanModificationDisabled);
79 private final MBeanServer configMBeanServer;
81 private final boolean blankTransaction;
84 private final ServiceReferenceWritableRegistry writableSRRegistry;
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, ServiceReferenceWritableRegistry 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;
108 public void copyExistingModulesAndProcessFactoryDiff(Collection<ModuleInternalInfo> existingModules, List<ModuleFactory> lastListOfFactories) {
109 // copy old configuration to this server
110 for (ModuleInternalInfo oldConfigInfo : existingModules) {
112 copyExistingModule(oldConfigInfo);
113 } catch (InstanceAlreadyExistsException e) {
114 throw new IllegalStateException("Error while copying " + oldConfigInfo, e);
117 processDefaultBeans(lastListOfFactories);
120 private synchronized void processDefaultBeans(List<ModuleFactory> lastListOfFactories) {
121 transactionStatus.checkNotCommitStarted();
122 transactionStatus.checkNotAborted();
124 Set<ModuleFactory> oldSet = new HashSet<>(lastListOfFactories);
125 Set<ModuleFactory> newSet = new HashSet<>(factoriesHolder.getModuleFactories());
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);
134 for (ModuleFactory moduleFactory : lastListOfFactories) {
135 if (newSet.contains(moduleFactory) == false) {
136 toBeRemoved.add(moduleFactory);
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());
147 boolean defaultBean = true;
148 putConfigBeanToJMXAndInternalMaps(module.getIdentifier(), module, moduleFactory, null, dependencyResolver, defaultBean);
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(
166 ModuleInternalInfo oldConfigBeanInfo)
167 throws InstanceAlreadyExistsException {
168 transactionStatus.checkNotCommitStarted();
169 transactionStatus.checkNotAborted();
170 ModuleIdentifier moduleIdentifier = oldConfigBeanInfo.getIdentifier();
171 dependencyResolverManager.assertNotExists(moduleIdentifier);
173 ModuleFactory moduleFactory = factoriesHolder
174 .findByModuleName(moduleIdentifier.getFactoryName());
177 DependencyResolver dependencyResolver = dependencyResolverManager.getOrCreate(moduleIdentifier);
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);
188 putConfigBeanToJMXAndInternalMaps(moduleIdentifier, module, moduleFactory, oldConfigBeanInfo, dependencyResolver,
189 oldConfigBeanInfo.isDefaultBean());
193 public synchronized ObjectName createModule(String factoryName,
194 String instanceName) throws InstanceAlreadyExistsException {
196 transactionStatus.checkNotCommitStarted();
197 transactionStatus.checkNotAborted();
198 ModuleIdentifier moduleIdentifier = new ModuleIdentifier(factoryName, instanceName);
199 dependencyResolverManager.assertNotExists(moduleIdentifier);
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);
211 private synchronized ObjectName putConfigBeanToJMXAndInternalMaps(
212 ModuleIdentifier moduleIdentifier, Module module,
213 ModuleFactory moduleFactory,
214 @Nullable ModuleInternalInfo maybeOldConfigBeanInfo, DependencyResolver dependencyResolver, boolean isDefaultBean)
215 throws InstanceAlreadyExistsException {
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());
222 if (dependencyResolver.getIdentifier().equals(moduleIdentifier) == false) {
223 throw new IllegalStateException("Incorrect name reported by dependency resolver. Expected "
224 + moduleIdentifier + ", got " + dependencyResolver.getIdentifier());
226 DynamicMBean writableDynamicWrapper = new DynamicWritableWrapper(
227 module, moduleIdentifier, getTransactionIdentifier(),
228 readOnlyAtomicBoolean, transactionsMBeanServer,
231 ObjectName writableON = ObjectNameUtil.createTransactionModuleON(
232 getTransactionIdentifier().getName(), moduleIdentifier);
233 // put wrapper to jmx
234 TransactionModuleJMXRegistration transactionModuleJMXRegistration = getTxModuleJMXRegistrator()
235 .registerMBean(writableDynamicWrapper, writableON);
237 dependencyResolverManager.put(
238 moduleIdentifier, module, moduleFactory,
239 maybeOldConfigBeanInfo, transactionModuleJMXRegistration, isDefaultBean);
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);
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 "
261 private synchronized void destroyModule(ModuleIdentifier moduleIdentifier) {
262 logger.debug("Destroying module {} in transaction {}", moduleIdentifier, this);
263 transactionStatus.checkNotAborted();
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");
272 // first remove refNames, it checks for objectname existence
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);
281 ModuleInternalTransactionalInfo removedTInfo = dependencyResolverManager.destroyModule(moduleIdentifier);
283 removedTInfo.getTransactionModuleJMXRegistration().close();
287 public long getParentVersion() {
288 return parentVersion;
292 public long getVersion() {
293 return currentVersion;
297 public synchronized void validateConfig() throws ValidationException {
298 if (configBeanModificationDisabled.get()) {
299 throw new IllegalStateException("Cannot start validation");
301 configBeanModificationDisabled.set(true);
305 configBeanModificationDisabled.set(false);
309 private void validate_noLocks() throws ValidationException {
310 transactionStatus.checkNotAborted();
311 logger.trace("Validating transaction {}", getTransactionIdentifier());
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();
320 } catch (Exception e) {
321 logger.warn("Validation exception in {}", getTransactionName(),
323 collectedExceptions.add(ValidationException
324 .createForSingleException(name, e));
327 if (collectedExceptions.size() > 0) {
328 throw ValidationException
329 .createFromCollectedValidationExceptions(collectedExceptions);
331 logger.trace("Validated transaction {}", getTransactionIdentifier());
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.
342 public synchronized CommitInfo validateBeforeCommitAndLockTransaction()
343 throws ValidationException {
344 transactionStatus.checkNotAborted();
345 transactionStatus.checkNotCommitStarted();
346 configBeanModificationDisabled.set(true);
349 } catch (ValidationException e) {
350 logger.trace("Commit failed on validation");
351 configBeanModificationDisabled.set(false); // recoverable error
354 // errors in this state are not recoverable. modules are not mutable
356 transactionStatus.setSecondPhaseCommitStarted();
357 return dependencyResolverManager.toCommitInfo();
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");
373 logger.trace("Committing transaction {}", getTransactionIdentifier());
375 // call getInstance()
376 for (Entry<ModuleIdentifier, Module> entry : dependencyResolverManager
377 .getAllModules().entrySet()) {
378 Module module = entry.getValue();
379 ModuleIdentifier name = entry.getKey();
381 logger.debug("About to commit {} in transaction {}",
382 name, getTransactionIdentifier());
383 module.getInstance();
384 } catch (Exception e) {
385 logger.error("Commit failed on {} in transaction {}", name,
386 getTransactionIdentifier(), e);
388 throw new IllegalStateException(
389 format("Error - getInstance() failed for %s in transaction %s",
390 name, getTransactionIdentifier()), e);
394 // count dependency order
396 logger.trace("Committed configuration {}", getTransactionIdentifier());
397 transactionStatus.setCommitted();
399 return dependencyResolverManager.getSortedModuleIdentifiers();
403 public synchronized void abortConfig() {
404 transactionStatus.checkNotCommitStarted();
405 transactionStatus.checkNotAborted();
409 private void internalAbort() {
410 logger.trace("Aborting {}", this);
411 transactionStatus.setAborted();
415 public void close() {
416 dependencyResolverManager.close();
417 txLookupRegistry.close();
421 public ObjectName getControllerObjectName() {
426 public String getTransactionName() {
427 return getTransactionIdentifier().getName();
434 public Set<ObjectName> lookupConfigBeans() {
435 return txLookupRegistry.lookupConfigBeans();
442 public Set<ObjectName> lookupConfigBeans(String moduleName) {
443 return txLookupRegistry.lookupConfigBeans(moduleName);
450 public ObjectName lookupConfigBean(String moduleName, String instanceName)
451 throws InstanceNotFoundException {
452 return txLookupRegistry.lookupConfigBean(moduleName, instanceName);
459 public Set<ObjectName> lookupConfigBeans(String moduleName, String instanceName) {
460 return txLookupRegistry.lookupConfigBeans(moduleName, instanceName);
467 public void checkConfigBeanExists(ObjectName objectName) throws InstanceNotFoundException {
468 txLookupRegistry.checkConfigBeanExists(objectName);
476 public Set<String> getAvailableModuleNames() {
477 return factoriesHolder.getModuleNames();
481 public boolean isClosed() {
482 return transactionStatus.isAbortedOrCommitted();
486 public String toString() {
487 StringBuilder sb = new StringBuilder();
488 sb.append("transactionName=");
489 sb.append(getTransactionName());
490 return sb.toString();
493 // @VisibleForTesting
495 TransactionModuleJMXRegistrator getTxModuleJMXRegistrator() {
496 return txLookupRegistry.getTxModuleJMXRegistrator();
499 public TransactionIdentifier getName() {
500 return getTransactionIdentifier();
504 public List<ModuleFactory> getCurrentlyRegisteredFactories() {
505 return new ArrayList<>(factoriesHolder.getModuleFactories());
509 public TransactionIdentifier getIdentifier() {
510 return getTransactionIdentifier();
514 public BundleContext getModuleFactoryBundleContext(String factoryName) {
515 Map.Entry<ModuleFactory, BundleContext> factoryBundleContextEntry = this.currentlyRegisteredFactories.get(factoryName);
516 if (factoryBundleContextEntry == null || factoryBundleContextEntry.getValue() == null) {
517 throw new NullPointerException("Bundle context of " + factoryName + " ModuleFactory not found.");
519 return factoryBundleContextEntry.getValue();
522 // service reference functionality:
526 public synchronized ObjectName lookupConfigBeanByServiceInterfaceName(String serviceInterfaceQName, String refName) {
527 return writableSRRegistry.lookupConfigBeanByServiceInterfaceName(serviceInterfaceQName, refName);
531 public synchronized Map<String, Map<String, ObjectName>> getServiceMapping() {
532 return writableSRRegistry.getServiceMapping();
536 public synchronized Map<String, ObjectName> lookupServiceReferencesByServiceInterfaceName(String serviceInterfaceQName) {
537 return writableSRRegistry.lookupServiceReferencesByServiceInterfaceName(serviceInterfaceQName);
541 public synchronized Set<String> lookupServiceInterfaceNames(ObjectName objectName) throws InstanceNotFoundException {
542 return writableSRRegistry.lookupServiceInterfaceNames(objectName);
546 public synchronized String getServiceInterfaceName(String namespace, String localName) {
547 return writableSRRegistry.getServiceInterfaceName(namespace, localName);
551 public synchronized ObjectName saveServiceReference(String serviceInterfaceName, String refName, ObjectName moduleON) throws InstanceNotFoundException {
552 return writableSRRegistry.saveServiceReference(serviceInterfaceName, refName, moduleON);
556 public synchronized void removeServiceReference(String serviceInterfaceName, String refName) throws InstanceNotFoundException {
557 writableSRRegistry.removeServiceReference(serviceInterfaceName, refName);
561 public synchronized void removeAllServiceReferences() {
562 writableSRRegistry.removeAllServiceReferences();
566 public boolean removeServiceReferences(ObjectName objectName) throws InstanceNotFoundException {
567 return writableSRRegistry.removeServiceReferences(objectName);
571 public ServiceReferenceWritableRegistry getWritableRegistry() {
572 return writableSRRegistry;
576 public TransactionIdentifier getTransactionIdentifier() {
577 return txLookupRegistry.getTransactionIdentifier();
581 public Set<String> getAvailableModuleFactoryQNames() {
582 return txLookupRegistry.getAvailableModuleFactoryQNames();
586 public void checkServiceReferenceExists(ObjectName objectName) throws InstanceNotFoundException {
587 writableSRRegistry.checkServiceReferenceExists(objectName);
591 public ObjectName getServiceReference(String serviceInterfaceQName, String refName) throws InstanceNotFoundException {
592 return writableSRRegistry.getServiceReference(serviceInterfaceQName, refName);