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.ValidationException;
13 import org.opendaylight.controller.config.api.jmx.ObjectNameUtil;
14 import org.opendaylight.controller.config.manager.impl.dependencyresolver.DependencyResolverManager;
15 import org.opendaylight.controller.config.manager.impl.dynamicmbean.DynamicWritableWrapper;
16 import org.opendaylight.controller.config.manager.impl.dynamicmbean.ReadOnlyAtomicBoolean;
17 import org.opendaylight.controller.config.manager.impl.dynamicmbean.ReadOnlyAtomicBoolean.ReadOnlyAtomicBooleanImpl;
18 import org.opendaylight.controller.config.manager.impl.factoriesresolver.HierarchicalConfigMBeanFactoriesHolder;
19 import org.opendaylight.controller.config.manager.impl.jmx.TransactionJMXRegistrator;
20 import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator;
21 import org.opendaylight.controller.config.manager.impl.jmx.TransactionModuleJMXRegistrator.TransactionModuleJMXRegistration;
22 import org.opendaylight.controller.config.manager.impl.util.LookupBeansUtil;
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.osgi.framework.BundleContext;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
30 import javax.annotation.Nullable;
31 import javax.annotation.concurrent.GuardedBy;
32 import javax.management.*;
34 import java.util.Map.Entry;
35 import java.util.concurrent.atomic.AtomicBoolean;
37 import static java.lang.String.format;
40 * This is a JMX bean representing current transaction. It contains
41 * {@link #transactionIdentifier}, unique version and parent version for
44 class ConfigTransactionControllerImpl implements
45 ConfigTransactionControllerInternal,
46 ConfigTransactionControllerImplMXBean,
47 Identifiable<TransactionIdentifier>{
48 private static final Logger logger = LoggerFactory.getLogger(ConfigTransactionControllerImpl.class);
50 private final TransactionIdentifier transactionIdentifier;
51 private final ObjectName controllerON;
52 private final TransactionJMXRegistrator transactionRegistrator;
53 private final TransactionModuleJMXRegistrator txModuleJMXRegistrator;
54 private final long parentVersion, currentVersion;
55 private final HierarchicalConfigMBeanFactoriesHolder factoriesHolder;
56 private final DependencyResolverManager dependencyResolverManager;
57 private final TransactionStatus transactionStatus;
58 private final MBeanServer transactionsMBeanServer;
59 private final List<ModuleFactory> currentlyRegisteredFactories;
62 * Disables ability of {@link DynamicWritableWrapper} to change attributes
66 private final AtomicBoolean configBeanModificationDisabled = new AtomicBoolean(
68 private final ReadOnlyAtomicBoolean readOnlyAtomicBoolean = new ReadOnlyAtomicBooleanImpl(
69 configBeanModificationDisabled);
70 private final MBeanServer configMBeanServer;
72 private final BundleContext bundleContext;
74 public ConfigTransactionControllerImpl(String transactionName,
75 TransactionJMXRegistrator transactionRegistrator,
76 long parentVersion, long currentVersion,
77 List<ModuleFactory> currentlyRegisteredFactories,
78 MBeanServer transactionsMBeanServer, MBeanServer configMBeanServer, BundleContext bundleContext) {
80 this.transactionIdentifier = new TransactionIdentifier(transactionName);
81 this.controllerON = ObjectNameUtil
82 .createTransactionControllerON(transactionName);
83 this.transactionRegistrator = transactionRegistrator;
84 txModuleJMXRegistrator = transactionRegistrator
85 .createTransactionModuleJMXRegistrator();
86 this.parentVersion = parentVersion;
87 this.currentVersion = currentVersion;
88 this.currentlyRegisteredFactories = currentlyRegisteredFactories;
89 this.factoriesHolder = new HierarchicalConfigMBeanFactoriesHolder(currentlyRegisteredFactories);
90 this.transactionStatus = new TransactionStatus();
91 this.dependencyResolverManager = new DependencyResolverManager(transactionName, transactionStatus);
92 this.transactionsMBeanServer = transactionsMBeanServer;
93 this.configMBeanServer = configMBeanServer;
94 this.bundleContext = bundleContext;
98 public void copyExistingModulesAndProcessFactoryDiff(Collection<ModuleInternalInfo> existingModules, List<ModuleFactory> lastListOfFactories) {
99 // copy old configuration to this server
100 for (ModuleInternalInfo oldConfigInfo : existingModules) {
102 copyExistingModule(oldConfigInfo);
103 } catch (InstanceAlreadyExistsException e) {
104 throw new IllegalStateException("Error while copying " + oldConfigInfo, e);
107 processDefaultBeans(lastListOfFactories);
110 private synchronized void processDefaultBeans(List<ModuleFactory> lastListOfFactories) {
111 transactionStatus.checkNotCommitStarted();
112 transactionStatus.checkNotAborted();
114 Set<ModuleFactory> oldSet = new HashSet<>(lastListOfFactories);
115 Set<ModuleFactory> newSet = new HashSet<>(currentlyRegisteredFactories);
117 List<ModuleFactory> toBeAdded = new ArrayList<>();
118 List<ModuleFactory> toBeRemoved = new ArrayList<>();
119 for(ModuleFactory moduleFactory: currentlyRegisteredFactories) {
120 if (oldSet.contains(moduleFactory) == false){
121 toBeAdded.add(moduleFactory);
124 for(ModuleFactory moduleFactory: lastListOfFactories){
125 if (newSet.contains(moduleFactory) == false) {
126 toBeRemoved.add(moduleFactory);
129 // add default modules
130 for (ModuleFactory moduleFactory : toBeAdded) {
131 Set<? extends Module> defaultModules = moduleFactory.getDefaultModules(dependencyResolverManager, bundleContext);
132 for (Module module : defaultModules) {
134 putConfigBeanToJMXAndInternalMaps(module.getIdentifier(), module, moduleFactory, null);
135 } catch (InstanceAlreadyExistsException e) {
136 throw new IllegalStateException(e);
141 // remove modules belonging to removed factories
142 for(ModuleFactory removedFactory: toBeRemoved){
143 List<ModuleIdentifier> modulesOfRemovedFactory = dependencyResolverManager.findAllByFactory(removedFactory);
144 for (ModuleIdentifier name : modulesOfRemovedFactory) {
151 private synchronized void copyExistingModule(
152 ModuleInternalInfo oldConfigBeanInfo)
153 throws InstanceAlreadyExistsException {
154 transactionStatus.checkNotCommitStarted();
155 transactionStatus.checkNotAborted();
156 ModuleIdentifier moduleIdentifier = oldConfigBeanInfo.getName();
157 dependencyResolverManager.assertNotExists(moduleIdentifier);
159 ModuleFactory moduleFactory = factoriesHolder
160 .findByModuleName(moduleIdentifier.getFactoryName());
163 DependencyResolver dependencyResolver = dependencyResolverManager
164 .getOrCreate(moduleIdentifier);
166 module = moduleFactory.createModule(
167 moduleIdentifier.getInstanceName(), dependencyResolver,
168 oldConfigBeanInfo.getReadableModule(), bundleContext);
169 } catch (Exception e) {
170 throw new IllegalStateException(format(
171 "Error while copying old configuration from %s to %s",
172 oldConfigBeanInfo, moduleFactory), e);
174 putConfigBeanToJMXAndInternalMaps(moduleIdentifier, module, moduleFactory, oldConfigBeanInfo);
178 public synchronized ObjectName createModule(String factoryName,
179 String instanceName) throws InstanceAlreadyExistsException {
181 transactionStatus.checkNotCommitStarted();
182 transactionStatus.checkNotAborted();
183 ModuleIdentifier moduleIdentifier = new ModuleIdentifier(factoryName, instanceName);
184 dependencyResolverManager.assertNotExists(moduleIdentifier);
187 ModuleFactory moduleFactory = factoriesHolder.findByModuleName(factoryName);
188 DependencyResolver dependencyResolver = dependencyResolverManager.getOrCreate(moduleIdentifier);
189 Module module = moduleFactory.createModule(instanceName, dependencyResolver, bundleContext);
190 return putConfigBeanToJMXAndInternalMaps(moduleIdentifier, module,
191 moduleFactory, null);
194 private synchronized ObjectName putConfigBeanToJMXAndInternalMaps(
195 ModuleIdentifier moduleIdentifier, Module module,
196 ModuleFactory moduleFactory,
197 @Nullable ModuleInternalInfo maybeOldConfigBeanInfo)
198 throws InstanceAlreadyExistsException {
199 logger.debug("Adding module {} to transaction {}", moduleIdentifier, this);
200 if (moduleIdentifier.equals(module.getIdentifier())==false) {
201 throw new IllegalStateException("Incorrect name reported by module. Expected "
202 + moduleIdentifier + ", got " + module.getIdentifier());
204 DynamicMBean writableDynamicWrapper = new DynamicWritableWrapper(
205 module, moduleIdentifier, transactionIdentifier,
206 readOnlyAtomicBoolean, transactionsMBeanServer,
209 ObjectName writableON = ObjectNameUtil.createTransactionModuleON(
210 transactionIdentifier.getName(), moduleIdentifier);
211 // put wrapper to jmx
212 TransactionModuleJMXRegistration transactionModuleJMXRegistration = txModuleJMXRegistrator
213 .registerMBean(writableDynamicWrapper, writableON);
214 ModuleInternalTransactionalInfo moduleInternalTransactionalInfo = new ModuleInternalTransactionalInfo(
215 moduleIdentifier, module, moduleFactory,
216 maybeOldConfigBeanInfo, transactionModuleJMXRegistration);
218 dependencyResolverManager.put(moduleInternalTransactionalInfo);
223 public void destroyModule(ObjectName objectName)
224 throws InstanceNotFoundException {
225 String foundTransactionName = ObjectNameUtil
226 .getTransactionName(objectName);
227 if (transactionIdentifier.getName().equals(foundTransactionName) == false) {
228 throw new IllegalArgumentException("Wrong transaction name "
231 ObjectNameUtil.checkDomain(objectName);
232 ModuleIdentifier moduleIdentifier = ObjectNameUtil.fromON(objectName,
233 ObjectNameUtil.TYPE_MODULE);
234 destroyModule(moduleIdentifier);
237 private void destroyModule(ModuleIdentifier moduleIdentifier) {
238 logger.debug("Destroying module {} in transaction {}", moduleIdentifier, this);
239 transactionStatus.checkNotAborted();
240 ModuleInternalTransactionalInfo removedTInfo = dependencyResolverManager.destroyModule(moduleIdentifier);
242 removedTInfo.getTransactionModuleJMXRegistration().close();
246 public long getParentVersion() {
247 return parentVersion;
251 public long getVersion() {
252 return currentVersion;
256 public synchronized void validateConfig() throws ValidationException {
257 if (configBeanModificationDisabled.get())
258 throw new IllegalStateException("Cannot start validation");
259 configBeanModificationDisabled.set(true);
263 configBeanModificationDisabled.set(false);
267 private void validate_noLocks() throws ValidationException {
268 transactionStatus.checkNotAborted();
269 logger.info("Validating transaction {}", transactionIdentifier);
271 List<ValidationException> collectedExceptions = new ArrayList<>();
272 for (Entry<ModuleIdentifier, Module> entry : dependencyResolverManager
273 .getAllModules().entrySet()) {
274 ModuleIdentifier name = entry.getKey();
275 Module module = entry.getValue();
278 } catch (Exception e) {
279 logger.warn("Validation exception in {}", getTransactionName(),
281 collectedExceptions.add(ValidationException
282 .createForSingleException(name, e));
285 if (collectedExceptions.size() > 0) {
286 throw ValidationException
287 .createFromCollectedValidationExceptions(collectedExceptions);
289 logger.info("Validated transaction {}", transactionIdentifier);
293 * If this method passes validation, it will grab
294 * {@link TransactionStatus#secondPhaseCommitStarted} lock. This lock will
295 * prevent calling @{link #validateBeforeCommitAndLockTransaction},
296 * effectively only allowing to call {@link #secondPhaseCommit} after
297 * successful return of this method.
300 public synchronized CommitInfo validateBeforeCommitAndLockTransaction()
301 throws ValidationException {
302 transactionStatus.checkNotAborted();
303 transactionStatus.checkNotCommitStarted();
304 configBeanModificationDisabled.set(true);
307 } catch (ValidationException e) {
308 logger.info("Commit failed on validation");
309 configBeanModificationDisabled.set(false); // recoverable error
312 // errors in this state are not recoverable. modules are not mutable
314 transactionStatus.setSecondPhaseCommitStarted();
315 return dependencyResolverManager.toCommitInfo();
322 public synchronized List<ModuleIdentifier> secondPhaseCommit() {
323 transactionStatus.checkNotAborted();
324 transactionStatus.checkCommitStarted();
325 if (configBeanModificationDisabled.get() == false) {
326 throw new IllegalStateException(
327 "Internal error - validateBeforeCommitAndLockTransaction should be called "
328 + "to obtain a lock");
331 logger.info("Committing transaction {}", transactionIdentifier);
333 // call getInstance()
334 for (Entry<ModuleIdentifier, Module> entry : dependencyResolverManager
335 .getAllModules().entrySet()) {
336 Module module = entry.getValue();
337 ModuleIdentifier name = entry.getKey();
339 logger.debug("About to commit {} in transaction {}",
340 transactionIdentifier, name);
341 module.getInstance();
342 } catch (Exception e) {
343 logger.error("Commit failed on {} in transaction {}", name,
344 transactionIdentifier, e);
346 throw new RuntimeException(
347 format("Error - getInstance() failed for %s in transaction %s",
348 name, transactionIdentifier), e);
352 // count dependency order
354 logger.info("Committed configuration {}", transactionIdentifier);
355 transactionStatus.setCommitted();
356 // unregister this and all modules from jmx
359 return dependencyResolverManager.getSortedModuleIdentifiers();
363 public synchronized void abortConfig() {
364 transactionStatus.checkNotCommitStarted();
365 transactionStatus.checkNotAborted();
369 private void internalAbort() {
370 transactionStatus.setAborted();
374 private void close() {
375 transactionRegistrator.close();
379 public ObjectName getControllerObjectName() {
384 public String getTransactionName() {
385 return transactionIdentifier.getName();
392 public Set<ObjectName> lookupConfigBeans() {
393 return lookupConfigBeans("*", "*");
400 public Set<ObjectName> lookupConfigBeans(String moduleName) {
401 return lookupConfigBeans(moduleName, "*");
408 public ObjectName lookupConfigBean(String moduleName, String instanceName)
409 throws InstanceNotFoundException {
410 return LookupBeansUtil.lookupConfigBean(this, moduleName, instanceName);
417 public Set<ObjectName> lookupConfigBeans(String moduleName,
418 String instanceName) {
419 ObjectName namePattern = ObjectNameUtil.createModulePattern(moduleName,
420 instanceName, transactionIdentifier.getName());
421 return txModuleJMXRegistrator.queryNames(namePattern, null);
425 public Set<String> getAvailableModuleNames() {
426 return factoriesHolder.getModuleNames();
430 public boolean isClosed() {
431 return transactionStatus.isAbortedOrCommitted();
435 public String toString() {
436 StringBuilder sb = new StringBuilder();
437 sb.append("transactionName=");
438 sb.append(getTransactionName());
439 return sb.toString();
442 // @VisibleForTesting
444 TransactionModuleJMXRegistrator getTxModuleJMXRegistrator() {
445 return txModuleJMXRegistrator;
448 public TransactionIdentifier getName() {
449 return transactionIdentifier;
453 public List<ModuleFactory> getCurrentlyRegisteredFactories() {
454 return currentlyRegisteredFactories;
458 public TransactionIdentifier getIdentifier() {
459 return transactionIdentifier;