Bug 3564 - Li - ofp when transaction chain manager exists in WORKING state, drop...
[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 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 javax.annotation.Nonnull;
17 import org.opendaylight.controller.md.sal.binding.api.BindingTransactionChain;
18 import org.opendaylight.controller.md.sal.binding.api.DataBroker;
19 import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
20 import org.opendaylight.controller.md.sal.common.api.data.AsyncTransaction;
21 import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
22 import org.opendaylight.controller.md.sal.common.api.data.TransactionChain;
23 import org.opendaylight.controller.md.sal.common.api.data.TransactionChainListener;
24 import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
25 import org.opendaylight.openflowplugin.api.openflow.connection.ConnectionContext;
26 import org.opendaylight.openflowplugin.impl.util.DeviceStateUtil;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.Node;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.inventory.rev130819.nodes.NodeKey;
29 import org.opendaylight.yangtools.concepts.Registration;
30 import org.opendaylight.yangtools.yang.binding.DataObject;
31 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
32 import org.opendaylight.yangtools.yang.binding.KeyedInstanceIdentifier;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 /**
37  * openflowplugin-impl
38  * org.opendaylight.openflowplugin.impl.device
39  * <p/>
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#submit()})
44  *
45  * @author <a href="mailto:vdemcak@cisco.com">Vaclav Demcak</a>
46  *         <p/>
47  *         Created: Apr 2, 2015
48  */
49 class TransactionChainManager implements TransactionChainListener, AutoCloseable {
50
51     private static final Logger LOG = LoggerFactory.getLogger(TransactionChainManager.class);
52
53     private final Object txLock = new Object();
54
55     private final DataBroker dataBroker;
56     private WriteTransaction wTx;
57     private BindingTransactionChain txChainFactory;
58     private boolean submitIsEnabled;
59
60     public TransactionChainManagerStatus getTransactionChainManagerStatus() {
61         return transactionChainManagerStatus;
62     }
63
64     private TransactionChainManagerStatus transactionChainManagerStatus;
65     private ReadyForNewTransactionChainHandler readyForNewTransactionChainHandler;
66     private final KeyedInstanceIdentifier<Node, NodeKey> nodeII;
67     private final ConnectionContext connectionContext;
68     private Registration managerRegistration;
69
70     TransactionChainManager(@Nonnull final DataBroker dataBroker,
71                             @Nonnull final ConnectionContext connectionContext,
72                             @Nonnull final Registration managerRegistration) {
73         this.dataBroker = Preconditions.checkNotNull(dataBroker);
74         this.nodeII = Preconditions.checkNotNull(DeviceStateUtil.createNodeInstanceIdentifier(connectionContext.getNodeId()));
75         this.connectionContext = Preconditions.checkNotNull(connectionContext);
76         this.managerRegistration = Preconditions.checkNotNull(managerRegistration);
77         createTxChain(dataBroker);
78         LOG.debug("created txChainManager");
79     }
80
81     private void createTxChain(final DataBroker dataBroker) {
82         txChainFactory = dataBroker.createTransactionChain(TransactionChainManager.this);
83         this.transactionChainManagerStatus = TransactionChainManagerStatus.WORKING;
84     }
85
86     void initialSubmitWriteTransaction() {
87         enableSubmit();
88         submitWriteTransaction();
89     }
90
91     public boolean attemptToRegisterHandler(final ReadyForNewTransactionChainHandler readyForNewTransactionChainHandler) {
92         if (null == this.readyForNewTransactionChainHandler) {
93             synchronized (this) {
94                 Preconditions.checkState(null != this.managerRegistration);
95                 this.readyForNewTransactionChainHandler = readyForNewTransactionChainHandler;
96             }
97             return true;
98         } else {
99             return false;
100         }
101     }
102
103     boolean submitWriteTransaction() {
104         if (!submitIsEnabled) {
105             LOG.trace("transaction not committed - submit block issued");
106             return false;
107         }
108
109         if (wTx == null) {
110             LOG.trace("nothing to commit - submit returns true");
111             return true;
112         }
113         synchronized (txLock) {
114             if (wTx == null) {
115                 LOG.trace("nothing to commit - submit returns true");
116                 return true;
117             }
118             wTx.submit();
119             wTx = null;
120         }
121         return true;
122     }
123
124     <T extends DataObject> void addDeleteOperationTotTxChain(final LogicalDatastoreType store,
125                                                              final InstanceIdentifier<T> path) {
126         final WriteTransaction writeTx = getTransactionSafely();
127         writeTx.delete(store, path);
128     }
129
130     <T extends DataObject> void writeToTransaction(final LogicalDatastoreType store,
131                                                    final InstanceIdentifier<T> path, final T data) {
132         final WriteTransaction writeTx = getTransactionSafely();
133         writeTx.put(store, path, data);
134     }
135
136     @Override
137     public void onTransactionChainFailed(final TransactionChain<?, ?> chain,
138                                          final AsyncTransaction<?, ?> transaction, final Throwable cause) {
139         LOG.warn("txChain failed -> recreating", cause);
140         recreateTxChain();
141     }
142
143     @Override
144     public void onTransactionChainSuccessful(final TransactionChain<?, ?> chain) {
145         // NOOP - only yet, here is probably place for notification to get new WriteTransaction
146     }
147
148     private void recreateTxChain() {
149         txChainFactory.close();
150         createTxChain(dataBroker);
151         synchronized (txLock) {
152             wTx = null;
153         }
154     }
155
156     private WriteTransaction getTransactionSafely() {
157         if (wTx == null && !TransactionChainManagerStatus.SHUTTING_DOWN.equals(transactionChainManagerStatus)) {
158             synchronized (txLock) {
159                 if (wTx == null) {
160                     wTx = txChainFactory.newWriteOnlyTransaction();
161                 }
162             }
163         }
164         return wTx;
165     }
166
167     @VisibleForTesting
168     void enableSubmit() {
169         submitIsEnabled = true;
170     }
171
172     @Override
173     public void close() {
174         LOG.debug("Removing node {} from operational DS.", nodeII);
175         synchronized (txLock) {
176             final WriteTransaction writeTx = getTransactionSafely();
177             this.transactionChainManagerStatus = TransactionChainManagerStatus.SHUTTING_DOWN;
178             writeTx.delete(LogicalDatastoreType.OPERATIONAL, nodeII);
179             LOG.debug("Delete node {} from operational DS put to write transaction.", nodeII);
180             CheckedFuture<Void, TransactionCommitFailedException> submitsFuture = writeTx.submit();
181             LOG.debug("Delete node {} from operational DS write transaction submitted.", nodeII);
182             Futures.addCallback(submitsFuture, new FutureCallback<Void>() {
183                 @Override
184                 public void onSuccess(final Void aVoid) {
185                     LOG.debug("Removing node {} from operational DS successful .", nodeII);
186                     notifyReadyForNewTransactionChainAndCloseFactory();
187                 }
188
189                 @Override
190                 public void onFailure(final Throwable throwable) {
191                     LOG.info("Attempt to close transaction chain factory failed.", throwable);
192                     notifyReadyForNewTransactionChainAndCloseFactory();
193                 }
194             });
195             wTx = null;
196         }
197     }
198
199     private void notifyReadyForNewTransactionChainAndCloseFactory() {
200         synchronized (this) {
201             if (null != readyForNewTransactionChainHandler) {
202                 readyForNewTransactionChainHandler.onReadyForNewTransactionChain(connectionContext);
203             }
204             try {
205                 LOG.debug("Closing registration in manager.");
206                 managerRegistration.close();
207             } catch (Exception e) {
208                 LOG.warn("Failed to close transaction chain manager's registration.", e);
209             }
210             managerRegistration = null;
211         }
212         txChainFactory.close();
213         LOG.debug("Transaction chain factory closed.");
214     }
215
216     public enum TransactionChainManagerStatus {
217         WORKING, SHUTTING_DOWN;
218     }
219
220 }