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.opendaylight.protocol.concepts.NamedObject;
43 import org.slf4j.Logger;
44 import org.slf4j.LoggerFactory;
47 * This is a JMX bean representing current transaction. It contains
48 * {@link #transactionIdentifier}, unique version and parent version for
51 class ConfigTransactionControllerImpl implements
52 ConfigTransactionControllerInternal,
53 ConfigTransactionControllerImplMXBean,
54 NamedObject<TransactionIdentifier> {
55 private static final Logger logger = LoggerFactory
56 .getLogger(ConfigTransactionControllerImpl.class);
58 private final TransactionIdentifier transactionIdentifier;
59 private final ObjectName controllerON;
60 private final TransactionJMXRegistrator transactionRegistrator;
61 private final TransactionModuleJMXRegistrator txModuleJMXRegistrator;
62 private final long parentVersion, currentVersion;
63 private final HierarchicalConfigMBeanFactoriesHolder factoriesHolder;
64 private final DependencyResolverManager dependencyResolverManager;
65 private final TransactionStatus transactionStatus;
66 private final MBeanServer transactionsMBeanServer;
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 public ConfigTransactionControllerImpl(String transactionName,
80 TransactionJMXRegistrator transactionRegistrator,
81 long parentVersion, long currentVersion,
82 List<? extends ModuleFactory> currentlyRegisteredFactories,
83 MBeanServer transactionsMBeanServer, MBeanServer configMBeanServer) {
85 this.transactionIdentifier = new TransactionIdentifier(transactionName);
86 this.controllerON = ObjectNameUtil
87 .createTransactionControllerON(transactionName);
88 this.transactionRegistrator = transactionRegistrator;
89 txModuleJMXRegistrator = transactionRegistrator
90 .createTransactionModuleJMXRegistrator();
91 this.parentVersion = parentVersion;
92 this.currentVersion = currentVersion;
93 this.factoriesHolder = new HierarchicalConfigMBeanFactoriesHolder(
94 currentlyRegisteredFactories);
95 this.transactionStatus = new TransactionStatus();
96 this.dependencyResolverManager = new DependencyResolverManager(
97 transactionName, transactionStatus);
98 this.transactionsMBeanServer = transactionsMBeanServer;
99 this.configMBeanServer = configMBeanServer;
103 public synchronized void copyExistingModule(
104 ModuleInternalInfo oldConfigBeanInfo)
105 throws InstanceAlreadyExistsException {
106 transactionStatus.checkNotCommitStarted();
107 transactionStatus.checkNotAborted();
108 ModuleIdentifier moduleIdentifier = oldConfigBeanInfo.getName();
109 dependencyResolverManager.assertNotExists(moduleIdentifier);
111 ModuleFactory moduleFactory = factoriesHolder
112 .findByModuleName(moduleIdentifier.getFactoryName());
115 DependencyResolver dependencyResolver = dependencyResolverManager
116 .getOrCreate(moduleIdentifier);
118 module = moduleFactory.createModule(
119 moduleIdentifier.getInstanceName(), dependencyResolver,
120 oldConfigBeanInfo.getReadableModule());
121 } catch (Exception e) {
122 throw new IllegalStateException(format(
123 "Error while copying old configuration from %s to %s",
124 oldConfigBeanInfo, moduleFactory), e);
126 putConfigBeanToJMXAndInternalMaps(moduleIdentifier, module,
127 moduleFactory, oldConfigBeanInfo);
131 public synchronized ObjectName createModule(String factoryName,
132 String instanceName) throws InstanceAlreadyExistsException {
134 transactionStatus.checkNotCommitStarted();
135 transactionStatus.checkNotAborted();
136 ModuleIdentifier moduleIdentifier = new ModuleIdentifier(factoryName,
138 dependencyResolverManager.assertNotExists(moduleIdentifier);
141 ModuleFactory moduleFactory = factoriesHolder
142 .findByModuleName(factoryName);
143 DependencyResolver dependencyResolver = dependencyResolverManager
144 .getOrCreate(moduleIdentifier);
145 Module module = moduleFactory.createModule(instanceName,
147 return putConfigBeanToJMXAndInternalMaps(moduleIdentifier, module,
148 moduleFactory, null);
151 private synchronized ObjectName putConfigBeanToJMXAndInternalMaps(
152 ModuleIdentifier moduleIdentifier, Module module,
153 ModuleFactory moduleFactory,
154 @Nullable ModuleInternalInfo maybeOldConfigBeanInfo)
155 throws InstanceAlreadyExistsException {
157 DynamicMBean writableDynamicWrapper = new DynamicWritableWrapper(
158 module, moduleIdentifier, transactionIdentifier,
159 readOnlyAtomicBoolean, transactionsMBeanServer,
162 ObjectName writableON = ObjectNameUtil.createTransactionModuleON(
163 transactionIdentifier.getName(), moduleIdentifier);
164 // put wrapper to jmx
165 TransactionModuleJMXRegistration transactionModuleJMXRegistration = txModuleJMXRegistrator
166 .registerMBean(writableDynamicWrapper, writableON);
167 ModuleInternalTransactionalInfo moduleInternalTransactionalInfo = new ModuleInternalTransactionalInfo(
168 moduleIdentifier, module, moduleFactory,
169 maybeOldConfigBeanInfo, transactionModuleJMXRegistration);
171 dependencyResolverManager.put(moduleInternalTransactionalInfo);
176 public void destroyModule(ObjectName objectName)
177 throws InstanceNotFoundException {
178 String foundTransactionName = ObjectNameUtil
179 .getTransactionName(objectName);
180 if (transactionIdentifier.getName().equals(foundTransactionName) == false) {
181 throw new IllegalArgumentException("Wrong transaction name "
184 ObjectNameUtil.checkDomain(objectName);
185 transactionStatus.checkNotAborted();
186 ModuleIdentifier moduleIdentifier = ObjectNameUtil.fromON(objectName,
187 ObjectNameUtil.TYPE_MODULE);
188 ModuleInternalTransactionalInfo removedTInfo = dependencyResolverManager
189 .destroyModule(moduleIdentifier);
191 removedTInfo.getTransactionModuleJMXRegistration().close();
195 public long getParentVersion() {
196 return parentVersion;
200 public long getVersion() {
201 return currentVersion;
205 public synchronized void validateConfig() throws ValidationException {
206 if (configBeanModificationDisabled.get())
207 throw new IllegalStateException("Cannot start validation");
208 configBeanModificationDisabled.set(true);
212 configBeanModificationDisabled.set(false);
216 private void validate_noLocks() throws ValidationException {
217 transactionStatus.checkNotAborted();
218 logger.info("Validating transaction {}", transactionIdentifier);
220 List<ValidationException> collectedExceptions = new ArrayList<>();
221 for (Entry<ModuleIdentifier, Module> entry : dependencyResolverManager
222 .getAllModules().entrySet()) {
223 ModuleIdentifier name = entry.getKey();
224 Module module = entry.getValue();
227 } catch (Exception e) {
228 logger.warn("Validation exception in {}", getTransactionName(),
230 collectedExceptions.add(ValidationException
231 .createForSingleException(name, e));
234 if (collectedExceptions.size() > 0) {
235 throw ValidationException
236 .createFromCollectedValidationExceptions(collectedExceptions);
238 logger.info("Validated transaction {}", transactionIdentifier);
242 * If this method passes validation, it will grab
243 * {@link TransactionStatus#secondPhaseCommitStarted} lock. This lock will
244 * prevent calling @{link #validateBeforeCommitAndLockTransaction},
245 * effectively only allowing to call {@link #secondPhaseCommit} after
246 * successful return of this method.
249 public synchronized CommitInfo validateBeforeCommitAndLockTransaction()
250 throws ValidationException {
251 transactionStatus.checkNotAborted();
252 transactionStatus.checkNotCommitStarted();
253 configBeanModificationDisabled.set(true);
256 } catch (ValidationException e) {
257 logger.info("Commit failed on validation");
258 configBeanModificationDisabled.set(false); // recoverable error
261 // errors in this state are not recoverable. modules are not mutable
263 transactionStatus.setSecondPhaseCommitStarted();
264 return dependencyResolverManager.toCommitInfo();
271 public synchronized List<ModuleIdentifier> secondPhaseCommit() {
272 transactionStatus.checkNotAborted();
273 transactionStatus.checkCommitStarted();
274 if (configBeanModificationDisabled.get() == false) {
275 throw new IllegalStateException(
276 "Internal error - validateBeforeCommitAndLockTransaction should be called "
277 + "to obtain a lock");
280 logger.info("Committing transaction {}", transactionIdentifier);
282 // call getInstance()
283 for (Entry<ModuleIdentifier, Module> entry : dependencyResolverManager
284 .getAllModules().entrySet()) {
285 Module module = entry.getValue();
286 ModuleIdentifier name = entry.getKey();
288 logger.debug("About to commit {} in transaction {}",
289 transactionIdentifier, name);
290 module.getInstance();
291 } catch (Exception e) {
292 logger.error("Commit failed on {} in transaction {}", name,
293 transactionIdentifier, e);
295 throw new RuntimeException(
296 format("Error - getInstance() failed for %s in transaction %s",
297 name, transactionIdentifier), e);
301 // count dependency order
303 logger.info("Committed configuration {}", transactionIdentifier);
304 transactionStatus.setCommitted();
305 // unregister this and all modules from jmx
308 return dependencyResolverManager.getSortedModuleIdentifiers();
312 public synchronized void abortConfig() {
313 transactionStatus.checkNotCommitStarted();
314 transactionStatus.checkNotAborted();
318 private void internalAbort() {
319 transactionStatus.setAborted();
323 private void close() {
324 transactionRegistrator.close();
328 public ObjectName getControllerObjectName() {
333 public String getTransactionName() {
334 return transactionIdentifier.getName();
341 public Set<ObjectName> lookupConfigBeans() {
342 return lookupConfigBeans("*", "*");
349 public Set<ObjectName> lookupConfigBeans(String moduleName) {
350 return lookupConfigBeans(moduleName, "*");
357 public ObjectName lookupConfigBean(String moduleName, String instanceName)
358 throws InstanceNotFoundException {
359 return LookupBeansUtil.lookupConfigBean(this, moduleName, instanceName);
366 public Set<ObjectName> lookupConfigBeans(String moduleName,
367 String instanceName) {
368 ObjectName namePattern = ObjectNameUtil.createModulePattern(moduleName,
369 instanceName, transactionIdentifier.getName());
370 return txModuleJMXRegistrator.queryNames(namePattern, null);
374 public Set<String> getAvailableModuleNames() {
375 return factoriesHolder.getModuleNames();
379 public boolean isClosed() {
380 return transactionStatus.isAbortedOrCommitted();
384 public String toString() {
385 StringBuilder sb = new StringBuilder();
386 sb.append("transactionName=");
387 sb.append(getTransactionName());
388 return sb.toString();
391 // @VisibleForTesting
393 TransactionModuleJMXRegistrator getTxModuleJMXRegistrator() {
394 return txModuleJMXRegistrator;
398 public TransactionIdentifier getName() {
399 return transactionIdentifier;