ca1ab8c8c513cb3bd9ea53c8aa887ae41cc8e63f
[mdsal.git] / dom / mdsal-dom-broker / src / main / java / org / opendaylight / mdsal / dom / broker / ShardedDOMTransactionChainAdapter.java
1 /*
2  * Copyright (c) 2016 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.mdsal.dom.broker;
9
10 import static com.google.common.base.Preconditions.checkState;
11 import static com.google.common.base.Verify.verifyNotNull;
12 import static java.util.Objects.requireNonNull;
13
14 import com.google.common.collect.ClassToInstanceMap;
15 import com.google.common.util.concurrent.FluentFuture;
16 import com.google.common.util.concurrent.FutureCallback;
17 import com.google.common.util.concurrent.MoreExecutors;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.EnumMap;
21 import java.util.Map;
22 import java.util.concurrent.atomic.AtomicLong;
23 import org.opendaylight.mdsal.common.api.CommitInfo;
24 import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
25 import org.opendaylight.mdsal.dom.api.DOMDataTreeCursorAwareTransaction;
26 import org.opendaylight.mdsal.dom.api.DOMDataTreeIdentifier;
27 import org.opendaylight.mdsal.dom.api.DOMDataTreeListener;
28 import org.opendaylight.mdsal.dom.api.DOMDataTreeLoopException;
29 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducer;
30 import org.opendaylight.mdsal.dom.api.DOMDataTreeProducerException;
31 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadTransaction;
32 import org.opendaylight.mdsal.dom.api.DOMDataTreeReadWriteTransaction;
33 import org.opendaylight.mdsal.dom.api.DOMDataTreeService;
34 import org.opendaylight.mdsal.dom.api.DOMDataTreeServiceExtension;
35 import org.opendaylight.mdsal.dom.api.DOMDataTreeTransaction;
36 import org.opendaylight.mdsal.dom.api.DOMDataTreeWriteTransaction;
37 import org.opendaylight.mdsal.dom.api.DOMTransactionChain;
38 import org.opendaylight.mdsal.dom.api.DOMTransactionChainListener;
39 import org.opendaylight.yangtools.concepts.ListenerRegistration;
40
41 public class ShardedDOMTransactionChainAdapter implements DOMTransactionChain {
42
43     private final DOMDataTreeService dataTreeService;
44     private final Object txChainIdentifier;
45     private final AtomicLong txNum = new AtomicLong();
46     private final DOMTransactionChainListener txChainListener;
47     private final CachedDataTreeService cachedDataTreeService;
48     private TransactionChainWriteTransaction writeTx;
49     private TransactionChainReadTransaction readTx;
50     private FluentFuture<? extends CommitInfo> writeTxCommitFuture;
51     private boolean finished = false;
52
53     public ShardedDOMTransactionChainAdapter(final Object txChainIdentifier,
54             final DOMDataTreeService dataTreeService, final DOMTransactionChainListener txChainListener) {
55         this.dataTreeService = requireNonNull(dataTreeService);
56         this.txChainIdentifier = requireNonNull(txChainIdentifier);
57         this.txChainListener = txChainListener;
58         this.cachedDataTreeService = new CachedDataTreeService(dataTreeService);
59     }
60
61     @Override
62     public DOMDataTreeReadTransaction newReadOnlyTransaction() {
63         checkRunning();
64         checkReadTxClosed();
65         checkWriteTxClosed();
66         readTx = new TransactionChainReadTransaction(newTransactionIdentifier(),
67                 new ShardedDOMReadTransactionAdapter(newTransactionIdentifier(), dataTreeService),
68                 writeTxCommitFuture, this);
69
70         return readTx;
71     }
72
73     @Override
74     public DOMDataTreeWriteTransaction newWriteOnlyTransaction() {
75         checkRunning();
76         checkWriteTxClosed();
77         checkReadTxClosed();
78         writeTx = new TransactionChainWriteTransaction(newTransactionIdentifier(),
79                 new ShardedDOMWriteTransactionAdapter(newTransactionIdentifier(),
80                         cachedDataTreeService), this);
81
82         return writeTx;
83     }
84
85     @Override
86     public DOMDataTreeReadWriteTransaction newReadWriteTransaction() {
87         checkRunning();
88         checkWriteTxClosed();
89         checkReadTxClosed();
90         ShardedDOMReadWriteTransactionAdapter adapter = new ShardedDOMReadWriteTransactionAdapter(
91                 newTransactionIdentifier(), cachedDataTreeService);
92         TransactionChainReadWriteTransaction readWriteTx = new TransactionChainReadWriteTransaction(
93                 newTransactionIdentifier(), adapter, adapter.getReadAdapter(), writeTxCommitFuture, this);
94
95         writeTx = readWriteTx;
96         return readWriteTx;
97     }
98
99     @Override
100     public void close() {
101         if (finished) {
102             // already closed, do nothing
103             return;
104         }
105
106         checkReadTxClosed();
107         checkWriteTxClosed();
108         writeTxCommitFuture.addCallback(new FutureCallback<CommitInfo>() {
109             @Override
110             public void onSuccess(final CommitInfo result) {
111                 txChainListener.onTransactionChainSuccessful(ShardedDOMTransactionChainAdapter.this);
112             }
113
114             @Override
115             public void onFailure(final Throwable throwable) {
116                 // We don't have to do nothing here,
117                 // tx should take car of it
118             }
119         }, MoreExecutors.directExecutor());
120
121         cachedDataTreeService.closeProducers();
122         finished = true;
123     }
124
125     public void closeReadTransaction() {
126         readTx = null;
127     }
128
129     public void closeWriteTransaction(final FluentFuture<? extends CommitInfo> commitFuture) {
130         writeTxCommitFuture = commitFuture;
131         writeTx = null;
132     }
133
134     private Object newTransactionIdentifier() {
135         return "DOM-CHAIN-" + txChainIdentifier + "-" + txNum.getAndIncrement();
136     }
137
138     private void checkWriteTxClosed() {
139         checkState(writeTx == null);
140     }
141
142     private void checkReadTxClosed() {
143         checkState(readTx == null);
144     }
145
146     private void checkRunning() {
147         checkState(!finished);
148     }
149
150     public void transactionFailed(final DOMDataTreeTransaction tx, final Throwable cause) {
151         txChainListener.onTransactionChainFailed(this, tx, cause);
152         if (writeTx != null) {
153             writeTx.cancel();
154         }
155         if (readTx != null) {
156             readTx.close();
157         }
158         cachedDataTreeService.closeProducers();
159         finished = true;
160     }
161
162     static class CachedDataTreeService implements DOMDataTreeService {
163
164         private final DOMDataTreeService delegateTreeService;
165         private final Map<LogicalDatastoreType, NoopCloseDataProducer> producersMap =
166                 new EnumMap<>(LogicalDatastoreType.class);
167
168         CachedDataTreeService(final DOMDataTreeService delegateTreeService) {
169             this.delegateTreeService = delegateTreeService;
170         }
171
172         void closeProducers() {
173             producersMap.values().forEach(NoopCloseDataProducer::closeDelegate);
174         }
175
176         @Override
177         public <T extends DOMDataTreeListener> ListenerRegistration<T> registerListener(final T listener,
178                 final Collection<DOMDataTreeIdentifier> subtrees, final boolean allowRxMerges,
179                 final Collection<DOMDataTreeProducer> producers) throws DOMDataTreeLoopException {
180             return delegateTreeService.registerListener(listener, subtrees, allowRxMerges, producers);
181         }
182
183         @Override
184         public ClassToInstanceMap<DOMDataTreeServiceExtension> getExtensions() {
185             return delegateTreeService.getExtensions();
186         }
187
188         @Override
189         public DOMDataTreeProducer createProducer(final Collection<DOMDataTreeIdentifier> subtrees) {
190             checkState(subtrees.size() == 1);
191             NoopCloseDataProducer producer = null;
192             for (final DOMDataTreeIdentifier treeId : subtrees) {
193                 producer = new NoopCloseDataProducer(delegateTreeService.createProducer(Collections.singleton(treeId)));
194                 producersMap.putIfAbsent(treeId.getDatastoreType(),
195                         producer);
196             }
197             return verifyNotNull(producer);
198         }
199
200         static class NoopCloseDataProducer implements DOMDataTreeProducer {
201
202             private final DOMDataTreeProducer delegateTreeProducer;
203
204             NoopCloseDataProducer(final DOMDataTreeProducer delegateTreeProducer) {
205                 this.delegateTreeProducer = delegateTreeProducer;
206             }
207
208             @Override
209             public DOMDataTreeCursorAwareTransaction createTransaction(final boolean isolated) {
210                 return delegateTreeProducer.createTransaction(isolated);
211             }
212
213             @Override
214             public DOMDataTreeProducer createProducer(final Collection<DOMDataTreeIdentifier> subtrees) {
215                 return delegateTreeProducer.createProducer(subtrees);
216             }
217
218             @Override
219             public void close() throws DOMDataTreeProducerException {
220                 // noop
221             }
222
223             public void closeDelegate() {
224                 try {
225                     delegateTreeProducer.close();
226                 } catch (final DOMDataTreeProducerException e) {
227                     throw new IllegalStateException("Trying to close DOMDataTreeProducer with open transaction", e);
228                 }
229             }
230         }
231     }
232 }