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
8 package org.opendaylight.openflowplugin.common.txchain;
10 import com.google.common.base.Preconditions;
11 import com.google.common.util.concurrent.FluentFuture;
12 import com.google.common.util.concurrent.FutureCallback;
13 import com.google.common.util.concurrent.ListenableFuture;
14 import com.google.common.util.concurrent.MoreExecutors;
15 import java.util.Objects;
16 import java.util.Optional;
17 import java.util.concurrent.CancellationException;
18 import java.util.concurrent.ExecutionException;
19 import java.util.concurrent.TimeUnit;
20 import java.util.concurrent.TimeoutException;
21 import javax.annotation.Nonnull;
22 import javax.annotation.concurrent.GuardedBy;
23 import org.opendaylight.mdsal.binding.api.DataBroker;
24 import org.opendaylight.mdsal.binding.api.ReadWriteTransaction;
25 import org.opendaylight.mdsal.binding.api.Transaction;
26 import org.opendaylight.mdsal.binding.api.TransactionChain;
27 import org.opendaylight.mdsal.binding.api.TransactionChainClosedException;
28 import org.opendaylight.mdsal.binding.api.TransactionChainListener;
29 import org.opendaylight.mdsal.binding.api.WriteTransaction;
30 import org.opendaylight.mdsal.common.api.CommitInfo;
31 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
32 import org.opendaylight.mdsal.common.api.TransactionCommitFailedException;
33 import org.opendaylight.yangtools.yang.binding.DataObject;
34 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
39 * The openflowplugin-impl.org.opendaylight.openflowplugin.impl.device
40 * package protected class for controlling {@link WriteTransaction} life cycle. It is
41 * a {@link TransactionChainListener} and provide package protected methods for writeToTransaction
42 * method (wrapped {@link WriteTransaction#put(LogicalDatastoreType, InstanceIdentifier, DataObject)})
43 * and submitTransaction method (wrapped {@link WriteTransaction#commit()}).
45 public class TransactionChainManager implements TransactionChainListener, AutoCloseable {
47 private static final Logger LOG = LoggerFactory.getLogger(TransactionChainManager.class);
48 private static final String CANNOT_WRITE_INTO_TRANSACTION = "Cannot write into transaction.";
50 private final Object txLock = new Object();
51 private final DataBroker dataBroker;
52 private final String nodeId;
55 private ReadWriteTransaction writeTx;
57 private TransactionChain transactionChain;
59 private boolean submitIsEnabled;
61 private FluentFuture<? extends CommitInfo> lastSubmittedFuture;
63 private volatile boolean initCommit;
66 private TransactionChainManagerStatus transactionChainManagerStatus = TransactionChainManagerStatus.SLEEPING;
68 public TransactionChainManager(@Nonnull final DataBroker dataBroker,
69 @Nonnull final String deviceIdentifier) {
70 this.dataBroker = dataBroker;
71 this.nodeId = deviceIdentifier;
72 this.lastSubmittedFuture = CommitInfo.emptyFluentFuture();
76 private void createTxChain() {
77 TransactionChain txChainFactoryTemp = transactionChain;
78 transactionChain = dataBroker.createTransactionChain(TransactionChainManager.this);
79 Optional.ofNullable(txChainFactoryTemp).ifPresent(TransactionChain::close);
82 public boolean initialSubmitWriteTransaction() {
84 return submitTransaction();
88 * Method change status for TxChainManager to WORKING and it has to make
89 * registration for this class instance as {@link TransactionChainListener} to provide possibility a make DS
90 * transactions. Call this method for MASTER role only.
92 public void activateTransactionManager() {
93 if (LOG.isDebugEnabled()) {
94 LOG.debug("activateTransactionManager for node {} transaction submit is set to {}",
95 this.nodeId, submitIsEnabled);
97 synchronized (txLock) {
98 if (TransactionChainManagerStatus.SLEEPING == transactionChainManagerStatus) {
99 Preconditions.checkState(transactionChain == null,
100 "TxChainFactory survive last close.");
101 Preconditions.checkState(writeTx == null,
102 "We have some unexpected WriteTransaction.");
103 this.transactionChainManagerStatus = TransactionChainManagerStatus.WORKING;
104 this.submitIsEnabled = false;
105 this.initCommit = true;
112 * Method change status for TxChainManger to SLEEPING and it unregisters
113 * this class instance as {@link TransactionChainListener} so it broke a possibility to write something to DS.
114 * Call this method for SLAVE only.
117 public FluentFuture<?> deactivateTransactionManager() {
118 if (LOG.isDebugEnabled()) {
119 LOG.debug("deactivateTransactionManager for node {}", this.nodeId);
121 final FluentFuture<? extends CommitInfo> future;
122 synchronized (txLock) {
123 if (TransactionChainManagerStatus.WORKING == transactionChainManagerStatus) {
124 transactionChainManagerStatus = TransactionChainManagerStatus.SLEEPING;
125 future = txChainShuttingDown();
126 Preconditions.checkState(writeTx == null,
127 "We have some unexpected WriteTransaction.");
128 future.addCallback(new FutureCallback<CommitInfo>() {
130 public void onSuccess(final CommitInfo result) {
131 closeTransactionChain();
135 public void onFailure(@Nonnull final Throwable throwable) {
136 closeTransactionChain();
138 }, MoreExecutors.directExecutor());
140 // ignoring redundant deactivate invocation
141 future = CommitInfo.emptyFluentFuture();
147 private void closeTransactionChain() {
148 if (writeTx != null) {
152 Optional.ofNullable(transactionChain).ifPresent(TransactionChain::close);
153 transactionChain = null;
157 public boolean submitTransaction() {
158 return submitTransaction(false);
162 public boolean submitTransaction(boolean doSync) {
163 synchronized (txLock) {
164 if (!submitIsEnabled) {
165 if (LOG.isTraceEnabled()) {
166 LOG.trace("transaction not committed - submit block issued");
170 if (Objects.isNull(writeTx)) {
171 if (LOG.isTraceEnabled()) {
172 LOG.trace("nothing to commit - submit returns true");
176 Preconditions.checkState(TransactionChainManagerStatus.WORKING == transactionChainManagerStatus,
177 "we have here Uncompleted Transaction for node {} and we are not MASTER",
179 final FluentFuture<? extends CommitInfo> submitFuture = writeTx.commit();
180 lastSubmittedFuture = submitFuture;
183 if (initCommit || doSync) {
185 submitFuture.get(5L, TimeUnit.SECONDS);
186 } catch (InterruptedException | ExecutionException | TimeoutException ex) {
187 LOG.error("Exception during INITIAL({}) || doSync({}) transaction submitting. ",
188 initCommit, doSync, ex);
195 submitFuture.addCallback(new FutureCallback<CommitInfo>() {
197 public void onSuccess(final CommitInfo result) {
202 public void onFailure(final Throwable throwable) {
203 if (throwable instanceof TransactionCommitFailedException) {
204 LOG.error("Transaction commit failed. ", throwable);
206 if (throwable instanceof CancellationException) {
207 LOG.warn("Submit task was canceled");
208 LOG.trace("Submit exception: ", throwable);
210 LOG.error("Exception during transaction submitting. ", throwable);
214 }, MoreExecutors.directExecutor());
219 public <T extends DataObject> void addDeleteOperationToTxChain(final LogicalDatastoreType store,
220 final InstanceIdentifier<T> path) {
221 synchronized (txLock) {
223 if (writeTx == null) {
224 LOG.debug("WriteTx is null for node {}. Delete {} was not realized.", this.nodeId, path);
225 throw new TransactionChainClosedException(CANNOT_WRITE_INTO_TRANSACTION);
228 writeTx.delete(store, path);
232 public <T extends DataObject> void writeToTransaction(final LogicalDatastoreType store,
233 final InstanceIdentifier<T> path,
235 final boolean createParents) {
236 synchronized (txLock) {
238 if (writeTx == null) {
239 LOG.debug("WriteTx is null for node {}. Write data for {} was not realized.", this.nodeId, path);
240 throw new TransactionChainClosedException(CANNOT_WRITE_INTO_TRANSACTION);
243 writeTx.put(store, path, data, createParents);
247 public <T extends DataObject> void mergeToTransaction(final LogicalDatastoreType store,
248 final InstanceIdentifier<T> path,
250 final boolean createParents) {
251 synchronized (txLock) {
253 if (writeTx == null) {
254 LOG.debug("WriteTx is null for node {}. Merge data for {} was not realized.", this.nodeId, path);
255 throw new TransactionChainClosedException(CANNOT_WRITE_INTO_TRANSACTION);
258 writeTx.merge(store, path, data, createParents);
262 public <T extends DataObject> ListenableFuture<Optional<T>>
263 readFromTransaction(final LogicalDatastoreType store, final InstanceIdentifier<T> path) {
264 synchronized (txLock) {
266 if (writeTx == null) {
267 LOG.debug("WriteTx is null for node {}. Read data for {} was not realized.", this.nodeId, path);
268 throw new TransactionChainClosedException(CANNOT_WRITE_INTO_TRANSACTION);
271 return writeTx.read(store, path);
276 public void onTransactionChainFailed(final TransactionChain chain,
277 final Transaction transaction, final Throwable cause) {
278 synchronized (txLock) {
279 if (TransactionChainManagerStatus.WORKING == transactionChainManagerStatus
280 && chain.equals(this.transactionChain)) {
281 LOG.warn("Transaction chain failed, recreating chain due to ", cause);
282 closeTransactionChain();
290 public void onTransactionChainSuccessful(final TransactionChain chain) {
295 private void ensureTransaction() {
296 if (writeTx == null && TransactionChainManagerStatus.WORKING == transactionChainManagerStatus
297 && transactionChain != null) {
298 writeTx = transactionChain.newReadWriteTransaction();
302 private void enableSubmit() {
303 synchronized (txLock) {
304 /* !!!IMPORTANT: never set true without transactionChain */
305 submitIsEnabled = transactionChain != null;
309 public ListenableFuture<?> shuttingDown() {
310 if (LOG.isDebugEnabled()) {
311 LOG.debug("TxManager is going SHUTTING_DOWN for node {}", this.nodeId);
313 synchronized (txLock) {
314 this.transactionChainManagerStatus = TransactionChainManagerStatus.SHUTTING_DOWN;
315 return txChainShuttingDown();
320 private FluentFuture<? extends CommitInfo> txChainShuttingDown() {
321 boolean wasSubmitEnabled = submitIsEnabled;
322 submitIsEnabled = false;
323 FluentFuture<? extends CommitInfo> future;
325 if (!wasSubmitEnabled || transactionChain == null) {
326 // stay with actual thread
327 future = CommitInfo.emptyFluentFuture();
329 if (writeTx != null) {
333 } else if (writeTx == null) {
334 // hijack md-sal thread
335 future = lastSubmittedFuture;
337 if (LOG.isDebugEnabled()) {
338 LOG.debug("Submitting all transactions for Node {}", this.nodeId);
340 // hijack md-sal thread
341 future = writeTx.commit();
349 public void close() {
350 if (LOG.isDebugEnabled()) {
351 LOG.debug("Setting transactionChainManagerStatus to SHUTTING_DOWN for {}", this.nodeId);
353 synchronized (txLock) {
354 closeTransactionChain();
358 private enum TransactionChainManagerStatus {
360 * txChainManager is working - is active (MASTER).
364 * txChainManager is sleeping - is not active (SLAVE or default init value).
368 * txChainManager is trying to be closed - device disconnecting.