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.DynamicMBean;
33 import javax.management.InstanceAlreadyExistsException;
34 import javax.management.InstanceNotFoundException;
35 import javax.management.MBeanServer;
36 import javax.management.ObjectName;
37 import java.util.ArrayList;
38 import java.util.Collection;
39 import java.util.HashSet;
40 import java.util.List;
41 import java.util.Map.Entry;
43 import java.util.concurrent.atomic.AtomicBoolean;
45 import static java.lang.String.format;
48 * This is a JMX bean representing current transaction. It contains
49 * {@link #transactionIdentifier}, unique version and parent version for
52 class ConfigTransactionControllerImpl implements
53 ConfigTransactionControllerInternal,
54 ConfigTransactionControllerImplMXBean,
55 Identifiable<TransactionIdentifier>{
56 private static final Logger logger = LoggerFactory.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;
67 private final List<ModuleFactory> currentlyRegisteredFactories;
70 * Disables ability of {@link DynamicWritableWrapper} to change attributes
74 private final AtomicBoolean configBeanModificationDisabled = new AtomicBoolean(
76 private final ReadOnlyAtomicBoolean readOnlyAtomicBoolean = new ReadOnlyAtomicBooleanImpl(
77 configBeanModificationDisabled);
78 private final MBeanServer configMBeanServer;
80 private final BundleContext bundleContext;
82 public ConfigTransactionControllerImpl(String transactionName,
83 TransactionJMXRegistrator transactionRegistrator,
84 long parentVersion, long currentVersion,
85 List<ModuleFactory> currentlyRegisteredFactories,
86 MBeanServer transactionsMBeanServer, MBeanServer configMBeanServer, BundleContext bundleContext) {
88 this.transactionIdentifier = new TransactionIdentifier(transactionName);
89 this.controllerON = ObjectNameUtil
90 .createTransactionControllerON(transactionName);
91 this.transactionRegistrator = transactionRegistrator;
92 txModuleJMXRegistrator = transactionRegistrator
93 .createTransactionModuleJMXRegistrator();
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(transactionName, transactionStatus);
100 this.transactionsMBeanServer = transactionsMBeanServer;
101 this.configMBeanServer = configMBeanServer;
102 this.bundleContext = bundleContext;
106 public void copyExistingModulesAndProcessFactoryDiff(Collection<ModuleInternalInfo> existingModules, List<ModuleFactory> lastListOfFactories) {
107 // copy old configuration to this server
108 for (ModuleInternalInfo oldConfigInfo : existingModules) {
110 copyExistingModule(oldConfigInfo);
111 } catch (InstanceAlreadyExistsException e) {
112 throw new IllegalStateException("Error while copying " + oldConfigInfo, e);
115 processDefaultBeans(lastListOfFactories);
118 private synchronized void processDefaultBeans(List<ModuleFactory> lastListOfFactories) {
119 transactionStatus.checkNotCommitStarted();
120 transactionStatus.checkNotAborted();
122 Set<ModuleFactory> oldSet = new HashSet<>(lastListOfFactories);
123 Set<ModuleFactory> newSet = new HashSet<>(currentlyRegisteredFactories);
125 List<ModuleFactory> toBeAdded = new ArrayList<>();
126 List<ModuleFactory> toBeRemoved = new ArrayList<>();
127 for(ModuleFactory moduleFactory: currentlyRegisteredFactories) {
128 if (oldSet.contains(moduleFactory) == false){
129 toBeAdded.add(moduleFactory);
132 for(ModuleFactory moduleFactory: lastListOfFactories){
133 if (newSet.contains(moduleFactory) == false) {
134 toBeRemoved.add(moduleFactory);
137 // add default modules
138 for (ModuleFactory moduleFactory : toBeAdded) {
139 Set<? extends Module> defaultModules = moduleFactory.getDefaultModules(dependencyResolverManager, bundleContext);
140 for (Module module : defaultModules) {
141 // ensure default module to be registered to jmx even if its module factory does not use dependencyResolverFactory
142 DependencyResolver dependencyResolver = dependencyResolverManager.getOrCreate(module.getIdentifier());
144 putConfigBeanToJMXAndInternalMaps(module.getIdentifier(), module, moduleFactory, null, dependencyResolver);
145 } catch (InstanceAlreadyExistsException e) {
146 throw new IllegalStateException(e);
151 // remove modules belonging to removed factories
152 for(ModuleFactory removedFactory: toBeRemoved){
153 List<ModuleIdentifier> modulesOfRemovedFactory = dependencyResolverManager.findAllByFactory(removedFactory);
154 for (ModuleIdentifier name : modulesOfRemovedFactory) {
161 private synchronized void copyExistingModule(
162 ModuleInternalInfo oldConfigBeanInfo)
163 throws InstanceAlreadyExistsException {
164 transactionStatus.checkNotCommitStarted();
165 transactionStatus.checkNotAborted();
166 ModuleIdentifier moduleIdentifier = oldConfigBeanInfo.getName();
167 dependencyResolverManager.assertNotExists(moduleIdentifier);
169 ModuleFactory moduleFactory = factoriesHolder
170 .findByModuleName(moduleIdentifier.getFactoryName());
173 DependencyResolver dependencyResolver = dependencyResolverManager
174 .getOrCreate(moduleIdentifier);
176 module = moduleFactory.createModule(
177 moduleIdentifier.getInstanceName(), dependencyResolver,
178 oldConfigBeanInfo.getReadableModule(), bundleContext);
179 } catch (Exception e) {
180 throw new IllegalStateException(format(
181 "Error while copying old configuration from %s to %s",
182 oldConfigBeanInfo, moduleFactory), e);
184 putConfigBeanToJMXAndInternalMaps(moduleIdentifier, module, moduleFactory, oldConfigBeanInfo, dependencyResolver);
188 public synchronized ObjectName createModule(String factoryName,
189 String instanceName) throws InstanceAlreadyExistsException {
191 transactionStatus.checkNotCommitStarted();
192 transactionStatus.checkNotAborted();
193 ModuleIdentifier moduleIdentifier = new ModuleIdentifier(factoryName, instanceName);
194 dependencyResolverManager.assertNotExists(moduleIdentifier);
197 ModuleFactory moduleFactory = factoriesHolder.findByModuleName(factoryName);
198 DependencyResolver dependencyResolver = dependencyResolverManager.getOrCreate(moduleIdentifier);
199 Module module = moduleFactory.createModule(instanceName, dependencyResolver, bundleContext);
200 return putConfigBeanToJMXAndInternalMaps(moduleIdentifier, module,
201 moduleFactory, null, dependencyResolver);
204 private synchronized ObjectName putConfigBeanToJMXAndInternalMaps(
205 ModuleIdentifier moduleIdentifier, Module module,
206 ModuleFactory moduleFactory,
207 @Nullable ModuleInternalInfo maybeOldConfigBeanInfo, DependencyResolver dependencyResolver)
208 throws InstanceAlreadyExistsException {
210 logger.debug("Adding module {} to transaction {}", moduleIdentifier, this);
211 if (moduleIdentifier.equals(module.getIdentifier())==false) {
212 throw new IllegalStateException("Incorrect name reported by module. Expected "
213 + moduleIdentifier + ", got " + module.getIdentifier());
215 if (dependencyResolver.getIdentifier().equals(moduleIdentifier) == false ) {
216 throw new IllegalStateException("Incorrect name reported by dependency resolver. Expected "
217 + moduleIdentifier + ", got " + dependencyResolver.getIdentifier());
219 DynamicMBean writableDynamicWrapper = new DynamicWritableWrapper(
220 module, moduleIdentifier, transactionIdentifier,
221 readOnlyAtomicBoolean, transactionsMBeanServer,
224 ObjectName writableON = ObjectNameUtil.createTransactionModuleON(
225 transactionIdentifier.getName(), moduleIdentifier);
226 // put wrapper to jmx
227 TransactionModuleJMXRegistration transactionModuleJMXRegistration = txModuleJMXRegistrator
228 .registerMBean(writableDynamicWrapper, writableON);
229 ModuleInternalTransactionalInfo moduleInternalTransactionalInfo = new ModuleInternalTransactionalInfo(
230 moduleIdentifier, module, moduleFactory,
231 maybeOldConfigBeanInfo, transactionModuleJMXRegistration);
233 dependencyResolverManager.put(moduleInternalTransactionalInfo);
238 public void destroyModule(ObjectName objectName)
239 throws InstanceNotFoundException {
240 String foundTransactionName = ObjectNameUtil
241 .getTransactionName(objectName);
242 if (transactionIdentifier.getName().equals(foundTransactionName) == false) {
243 throw new IllegalArgumentException("Wrong transaction name "
246 ObjectNameUtil.checkDomain(objectName);
247 ModuleIdentifier moduleIdentifier = ObjectNameUtil.fromON(objectName,
248 ObjectNameUtil.TYPE_MODULE);
249 destroyModule(moduleIdentifier);
252 private void destroyModule(ModuleIdentifier moduleIdentifier) {
253 logger.debug("Destroying module {} in transaction {}", moduleIdentifier, this);
254 transactionStatus.checkNotAborted();
255 ModuleInternalTransactionalInfo removedTInfo = dependencyResolverManager.destroyModule(moduleIdentifier);
257 removedTInfo.getTransactionModuleJMXRegistration().close();
261 public long getParentVersion() {
262 return parentVersion;
266 public long getVersion() {
267 return currentVersion;
271 public synchronized void validateConfig() throws ValidationException {
272 if (configBeanModificationDisabled.get())
273 throw new IllegalStateException("Cannot start validation");
274 configBeanModificationDisabled.set(true);
278 configBeanModificationDisabled.set(false);
282 private void validate_noLocks() throws ValidationException {
283 transactionStatus.checkNotAborted();
284 logger.info("Validating transaction {}", transactionIdentifier);
286 List<ValidationException> collectedExceptions = new ArrayList<>();
287 for (Entry<ModuleIdentifier, Module> entry : dependencyResolverManager
288 .getAllModules().entrySet()) {
289 ModuleIdentifier name = entry.getKey();
290 Module module = entry.getValue();
293 } catch (Exception e) {
294 logger.warn("Validation exception in {}", getTransactionName(),
296 collectedExceptions.add(ValidationException
297 .createForSingleException(name, e));
300 if (collectedExceptions.size() > 0) {
301 throw ValidationException
302 .createFromCollectedValidationExceptions(collectedExceptions);
304 logger.info("Validated transaction {}", transactionIdentifier);
308 * If this method passes validation, it will grab
309 * {@link TransactionStatus#secondPhaseCommitStarted} lock. This lock will
310 * prevent calling @{link #validateBeforeCommitAndLockTransaction},
311 * effectively only allowing to call {@link #secondPhaseCommit} after
312 * successful return of this method.
315 public synchronized CommitInfo validateBeforeCommitAndLockTransaction()
316 throws ValidationException {
317 transactionStatus.checkNotAborted();
318 transactionStatus.checkNotCommitStarted();
319 configBeanModificationDisabled.set(true);
322 } catch (ValidationException e) {
323 logger.info("Commit failed on validation");
324 configBeanModificationDisabled.set(false); // recoverable error
327 // errors in this state are not recoverable. modules are not mutable
329 transactionStatus.setSecondPhaseCommitStarted();
330 return dependencyResolverManager.toCommitInfo();
337 public synchronized List<ModuleIdentifier> secondPhaseCommit() {
338 transactionStatus.checkNotAborted();
339 transactionStatus.checkCommitStarted();
340 if (configBeanModificationDisabled.get() == false) {
341 throw new IllegalStateException(
342 "Internal error - validateBeforeCommitAndLockTransaction should be called "
343 + "to obtain a lock");
346 logger.info("Committing transaction {}", transactionIdentifier);
348 // call getInstance()
349 for (Entry<ModuleIdentifier, Module> entry : dependencyResolverManager
350 .getAllModules().entrySet()) {
351 Module module = entry.getValue();
352 ModuleIdentifier name = entry.getKey();
354 logger.debug("About to commit {} in transaction {}",
355 name, transactionIdentifier);
356 module.getInstance();
357 } catch (Exception e) {
358 logger.error("Commit failed on {} in transaction {}", name,
359 transactionIdentifier, e);
361 throw new RuntimeException(
362 format("Error - getInstance() failed for %s in transaction %s",
363 name, transactionIdentifier), e);
367 // count dependency order
369 logger.info("Committed configuration {}", transactionIdentifier);
370 transactionStatus.setCommitted();
371 // unregister this and all modules from jmx
374 return dependencyResolverManager.getSortedModuleIdentifiers();
378 public synchronized void abortConfig() {
379 transactionStatus.checkNotCommitStarted();
380 transactionStatus.checkNotAborted();
384 private void internalAbort() {
385 transactionStatus.setAborted();
389 private void close() {
390 transactionRegistrator.close();
394 public ObjectName getControllerObjectName() {
399 public String getTransactionName() {
400 return transactionIdentifier.getName();
407 public Set<ObjectName> lookupConfigBeans() {
408 return lookupConfigBeans("*", "*");
415 public Set<ObjectName> lookupConfigBeans(String moduleName) {
416 return lookupConfigBeans(moduleName, "*");
423 public ObjectName lookupConfigBean(String moduleName, String instanceName)
424 throws InstanceNotFoundException {
425 return LookupBeansUtil.lookupConfigBean(this, moduleName, instanceName);
432 public Set<ObjectName> lookupConfigBeans(String moduleName,
433 String instanceName) {
434 ObjectName namePattern = ObjectNameUtil.createModulePattern(moduleName,
435 instanceName, transactionIdentifier.getName());
436 return txModuleJMXRegistrator.queryNames(namePattern, null);
440 public Set<String> getAvailableModuleNames() {
441 return factoriesHolder.getModuleNames();
445 public boolean isClosed() {
446 return transactionStatus.isAbortedOrCommitted();
450 public String toString() {
451 StringBuilder sb = new StringBuilder();
452 sb.append("transactionName=");
453 sb.append(getTransactionName());
454 return sb.toString();
457 // @VisibleForTesting
459 TransactionModuleJMXRegistrator getTxModuleJMXRegistrator() {
460 return txModuleJMXRegistrator;
463 public TransactionIdentifier getName() {
464 return transactionIdentifier;
468 public List<ModuleFactory> getCurrentlyRegisteredFactories() {
469 return currentlyRegisteredFactories;
473 public TransactionIdentifier getIdentifier() {
474 return transactionIdentifier;