Bug 4957 Fix methods for change TxChainManager
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / device / TransactionChainManager.java
1 /**
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8
9 package org.opendaylight.openflowplugin.impl.device;
10
11 import javax.annotation.Nonnull;
12 import javax.annotation.Nullable;
13 import javax.annotation.concurrent.GuardedBy;
14
15 import com.google.common.annotations.VisibleForTesting;
16 import com.google.common.base.Preconditions;
17 import com.google.common.util.concurrent.CheckedFuture;
18 import com.google.common.util.concurrent.FutureCallback;
19 import com.google.common.util.concurrent.Futures;
20 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
21 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
22 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
23 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
24 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
25 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
26 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
27 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
28 import org.opendaylight.openflowplugin.api.openflow.device.DeviceState;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
31 import org.opendaylight.yangtools.yang.binding.DataObject;
32 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
33 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
34 import org.slf4j.Logger;
35 import org.slf4j.LoggerFactory;
36
37 /**
38  * openflowplugin-impl
39  * org.opendaylight.openflowplugin.impl.device
40  * <p/>
41  * Package protected class for controlling {@link WriteTransaction} life cycle. It is
42  * a {@link TransactionChainListener} and provide package protected methods for writeToTransaction
43  * method (wrapped {@link WriteTransaction#put(LogicalDatastoreType, InstanceIdentifier, DataObject)})
44  * and submitTransaction method (wrapped {@link WriteTransaction#submit()})
45  *
46  * @author <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
47  *         </p>
48  *         Created: Apr 2, 2015
49  */
50 class TransactionChainManager implements TransactionChainListener, AutoCloseable {
51
52     private static final Logger LOG = LoggerFactory.getLogger(TransactionChainManager.class);
53
54     private final Object txLock = new Object();
55
56     private final DataBroker dataBroker;
57     private final DeviceState deviceState;
58     @GuardedBy("txLock")
59     private WriteTransaction wTx;
60     @GuardedBy("txLock")
61     private BindingTransactionChain txChainFactory;
62     private boolean submitIsEnabled;
63
64     public TransactionChainManagerStatus getTransactionChainManagerStatus() {
65         return transactionChainManagerStatus;
66     }
67
68     @GuardedBy("txLock")
69     private TransactionChainManagerStatus transactionChainManagerStatus;
70     private final KeyedInstanceIdentifier<Node, NodeKey> nodeII;
71
72     TransactionChainManager(@Nonnull final DataBroker dataBroker,
73                             @Nonnull final DeviceState deviceState) {
74         this.dataBroker = Preconditions.checkNotNull(dataBroker);
75         this.deviceState = Preconditions.checkNotNull(deviceState);
76         this.nodeII = Preconditions.checkNotNull(deviceState.getNodeInstanceIdentifier());
77         this.transactionChainManagerStatus = TransactionChainManagerStatus.SLEEPING;
78         LOG.debug("created txChainManager");
79     }
80
81     @GuardedBy("txLock")
82     private void createTxChain() {
83         if (txChainFactory != null) {
84             txChainFactory.close();
85         }
86         txChainFactory = dataBroker.createTransactionChain(TransactionChainManager.this);
87     }
88
89     void initialSubmitWriteTransaction() {
90         enableSubmit();
91         submitWriteTransaction();
92     }
93
94     /**
95      * Method change status for TxChainManager to {@link TransactionChainManagerStatus#WORKING} and it has to make
96      * registration for this class instance as {@link TransactionChainListener} to provide possibility a make DS
97      * transactions. Call this method for MASTER role only.
98      */
99     public void activateTransactionManager() {
100         LOG.trace("activetTransactionManaager for node {} transaction submit is set to {}", deviceState.getNodeId());
101         synchronized (txLock) {
102             if (TransactionChainManagerStatus.SLEEPING.equals(transactionChainManagerStatus)) {
103                 LOG.debug("Transaction Factory create {}", deviceState.getNodeId());
104                 Preconditions.checkState(txChainFactory == null, "TxChainFactory survive last close.");
105                 Preconditions.checkState(wTx == null, "We have some unexpected WriteTransaction.");
106                 this.transactionChainManagerStatus = TransactionChainManagerStatus.WORKING;
107                 createTxChain();
108             } else {
109                 LOG.debug("Transaction is active {}", deviceState.getNodeId());
110             }
111         }
112     }
113
114     /**
115      * Method change status for TxChainManger to {@link TransactionChainManagerStatus#SLEEPING} and it unregisters
116      * this class instance as {@link TransactionChainListener} so it broke a possibility to write something to DS.
117      * Call this method for SLAVE only.
118      */
119     public void deactivateTransactionManager() {
120         synchronized (txLock) {
121             if (TransactionChainManagerStatus.WORKING.equals(transactionChainManagerStatus)) {
122                 LOG.debug("Submitting all transactions if we were in status WORKING for Node", deviceState.getNodeId());
123                 submitWriteTransaction();
124                 Preconditions.checkState(wTx == null, "We have some unexpected WriteTransaction.");
125                 LOG.debug("Transaction Factory delete for Node {}", deviceState.getNodeId());
126                 transactionChainManagerStatus = TransactionChainManagerStatus.SLEEPING;
127                 txChainFactory.close();
128                 txChainFactory = null;
129             }
130         }
131     }
132
133     boolean submitWriteTransaction() {
134         if (!submitIsEnabled) {
135             LOG.trace("transaction not committed - submit block issued");
136             return false;
137         }
138         synchronized (txLock) {
139             if (wTx == null) {
140                 LOG.trace("nothing to commit - submit returns true");
141                 return true;
142             }
143             Preconditions.checkState(TransactionChainManagerStatus.WORKING.equals(transactionChainManagerStatus),
144                     "we have here Uncompleted Transaction for node {} and we are not MASTER", nodeII);      
145             final CheckedFuture<Void, TransactionCommitFailedException> submitFuture = wTx.submit();
146             Futures.addCallback(submitFuture, new FutureCallback<Void>() {
147                 @Override
148                 public void onSuccess(Void result) {
149                     //no action required
150                 }
151
152                 @Override
153                 public void onFailure(Throwable t) {
154                     if (t instanceof TransactionCommitFailedException) {
155                         LOG.error("Transaction commit failed. {}", t);
156                     } else {
157                         LOG.error("Exception during transaction submitting. {}", t);
158                     }
159                 }
160             });
161             wTx = null;
162         }
163         return true;
164     }
165
166     <T extends DataObject> void addDeleteOperationTotTxChain(final LogicalDatastoreType store,
167                                                              final InstanceIdentifier<T> path) {
168         final WriteTransaction writeTx = getTransactionSafely();
169         if (writeTx != null) {
170             writeTx.delete(store, path);
171         } else {
172             LOG.debug("WriteTx is null for node {}. Delete {} was not realized.", nodeII, path);
173         }
174     }
175
176     <T extends DataObject> void writeToTransaction(final LogicalDatastoreType store,
177                                                    final InstanceIdentifier<T> path, final T data) {
178         final WriteTransaction writeTx = getTransactionSafely();
179         if (writeTx != null) {
180             writeTx.put(store, path, data);
181         } else {
182             LOG.debug("WriteTx is null for node {}. Write data for {} was not realized.", nodeII, path);
183         }
184     }
185
186     @Override
187     public void onTransactionChainFailed(final TransactionChain<?, ?> chain,
188                                          final AsyncTransaction<?, ?> transaction, final Throwable cause) {
189         LOG.warn("txChain failed -> recreating", cause);
190         if (transactionChainManagerStatus.equals(TransactionChainManagerStatus.WORKING)) {
191             recreateTxChain();
192         }
193     }
194
195     @Override
196     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
197         // NOOP
198     }
199
200     private void recreateTxChain() {
201         synchronized (txLock) {
202             createTxChain();
203             wTx = null;
204         }
205     }
206
207     @Nullable
208     private WriteTransaction getTransactionSafely() {
209         if (wTx == null && TransactionChainManagerStatus.WORKING.equals(transactionChainManagerStatus)) {
210             synchronized (txLock) {
211                 if (wTx == null && TransactionChainManagerStatus.WORKING.equals(transactionChainManagerStatus)) {
212                     if (wTx == null && txChainFactory != null) {
213                         wTx = txChainFactory.newWriteOnlyTransaction();
214                     }
215                 }
216             }
217         }
218         return wTx;
219     }
220
221     @VisibleForTesting
222     void enableSubmit() {
223         submitIsEnabled = true;
224     }
225
226     @Override
227     public void close() {
228         LOG.info("Setting transactionChainManagerStatus to WAITING_TO_BE_SHUT, will wait for ownershipservice to notify", nodeII);
229         // we can finish in initial phase
230         initialSubmitWriteTransaction();
231         synchronized (txLock) {
232             if (txChainFactory != null) {
233                 txChainFactory.close();
234                 txChainFactory = null;
235             }
236             this.transactionChainManagerStatus = TransactionChainManagerStatus.WAITING_TO_BE_SHUT;
237         }
238         Preconditions.checkState(wTx == null);
239         Preconditions.checkState(txChainFactory == null);
240     }
241
242     public enum TransactionChainManagerStatus {
243         WORKING, SLEEPING, WAITING_TO_BE_SHUT, SHUTTING_DOWN;
244     }
245
246
247
248 }