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;
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.Map.Entry;
16 import java.util.concurrent.atomic.AtomicBoolean;
18 import javax.annotation.Nullable;
19 import javax.annotation.concurrent.GuardedBy;
20 import javax.management.DynamicMBean;
21 import javax.management.InstanceAlreadyExistsException;
22 import javax.management.InstanceNotFoundException;
23 import javax.management.MBeanServer;
24 import javax.management.ObjectName;
26 import org.opendaylight.controller.config.api.DependencyResolver;
27 import org.opendaylight.controller.config.api.ModuleIdentifier;
28 import org.opendaylight.controller.config.api.ValidationException;
29 import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
30 import org.opendaylight.controller.config.manager.impl.dependencyresolver.DependencyResolverManager;
31 import org.opendaylight.controller.config.manager.impl.dynamicmbean.DynamicWritableWrapper;
32 import org.opendaylight.controller.config.manager.impl.dynamicmbean.ReadOnlyAtomicBoolean;
33 import org.opendaylight.controller.config.manager.impl.dynamicmbean.ReadOnlyAtomicBoolean.ReadOnlyAtomicBooleanImpl;
34 import org.opendaylight.controller.config.manager.impl.factoriesresolver.HierarchicalConfigMBeanFactoriesHolder;
35 import org.opendaylight.controller.config.manager.impl.jmx.TransactionJMXRegistrator;
36 import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator;
37 import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator
38 .TransactionModuleJMXRegistration;
39 import org.opendaylight.controller.config.manager.impl.util.LookupBeansUtil;
40 import org.opendaylight.controller.config.spi.Module;
41 import org.opendaylight.controller.config.spi.ModuleFactory;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
46 * This is a JMX bean representing current transaction. It contains
47 * {@link #transactionIdentifier}, unique version and parent version for
50 class ConfigTransactionControllerImpl implements
51 ConfigTransactionControllerInternal,
52 ConfigTransactionControllerImplMXBean {
53 private static final Logger logger = LoggerFactory
54 .getLogger(ConfigTransactionControllerImpl.class);
56 private final TransactionIdentifier transactionIdentifier;
57 private final ObjectName controllerON;
58 private final TransactionJMXRegistrator transactionRegistrator;
59 private final TransactionModuleJMXRegistrator txModuleJMXRegistrator;
60 private final long parentVersion, currentVersion;
61 private final HierarchicalConfigMBeanFactoriesHolder factoriesHolder;
62 private final DependencyResolverManager dependencyResolverManager;
63 private final TransactionStatus transactionStatus;
64 private final MBeanServer transactionsMBeanServer;
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<? extends 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.factoriesHolder = new HierarchicalConfigMBeanFactoriesHolder(
92 currentlyRegisteredFactories);
93 this.transactionStatus = new TransactionStatus();
94 this.dependencyResolverManager = new DependencyResolverManager(
95 transactionName, transactionStatus);
96 this.transactionsMBeanServer = transactionsMBeanServer;
97 this.configMBeanServer = configMBeanServer;
101 public synchronized void copyExistingModule(
102 ModuleInternalInfo oldConfigBeanInfo)
103 throws InstanceAlreadyExistsException {
104 transactionStatus.checkNotCommitStarted();
105 transactionStatus.checkNotAborted();
106 ModuleIdentifier moduleIdentifier = oldConfigBeanInfo.getName();
107 dependencyResolverManager.assertNotExists(moduleIdentifier);
109 ModuleFactory moduleFactory = factoriesHolder
110 .findByModuleName(moduleIdentifier.getFactoryName());
113 DependencyResolver dependencyResolver = dependencyResolverManager
114 .getOrCreate(moduleIdentifier);
116 module = moduleFactory.createModule(
117 moduleIdentifier.getInstanceName(), dependencyResolver,
118 oldConfigBeanInfo.getReadableModule());
119 } catch (Exception e) {
120 throw new IllegalStateException(format(
121 "Error while copying old configuration from %s to %s",
122 oldConfigBeanInfo, moduleFactory), e);
124 putConfigBeanToJMXAndInternalMaps(moduleIdentifier, module,
125 moduleFactory, oldConfigBeanInfo);
129 public synchronized ObjectName createModule(String factoryName,
130 String instanceName) throws InstanceAlreadyExistsException {
132 transactionStatus.checkNotCommitStarted();
133 transactionStatus.checkNotAborted();
134 ModuleIdentifier moduleIdentifier = new ModuleIdentifier(factoryName,
136 dependencyResolverManager.assertNotExists(moduleIdentifier);
139 ModuleFactory moduleFactory = factoriesHolder
140 .findByModuleName(factoryName);
141 DependencyResolver dependencyResolver = dependencyResolverManager
142 .getOrCreate(moduleIdentifier);
143 Module module = moduleFactory.createModule(instanceName,
145 return putConfigBeanToJMXAndInternalMaps(moduleIdentifier, module,
146 moduleFactory, null);
149 private synchronized ObjectName putConfigBeanToJMXAndInternalMaps(
150 ModuleIdentifier moduleIdentifier, Module module,
151 ModuleFactory moduleFactory,
152 @Nullable ModuleInternalInfo maybeOldConfigBeanInfo)
153 throws InstanceAlreadyExistsException {
155 DynamicMBean writableDynamicWrapper = new DynamicWritableWrapper(
156 module, moduleIdentifier, transactionIdentifier,
157 readOnlyAtomicBoolean, transactionsMBeanServer,
160 ObjectName writableON = ObjectNameUtil.createTransactionModuleON(
161 transactionIdentifier.getName(), moduleIdentifier);
162 // put wrapper to jmx
163 TransactionModuleJMXRegistration transactionModuleJMXRegistration = txModuleJMXRegistrator
164 .registerMBean(writableDynamicWrapper, writableON);
165 ModuleInternalTransactionalInfo moduleInternalTransactionalInfo = new ModuleInternalTransactionalInfo(
166 moduleIdentifier, module, moduleFactory,
167 maybeOldConfigBeanInfo, transactionModuleJMXRegistration);
169 dependencyResolverManager.put(moduleInternalTransactionalInfo);
174 public void destroyModule(ObjectName objectName)
175 throws InstanceNotFoundException {
176 String foundTransactionName = ObjectNameUtil
177 .getTransactionName(objectName);
178 if (transactionIdentifier.getName().equals(foundTransactionName) == false) {
179 throw new IllegalArgumentException("Wrong transaction name "
182 ObjectNameUtil.checkDomain(objectName);
183 transactionStatus.checkNotAborted();
184 ModuleIdentifier moduleIdentifier = ObjectNameUtil.fromON(objectName,
185 ObjectNameUtil.TYPE_MODULE);
186 ModuleInternalTransactionalInfo removedTInfo = dependencyResolverManager
187 .destroyModule(moduleIdentifier);
189 removedTInfo.getTransactionModuleJMXRegistration().close();
193 public long getParentVersion() {
194 return parentVersion;
198 public long getVersion() {
199 return currentVersion;
203 public synchronized void validateConfig() throws ValidationException {
204 if (configBeanModificationDisabled.get())
205 throw new IllegalStateException("Cannot start validation");
206 configBeanModificationDisabled.set(true);
210 configBeanModificationDisabled.set(false);
214 private void validate_noLocks() throws ValidationException {
215 transactionStatus.checkNotAborted();
216 logger.info("Validating transaction {}", transactionIdentifier);
218 List<ValidationException> collectedExceptions = new ArrayList<>();
219 for (Entry<ModuleIdentifier, Module> entry : dependencyResolverManager
220 .getAllModules().entrySet()) {
221 ModuleIdentifier name = entry.getKey();
222 Module module = entry.getValue();
225 } catch (Exception e) {
226 logger.warn("Validation exception in {}", getTransactionName(),
228 collectedExceptions.add(ValidationException
229 .createForSingleException(name, e));
232 if (collectedExceptions.size() > 0) {
233 throw ValidationException
234 .createFromCollectedValidationExceptions(collectedExceptions);
236 logger.info("Validated transaction {}", transactionIdentifier);
240 * If this method passes validation, it will grab
241 * {@link TransactionStatus#secondPhaseCommitStarted} lock. This lock will
242 * prevent calling @{link #validateBeforeCommitAndLockTransaction},
243 * effectively only allowing to call {@link #secondPhaseCommit} after
244 * successful return of this method.
247 public synchronized CommitInfo validateBeforeCommitAndLockTransaction()
248 throws ValidationException {
249 transactionStatus.checkNotAborted();
250 transactionStatus.checkNotCommitStarted();
251 configBeanModificationDisabled.set(true);
254 } catch (ValidationException e) {
255 logger.info("Commit failed on validation");
256 configBeanModificationDisabled.set(false); // recoverable error
259 // errors in this state are not recoverable. modules are not mutable
261 transactionStatus.setSecondPhaseCommitStarted();
262 return dependencyResolverManager.toCommitInfo();
269 public synchronized List<ModuleIdentifier> secondPhaseCommit() {
270 transactionStatus.checkNotAborted();
271 transactionStatus.checkCommitStarted();
272 if (configBeanModificationDisabled.get() == false) {
273 throw new IllegalStateException(
274 "Internal error - validateBeforeCommitAndLockTransaction should be called "
275 + "to obtain a lock");
278 logger.info("Committing transaction {}", transactionIdentifier);
280 // call getInstance()
281 for (Entry<ModuleIdentifier, Module> entry : dependencyResolverManager
282 .getAllModules().entrySet()) {
283 Module module = entry.getValue();
284 ModuleIdentifier name = entry.getKey();
286 logger.debug("About to commit {} in transaction {}",
287 transactionIdentifier, name);
288 module.getInstance();
289 } catch (Exception e) {
290 logger.error("Commit failed on {} in transaction {}", name,
291 transactionIdentifier, e);
293 throw new RuntimeException(
294 format("Error - getInstance() failed for %s in transaction %s",
295 name, transactionIdentifier), e);
299 // count dependency order
301 logger.info("Committed configuration {}", transactionIdentifier);
302 transactionStatus.setCommitted();
303 // unregister this and all modules from jmx
306 return dependencyResolverManager.getSortedModuleIdentifiers();
310 public synchronized void abortConfig() {
311 transactionStatus.checkNotCommitStarted();
312 transactionStatus.checkNotAborted();
316 private void internalAbort() {
317 transactionStatus.setAborted();
321 private void close() {
322 transactionRegistrator.close();
326 public ObjectName getControllerObjectName() {
331 public String getTransactionName() {
332 return transactionIdentifier.getName();
339 public Set<ObjectName> lookupConfigBeans() {
340 return lookupConfigBeans("*", "*");
347 public Set<ObjectName> lookupConfigBeans(String moduleName) {
348 return lookupConfigBeans(moduleName, "*");
355 public ObjectName lookupConfigBean(String moduleName, String instanceName)
356 throws InstanceNotFoundException {
357 return LookupBeansUtil.lookupConfigBean(this, moduleName, instanceName);
364 public Set<ObjectName> lookupConfigBeans(String moduleName,
365 String instanceName) {
366 ObjectName namePattern = ObjectNameUtil.createModulePattern(moduleName,
367 instanceName, transactionIdentifier.getName());
368 return txModuleJMXRegistrator.queryNames(namePattern, null);
372 public Set<String> getAvailableModuleNames() {
373 return factoriesHolder.getModuleNames();
377 public boolean isClosed() {
378 return transactionStatus.isAbortedOrCommitted();
382 public String toString() {
383 StringBuilder sb = new StringBuilder();
384 sb.append("transactionName=");
385 sb.append(getTransactionName());
386 return sb.toString();
389 // @VisibleForTesting
391 TransactionModuleJMXRegistrator getTxModuleJMXRegistrator() {
392 return txModuleJMXRegistrator;
395 public TransactionIdentifier getName() {
396 return transactionIdentifier;