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.Collections;
15 import java.util.List;
17 import javax.management.InstanceNotFoundException;
18 import javax.management.ObjectName;
19 import org.opendaylight.controller.config.api.ConflictingVersionException;
20 import org.opendaylight.controller.config.api.ValidationException;
21 import org.opendaylight.controller.config.api.jmx.CommitStatus;
22 import org.opendaylight.controller.config.util.ConfigRegistryClient;
23 import org.opendaylight.controller.config.util.ConfigTransactionClient;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
27 public class TransactionProvider implements AutoCloseable {
28 private static final Logger LOG = LoggerFactory.getLogger(TransactionProvider.class);
30 private final ConfigRegistryClient configRegistryClient;
32 private final String sessionIdForReporting;
33 private ObjectName candidateTx;
34 private ObjectName readTx;
35 private final List<ObjectName> allOpenedTransactions = new ArrayList<>();
36 private static final String NO_TRANSACTION_FOUND_FOR_SESSION = "No transaction found for session ";
38 public TransactionProvider(ConfigRegistryClient configRegistryClient, String sessionIdForReporting) {
39 this.configRegistryClient = configRegistryClient;
40 this.sessionIdForReporting = sessionIdForReporting;
44 public synchronized void close() {
45 for (ObjectName tx : allOpenedTransactions) {
47 if (isStillOpenTransaction(tx)) {
48 configRegistryClient.getConfigTransactionClient(tx).abortConfig();
50 } catch (Exception e) {
51 LOG.debug("Ignoring exception while closing transaction {}", tx, e);
54 allOpenedTransactions.clear();
57 public synchronized Optional<ObjectName> getTransaction() {
59 if (candidateTx == null){
60 return Optional.absent();
63 // Transaction was already closed somehow
64 if (!isStillOpenTransaction(candidateTx)) {
65 LOG.warn("Fixing illegal state: transaction {} was closed in {}", candidateTx, sessionIdForReporting);
67 return Optional.absent();
69 return Optional.of(candidateTx);
72 public synchronized Optional<ObjectName> getReadTransaction() {
75 return Optional.absent();
78 // Transaction was already closed somehow
79 if (!isStillOpenTransaction(readTx)) {
80 LOG.warn("Fixing illegal state: transaction {} was closed in {}", readTx, sessionIdForReporting);
82 return Optional.absent();
84 return Optional.of(readTx);
87 private boolean isStillOpenTransaction(ObjectName transaction) {
88 return configRegistryClient.getOpenConfigs().contains(transaction);
91 public synchronized ObjectName getOrCreateTransaction() {
92 Optional<ObjectName> ta = getTransaction();
97 candidateTx = configRegistryClient.beginConfig();
98 allOpenedTransactions.add(candidateTx);
102 public synchronized ObjectName getOrCreateReadTransaction() {
103 Optional<ObjectName> ta = getReadTransaction();
105 if (ta.isPresent()) {
108 readTx = configRegistryClient.beginConfig();
109 allOpenedTransactions.add(readTx);
114 * Used for editConfig test option
116 public synchronized ObjectName getTestTransaction() {
117 ObjectName testTx = configRegistryClient.beginConfig();
118 allOpenedTransactions.add(testTx);
123 * Commit and notification send must be atomic
125 public CommitStatus commitTransaction() throws ValidationException, ConflictingVersionException {
126 return commitTransaction(configRegistryClient);
130 * Commit and notification send must be atomic
131 * @param configRegistryClient
133 public synchronized CommitStatus commitTransaction(final ConfigRegistryClient configRegistryClient) throws ValidationException, ConflictingVersionException {
134 if (!getTransaction().isPresent()){
135 //making empty commit without prior opened transaction, just return commit status with empty lists
136 LOG.debug("Making commit without open candidate transaction for session {}", sessionIdForReporting);
137 return new CommitStatus(Collections.EMPTY_LIST, Collections.EMPTY_LIST, Collections.EMPTY_LIST);
139 final Optional<ObjectName> maybeTaON = getTransaction();
140 ObjectName taON = maybeTaON.get();
142 CommitStatus status = configRegistryClient.commitConfig(taON);
144 allOpenedTransactions.remove(candidateTx);
147 } catch (ValidationException validationException) {
148 // no clean up: user can reconfigure and recover this transaction
149 LOG.warn("Transaction {} failed on {}", taON, validationException.toString());
150 throw validationException;
151 } catch (ConflictingVersionException e) {
152 LOG.debug("Exception while commit of {}, aborting transaction", taON, e);
159 public synchronized void abortTransaction() {
160 LOG.debug("Aborting current transaction");
161 Optional<ObjectName> taON = getTransaction();
162 Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + sessionIdForReporting);
164 ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
165 transactionClient.abortConfig();
166 allOpenedTransactions.remove(candidateTx);
170 public synchronized void closeReadTransaction() {
171 LOG.debug("Closing read transaction");
172 Optional<ObjectName> taON = getReadTransaction();
173 Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + sessionIdForReporting);
175 ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
176 transactionClient.abortConfig();
177 allOpenedTransactions.remove(readTx);
181 public synchronized void abortTestTransaction(ObjectName testTx) {
182 LOG.debug("Aborting transaction {}", testTx);
183 ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(testTx);
184 allOpenedTransactions.remove(testTx);
185 transactionClient.abortConfig();
188 public void validateTransaction() throws ValidationException {
189 Optional<ObjectName> taON = getTransaction();
190 Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + sessionIdForReporting);
192 ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
193 transactionClient.validateConfig();
196 public void validateTestTransaction(ObjectName taON) throws ValidationException {
197 ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON);
198 transactionClient.validateConfig();
201 public void wipeTestTransaction(ObjectName taON) {
202 wipeInternal(taON, true);
206 * Wiping means removing all module instances keeping the transaction open + service references.
208 synchronized void wipeInternal(ObjectName taON, boolean isTest) {
209 ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON);
211 Set<ObjectName> lookupConfigBeans = transactionClient.lookupConfigBeans();
212 int i = lookupConfigBeans.size();
213 for (ObjectName instance : lookupConfigBeans) {
215 transactionClient.destroyModule(instance);
216 } catch (InstanceNotFoundException e) {
218 LOG.debug("Unable to clean configuration in transactiom {}", taON, e);
220 LOG.warn("Unable to clean configuration in transactiom {}", taON, e);
223 throw new IllegalStateException("Unable to clean configuration in transactiom " + taON, e);
226 LOG.debug("Transaction {} wiped clean of {} config beans", taON, i);
228 transactionClient.removeAllServiceReferences();
229 LOG.debug("Transaction {} wiped clean of all service references", taON);
232 public void wipeTransaction() {
233 Optional<ObjectName> taON = getTransaction();
234 Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + sessionIdForReporting);
235 wipeInternal(taON.get(), false);