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 java.lang.String.format;
13 import java.util.Map.Entry;
14 import java.util.concurrent.atomic.AtomicBoolean;
16 import javax.annotation.Nullable;
17 import javax.annotation.concurrent.GuardedBy;
18 import javax.management.DynamicMBean;
19 import javax.management.InstanceAlreadyExistsException;
20 import javax.management.InstanceNotFoundException;
21 import javax.management.MBeanServer;
22 import javax.management.ObjectName;
24 import org.opendaylight.controller.config.api.DependencyResolver;
25 import org.opendaylight.controller.config.api.ModuleIdentifier;
26 import org.opendaylight.controller.config.api.ValidationException;
27 import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
28 import org.opendaylight.controller.config.manager.impl.dependencyresolver.DependencyResolverManager;
29 import org.opendaylight.controller.config.manager.impl.dynamicmbean.DynamicWritableWrapper;
30 import org.opendaylight.controller.config.manager.impl.dynamicmbean.ReadOnlyAtomicBoolean;
31 import org.opendaylight.controller.config.manager.impl.dynamicmbean.ReadOnlyAtomicBoolean.ReadOnlyAtomicBooleanImpl;
32 import org.opendaylight.controller.config.manager.impl.factoriesresolver.HierarchicalConfigMBeanFactoriesHolder;
33 import org.opendaylight.controller.config.manager.impl.jmx.TransactionJMXRegistrator;
34 import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator;
35 import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator
36 .TransactionModuleJMXRegistration;
37 import org.opendaylight.controller.config.manager.impl.util.LookupBeansUtil;
38 import org.opendaylight.controller.config.spi.Module;
39 import org.opendaylight.controller.config.spi.ModuleFactory;
40 import org.opendaylight.yangtools.concepts.Identifiable;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
45 * This is a JMX bean representing current transaction. It contains
46 * {@link #transactionIdentifier}, unique version and parent version for
49 class ConfigTransactionControllerImpl implements
50 ConfigTransactionControllerInternal,
51 ConfigTransactionControllerImplMXBean,
52 Identifiable<TransactionIdentifier>{
53 private static final Logger logger = LoggerFactory.getLogger(ConfigTransactionControllerImpl.class);
55 private final TransactionIdentifier transactionIdentifier;
56 private final ObjectName controllerON;
57 private final TransactionJMXRegistrator transactionRegistrator;
58 private final TransactionModuleJMXRegistrator txModuleJMXRegistrator;
59 private final long parentVersion, currentVersion;
60 private final HierarchicalConfigMBeanFactoriesHolder factoriesHolder;
61 private final DependencyResolverManager dependencyResolverManager;
62 private final TransactionStatus transactionStatus;
63 private final MBeanServer transactionsMBeanServer;
64 private final List<ModuleFactory> currentlyRegisteredFactories;
67 * Disables ability of {@link DynamicWritableWrapper} to change attributes
71 private final AtomicBoolean configBeanModificationDisabled = new AtomicBoolean(
73 private final ReadOnlyAtomicBoolean readOnlyAtomicBoolean = new ReadOnlyAtomicBooleanImpl(
74 configBeanModificationDisabled);
75 private final MBeanServer configMBeanServer;
77 public ConfigTransactionControllerImpl(String transactionName,
78 TransactionJMXRegistrator transactionRegistrator,
79 long parentVersion, long currentVersion,
80 List<ModuleFactory> currentlyRegisteredFactories,
81 MBeanServer transactionsMBeanServer, MBeanServer configMBeanServer) {
83 this.transactionIdentifier = new TransactionIdentifier(transactionName);
84 this.controllerON = ObjectNameUtil
85 .createTransactionControllerON(transactionName);
86 this.transactionRegistrator = transactionRegistrator;
87 txModuleJMXRegistrator = transactionRegistrator
88 .createTransactionModuleJMXRegistrator();
89 this.parentVersion = parentVersion;
90 this.currentVersion = currentVersion;
91 this.currentlyRegisteredFactories = currentlyRegisteredFactories;
92 this.factoriesHolder = new HierarchicalConfigMBeanFactoriesHolder(currentlyRegisteredFactories);
93 this.transactionStatus = new TransactionStatus();
94 this.dependencyResolverManager = new DependencyResolverManager(transactionName, transactionStatus);
95 this.transactionsMBeanServer = transactionsMBeanServer;
96 this.configMBeanServer = configMBeanServer;
100 public void copyExistingModulesAndProcessFactoryDiff(Collection<ModuleInternalInfo> existingModules, List<ModuleFactory> lastListOfFactories) {
101 // copy old configuration to this server
102 for (ModuleInternalInfo oldConfigInfo : existingModules) {
104 copyExistingModule(oldConfigInfo);
105 } catch (InstanceAlreadyExistsException e) {
106 throw new IllegalStateException("Error while copying " + oldConfigInfo, e);
109 processDefaultBeans(lastListOfFactories);
112 private synchronized void processDefaultBeans(List<ModuleFactory> lastListOfFactories) {
113 transactionStatus.checkNotCommitStarted();
114 transactionStatus.checkNotAborted();
116 Set<ModuleFactory> oldSet = new HashSet<>(lastListOfFactories);
117 Set<ModuleFactory> newSet = new HashSet<>(currentlyRegisteredFactories);
119 List<ModuleFactory> toBeAdded = new ArrayList<>();
120 List<ModuleFactory> toBeRemoved = new ArrayList<>();
121 for(ModuleFactory moduleFactory: currentlyRegisteredFactories) {
122 if (oldSet.contains(moduleFactory) == false){
123 toBeAdded.add(moduleFactory);
126 for(ModuleFactory moduleFactory: lastListOfFactories){
127 if (newSet.contains(moduleFactory) == false) {
128 toBeRemoved.add(moduleFactory);
131 // add default modules
132 for (ModuleFactory moduleFactory : toBeAdded) {
133 Set<? extends Module> defaultModules = moduleFactory.getDefaultModules(dependencyResolverManager);
134 for (Module module : defaultModules) {
136 putConfigBeanToJMXAndInternalMaps(module.getIdentifier(), module, moduleFactory, null);
137 } catch (InstanceAlreadyExistsException e) {
138 throw new IllegalStateException(e);
143 // remove modules belonging to removed factories
144 for(ModuleFactory removedFactory: toBeRemoved){
145 List<ModuleIdentifier> modulesOfRemovedFactory = dependencyResolverManager.findAllByFactory(removedFactory);
146 for (ModuleIdentifier name : modulesOfRemovedFactory) {
153 private synchronized void copyExistingModule(
154 ModuleInternalInfo oldConfigBeanInfo)
155 throws InstanceAlreadyExistsException {
156 transactionStatus.checkNotCommitStarted();
157 transactionStatus.checkNotAborted();
158 ModuleIdentifier moduleIdentifier = oldConfigBeanInfo.getName();
159 dependencyResolverManager.assertNotExists(moduleIdentifier);
161 ModuleFactory moduleFactory = factoriesHolder
162 .findByModuleName(moduleIdentifier.getFactoryName());
165 DependencyResolver dependencyResolver = dependencyResolverManager
166 .getOrCreate(moduleIdentifier);
168 module = moduleFactory.createModule(
169 moduleIdentifier.getInstanceName(), dependencyResolver,
170 oldConfigBeanInfo.getReadableModule());
171 } catch (Exception e) {
172 throw new IllegalStateException(format(
173 "Error while copying old configuration from %s to %s",
174 oldConfigBeanInfo, moduleFactory), e);
176 putConfigBeanToJMXAndInternalMaps(moduleIdentifier, module, moduleFactory, oldConfigBeanInfo);
180 public synchronized ObjectName createModule(String factoryName,
181 String instanceName) throws InstanceAlreadyExistsException {
183 transactionStatus.checkNotCommitStarted();
184 transactionStatus.checkNotAborted();
185 ModuleIdentifier moduleIdentifier = new ModuleIdentifier(factoryName, instanceName);
186 dependencyResolverManager.assertNotExists(moduleIdentifier);
189 ModuleFactory moduleFactory = factoriesHolder.findByModuleName(factoryName);
190 DependencyResolver dependencyResolver = dependencyResolverManager.getOrCreate(moduleIdentifier);
191 Module module = moduleFactory.createModule(instanceName, dependencyResolver);
192 return putConfigBeanToJMXAndInternalMaps(moduleIdentifier, module,
193 moduleFactory, null);
196 private synchronized ObjectName putConfigBeanToJMXAndInternalMaps(
197 ModuleIdentifier moduleIdentifier, Module module,
198 ModuleFactory moduleFactory,
199 @Nullable ModuleInternalInfo maybeOldConfigBeanInfo)
200 throws InstanceAlreadyExistsException {
201 logger.debug("Adding module {} to transaction {}", moduleIdentifier, this);
202 if (moduleIdentifier.equals(module.getIdentifier())==false) {
203 throw new IllegalStateException("Incorrect name reported by module. Expected "
204 + moduleIdentifier + ", got " + module.getIdentifier());
206 DynamicMBean writableDynamicWrapper = new DynamicWritableWrapper(
207 module, moduleIdentifier, transactionIdentifier,
208 readOnlyAtomicBoolean, transactionsMBeanServer,
211 ObjectName writableON = ObjectNameUtil.createTransactionModuleON(
212 transactionIdentifier.getName(), moduleIdentifier);
213 // put wrapper to jmx
214 TransactionModuleJMXRegistration transactionModuleJMXRegistration = txModuleJMXRegistrator
215 .registerMBean(writableDynamicWrapper, writableON);
216 ModuleInternalTransactionalInfo moduleInternalTransactionalInfo = new ModuleInternalTransactionalInfo(
217 moduleIdentifier, module, moduleFactory,
218 maybeOldConfigBeanInfo, transactionModuleJMXRegistration);
220 dependencyResolverManager.put(moduleInternalTransactionalInfo);
225 public void destroyModule(ObjectName objectName)
226 throws InstanceNotFoundException {
227 String foundTransactionName = ObjectNameUtil
228 .getTransactionName(objectName);
229 if (transactionIdentifier.getName().equals(foundTransactionName) == false) {
230 throw new IllegalArgumentException("Wrong transaction name "
233 ObjectNameUtil.checkDomain(objectName);
234 ModuleIdentifier moduleIdentifier = ObjectNameUtil.fromON(objectName,
235 ObjectNameUtil.TYPE_MODULE);
236 destroyModule(moduleIdentifier);
239 private void destroyModule(ModuleIdentifier moduleIdentifier) {
240 logger.debug("Destroying module {} in transaction {}", moduleIdentifier, this);
241 transactionStatus.checkNotAborted();
242 ModuleInternalTransactionalInfo removedTInfo = dependencyResolverManager.destroyModule(moduleIdentifier);
244 removedTInfo.getTransactionModuleJMXRegistration().close();
248 public long getParentVersion() {
249 return parentVersion;
253 public long getVersion() {
254 return currentVersion;
258 public synchronized void validateConfig() throws ValidationException {
259 if (configBeanModificationDisabled.get())
260 throw new IllegalStateException("Cannot start validation");
261 configBeanModificationDisabled.set(true);
265 configBeanModificationDisabled.set(false);
269 private void validate_noLocks() throws ValidationException {
270 transactionStatus.checkNotAborted();
271 logger.info("Validating transaction {}", transactionIdentifier);
273 List<ValidationException> collectedExceptions = new ArrayList<>();
274 for (Entry<ModuleIdentifier, Module> entry : dependencyResolverManager
275 .getAllModules().entrySet()) {
276 ModuleIdentifier name = entry.getKey();
277 Module module = entry.getValue();
280 } catch (Exception e) {
281 logger.warn("Validation exception in {}", getTransactionName(),
283 collectedExceptions.add(ValidationException
284 .createForSingleException(name, e));
287 if (collectedExceptions.size() > 0) {
288 throw ValidationException
289 .createFromCollectedValidationExceptions(collectedExceptions);
291 logger.info("Validated transaction {}", transactionIdentifier);
295 * If this method passes validation, it will grab
296 * {@link TransactionStatus#secondPhaseCommitStarted} lock. This lock will
297 * prevent calling @{link #validateBeforeCommitAndLockTransaction},
298 * effectively only allowing to call {@link #secondPhaseCommit} after
299 * successful return of this method.
302 public synchronized CommitInfo validateBeforeCommitAndLockTransaction()
303 throws ValidationException {
304 transactionStatus.checkNotAborted();
305 transactionStatus.checkNotCommitStarted();
306 configBeanModificationDisabled.set(true);
309 } catch (ValidationException e) {
310 logger.info("Commit failed on validation");
311 configBeanModificationDisabled.set(false); // recoverable error
314 // errors in this state are not recoverable. modules are not mutable
316 transactionStatus.setSecondPhaseCommitStarted();
317 return dependencyResolverManager.toCommitInfo();
324 public synchronized List<ModuleIdentifier> secondPhaseCommit() {
325 transactionStatus.checkNotAborted();
326 transactionStatus.checkCommitStarted();
327 if (configBeanModificationDisabled.get() == false) {
328 throw new IllegalStateException(
329 "Internal error - validateBeforeCommitAndLockTransaction should be called "
330 + "to obtain a lock");
333 logger.info("Committing transaction {}", transactionIdentifier);
335 // call getInstance()
336 for (Entry<ModuleIdentifier, Module> entry : dependencyResolverManager
337 .getAllModules().entrySet()) {
338 Module module = entry.getValue();
339 ModuleIdentifier name = entry.getKey();
341 logger.debug("About to commit {} in transaction {}",
342 transactionIdentifier, name);
343 module.getInstance();
344 } catch (Exception e) {
345 logger.error("Commit failed on {} in transaction {}", name,
346 transactionIdentifier, e);
348 throw new RuntimeException(
349 format("Error - getInstance() failed for %s in transaction %s",
350 name, transactionIdentifier), e);
354 // count dependency order
356 logger.info("Committed configuration {}", transactionIdentifier);
357 transactionStatus.setCommitted();
358 // unregister this and all modules from jmx
361 return dependencyResolverManager.getSortedModuleIdentifiers();
365 public synchronized void abortConfig() {
366 transactionStatus.checkNotCommitStarted();
367 transactionStatus.checkNotAborted();
371 private void internalAbort() {
372 transactionStatus.setAborted();
376 private void close() {
377 transactionRegistrator.close();
381 public ObjectName getControllerObjectName() {
386 public String getTransactionName() {
387 return transactionIdentifier.getName();
394 public Set<ObjectName> lookupConfigBeans() {
395 return lookupConfigBeans("*", "*");
402 public Set<ObjectName> lookupConfigBeans(String moduleName) {
403 return lookupConfigBeans(moduleName, "*");
410 public ObjectName lookupConfigBean(String moduleName, String instanceName)
411 throws InstanceNotFoundException {
412 return LookupBeansUtil.lookupConfigBean(this, moduleName, instanceName);
419 public Set<ObjectName> lookupConfigBeans(String moduleName,
420 String instanceName) {
421 ObjectName namePattern = ObjectNameUtil.createModulePattern(moduleName,
422 instanceName, transactionIdentifier.getName());
423 return txModuleJMXRegistrator.queryNames(namePattern, null);
427 public Set<String> getAvailableModuleNames() {
428 return factoriesHolder.getModuleNames();
432 public boolean isClosed() {
433 return transactionStatus.isAbortedOrCommitted();
437 public String toString() {
438 StringBuilder sb = new StringBuilder();
439 sb.append("transactionName=");
440 sb.append(getTransactionName());
441 return sb.toString();
444 // @VisibleForTesting
446 TransactionModuleJMXRegistrator getTxModuleJMXRegistrator() {
447 return txModuleJMXRegistrator;
450 public TransactionIdentifier getName() {
451 return transactionIdentifier;
455 public List<ModuleFactory> getCurrentlyRegisteredFactories() {
456 return currentlyRegisteredFactories;
460 public TransactionIdentifier getIdentifier() {
461 return transactionIdentifier;