2 * Copyright (c) 2015 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
9 package org.opendaylight.controller.config.facade.xml.transactions;
11 import com.google.common.base.Optional;
12 import com.google.common.base.Preconditions;
13 import java.util.ArrayList;
14 import java.util.List;
16 import javax.management.InstanceNotFoundException;
17 import javax.management.ObjectName;
18 import org.opendaylight.controller.config.api.ConflictingVersionException;
19 import org.opendaylight.controller.config.api.ValidationException;
20 import org.opendaylight.controller.config.api.jmx.CommitStatus;
21 import org.opendaylight.controller.config.facade.xml.exception.NoTransactionFoundException;
22 import org.opendaylight.controller.config.util.ConfigRegistryClient;
23 import org.opendaylight.controller.config.util.ConfigTransactionClient;
24 import org.opendaylight.controller.config.util.xml.DocumentedException;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
28 public class TransactionProvider implements AutoCloseable {
29 private static final Logger LOG = LoggerFactory.getLogger(TransactionProvider.class);
31 private final ConfigRegistryClient configRegistryClient;
33 private final String sessionIdForReporting;
34 private ObjectName candidateTx;
35 private ObjectName readTx;
36 private final List<ObjectName> allOpenedTransactions = new ArrayList<>();
37 private static final String NO_TRANSACTION_FOUND_FOR_SESSION = "No transaction found for session ";
39 public TransactionProvider(ConfigRegistryClient configRegistryClient, String sessionIdForReporting) {
40 this.configRegistryClient = configRegistryClient;
41 this.sessionIdForReporting = sessionIdForReporting;
45 public synchronized void close() {
46 for (ObjectName tx : allOpenedTransactions) {
48 if (isStillOpenTransaction(tx)) {
49 configRegistryClient.getConfigTransactionClient(tx).abortConfig();
51 } catch (Exception e) {
52 LOG.debug("Ignoring exception while closing transaction {}", tx, e);
55 allOpenedTransactions.clear();
58 public synchronized Optional<ObjectName> getTransaction() {
60 if (candidateTx == null){
61 return Optional.absent();
64 // Transaction was already closed somehow
65 if (!isStillOpenTransaction(candidateTx)) {
66 LOG.warn("Fixing illegal state: transaction {} was closed in {}", candidateTx, sessionIdForReporting);
68 return Optional.absent();
70 return Optional.of(candidateTx);
73 public synchronized Optional<ObjectName> getReadTransaction() {
76 return Optional.absent();
79 // Transaction was already closed somehow
80 if (!isStillOpenTransaction(readTx)) {
81 LOG.warn("Fixing illegal state: transaction {} was closed in {}", readTx, sessionIdForReporting);
83 return Optional.absent();
85 return Optional.of(readTx);
88 private boolean isStillOpenTransaction(ObjectName transaction) {
89 return configRegistryClient.getOpenConfigs().contains(transaction);
92 public synchronized ObjectName getOrCreateTransaction() {
93 Optional<ObjectName> ta = getTransaction();
98 candidateTx = configRegistryClient.beginConfig();
99 allOpenedTransactions.add(candidateTx);
103 public synchronized ObjectName getOrCreateReadTransaction() {
104 Optional<ObjectName> ta = getReadTransaction();
106 if (ta.isPresent()) {
109 readTx = configRegistryClient.beginConfig();
110 allOpenedTransactions.add(readTx);
115 * Used for editConfig test option
117 public synchronized ObjectName getTestTransaction() {
118 ObjectName testTx = configRegistryClient.beginConfig();
119 allOpenedTransactions.add(testTx);
124 * Commit and notification send must be atomic
126 public CommitStatus commitTransaction() throws ValidationException, ConflictingVersionException,
127 NoTransactionFoundException {
128 return commitTransaction(configRegistryClient);
132 * Commit and notification send must be atomic
133 * @param configRegistryClient
135 public synchronized CommitStatus commitTransaction(final ConfigRegistryClient configRegistryClient) throws ValidationException, ConflictingVersionException, NoTransactionFoundException {
136 if (!getTransaction().isPresent()){
137 throw new NoTransactionFoundException("No transaction found for session " + sessionIdForReporting,
138 DocumentedException.ErrorType.application,
139 DocumentedException.ErrorTag.operation_failed,
140 DocumentedException.ErrorSeverity.error);
142 final Optional<ObjectName> maybeTaON = getTransaction();
143 ObjectName taON = maybeTaON.get();
145 CommitStatus status = configRegistryClient.commitConfig(taON);
147 allOpenedTransactions.remove(candidateTx);
150 } catch (ValidationException validationException) {
151 // no clean up: user can reconfigure and recover this transaction
152 LOG.warn("Transaction {} failed on {}", taON, validationException.toString());
153 throw validationException;
154 } catch (ConflictingVersionException e) {
155 LOG.error("Exception while commit of {}, aborting transaction", taON, e);
162 public synchronized void abortTransaction() {
163 LOG.debug("Aborting current transaction");
164 Optional<ObjectName> taON = getTransaction();
165 Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + sessionIdForReporting);
167 ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
168 transactionClient.abortConfig();
169 allOpenedTransactions.remove(candidateTx);
173 public synchronized void closeReadTransaction() {
174 LOG.debug("Closing read transaction");
175 Optional<ObjectName> taON = getReadTransaction();
176 Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + sessionIdForReporting);
178 ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
179 transactionClient.abortConfig();
180 allOpenedTransactions.remove(readTx);
184 public synchronized void abortTestTransaction(ObjectName testTx) {
185 LOG.debug("Aborting transaction {}", testTx);
186 ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(testTx);
187 allOpenedTransactions.remove(testTx);
188 transactionClient.abortConfig();
191 public void validateTransaction() throws ValidationException {
192 Optional<ObjectName> taON = getTransaction();
193 Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + sessionIdForReporting);
195 ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
196 transactionClient.validateConfig();
199 public void validateTestTransaction(ObjectName taON) throws ValidationException {
200 ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON);
201 transactionClient.validateConfig();
204 public void wipeTestTransaction(ObjectName taON) {
205 wipeInternal(taON, true);
209 * Wiping means removing all module instances keeping the transaction open + service references.
211 synchronized void wipeInternal(ObjectName taON, boolean isTest) {
212 ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON);
214 Set<ObjectName> lookupConfigBeans = transactionClient.lookupConfigBeans();
215 int i = lookupConfigBeans.size();
216 for (ObjectName instance : lookupConfigBeans) {
218 transactionClient.destroyModule(instance);
219 } catch (InstanceNotFoundException e) {
221 LOG.debug("Unable to clean configuration in transactiom {}", taON, e);
223 LOG.warn("Unable to clean configuration in transactiom {}", taON, e);
226 throw new IllegalStateException("Unable to clean configuration in transactiom " + taON, e);
229 LOG.debug("Transaction {} wiped clean of {} config beans", taON, i);
231 transactionClient.removeAllServiceReferences();
232 LOG.debug("Transaction {} wiped clean of all service references", taON);
235 public void wipeTransaction() {
236 Optional<ObjectName> taON = getTransaction();
237 Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + sessionIdForReporting);
238 wipeInternal(taON.get(), false);