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 javax.annotation.Nonnull;
21 import javax.annotation.Nullable;
22 import javax.annotation.concurrent.GuardedBy;
23 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
24 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
25 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
26 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
27 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
28 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
29 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainClosedException;
30 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
31 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
32 import org.opendaylight.openflowplugin.api.openflow.device.DeviceInfo;
33 import org.opendaylight.openflowplugin.api.openflow.lifecycle.LifecycleService;
34 import org.opendaylight.yangtools.yang.binding.DataObject;
35 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
41 * org.opendaylight.openflowplugin.impl.device
43 * Package protected class for controlling {@link WriteTransaction} life cycle. It is
44 * a {@link TransactionChainListener} and provide package protected methods for writeToTransaction
45 * method (wrapped {@link WriteTransaction#put(LogicalDatastoreType, InstanceIdentifier, DataObject)})
46 * and submitTransaction method (wrapped {@link WriteTransaction#submit()})
48 class TransactionChainManager implements TransactionChainListener, AutoCloseable {
50 private static final Logger LOG = LoggerFactory.getLogger(TransactionChainManager.class);
51 private static final String CANNOT_WRITE_INTO_TRANSACTION = "Cannot write into transaction.";
53 private final Object txLock = new Object();
54 private final DataBroker dataBroker;
55 private final String nodeId;
56 private LifecycleService lifecycleService;
59 private WriteTransaction wTx;
61 private BindingTransactionChain txChainFactory;
63 private boolean submitIsEnabled;
65 private ListenableFuture<Void> lastSubmittedFuture;
67 private boolean initCommit;
70 private TransactionChainManagerStatus transactionChainManagerStatus = TransactionChainManagerStatus.SLEEPING;
72 TransactionChainManager(@Nonnull final DataBroker dataBroker,
73 @Nonnull final DeviceInfo deviceInfo) {
74 this.dataBroker = dataBroker;
75 this.nodeId = deviceInfo.getNodeInstanceIdentifier().getKey().getId().getValue();
76 this.transactionChainManagerStatus = TransactionChainManagerStatus.SLEEPING;
77 this.lastSubmittedFuture = Futures.immediateFuture(null);
81 private void createTxChain() {
82 BindingTransactionChain txChainFactoryTemp = txChainFactory;
83 txChainFactory = dataBroker.createTransactionChain(TransactionChainManager.this);
84 Optional.ofNullable(txChainFactoryTemp).ifPresent(TransactionChain::close);
87 public void setLifecycleService(final LifecycleService lifecycleService) {
88 this.lifecycleService = lifecycleService;
91 void initialSubmitWriteTransaction() {
93 submitWriteTransaction();
97 * Method change status for TxChainManager to {@link TransactionChainManagerStatus#WORKING} and it has to make
98 * registration for this class instance as {@link TransactionChainListener} to provide possibility a make DS
99 * transactions. Call this method for MASTER role only.
101 void activateTransactionManager() {
102 if (LOG.isDebugEnabled()) {
103 LOG.debug("activateTransactionManager for node {} transaction submit is set to {}", this.nodeId, submitIsEnabled);
105 synchronized (txLock) {
106 if (TransactionChainManagerStatus.SLEEPING.equals(transactionChainManagerStatus)) {
107 Preconditions.checkState(txChainFactory == null, "TxChainFactory survive last close.");
108 Preconditions.checkState(wTx == null, "We have some unexpected WriteTransaction.");
109 this.transactionChainManagerStatus = TransactionChainManagerStatus.WORKING;
110 this.submitIsEnabled = false;
111 this.initCommit = true;
118 * Method change status for TxChainManger to {@link TransactionChainManagerStatus#SLEEPING} and it unregisters
119 * this class instance as {@link TransactionChainListener} so it broke a possibility to write something to DS.
120 * Call this method for SLAVE only.
123 ListenableFuture<Void> deactivateTransactionManager() {
124 if (LOG.isDebugEnabled()) {
125 LOG.debug("deactivateTransactionManager for node {}", this.nodeId);
127 final ListenableFuture<Void> future;
128 synchronized (txLock) {
129 if (TransactionChainManagerStatus.WORKING.equals(transactionChainManagerStatus)) {
130 transactionChainManagerStatus = TransactionChainManagerStatus.SLEEPING;
131 future = txChainShuttingDown();
132 Preconditions.checkState(wTx == null, "We have some unexpected WriteTransaction.");
133 Futures.addCallback(future, new FutureCallback<Void>() {
135 public void onSuccess(final Void result) {
136 removeTxChainFactory();
140 public void onFailure(final Throwable t) {
141 removeTxChainFactory();
145 // TODO : ignoring redundant deactivate invocation
146 future = Futures.immediateCheckedFuture(null);
152 private void removeTxChainFactory() {
153 Optional.ofNullable(txChainFactory).ifPresent(TransactionChain::close);
154 txChainFactory = null;
157 boolean submitWriteTransaction() {
158 synchronized (txLock) {
159 if (!submitIsEnabled) {
160 if (LOG.isTraceEnabled()) {
161 LOG.trace("transaction not committed - submit block issued");
165 if (Objects.isNull(wTx)) {
166 if (LOG.isTraceEnabled()) {
167 LOG.trace("nothing to commit - submit returns true");
171 Preconditions.checkState(TransactionChainManagerStatus.WORKING.equals(transactionChainManagerStatus),
172 "we have here Uncompleted Transaction for node {} and we are not MASTER", this.nodeId);
173 final CheckedFuture<Void, TransactionCommitFailedException> submitFuture = wTx.submit();
174 Futures.addCallback(submitFuture, new FutureCallback<Void>() {
176 public void onSuccess(final Void result) {
183 public void onFailure(final Throwable t) {
184 if (t instanceof TransactionCommitFailedException) {
185 LOG.error("Transaction commit failed. ", t);
187 if (t instanceof CancellationException) {
188 LOG.warn("Submit task was canceled");
189 LOG.trace("Submit exception: ", t);
191 LOG.error("Exception during transaction submitting. ", t);
196 Optional.ofNullable(lifecycleService).ifPresent(LifecycleService::closeConnection);
200 lastSubmittedFuture = submitFuture;
206 <T extends DataObject> void addDeleteOperationTotTxChain(final LogicalDatastoreType store,
207 final InstanceIdentifier<T> path){
208 final WriteTransaction writeTx = getTransactionSafely();
209 if (Objects.nonNull(writeTx)) {
210 writeTx.delete(store, path);
212 if (LOG.isDebugEnabled()) {
213 LOG.debug("WriteTx is null for node {}. Delete {} was not realized.", this.nodeId, path);
215 throw new TransactionChainClosedException(CANNOT_WRITE_INTO_TRANSACTION);
219 <T extends DataObject> void writeToTransaction(final LogicalDatastoreType store,
220 final InstanceIdentifier<T> path,
222 final boolean createParents){
223 final WriteTransaction writeTx = getTransactionSafely();
224 if (Objects.nonNull(writeTx)) {
225 writeTx.put(store, path, data, createParents);
227 if (LOG.isDebugEnabled()) {
228 LOG.debug("WriteTx is null for node {}. Write data for {} was not realized.", this.nodeId, path);
230 throw new TransactionChainClosedException(CANNOT_WRITE_INTO_TRANSACTION);
235 public void onTransactionChainFailed(final TransactionChain<?, ?> chain,
236 final AsyncTransaction<?, ?> transaction, final Throwable cause) {
237 if (transactionChainManagerStatus.equals(TransactionChainManagerStatus.WORKING)) {
238 LOG.warn("Transaction chain failed, recreating chain due to ", cause);
244 public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
248 private void recreateTxChain() {
249 synchronized (txLock) {
256 private WriteTransaction getTransactionSafely() {
257 synchronized (txLock) {
258 if (wTx == null && TransactionChainManagerStatus.WORKING.equals(transactionChainManagerStatus)) {
259 Optional.ofNullable(txChainFactory).ifPresent(bindingTransactionChain -> wTx = txChainFactory.newWriteOnlyTransaction());
266 void enableSubmit() {
267 synchronized (txLock) {
268 /* !!!IMPORTANT: never set true without txChainFactory */
269 submitIsEnabled = txChainFactory != null;
273 ListenableFuture<Void> shuttingDown() {
274 if (LOG.isDebugEnabled()) {
275 LOG.debug("TxManager is going SHUTTING_DOWN for node {}", this.nodeId);
277 ListenableFuture<Void> future;
278 synchronized (txLock) {
279 this.transactionChainManagerStatus = TransactionChainManagerStatus.SHUTTING_DOWN;
280 future = txChainShuttingDown();
286 private ListenableFuture<Void> txChainShuttingDown() {
287 submitIsEnabled = false;
288 ListenableFuture<Void> future;
289 if (txChainFactory == null) {
290 // stay with actual thread
291 future = Futures.immediateCheckedFuture(null);
292 } else if (wTx == null) {
293 // hijack md-sal thread
294 future = lastSubmittedFuture;
296 if (LOG.isDebugEnabled()) {
297 LOG.debug("Submitting all transactions for Node {}", this.nodeId);
299 // hijack md-sal thread
300 future = wTx.submit();
307 public void close() {
308 if (LOG.isDebugEnabled()) {
309 LOG.debug("Setting transactionChainManagerStatus to SHUTTING_DOWN for {}", this.nodeId);
311 synchronized (txLock) {
312 removeTxChainFactory();
316 private enum TransactionChainManagerStatus {
317 /** txChainManager is sleeping - is not active (SLAVE or default init value) */
319 /** txChainManager is working - is active (MASTER) */
321 /** txChainManager is trying to be closed - device disconnecting */