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