2 * Copyright (c) 2015, 2017 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;
14 import java.util.ArrayList;
15 import java.util.Collections;
16 import java.util.List;
19 import javax.management.InstanceNotFoundException;
20 import javax.management.ObjectName;
22 import org.opendaylight.controller.config.api.ConflictingVersionException;
23 import org.opendaylight.controller.config.api.ValidationException;
24 import org.opendaylight.controller.config.api.jmx.CommitStatus;
25 import org.opendaylight.controller.config.util.ConfigRegistryClient;
26 import org.opendaylight.controller.config.util.ConfigTransactionClient;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
30 public class TransactionProvider implements AutoCloseable {
31 private static final Logger LOG = LoggerFactory.getLogger(TransactionProvider.class);
33 private final ConfigRegistryClient configRegistryClient;
35 private final String sessionIdForReporting;
36 private ObjectName candidateTx;
37 private ObjectName readTx;
38 private final List<ObjectName> allOpenedTransactions = new ArrayList<>();
39 private static final String NO_TRANSACTION_FOUND_FOR_SESSION = "No transaction found for session ";
41 public TransactionProvider(final ConfigRegistryClient configRegistryClient, final String sessionIdForReporting) {
42 this.configRegistryClient = configRegistryClient;
43 this.sessionIdForReporting = sessionIdForReporting;
47 @SuppressWarnings("IllegalCatch")
48 public synchronized void close() {
49 for (ObjectName tx : allOpenedTransactions) {
51 if (isStillOpenTransaction(tx)) {
52 configRegistryClient.getConfigTransactionClient(tx).abortConfig();
54 } catch (final RuntimeException e) {
55 LOG.debug("Ignoring exception while closing transaction {}", tx, e);
58 allOpenedTransactions.clear();
61 public synchronized Optional<ObjectName> getTransaction() {
63 if (candidateTx == null) {
64 return Optional.absent();
67 // Transaction was already closed somehow
68 if (!isStillOpenTransaction(candidateTx)) {
69 LOG.warn("Fixing illegal state: transaction {} was closed in {}", candidateTx, sessionIdForReporting);
71 return Optional.absent();
73 return Optional.of(candidateTx);
76 public synchronized Optional<ObjectName> getReadTransaction() {
79 return Optional.absent();
82 // Transaction was already closed somehow
83 if (!isStillOpenTransaction(readTx)) {
84 LOG.warn("Fixing illegal state: transaction {} was closed in {}", readTx, sessionIdForReporting);
86 return Optional.absent();
88 return Optional.of(readTx);
91 private boolean isStillOpenTransaction(final ObjectName transaction) {
92 return configRegistryClient.getOpenConfigs().contains(transaction);
95 public synchronized ObjectName getOrCreateTransaction() {
96 Optional<ObjectName> ta = getTransaction();
101 candidateTx = configRegistryClient.beginConfig();
102 allOpenedTransactions.add(candidateTx);
106 public synchronized ObjectName getOrCreateReadTransaction() {
107 Optional<ObjectName> ta = getReadTransaction();
109 if (ta.isPresent()) {
112 readTx = configRegistryClient.beginConfig();
113 allOpenedTransactions.add(readTx);
118 * Used for editConfig test option.
120 public synchronized ObjectName getTestTransaction() {
121 ObjectName testTx = configRegistryClient.beginConfig();
122 allOpenedTransactions.add(testTx);
127 * Commit and notification send must be atomic.
129 public CommitStatus commitTransaction() throws ValidationException, ConflictingVersionException {
130 return commitTransaction(configRegistryClient);
134 * Commit and notification send must be atomic.
136 public synchronized CommitStatus commitTransaction(final ConfigRegistryClient client)
137 throws ValidationException, ConflictingVersionException {
138 if (!getTransaction().isPresent()) {
139 // making empty commit without prior opened transaction, just return commit
140 // status with empty lists
141 LOG.debug("Making commit without open candidate transaction for session {}", sessionIdForReporting);
142 return new CommitStatus(Collections.emptyList(), Collections.emptyList(), Collections.emptyList());
144 final Optional<ObjectName> maybeTaON = getTransaction();
145 ObjectName taON = maybeTaON.get();
147 CommitStatus status = client.commitConfig(taON);
149 allOpenedTransactions.remove(candidateTx);
152 } catch (final ValidationException validationException) {
153 // no clean up: user can reconfigure and recover this transaction
154 LOG.warn("Transaction {} failed on {}", taON, validationException.toString());
155 throw validationException;
156 } catch (final ConflictingVersionException e) {
157 LOG.debug("Exception while commit of {}, aborting transaction", taON, e);
164 public synchronized void abortTransaction() {
165 LOG.debug("Aborting current transaction");
166 Optional<ObjectName> taON = getTransaction();
167 Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + sessionIdForReporting);
169 ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
170 transactionClient.abortConfig();
171 allOpenedTransactions.remove(candidateTx);
175 public synchronized void closeReadTransaction() {
176 LOG.debug("Closing read transaction");
177 Optional<ObjectName> taON = getReadTransaction();
178 Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + sessionIdForReporting);
180 ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
181 transactionClient.abortConfig();
182 allOpenedTransactions.remove(readTx);
186 public synchronized void abortTestTransaction(final ObjectName testTx) {
187 LOG.debug("Aborting transaction {}", testTx);
188 ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(testTx);
189 allOpenedTransactions.remove(testTx);
190 transactionClient.abortConfig();
193 public void validateTransaction() throws ValidationException {
194 Optional<ObjectName> taON = getTransaction();
195 Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + sessionIdForReporting);
197 ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON.get());
198 transactionClient.validateConfig();
201 public void validateTestTransaction(final ObjectName taON) throws ValidationException {
202 ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON);
203 transactionClient.validateConfig();
206 public void wipeTestTransaction(final ObjectName taON) {
207 wipeInternal(taON, true);
211 * Wiping means removing all module instances keeping the transaction open +
212 * service references.
214 synchronized void wipeInternal(final ObjectName taON, final boolean isTest) {
215 ConfigTransactionClient transactionClient = configRegistryClient.getConfigTransactionClient(taON);
217 Set<ObjectName> lookupConfigBeans = transactionClient.lookupConfigBeans();
218 int index = lookupConfigBeans.size();
219 for (ObjectName instance : lookupConfigBeans) {
221 transactionClient.destroyModule(instance);
222 } catch (final InstanceNotFoundException e) {
224 LOG.debug("Unable to clean configuration in transactiom {}", taON, e);
226 LOG.warn("Unable to clean configuration in transactiom {}", taON, e);
229 throw new IllegalStateException("Unable to clean configuration in transactiom " + taON, e);
232 LOG.debug("Transaction {} wiped clean of {} config beans", taON, index);
234 transactionClient.removeAllServiceReferences();
235 LOG.debug("Transaction {} wiped clean of all service references", taON);
238 public void wipeTransaction() {
239 Optional<ObjectName> taON = getTransaction();
240 Preconditions.checkState(taON.isPresent(), NO_TRANSACTION_FOUND_FOR_SESSION + sessionIdForReporting);
241 wipeInternal(taON.get(), false);