BUG 3045 : Use non-strict parsing in hello message.
[controller.git] / opendaylight / md-sal / sal-inmemory-datastore / src / main / java / org / opendaylight / controller / md / sal / dom / store / impl / DOMStoreTransactionChainImpl.java
1 /*
2  * Copyright (c) 2014 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 package org.opendaylight.controller.md.sal.dom.store.impl;
9
10 import com.google.common.base.Preconditions;
11 import java.util.AbstractMap.SimpleEntry;
12 import java.util.Map.Entry;
13 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
14 import org.opendaylight.controller.md.sal.dom.store.impl.SnapshotBackedWriteTransaction.TransactionReadyPrototype;
15 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadTransaction;
16 import org.opendaylight.controller.sal.core.spi.data.DOMStoreReadWriteTransaction;
17 import org.opendaylight.controller.sal.core.spi.data.DOMStoreThreePhaseCommitCohort;
18 import org.opendaylight.controller.sal.core.spi.data.DOMStoreTransactionChain;
19 import org.opendaylight.controller.sal.core.spi.data.DOMStoreWriteTransaction;
20 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeModification;
21 import org.opendaylight.yangtools.yang.data.api.schema.tree.DataTreeSnapshot;
22 import org.slf4j.Logger;
23 import org.slf4j.LoggerFactory;
24
25 final class DOMStoreTransactionChainImpl extends TransactionReadyPrototype implements DOMStoreTransactionChain {
26     private static abstract class State {
27         /**
28          * Allocate a new snapshot.
29          *
30          * @return A new snapshot
31          */
32         protected abstract DataTreeSnapshot getSnapshot();
33     }
34
35     private static final class Idle extends State {
36         private final InMemoryDOMDataStore store;
37
38         Idle(final InMemoryDOMDataStore store) {
39             this.store = Preconditions.checkNotNull(store);
40         }
41
42         @Override
43         protected DataTreeSnapshot getSnapshot() {
44             return store.takeSnapshot();
45         }
46     }
47
48     /**
49      * We have a transaction out there.
50      */
51     private static final class Allocated extends State {
52         private static final AtomicReferenceFieldUpdater<Allocated, DataTreeSnapshot> SNAPSHOT_UPDATER =
53                 AtomicReferenceFieldUpdater.newUpdater(Allocated.class, DataTreeSnapshot.class, "snapshot");
54         private final DOMStoreWriteTransaction transaction;
55         private volatile DataTreeSnapshot snapshot;
56
57         Allocated(final DOMStoreWriteTransaction transaction) {
58             this.transaction = Preconditions.checkNotNull(transaction);
59         }
60
61         public DOMStoreWriteTransaction getTransaction() {
62             return transaction;
63         }
64
65         @Override
66         protected DataTreeSnapshot getSnapshot() {
67             final DataTreeSnapshot ret = snapshot;
68             Preconditions.checkState(ret != null, "Previous transaction %s is not ready yet", transaction.getIdentifier());
69             return ret;
70         }
71
72         void setSnapshot(final DataTreeSnapshot snapshot) {
73             final boolean success = SNAPSHOT_UPDATER.compareAndSet(this, null, snapshot);
74             Preconditions.checkState(success, "Transaction %s has already been marked as ready", transaction.getIdentifier());
75         }
76     }
77
78     /**
79      * Chain is logically shut down, no further allocation allowed.
80      */
81     private static final class Shutdown extends State {
82         private final String message;
83
84         Shutdown(final String message) {
85             this.message = Preconditions.checkNotNull(message);
86         }
87
88         @Override
89         protected DataTreeSnapshot getSnapshot() {
90             throw new IllegalStateException(message);
91         }
92     }
93
94     private static final AtomicReferenceFieldUpdater<DOMStoreTransactionChainImpl, State> STATE_UPDATER =
95             AtomicReferenceFieldUpdater.newUpdater(DOMStoreTransactionChainImpl.class, State.class, "state");
96     private static final Logger LOG = LoggerFactory.getLogger(DOMStoreTransactionChainImpl.class);
97     private static final Shutdown CLOSED = new Shutdown("Transaction chain is closed");
98     private static final Shutdown FAILED = new Shutdown("Transaction chain has failed");
99     private final InMemoryDOMDataStore store;
100     private final Idle idleState;
101     private volatile State state;
102
103     DOMStoreTransactionChainImpl(final InMemoryDOMDataStore store) {
104         this.store = Preconditions.checkNotNull(store);
105         idleState = new Idle(store);
106         state = idleState;
107     }
108
109     private Entry<State, DataTreeSnapshot> getSnapshot() {
110         final State localState = state;
111         return new SimpleEntry<>(localState, localState.getSnapshot());
112     }
113
114     private boolean recordTransaction(final State expected, final DOMStoreWriteTransaction transaction) {
115         final State state = new Allocated(transaction);
116         return STATE_UPDATER.compareAndSet(this, expected, state);
117     }
118
119     @Override
120     public DOMStoreReadTransaction newReadOnlyTransaction() {
121         final Entry<State, DataTreeSnapshot> entry = getSnapshot();
122         return new SnapshotBackedReadTransaction(store.nextIdentifier(), store.getDebugTransactions(), entry.getValue());
123     }
124
125     @Override
126     public DOMStoreReadWriteTransaction newReadWriteTransaction() {
127         Entry<State, DataTreeSnapshot> entry;
128         DOMStoreReadWriteTransaction ret;
129
130         do {
131             entry = getSnapshot();
132             ret = new SnapshotBackedReadWriteTransaction(store.nextIdentifier(),
133                 store.getDebugTransactions(), entry.getValue(), this);
134         } while (!recordTransaction(entry.getKey(), ret));
135
136         return ret;
137     }
138
139     @Override
140     public DOMStoreWriteTransaction newWriteOnlyTransaction() {
141         Entry<State, DataTreeSnapshot> entry;
142         DOMStoreWriteTransaction ret;
143
144         do {
145             entry = getSnapshot();
146             ret = new SnapshotBackedWriteTransaction(store.nextIdentifier(),
147                 store.getDebugTransactions(), entry.getValue(), this);
148         } while (!recordTransaction(entry.getKey(), ret));
149
150         return ret;
151     }
152
153     @Override
154     protected void transactionAborted(final SnapshotBackedWriteTransaction tx) {
155         final State localState = state;
156         if (localState instanceof Allocated) {
157             final Allocated allocated = (Allocated)localState;
158             if (allocated.getTransaction().equals(tx)) {
159                 final boolean success = STATE_UPDATER.compareAndSet(this, localState, idleState);
160                 if (!success) {
161                     LOG.info("State already transitioned from {} to {}", localState, state);
162                 }
163             }
164         }
165     }
166
167     @Override
168     protected DOMStoreThreePhaseCommitCohort transactionReady(final SnapshotBackedWriteTransaction tx, final DataTreeModification tree) {
169         final State localState = state;
170
171         if (localState instanceof Allocated) {
172             final Allocated allocated = (Allocated)localState;
173             final DOMStoreWriteTransaction transaction = allocated.getTransaction();
174             Preconditions.checkState(tx.equals(transaction), "Mis-ordered ready transaction %s last allocated was %s", tx, transaction);
175             allocated.setSnapshot(tree);
176         } else {
177             LOG.debug("Ignoring transaction {} readiness due to state {}", tx, localState);
178         }
179
180         return new ChainedTransactionCommitImpl(tx, store.transactionReady(tx, tree), this);
181     }
182
183     @Override
184     public void close() {
185         final State localState = state;
186
187         do {
188             Preconditions.checkState(!CLOSED.equals(localState), "Transaction chain {} has been closed", this);
189
190             if (FAILED.equals(localState)) {
191                 LOG.debug("Ignoring user close in failed state");
192                 return;
193             }
194         } while (!STATE_UPDATER.compareAndSet(this, localState, CLOSED));
195     }
196
197     void onTransactionFailed(final SnapshotBackedWriteTransaction transaction, final Throwable t) {
198         LOG.debug("Transaction chain {} failed on transaction {}", this, transaction, t);
199         state = FAILED;
200     }
201
202     void onTransactionCommited(final SnapshotBackedWriteTransaction transaction) {
203         // If the committed transaction was the one we allocated last,
204         // we clear it and the ready snapshot, so the next transaction
205         // allocated refers to the data tree directly.
206         final State localState = state;
207
208         if (!(localState instanceof Allocated)) {
209             LOG.debug("Ignoring successful transaction {} in state {}", transaction, localState);
210             return;
211         }
212
213         final Allocated allocated = (Allocated)localState;
214         final DOMStoreWriteTransaction tx = allocated.getTransaction();
215         if (!tx.equals(transaction)) {
216             LOG.debug("Ignoring non-latest successful transaction {} in state {}", transaction, allocated);
217             return;
218         }
219
220         if (!STATE_UPDATER.compareAndSet(this, localState, idleState)) {
221             LOG.debug("Transaction chain {} has already transitioned from {} to {}, not making it idle", this, localState, state);
222         }
223     }
224 }