bump netty to version 4.0.10.Final
[controller.git] / opendaylight / config / config-manager / src / main / java / org / opendaylight / controller / config / manager / impl / ConfigTransactionControllerImpl.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.config.manager.impl;
9
10 import static java.lang.String.format;
11
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.Map.Entry;
15 import java.util.Set;
16 import java.util.concurrent.atomic.AtomicBoolean;
17
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;
25
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;
44
45 /**
46  * This is a JMX bean representing current transaction. It contains
47  * {@link #transactionIdentifier}, unique version and parent version for
48  * optimistic locking.
49  */
50 class ConfigTransactionControllerImpl implements
51         ConfigTransactionControllerInternal,
52         ConfigTransactionControllerImplMXBean {
53     private static final Logger logger = LoggerFactory
54             .getLogger(ConfigTransactionControllerImpl.class);
55
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;
65
66     /**
67      * Disables ability of {@link DynamicWritableWrapper} to change attributes
68      * during validation.
69      */
70     @GuardedBy("this")
71     private final AtomicBoolean configBeanModificationDisabled = new AtomicBoolean(
72             false);
73     private final ReadOnlyAtomicBoolean readOnlyAtomicBoolean = new ReadOnlyAtomicBooleanImpl(
74             configBeanModificationDisabled);
75     private final MBeanServer configMBeanServer;
76
77     public ConfigTransactionControllerImpl(String transactionName,
78             TransactionJMXRegistrator transactionRegistrator,
79             long parentVersion, long currentVersion,
80             List<? extends ModuleFactory> currentlyRegisteredFactories,
81             MBeanServer transactionsMBeanServer, MBeanServer configMBeanServer) {
82
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;
98     }
99
100     @Override
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);
108
109         ModuleFactory moduleFactory = factoriesHolder
110                 .findByModuleName(moduleIdentifier.getFactoryName());
111
112         Module module;
113         DependencyResolver dependencyResolver = dependencyResolverManager
114                 .getOrCreate(moduleIdentifier);
115         try {
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);
123         }
124         putConfigBeanToJMXAndInternalMaps(moduleIdentifier, module,
125                 moduleFactory, oldConfigBeanInfo);
126     }
127
128     @Override
129     public synchronized ObjectName createModule(String factoryName,
130             String instanceName) throws InstanceAlreadyExistsException {
131
132         transactionStatus.checkNotCommitStarted();
133         transactionStatus.checkNotAborted();
134         ModuleIdentifier moduleIdentifier = new ModuleIdentifier(factoryName,
135                 instanceName);
136         dependencyResolverManager.assertNotExists(moduleIdentifier);
137
138         // find factory
139         ModuleFactory moduleFactory = factoriesHolder
140                 .findByModuleName(factoryName);
141         DependencyResolver dependencyResolver = dependencyResolverManager
142                 .getOrCreate(moduleIdentifier);
143         Module module = moduleFactory.createModule(instanceName,
144                 dependencyResolver);
145         return putConfigBeanToJMXAndInternalMaps(moduleIdentifier, module,
146                 moduleFactory, null);
147     }
148
149     private synchronized ObjectName putConfigBeanToJMXAndInternalMaps(
150             ModuleIdentifier moduleIdentifier, Module module,
151             ModuleFactory moduleFactory,
152             @Nullable ModuleInternalInfo maybeOldConfigBeanInfo)
153             throws InstanceAlreadyExistsException {
154
155         DynamicMBean writableDynamicWrapper = new DynamicWritableWrapper(
156                 module, moduleIdentifier, transactionIdentifier,
157                 readOnlyAtomicBoolean, transactionsMBeanServer,
158                 configMBeanServer);
159
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);
168
169         dependencyResolverManager.put(moduleInternalTransactionalInfo);
170         return writableON;
171     }
172
173     @Override
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 "
180                     + objectName);
181         }
182         ObjectNameUtil.checkDomain(objectName);
183         transactionStatus.checkNotAborted();
184         ModuleIdentifier moduleIdentifier = ObjectNameUtil.fromON(objectName,
185                 ObjectNameUtil.TYPE_MODULE);
186         ModuleInternalTransactionalInfo removedTInfo = dependencyResolverManager
187                 .destroyModule(moduleIdentifier);
188         // remove from jmx
189         removedTInfo.getTransactionModuleJMXRegistration().close();
190     }
191
192     @Override
193     public long getParentVersion() {
194         return parentVersion;
195     }
196
197     @Override
198     public long getVersion() {
199         return currentVersion;
200     }
201
202     @Override
203     public synchronized void validateConfig() throws ValidationException {
204         if (configBeanModificationDisabled.get())
205             throw new IllegalStateException("Cannot start validation");
206         configBeanModificationDisabled.set(true);
207         try {
208             validate_noLocks();
209         } finally {
210             configBeanModificationDisabled.set(false);
211         }
212     }
213
214     private void validate_noLocks() throws ValidationException {
215         transactionStatus.checkNotAborted();
216         logger.info("Validating transaction {}", transactionIdentifier);
217         // call validate()
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();
223             try {
224                 module.validate();
225             } catch (Exception e) {
226                 logger.warn("Validation exception in {}", getTransactionName(),
227                         e);
228                 collectedExceptions.add(ValidationException
229                         .createForSingleException(name, e));
230             }
231         }
232         if (collectedExceptions.size() > 0) {
233             throw ValidationException
234                     .createFromCollectedValidationExceptions(collectedExceptions);
235         }
236         logger.info("Validated transaction {}", transactionIdentifier);
237     }
238
239     /**
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.
245      */
246     @Override
247     public synchronized CommitInfo validateBeforeCommitAndLockTransaction()
248             throws ValidationException {
249         transactionStatus.checkNotAborted();
250         transactionStatus.checkNotCommitStarted();
251         configBeanModificationDisabled.set(true);
252         try {
253             validate_noLocks();
254         } catch (ValidationException e) {
255             logger.info("Commit failed on validation");
256             configBeanModificationDisabled.set(false); // recoverable error
257             throw e;
258         }
259         // errors in this state are not recoverable. modules are not mutable
260         // anymore.
261         transactionStatus.setSecondPhaseCommitStarted();
262         return dependencyResolverManager.toCommitInfo();
263     }
264
265     /**
266      * {@inheritDoc}
267      */
268     @Override
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");
276         }
277
278         logger.info("Committing transaction {}", transactionIdentifier);
279
280         // call getInstance()
281         for (Entry<ModuleIdentifier, Module> entry : dependencyResolverManager
282                 .getAllModules().entrySet()) {
283             Module module = entry.getValue();
284             ModuleIdentifier name = entry.getKey();
285             try {
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);
292                 internalAbort();
293                 throw new RuntimeException(
294                         format("Error - getInstance() failed for %s in transaction %s",
295                                 name, transactionIdentifier), e);
296             }
297         }
298
299         // count dependency order
300
301         logger.info("Committed configuration {}", transactionIdentifier);
302         transactionStatus.setCommitted();
303         // unregister this and all modules from jmx
304         close();
305
306         return dependencyResolverManager.getSortedModuleIdentifiers();
307     }
308
309     @Override
310     public synchronized void abortConfig() {
311         transactionStatus.checkNotCommitStarted();
312         transactionStatus.checkNotAborted();
313         internalAbort();
314     }
315
316     private void internalAbort() {
317         transactionStatus.setAborted();
318         close();
319     }
320
321     private void close() {
322         transactionRegistrator.close();
323     }
324
325     @Override
326     public ObjectName getControllerObjectName() {
327         return controllerON;
328     }
329
330     @Override
331     public String getTransactionName() {
332         return transactionIdentifier.getName();
333     }
334
335     /**
336      * {@inheritDoc}
337      */
338     @Override
339     public Set<ObjectName> lookupConfigBeans() {
340         return lookupConfigBeans("*", "*");
341     }
342
343     /**
344      * {@inheritDoc}
345      */
346     @Override
347     public Set<ObjectName> lookupConfigBeans(String moduleName) {
348         return lookupConfigBeans(moduleName, "*");
349     }
350
351     /**
352      * {@inheritDoc}
353      */
354     @Override
355     public ObjectName lookupConfigBean(String moduleName, String instanceName)
356             throws InstanceNotFoundException {
357         return LookupBeansUtil.lookupConfigBean(this, moduleName, instanceName);
358     }
359
360     /**
361      * {@inheritDoc}
362      */
363     @Override
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);
369     }
370
371     @Override
372     public Set<String> getAvailableModuleNames() {
373         return factoriesHolder.getModuleNames();
374     }
375
376     @Override
377     public boolean isClosed() {
378         return transactionStatus.isAbortedOrCommitted();
379     }
380
381     @Override
382     public String toString() {
383         StringBuilder sb = new StringBuilder();
384         sb.append("transactionName=");
385         sb.append(getTransactionName());
386         return sb.toString();
387     }
388
389     // @VisibleForTesting
390
391     TransactionModuleJMXRegistrator getTxModuleJMXRegistrator() {
392         return txModuleJMXRegistrator;
393     }
394
395     public TransactionIdentifier getName() {
396         return transactionIdentifier;
397     }
398 }