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.openflowplugin.impl.device;
11 import com.google.common.annotations.VisibleForTesting;
12 import com.google.common.base.Preconditions;
13 import com.google.common.util.concurrent.CheckedFuture;
14 import com.google.common.util.concurrent.FutureCallback;
15 import com.google.common.util.concurrent.Futures;
16 import com.google.common.util.concurrent.ListenableFuture;
17 import java.util.Objects;
18 import java.util.Optional;
19 import java.util.concurrent.CancellationException;
20 import java.util.concurrent.ExecutionException;
21 import java.util.concurrent.TimeUnit;
22 import java.util.concurrent.TimeoutException;
23 import javax.annotation.Nonnull;
24 import javax.annotation.Nullable;
25 import javax.annotation.concurrent.GuardedBy;
26 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
27 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
28 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
29 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
30 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
31 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
32 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainClosedException;
33 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
34 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
35 import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
36 import org.opendaylight.openflowplugin.api.openflow.lifecycle.LifecycleService;
37 import org.opendaylight.yangtools.yang.binding.DataObject;
38 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
44 * org.opendaylight.openflowplugin.impl.device
46 * Package protected class for controlling {@link WriteTransaction} life cycle. It is
47 * a {@link TransactionChainListener} and provide package protected methods for writeToTransaction
48 * method (wrapped {@link WriteTransaction#put(LogicalDatastoreType, InstanceIdentifier, DataObject)})
49 * and submitTransaction method (wrapped {@link WriteTransaction#submit()})
51 class TransactionChainManager implements TransactionChainListener, AutoCloseable {
53 private static final Logger LOG = LoggerFactory.getLogger(TransactionChainManager.class);
54 private static final String CANNOT_WRITE_INTO_TRANSACTION = "Cannot write into transaction.";
56 private final Object txLock = new Object();
57 private final DataBroker dataBroker;
58 private final String nodeId;
59 private LifecycleService lifecycleService;
62 private WriteTransaction wTx;
64 private BindingTransactionChain txChainFactory;
66 private boolean submitIsEnabled;
68 private ListenableFuture<Void> lastSubmittedFuture;
70 private volatile boolean initCommit;
73 private TransactionChainManagerStatus transactionChainManagerStatus = TransactionChainManagerStatus.SLEEPING;
75 TransactionChainManager(@Nonnull final DataBroker dataBroker,
76 @Nonnull final DeviceInfo deviceInfo) {
77 this.dataBroker = dataBroker;
78 this.nodeId = deviceInfo.getNodeInstanceIdentifier().getKey().getId().getValue();
79 this.lastSubmittedFuture = Futures.immediateFuture(null);
83 private void createTxChain() {
84 BindingTransactionChain txChainFactoryTemp = txChainFactory;
85 txChainFactory = dataBroker.createTransactionChain(TransactionChainManager.this);
86 Optional.ofNullable(txChainFactoryTemp).ifPresent(TransactionChain::close);
89 public void setLifecycleService(final LifecycleService lifecycleService) {
90 this.lifecycleService = lifecycleService;
93 boolean initialSubmitWriteTransaction() {
95 return submitWriteTransaction();
99 * Method change status for TxChainManager to {@link TransactionChainManagerStatus#WORKING} and it has to make
100 * registration for this class instance as {@link TransactionChainListener} to provide possibility a make DS
101 * transactions. Call this method for MASTER role only.
103 void activateTransactionManager() {
104 if (LOG.isDebugEnabled()) {
105 LOG.debug("activateTransactionManager for node {} transaction submit is set to {}", this.nodeId, submitIsEnabled);
107 synchronized (txLock) {
108 if (TransactionChainManagerStatus.SLEEPING == transactionChainManagerStatus) {
109 Preconditions.checkState(txChainFactory == null, "TxChainFactory survive last close.");
110 Preconditions.checkState(wTx == null, "We have some unexpected WriteTransaction.");
111 this.transactionChainManagerStatus = TransactionChainManagerStatus.WORKING;
112 this.submitIsEnabled = false;
113 this.initCommit = true;
120 * Method change status for TxChainManger to {@link TransactionChainManagerStatus#SLEEPING} and it unregisters
121 * this class instance as {@link TransactionChainListener} so it broke a possibility to write something to DS.
122 * Call this method for SLAVE only.
125 ListenableFuture<Void> deactivateTransactionManager() {
126 if (LOG.isDebugEnabled()) {
127 LOG.debug("deactivateTransactionManager for node {}", this.nodeId);
129 final ListenableFuture<Void> future;
130 synchronized (txLock) {
131 if (TransactionChainManagerStatus.WORKING == transactionChainManagerStatus) {
132 transactionChainManagerStatus = TransactionChainManagerStatus.SLEEPING;
133 future = txChainShuttingDown();
134 Preconditions.checkState(wTx == null, "We have some unexpected WriteTransaction.");
135 Futures.addCallback(future, new FutureCallback<Void>() {
137 public void onSuccess(final Void result) {
138 removeTxChainFactory();
142 public void onFailure(final Throwable t) {
143 removeTxChainFactory();
147 // TODO : ignoring redundant deactivate invocation
148 future = Futures.immediateCheckedFuture(null);
154 private void removeTxChainFactory() {
155 Optional.ofNullable(txChainFactory).ifPresent(TransactionChain::close);
156 txChainFactory = null;
159 boolean submitWriteTransaction() {
160 synchronized (txLock) {
161 if (!submitIsEnabled) {
162 if (LOG.isTraceEnabled()) {
163 LOG.trace("transaction not committed - submit block issued");
167 if (Objects.isNull(wTx)) {
168 if (LOG.isTraceEnabled()) {
169 LOG.trace("nothing to commit - submit returns true");
173 Preconditions.checkState(TransactionChainManagerStatus.WORKING == transactionChainManagerStatus,
174 "we have here Uncompleted Transaction for node {} and we are not MASTER", this.nodeId);
175 final CheckedFuture<Void, TransactionCommitFailedException> submitFuture = wTx.submit();
176 lastSubmittedFuture = submitFuture;
181 submitFuture.get(5L, TimeUnit.SECONDS);
182 } catch (InterruptedException | ExecutionException | TimeoutException ex) {
183 LOG.error("Exception during INITIAL transaction submitting. ", ex);
190 Futures.addCallback(submitFuture, new FutureCallback<Void>() {
192 public void onSuccess(final Void result) {
197 public void onFailure(final Throwable t) {
198 if (t instanceof TransactionCommitFailedException) {
199 LOG.error("Transaction commit failed. ", t);
201 if (t instanceof CancellationException) {
202 LOG.warn("Submit task was canceled");
203 LOG.trace("Submit exception: ", t);
205 LOG.error("Exception during transaction submitting. ", t);
214 <T extends DataObject> void addDeleteOperationTotTxChain(final LogicalDatastoreType store,
215 final InstanceIdentifier<T> path){
216 synchronized (txLock) {
219 LOG.debug("WriteTx is null for node {}. Delete {} was not realized.", this.nodeId, path);
220 throw new TransactionChainClosedException(CANNOT_WRITE_INTO_TRANSACTION);
223 wTx.delete(store, path);
227 <T extends DataObject> void writeToTransaction(final LogicalDatastoreType store,
228 final InstanceIdentifier<T> path,
230 final boolean createParents){
231 synchronized (txLock) {
234 LOG.debug("WriteTx is null for node {}. Write data for {} was not realized.", this.nodeId, path);
235 throw new TransactionChainClosedException(CANNOT_WRITE_INTO_TRANSACTION);
238 wTx.put(store, path, data, createParents);
243 public void onTransactionChainFailed(final TransactionChain<?, ?> chain,
244 final AsyncTransaction<?, ?> transaction, final Throwable cause) {
245 synchronized (txLock) {
246 if (TransactionChainManagerStatus.WORKING == transactionChainManagerStatus) {
247 LOG.warn("Transaction chain failed, recreating chain due to ", cause);
255 public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
261 private void ensureTransaction() {
262 if (wTx == null && TransactionChainManagerStatus.WORKING == transactionChainManagerStatus
263 && txChainFactory != null) {
264 wTx = txChainFactory.newWriteOnlyTransaction();
269 void enableSubmit() {
270 synchronized (txLock) {
271 /* !!!IMPORTANT: never set true without txChainFactory */
272 submitIsEnabled = txChainFactory != null;
276 ListenableFuture<Void> shuttingDown() {
277 if (LOG.isDebugEnabled()) {
278 LOG.debug("TxManager is going SHUTTING_DOWN for node {}", this.nodeId);
280 synchronized (txLock) {
281 this.transactionChainManagerStatus = TransactionChainManagerStatus.SHUTTING_DOWN;
282 return txChainShuttingDown();
287 private ListenableFuture<Void> txChainShuttingDown() {
288 submitIsEnabled = false;
289 ListenableFuture<Void> future;
290 if (txChainFactory == null) {
291 // stay with actual thread
292 future = Futures.immediateCheckedFuture(null);
293 } else if (wTx == null) {
294 // hijack md-sal thread
295 future = lastSubmittedFuture;
297 if (LOG.isDebugEnabled()) {
298 LOG.debug("Submitting all transactions for Node {}", this.nodeId);
300 // hijack md-sal thread
301 future = wTx.submit();
308 public void close() {
309 if (LOG.isDebugEnabled()) {
310 LOG.debug("Setting transactionChainManagerStatus to SHUTTING_DOWN for {}", this.nodeId);
312 synchronized (txLock) {
313 removeTxChainFactory();
317 private enum TransactionChainManagerStatus {
318 /** txChainManager is sleeping - is not active (SLAVE or default init value) */
320 /** txChainManager is working - is active (MASTER) */
322 /** txChainManager is trying to be closed - device disconnecting */